From 0b376cc42fb178639adf4a174a654cfd73a2b18d Mon Sep 17 00:00:00 2001 From: Tom Carpay Date: Mon, 27 Sep 2021 14:17:21 +0000 Subject: [PATCH] add routine to add EDE to ACL:refused at correct location --- daemon/worker.c | 110 +++++++++++++++++++++++++++++++++++++++---- services/localzone.c | 1 + sldns/pkthdr.h | 4 ++ util/data/msgparse.c | 2 +- util/data/msgparse.h | 8 ++++ 5 files changed, 114 insertions(+), 11 deletions(-) diff --git a/daemon/worker.c b/daemon/worker.c index 40e02fd5e..b43ee6a6b 100644 --- a/daemon/worker.c +++ b/daemon/worker.c @@ -1028,6 +1028,8 @@ deny_refuse(struct comm_point* c, enum acl_access acl, worker->stats.unwanted_queries++; return 0; } else if(acl == refuse) { + size_t opt_rr_mark; + log_addr(VERB_ALGO, "refused query from", &repinfo->addr, repinfo->addrlen); log_buf(VERB_ALGO, "refuse", c->buffer); @@ -1037,15 +1039,109 @@ deny_refuse(struct comm_point* c, enum acl_access acl, comm_point_drop_reply(repinfo); return 0; /* discard this */ } - sldns_buffer_set_limit(c->buffer, LDNS_HEADER_SIZE); - sldns_buffer_write_at(c->buffer, 4, - (uint8_t*)"\0\0\0\0\0\0\0\0", 8); + log_assert(LDNS_QDCOUNT(sldns_buffer_begin(c->buffer)) == 1); + + sldns_buffer_skip(c->buffer, LDNS_HEADER_SIZE); /* skip header */ + + if (!query_dname_len(c->buffer)) { + LDNS_RCODE_SET(sldns_buffer_begin(c->buffer), + LDNS_RCODE_FORMERR); + return 1; + } + if (sldns_buffer_remaining(c->buffer) < 2 * sizeof(uint16_t)) { + LDNS_RCODE_SET(sldns_buffer_begin(c->buffer), + LDNS_RCODE_FORMERR); + return 1; + } + sldns_buffer_skip(c->buffer, (ssize_t)sizeof(uint16_t)); /* skip qtype */ + sldns_buffer_skip(c->buffer, (ssize_t)sizeof(uint16_t)); /* skip qclass */ + LDNS_QR_SET(sldns_buffer_begin(c->buffer)); LDNS_RCODE_SET(sldns_buffer_begin(c->buffer), LDNS_RCODE_REFUSED); - sldns_buffer_set_position(c->buffer, LDNS_HEADER_SIZE); + + /* check edns section is present */ + if(LDNS_ARCOUNT(sldns_buffer_begin(c->buffer)) != 1) { + LDNS_ANCOUNT_SET(sldns_buffer_begin(c->buffer), 0); + LDNS_NSCOUNT_SET(sldns_buffer_begin(c->buffer), 0); + LDNS_ARCOUNT_SET(sldns_buffer_begin(c->buffer), 0); + sldns_buffer_flip(c->buffer); + log_buf(VERB_ALGO, "Refused without EDE", c->buffer); + return 1; + } + + /* The OPT RR to be returned should come directly after + * the query, so mark this spot. + */ + opt_rr_mark = sldns_buffer_position(c->buffer); + + if(LDNS_ANCOUNT(sldns_buffer_begin(c->buffer)) != 0 || + LDNS_NSCOUNT(sldns_buffer_begin(c->buffer)) != 0) { + if(!skip_pkt_rrs(c->buffer, ((int)LDNS_ANCOUNT(sldns_buffer_begin(c->buffer)))+ + ((int)LDNS_NSCOUNT(sldns_buffer_begin(c->buffer))))) { + comm_point_drop_reply(repinfo); + return 0; + } + } + /* Do we have a valid OPT RR here? If not return FORMERR */ + /* domain name must be the root of length 1. */ + if(pkt_dname_len(c->buffer) != 1) { + LDNS_RCODE_SET(sldns_buffer_begin(c->buffer), + LDNS_RCODE_FORMERR); + return 1; + } + if(sldns_buffer_read_u16(c->buffer) != LDNS_RR_TYPE_OPT) { + LDNS_RCODE_SET(sldns_buffer_begin(c->buffer), + LDNS_RCODE_FORMERR); + return 1; + } + /* Write OPT RR directly after the query, + * so without the (possibly skipped) Answer and NS RRs + */ + LDNS_ANCOUNT_SET(sldns_buffer_begin(c->buffer), 0); + LDNS_NSCOUNT_SET(sldns_buffer_begin(c->buffer), 0); + sldns_buffer_set_position(c->buffer, opt_rr_mark); + + /* check if OPT record can be written + * 17 == root label (1) + RR type (2) + UDP Size (2) + * + Fields (4) + rdata len (2) + EDE Option code (2) + * + EDE Option length (2) + EDE info-code (2) + */ + if (sldns_buffer_available(c->buffer, 17) == 0) { + LDNS_ARCOUNT_SET(sldns_buffer_begin(c->buffer), 0); + sldns_buffer_flip(c->buffer); + log_buf(VERB_ALGO, "Refused without EDE", c->buffer); + return 1; + } + + LDNS_ARCOUNT_SET(sldns_buffer_begin(c->buffer), 1); + + /* root label */ + sldns_buffer_write_u8(c->buffer, 0); + sldns_buffer_write_u16(c->buffer, LDNS_RR_TYPE_OPT); + sldns_buffer_write_u16(c->buffer, EDNS_ADVERTISED_SIZE); + + /* write OPT Record TTL Field */ + sldns_buffer_write_u32(c->buffer, 0); + + /* write rdata len: EDE option + length + info-code */ + sldns_buffer_write_u16(c->buffer, 6); + + /* write OPTIONS; add EDE option code */ + sldns_buffer_write_u16(c->buffer, LDNS_EDNS_EDE); + + /* write single EDE option length (for just 1 info-code) */ + sldns_buffer_write_u16(c->buffer, 2); + + /* write single EDE info-code */ + sldns_buffer_write_u16(c->buffer, LDNS_EDE_PROHIBITED); + sldns_buffer_flip(c->buffer); + + log_buf(VERB_ALGO, "EDE added, buffer:", c->buffer); + return 1; + } return -1; @@ -1155,12 +1251,6 @@ worker_handle_request(struct comm_point* c, void* arg, int error, acl = acl_get_control(acladdr); if((ret=deny_refuse_all(c, acl, worker, repinfo)) != -1) { - /* parse packet to check for EDNS. Add EDE blocked if possible */ - sldns_buffer_rewind(c->buffer); - if (msgparse_check_edns_in_packet(c->buffer)) - EDNS_OPT_APPEND_EDE(&edns, worker->scratchpad, - LDNS_EDE_BLOCKED, ""); - if(ret == 1) goto send_reply; return ret; diff --git a/services/localzone.c b/services/localzone.c index ce1d654fc..d296d10de 100644 --- a/services/localzone.c +++ b/services/localzone.c @@ -1604,6 +1604,7 @@ local_data_answer(struct local_zone* z, struct module_env* env, return local_encode(qinfo, env, edns, repinfo, buf, temp, &r, 1, LDNS_RCODE_NOERROR); } + // @TODO add EDE? return local_encode(qinfo, env, edns, repinfo, buf, temp, lr->rrset, 1, LDNS_RCODE_NOERROR); } diff --git a/sldns/pkthdr.h b/sldns/pkthdr.h index de9952ea7..c32e7d285 100644 --- a/sldns/pkthdr.h +++ b/sldns/pkthdr.h @@ -97,18 +97,22 @@ extern "C" { #define QDCOUNT(wirebuf) (ntohs(*(uint16_t *)(wirebuf+QDCOUNT_OFF))) */ #define LDNS_QDCOUNT(wirebuf) (sldns_read_uint16(wirebuf+LDNS_QDCOUNT_OFF)) +#define LDNS_QDCOUNT_SET(wirebuf, i) (sldns_write_uint16(wirebuf+LDNS_QDCOUNT_OFF, i)) /* Counter of the answer section */ #define LDNS_ANCOUNT_OFF 6 #define LDNS_ANCOUNT(wirebuf) (sldns_read_uint16(wirebuf+LDNS_ANCOUNT_OFF)) +#define LDNS_ANCOUNT_SET(wirebuf, i) (sldns_write_uint16(wirebuf+LDNS_ANCOUNT_OFF, i)) /* Counter of the authority section */ #define LDNS_NSCOUNT_OFF 8 #define LDNS_NSCOUNT(wirebuf) (sldns_read_uint16(wirebuf+LDNS_NSCOUNT_OFF)) +#define LDNS_NSCOUNT_SET(wirebuf, i) (sldns_write_uint16(wirebuf+LDNS_NSCOUNT_OFF, i)) /* Counter of the additional section */ #define LDNS_ARCOUNT_OFF 10 #define LDNS_ARCOUNT(wirebuf) (sldns_read_uint16(wirebuf+LDNS_ARCOUNT_OFF)) +#define LDNS_ARCOUNT_SET(wirebuf, i) (sldns_write_uint16(wirebuf+LDNS_ARCOUNT_OFF, i)) /** * The sections of a packet diff --git a/util/data/msgparse.c b/util/data/msgparse.c index db3e229e1..1435e3798 100644 --- a/util/data/msgparse.c +++ b/util/data/msgparse.c @@ -1048,7 +1048,7 @@ skip_pkt_rr(sldns_buffer* pkt) } /** skip RRs from packet */ -static int +int skip_pkt_rrs(sldns_buffer* pkt, int num) { int i; diff --git a/util/data/msgparse.h b/util/data/msgparse.h index fca323728..fe64a7dec 100644 --- a/util/data/msgparse.h +++ b/util/data/msgparse.h @@ -284,6 +284,14 @@ int parse_packet(struct sldns_buffer* pkt, struct msg_parse* msg, int parse_extract_edns(struct msg_parse* msg, struct edns_data* edns, struct regional* region); +/** + * Skip RRs from packet + * @param pkt:the packet. position at start must be right after the query + * section. At end, right after EDNS data or no movement if failed. + * @param num: Limit of the number of records we want to parse. + * @return: 0 on success, 1 on failure*/ +int skip_pkt_rrs(struct sldns_buffer* pkt, int num); + /** * If EDNS data follows a query section, extract it and initialize edns struct. * @param pkt: the packet. position at start must be right after the query -- 2.47.2