Rewrite query parsing and answer formating using libunbound provided
functions.
With this we can filter out DNSSEC RRsets if the client did not ask
for them.
We will also be able to send truncated answers to indicate to the
client to switch to tcp (disabled for now since we have no tcp support
yet).

Tests, OKs?

diff --git frontend.c frontend.c
index 9f91396c4c3..8cb73dffa57 100644
--- frontend.c
+++ frontend.c
@@ -48,7 +48,11 @@
 #include "libunbound/sldns/sbuffer.h"
 #include "libunbound/sldns/str2wire.h"
 #include "libunbound/sldns/wire2str.h"
+#include "libunbound/util/alloc.h"
+#include "libunbound/util/net_help.h"
+#include "libunbound/util/regional.h"
 #include "libunbound/util/data/dname.h"
+#include "libunbound/util/data/msgencode.h"
 #include "libunbound/util/data/msgparse.h"
 #include "libunbound/util/data/msgreply.h"
 
@@ -81,14 +85,13 @@ struct pending_query {
        TAILQ_ENTRY(pending_query)       entry;
        struct sockaddr_storage          from;
        struct sldns_buffer             *qbuf;
-       ssize_t                          len;
+       struct sldns_buffer             *abuf;
+       struct regional                 *region;
+       struct query_info                qinfo;
+       struct msg_parse                *qmsg;
+       struct edns_data                 edns;
        uint64_t                         imsg_id;
        int                              fd;
-       int                              bogus;
-       int                              rcode_override;
-       int                              answer_len;
-       int                              received;
-       uint8_t                         *answer;
 };
 
 TAILQ_HEAD(, pending_query)     pending_queries;
@@ -102,8 +105,12 @@ __dead void                 frontend_shutdown(void);
 void                    frontend_sig_handler(int, short, void *);
 void                    frontend_startup(void);
 void                    udp_receive(int, short, void *);
+void                    handle_query(struct pending_query *);
+void                    free_pending_query(struct pending_query *);
 int                     check_query(sldns_buffer*);
+void                    noerror_answer(struct pending_query *);
 void                    chaos_answer(struct pending_query *);
+void                    error_answer(struct pending_query *, int rcode);
 void                    send_answer(struct pending_query *);
 void                    route_receive(int, short, void *);
 void                    handle_route_message(struct rt_msghdr *,
@@ -471,37 +478,38 @@ frontend_dispatch_resolver(int fd, short event, void 
*bula)
                        }
 
                        if (answer_header->srvfail) {
-                               free(pq->answer);
-                               pq->answer_len = 0;
-                               pq->answer = NULL;
-                               pq->rcode_override = LDNS_RCODE_SERVFAIL;
+                               error_answer(pq, LDNS_RCODE_SERVFAIL);
                                send_answer(pq);
                                break;
                        }
 
-                       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 (answer_header->bogus && !(pq->qmsg->flags &
+                           BIT_CD)) {
+                               error_answer(pq, LDNS_RCODE_SERVFAIL);
+                               send_answer(pq);
+                               break;
+                       }
+
+                       if (sldns_buffer_position(pq->abuf) == 0 &&
+                           !sldns_buffer_set_capacity(pq->abuf,
+                           answer_header->answer_len)) {
+                               error_answer(pq, LDNS_RCODE_SERVFAIL);
+                               send_answer(pq);
+                               break;
                        }
 
-                       if (pq->received + data_len > pq->answer_len)
+                       if (sldns_buffer_position(pq->abuf) + data_len >
+                           sldns_buffer_capacity(pq->abuf))
                                fatalx("%s: IMSG_ANSWER answer too big: %d",
                                    __func__, data_len);
+                       sldns_buffer_write(pq->abuf, data, data_len);
 
