From: Willem Toorop Date: Sat, 15 Mar 2025 15:22:29 +0000 (+0100) Subject: Store responses received on listen interface in cache X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=d9d6dd31dc22be32ceeb0f816c4f6fc292a788b0;p=thirdparty%2Funbound.git Store responses received on listen interface in cache --- diff --git a/daemon/worker.c b/daemon/worker.c index ba5118a74..4f8d33caf 100644 --- a/daemon/worker.c +++ b/daemon/worker.c @@ -272,6 +272,11 @@ worker_handle_service_reply(struct comm_point* c, void* arg, int error, return 0; } +#define REQUEST_OK 0 +#define DROP_REQUEST -1 +#define RESPONSE_MESSAGE -2 + + /** ratelimit error replies * @param worker: the worker struct with ratelimit counter * @param err: error code that would be wanted. @@ -283,7 +288,7 @@ worker_err_ratelimit(struct worker* worker, int err) if(worker->err_limit_time == *worker->env.now) { /* see if limit is exceeded for this second */ if(worker->err_limit_count++ > ERROR_RATELIMIT) - return -1; + return DROP_REQUEST; } else { /* new second, new limits */ worker->err_limit_time = *worker->env.now; @@ -296,6 +301,9 @@ worker_err_ratelimit(struct worker* worker, int err) * Structure holding the result of the worker_check_request function. * Based on configuration it could be called up to four times; ideally should * be called once. + * When value is a positive number, it countains the error to return. + * Otherwise DROP_REQUEST (-1) is returned, or RESPONSE_MESSAGE (-2) in + * case the qr bit was set. Value is set to REQUEST_OK (0) if all is good. */ struct check_request_result { int checked; @@ -314,18 +322,18 @@ worker_check_request(sldns_buffer* pkt, struct worker* worker, out->checked = 1; if(sldns_buffer_limit(pkt) < LDNS_HEADER_SIZE) { verbose(VERB_QUERY, "request too short, discarded"); - out->value = -1; + out->value = DROP_REQUEST; return; } if(sldns_buffer_limit(pkt) > NORMAL_UDP_SIZE && worker->daemon->cfg->harden_large_queries) { verbose(VERB_QUERY, "request too large, discarded"); - out->value = -1; + out->value = DROP_REQUEST; return; } if(LDNS_QR_WIRE(sldns_buffer_begin(pkt))) { - verbose(VERB_QUERY, "request has QR bit on, discarded"); - out->value = -1; + /* verbose(VERB_QUERY, "request has QR bit on, discarded"); */ + out->value = RESPONSE_MESSAGE; return; } if(LDNS_TC_WIRE(sldns_buffer_begin(pkt))) { @@ -367,10 +375,39 @@ worker_check_request(sldns_buffer* pkt, struct worker* worker, out->value = worker_err_ratelimit(worker, LDNS_RCODE_FORMERR); return; } - out->value = 0; + out->value = REQUEST_OK; return; } +/** check response sanity. + * @param pkt: the wire packet to examine for sanity. + * @param worker: parameters for checking. + * @param out: 1 on success, otherwise 0. +*/ +static int +worker_check_response(sldns_buffer* pkt, struct worker* worker) +{ + if(LDNS_TC_WIRE(sldns_buffer_begin(pkt))) { + LDNS_TC_CLR(sldns_buffer_begin(pkt)); + verbose(VERB_QUERY, "response bad, has TC bit on"); + return 0; + } + if(LDNS_OPCODE_WIRE(sldns_buffer_begin(pkt)) != LDNS_PACKET_QUERY) { + verbose(VERB_QUERY, "not a query response"); + return 0; + } + if(LDNS_QDCOUNT(sldns_buffer_begin(pkt)) != 1) { + verbose(VERB_QUERY, "request wrong nr qd=%d", + LDNS_QDCOUNT(sldns_buffer_begin(pkt))); + return 0; + } + if(LDNS_ANCOUNT(sldns_buffer_begin(pkt)) == 0) { + verbose(VERB_QUERY, "response must be an answer message"); + return 0; + } + return 1; +} + void worker_handle_control_cmd(struct tube* ATTR_UNUSED(tube), uint8_t* msg, size_t len, int error, void* arg) @@ -1138,8 +1175,8 @@ deny_refuse(struct comm_point* c, enum acl_access acl, if(worker->stats.extended) worker->stats.unwanted_queries++; worker_check_request(c->buffer, worker, check_result); - if(check_result->value != 0) { - if(check_result->value != -1) { + if(check_result->value != REQUEST_OK) { + if(check_result->value > 0) { LDNS_QR_SET(sldns_buffer_begin(c->buffer)); LDNS_RCODE_SET(sldns_buffer_begin(c->buffer), check_result->value); @@ -1416,7 +1453,7 @@ worker_handle_request(struct comm_point* c, void* arg, int error, char buf[LDNS_MAX_DOMAINLEN]; /* Check if this is unencrypted and asking for certs */ worker_check_request(c->buffer, worker, &check_result); - if(check_result.value != 0) { + if(check_result.value != REQUEST_OK) { verbose(VERB_ALGO, "dnscrypt: worker check request: bad query."); log_addr(VERB_CLIENT,"from",&repinfo->client_addr, @@ -1479,10 +1516,31 @@ worker_handle_request(struct comm_point* c, void* arg, int error, } worker_check_request(c->buffer, worker, &check_result); - if(check_result.value != 0) { + if (check_result.value == RESPONSE_MESSAGE) { + struct reply_info *rep = NULL; + int r; + + if (!worker_check_response(c->buffer, worker)) { + log_err("Not a suitable response to cache"); + comm_point_drop_reply(repinfo); + return 0; + } + if((r = reply_info_parse(c->buffer, worker->env.alloc, &qinfo, + &rep, worker->scratchpad, &edns))) { + log_err("bad response. Will not cache it (%d)", r); + comm_point_drop_reply(repinfo); + return 0; + } + dns_cache_store(&worker->env, &qinfo, rep, 0 /* is_referral */, + 0 /* leeway */, 0 /* pside */, + NULL /* region */, 0 /* flags */, + *worker->env.now, 0 /* is_valrec */); + comm_point_drop_reply(repinfo); + return 0; + } else if(check_result.value != REQUEST_OK) { verbose(VERB_ALGO, "worker check request: bad query."); log_addr(VERB_CLIENT,"from",&repinfo->client_addr, repinfo->client_addrlen); - if(check_result.value != -1) { + if(check_result.value > REQUEST_OK) { LDNS_QR_SET(sldns_buffer_begin(c->buffer)); LDNS_RCODE_SET(sldns_buffer_begin(c->buffer), check_result.value);