On Fri, Jan 08, 2021 at 06:37:35PM +0100, Florian Obser wrote:
> 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?
>
It's better to leak less memory...
diff --git frontend.c frontend.c
index 9f91396c4c3..60dafd81a79 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,95 @@ 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);
+ alloc_clear(&alloc);
+ return;
+
+ srvfail:
+ reply_info_parsedelete(rinfo, &alloc);
+ alloc_clear(&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 +870,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.