-                       memcpy(pq->answer + pq->received, data, data_len);
-                       pq->received += data_len;
-
-                       if (pq->received == pq->answer_len)
+                       if (sldns_buffer_position(pq->abuf) ==
+                           sldns_buffer_capacity(pq->abuf)) {
+                               sldns_buffer_flip(pq->abuf);
+                               noerror_answer(pq);
                                send_answer(pq);
+                       }
                        break;
                }
                case IMSG_CTL_RESOLVER_INFO:
@@ -558,22 +566,24 @@ frontend_startup(void)
        frontend_imsg_compose_main(IMSG_STARTUP_DONE, 0, NULL, 0);
 }
 
+void
+free_pending_query(struct pending_query *pq)
+{
+       if (!pq)
+               return;
+
+       regional_destroy(pq->region);
+       sldns_buffer_free(pq->qbuf);
+       sldns_buffer_free(pq->abuf);
+       free(pq);
+}
+
 void
 udp_receive(int fd, short events, void *arg)
 {
        struct udp_ev           *udpev = (struct udp_ev *)arg;
-       struct pending_query    *pq;
-       struct query_imsg        query_imsg;
-       struct query_info        qinfo;
-       struct bl_node           find;
-       ssize_t                  len, dname_len;
-       int                      ret;
-       char                    *str;
-       char                     dname[LDNS_MAX_DOMAINLEN + 1];
-       char                     qclass_buf[16];
-       char                     qtype_buf[16];
-
-       memset(&qinfo, 0, sizeof(qinfo));
+       struct pending_query    *pq = NULL;
+       ssize_t                  len;
 
        if ((len = recvmsg(fd, &udpev->rcvmhdr, 0)) == -1) {
                log_warn("recvmsg");
@@ -585,8 +595,6 @@ udp_receive(int fd, short events, void *arg)
                return;
        }
 
-       pq->rcode_override = LDNS_RCODE_NOERROR;
-       pq->len = len;
        pq->from = udpev->from;
        pq->fd = fd;
 
@@ -594,93 +602,132 @@ udp_receive(int fd, short events, void *arg)
                arc4random_buf(&pq->imsg_id, sizeof(pq->imsg_id));
        } while(find_pending_query(pq->imsg_id) != NULL);
 
-       if ((pq->qbuf = sldns_buffer_new(len)) == NULL) {
-               log_warnx("sldns_buffer_new");
-               goto drop;
+       pq->qbuf = sldns_buffer_new(len);
+       pq->abuf = sldns_buffer_new(len); /* make sure we can send errors */
+       pq->region = regional_create();
+       pq->qmsg = regional_alloc(pq->region, sizeof(*pq->qmsg));
+
+       if (!pq->qbuf || !pq->abuf || !pq->region || !pq->qmsg) {
+               log_warnx("out of memory");
+               free_pending_query(pq);
+               return;
        }
-       sldns_buffer_clear(pq->qbuf);
+
+       memset(pq->qmsg, 0, sizeof(*pq->qmsg));
        sldns_buffer_write(pq->qbuf, udpev->query, len);
        sldns_buffer_flip(pq->qbuf);
+       handle_query(pq);
+}
+
+void
+handle_query(struct pending_query *pq)
+{
+       struct query_imsg        query_imsg;
+       struct bl_node           find;
+       int                      rcode;
+       char                    *str;
+       char                     dname[LDNS_MAX_DOMAINLEN + 1];
+       char                     qclass_buf[16];
+       char                     qtype_buf[16];
 
        if (log_getverbose() & OPT_VERBOSE2 && (str =
-           sldns_wire2str_pkt(udpev->query, len)) != NULL) {
+           sldns_wire2str_pkt(sldns_buffer_begin(pq->qbuf),
+           sldns_buffer_limit(pq->qbuf))) != NULL) {
                log_debug("from: %s\n%s", ip_port((struct sockaddr *)
-                   &udpev->from), str);
+                   &pq->from), str);
                free(str);
        }
 
-       if ((ret = check_query(pq->qbuf)) != LDNS_RCODE_NOERROR) {
-               if (ret == -1)
-                       goto drop;
-               else
-                       pq->rcode_override = ret;
+       if (!query_info_parse(&pq->qinfo, pq->qbuf)) {
+               log_warnx("query_info_parse failed");
+               goto drop;
+       }
+
+       sldns_buffer_rewind(pq->qbuf);
+
+       if (parse_packet(pq->qbuf, pq->qmsg, pq->region) !=
+           LDNS_RCODE_NOERROR) {
+               log_warnx("parse_packet failed");
+               goto drop;
+       }
+
+       rcode = check_query(pq->qbuf);
+       switch (rcode) {
+       case LDNS_RCODE_NOERROR:
+               break;
+       case -1:
+               goto drop;
+       default:
+               error_answer(pq, rcode);
                goto send_answer;
        }
 
-       if (!query_info_parse(&qinfo, pq->qbuf)) {
-               pq->rcode_override = LDNS_RCODE_FORMERR;
+       rcode = parse_extract_edns(pq->qmsg, &pq->edns, pq->region);
+       if (rcode != LDNS_RCODE_NOERROR) {
+               error_answer(pq, rcode);
                goto send_answer;
        }
 
-       if ((dname_len = dname_valid(qinfo.qname, qinfo.qname_len)) == 0) {
-               pq->rcode_override = LDNS_RCODE_FORMERR;
+       if (!dname_valid(pq->qinfo.qname, pq->qinfo.qname_len)) {
+               error_answer(pq, LDNS_RCODE_FORMERR);
                goto send_answer;
        }
-       dname_str(qinfo.qname, dname);
+       dname_str(pq->qinfo.qname, dname);
 
-       sldns_wire2str_class_buf(qinfo.qclass, qclass_buf, sizeof(qclass_buf));
-       sldns_wire2str_type_buf(qinfo.qtype, qtype_buf, sizeof(qtype_buf));
-       log_debug("%s: %s %s %s ?", ip_port((struct sockaddr *)&udpev->from),
+       sldns_wire2str_class_buf(pq->qinfo.qclass, qclass_buf,
+           sizeof(qclass_buf));
+       sldns_wire2str_type_buf(pq->qinfo.qtype, qtype_buf, sizeof(qtype_buf));
+       log_debug("%s: %s %s %s ?", ip_port((struct sockaddr *)&pq->from),
            dname, qclass_buf, qtype_buf);
 
        find.domain = dname;
        if (RB_FIND(bl_tree, &bl_head, &find) != NULL) {
                if (frontend_conf->blocklist_log)
                        log_info("blocking %s", dname);
-               pq->rcode_override = LDNS_RCODE_REFUSED;
+               error_answer(pq, LDNS_RCODE_REFUSED);
                goto send_answer;
        }
 
-       if (qinfo.qtype == LDNS_RR_TYPE_AXFR || qinfo.qtype ==
+       if (pq->qinfo.qtype == LDNS_RR_TYPE_AXFR || pq->qinfo.qtype ==
            LDNS_RR_TYPE_IXFR) {
-               pq->rcode_override = LDNS_RCODE_REFUSED;
+               error_answer(pq, LDNS_RCODE_REFUSED);
                goto send_answer;
        }
 
-       if(qinfo.qtype == LDNS_RR_TYPE_OPT ||
-           qinfo.qtype == LDNS_RR_TYPE_TSIG ||
-           qinfo.qtype == LDNS_RR_TYPE_TKEY ||
-           qinfo.qtype == LDNS_RR_TYPE_MAILA ||
-           qinfo.qtype == LDNS_RR_TYPE_MAILB ||
-           (qinfo.qtype >= 128 && qinfo.qtype <= 248)) {
-               pq->rcode_override = LDNS_RCODE_FORMERR;
+       if(pq->qinfo.qtype == LDNS_RR_TYPE_OPT ||
+           pq->qinfo.qtype == LDNS_RR_TYPE_TSIG ||
+           pq->qinfo.qtype == LDNS_RR_TYPE_TKEY ||
+           pq->qinfo.qtype == LDNS_RR_TYPE_MAILA ||
+           pq->qinfo.qtype == LDNS_RR_TYPE_MAILB ||
+           (pq->qinfo.qtype >= 128 && pq->qinfo.qtype <= 248)) {
+               error_answer(pq, LDNS_RCODE_FORMERR);
                goto send_answer;
        }
 
-       if (qinfo.qclass == LDNS_RR_CLASS_CH) {
+       if (pq->qinfo.qclass == LDNS_RR_CLASS_CH) {
                if (strcasecmp(dname, "version.server.") == 0 ||
                    strcasecmp(dname, "version.bind.") == 0) {
                        chaos_answer(pq);
                } else
-                       pq->rcode_override = LDNS_RCODE_REFUSED;
+                       error_answer(pq, LDNS_RCODE_REFUSED);
                goto send_answer;
        }
 
        if (strlcpy(query_imsg.qname, dname, sizeof(query_imsg.qname)) >=
            sizeof(query_imsg.qname)) {
                log_warnx("qname too long");
-               pq->rcode_override = LDNS_RCODE_FORMERR;
+               error_answer(pq, LDNS_RCODE_FORMERR);
                goto send_answer;
        }
        query_imsg.id = pq->imsg_id;
-       query_imsg.t = qinfo.qtype;
-       query_imsg.c = qinfo.qclass;
+       query_imsg.t = pq->qinfo.qtype;
+       query_imsg.c = pq->qinfo.qclass;
 
        if (frontend_imsg_compose_resolver(IMSG_QUERY, 0, &query_imsg,
            sizeof(query_imsg)) != -1)
                TAILQ_INSERT_TAIL(&pending_queries, pq, entry);
        else {
-               pq->rcode_override = LDNS_RCODE_SERVFAIL;
+               error_answer(pq, LDNS_RCODE_SERVFAIL);
                goto send_answer;
        }
        return;
@@ -688,58 +735,93 @@ udp_receive(int fd, short events, void *arg)
  send_answer:
        TAILQ_INSERT_TAIL(&pending_queries, pq, entry);
        send_answer(pq);
-       pq = NULL;
+       return;
+
  drop:
-       if (pq != NULL)
-               sldns_buffer_free(pq->qbuf);
-       free(pq);
+       free_pending_query(pq);
+}
+
+void
+noerror_answer(struct pending_query *pq)
+{
+       struct query_info        qinfo;
+       struct reply_info       *rinfo = NULL;
+       struct alloc_cache       alloc;
+       struct edns_data         edns;
+
+       alloc_init(&alloc, NULL, 0);
+       memset(&qinfo, 0, sizeof(qinfo));
+       /* read past query section */
+       if (!query_info_parse(&qinfo, pq->abuf))
+               goto srvfail;
+
+       if (reply_info_parse(pq->abuf, &alloc, &qinfo, &rinfo, pq->region,
+           &edns) != 0)
+               goto srvfail;
+
+       sldns_buffer_clear(pq->abuf);
+       if (reply_info_encode(&pq->qinfo, rinfo, pq->qmsg->id, rinfo->flags,
+           pq->abuf, 0, pq->region, UINT16_MAX, /* XXX pq->edns.udp_size, */
+           pq->edns.bits & EDNS_DO, 1) == 0)
+               goto srvfail;
+
+       reply_info_parsedelete(rinfo, &alloc);
+       return;
+
+ srvfail:
+       reply_info_parsedelete(rinfo, &alloc);
+       error_answer(pq, LDNS_RCODE_SERVFAIL);
 }
 
 void
 chaos_answer(struct pending_query *pq)
 {
-       struct sldns_buffer      buf, *pkt = &buf;
-       size_t                   size, len;
-       char                    *name = "unwind";
+       size_t           len;
+       const char      *name = "unwind";
 
        len = strlen(name);
-       size = sldns_buffer_capacity(pq->qbuf) + COMPRESSED_RR_SIZE + 1 + len;
-
-       if (pq->answer != 0)
-               fatal("chaos_answer");
-       if ((pq->answer = calloc(1, size)) == NULL)
+       if (!sldns_buffer_set_capacity(pq->abuf,
+           sldns_buffer_capacity(pq->qbuf) + COMPRESSED_RR_SIZE + 1 + len)) {
+               error_answer(pq, LDNS_RCODE_SERVFAIL);
                return;
-       pq->answer_len = size;
-       memcpy(pq->answer, sldns_buffer_begin(pq->qbuf),
-           sldns_buffer_capacity(pq->qbuf));
-       sldns_buffer_init_frm_data(pkt, pq->answer, size);
+       }
+
+       sldns_buffer_copy(pq->abuf, pq->qbuf);
 
-       sldns_buffer_clear(pkt);
+       sldns_buffer_clear(pq->abuf);
 
-       sldns_buffer_skip(pkt, sizeof(uint16_t));       /* skip id */
-       sldns_buffer_write_u16(pkt, 0);                 /* clear flags */
-       LDNS_QR_SET(sldns_buffer_begin(pkt));
-       LDNS_RA_SET(sldns_buffer_begin(pkt));
+       sldns_buffer_skip(pq->abuf, sizeof(uint16_t));  /* skip id */
+       sldns_buffer_write_u16(pq->abuf, 0);            /* clear flags */
+       LDNS_QR_SET(sldns_buffer_begin(pq->abuf));
+       LDNS_RA_SET(sldns_buffer_begin(pq->abuf));
        if (LDNS_RD_WIRE(sldns_buffer_begin(pq->qbuf)))
-               LDNS_RD_SET(sldns_buffer_begin(pkt));
+               LDNS_RD_SET(sldns_buffer_begin(pq->abuf));
        if (LDNS_CD_WIRE(sldns_buffer_begin(pq->qbuf)))
-               LDNS_CD_SET(sldns_buffer_begin(pkt));
-       LDNS_RCODE_SET(sldns_buffer_begin(pkt), LDNS_RCODE_NOERROR);
-       sldns_buffer_write_u16(pkt, 1);                 /* qdcount */
-       sldns_buffer_write_u16(pkt, 1);                 /* ancount */
-       sldns_buffer_write_u16(pkt, 0);                 /* nscount */
-       sldns_buffer_write_u16(pkt, 0);                 /* arcount */
-       (void)query_dname_len(pkt);                     /* skip qname */
-       sldns_buffer_skip(pkt, sizeof(uint16_t));       /* skip qtype */
-       sldns_buffer_skip(pkt, sizeof(uint16_t));       /* skip qclass */
-
-       sldns_buffer_write_u16(pkt, 0xc00c);            /* ptr to query */
-       sldns_buffer_write_u16(pkt, LDNS_RR_TYPE_TXT);
-       sldns_buffer_write_u16(pkt, LDNS_RR_CLASS_CH);
-       sldns_buffer_write_u32(pkt, 0);                 /* TTL */
-       sldns_buffer_write_u16(pkt, 1 + len);           /* RDLENGTH */
-       sldns_buffer_write_u8(pkt, len);                /* length octed */
-       sldns_buffer_write(pkt, name, len);
+               LDNS_CD_SET(sldns_buffer_begin(pq->abuf));
+       LDNS_RCODE_SET(sldns_buffer_begin(pq->abuf), LDNS_RCODE_NOERROR);
+       sldns_buffer_write_u16(pq->abuf, 1);            /* qdcount */
+       sldns_buffer_write_u16(pq->abuf, 1);            /* ancount */
+       sldns_buffer_write_u16(pq->abuf, 0);            /* nscount */
+       sldns_buffer_write_u16(pq->abuf, 0);            /* arcount */
+       (void)query_dname_len(pq->abuf);                /* skip qname */
+       sldns_buffer_skip(pq->abuf, sizeof(uint16_t));  /* skip qtype */
+       sldns_buffer_skip(pq->abuf, sizeof(uint16_t));  /* skip qclass */
+
+       sldns_buffer_write_u16(pq->abuf, 0xc00c);       /* ptr to query */
+       sldns_buffer_write_u16(pq->abuf, LDNS_RR_TYPE_TXT);
+       sldns_buffer_write_u16(pq->abuf, LDNS_RR_CLASS_CH);
+       sldns_buffer_write_u32(pq->abuf, 0);            /* TTL */
+       sldns_buffer_write_u16(pq->abuf, 1 + len);      /* RDLENGTH */
+       sldns_buffer_write_u8(pq->abuf, len);           /* length octed */
+       sldns_buffer_write(pq->abuf, name, len);
+}
+
+void
+error_answer(struct pending_query *pq, int rcode)
+{
+       sldns_buffer_clear(pq->abuf);
+       error_encode(pq->abuf, rcode, &pq->qinfo, pq->qmsg->id,
+           pq->qmsg->flags, pq->edns.edns_present ? &pq->edns : NULL);
 }
 
 int
@@ -786,59 +868,24 @@ check_query(sldns_buffer* pkt)
 void
 send_answer(struct pending_query *pq)
 {
-       ssize_t  len;
        char    *str;
-       uint8_t *answer;
-
-       answer = pq->answer;
-       len = pq->answer_len;
-
-       if (answer == NULL) {
-               answer = sldns_buffer_begin(pq->qbuf);
-               len = pq->len;
-
-               LDNS_QR_SET(answer);
-               LDNS_RA_SET(answer);
-               if (pq->rcode_override != LDNS_RCODE_NOERROR)
-                       LDNS_RCODE_SET(answer, pq->rcode_override);
-               else
-                       LDNS_RCODE_SET(answer, LDNS_RCODE_SERVFAIL);
-       } else {
-               if (pq->bogus) {
-                       if(LDNS_CD_WIRE(sldns_buffer_begin(pq->qbuf))) {
-                               LDNS_ID_SET(answer, LDNS_ID_WIRE(
-                                   sldns_buffer_begin(pq->qbuf)));
-                               LDNS_CD_SET(answer);
-                       } else {
-                               answer = sldns_buffer_begin(pq->qbuf);
-                               len = pq->len;
-
-                               LDNS_QR_SET(answer);
-                               LDNS_RA_SET(answer);
-                               LDNS_RCODE_SET(answer, LDNS_RCODE_SERVFAIL);
-                       }
-               } else {
-                       LDNS_ID_SET(answer, LDNS_ID_WIRE(sldns_buffer_begin(
-                           pq->qbuf)));
-               }
-       }
 
        if (log_getverbose() & OPT_VERBOSE2 && (str =
-           sldns_wire2str_pkt(answer, len)) != NULL) {
-               log_debug("to: %s\n%s",
-                   ip_port((struct sockaddr *)&pq->from),str);
+           sldns_wire2str_pkt(sldns_buffer_begin(pq->abuf),
+           sldns_buffer_limit(pq->abuf))) != NULL) {
+               log_debug("from: %s\n%s", ip_port((struct sockaddr *)
+                   &pq->from), str);
                free(str);
-               log_debug("pending query count: %d", pending_query_cnt());
        }
 
-       if(sendto(pq->fd, answer, len, 0, (struct sockaddr *)&pq->from,
+       if(sendto(pq->fd, sldns_buffer_begin(pq->abuf),
+           sldns_buffer_limit(pq->abuf), 0, (struct sockaddr *)&pq->from,
            pq->from.ss_len) == -1)
                log_warn("sendto");
 
        TAILQ_REMOVE(&pending_queries, pq, entry);
-       sldns_buffer_free(pq->qbuf);
-       free(pq->answer);
-       free(pq);
+       free_pending_query(pq);
+
 }
 
 char*


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

Reply via email to