aboutsummaryrefslogtreecommitdiff
path: root/daemon/worker.c
diff options
context:
space:
mode:
Diffstat (limited to 'daemon/worker.c')
-rw-r--r--daemon/worker.c42
1 files changed, 34 insertions, 8 deletions
diff --git a/daemon/worker.c b/daemon/worker.c
index 5edc21dd46e7..79aec4d3a4b2 100644
--- a/daemon/worker.c
+++ b/daemon/worker.c
@@ -71,7 +71,7 @@
#include "validator/val_anchor.h"
#include "libunbound/context.h"
#include "libunbound/libworker.h"
-#include "ldns/sbuffer.h"
+#include "sldns/sbuffer.h"
#ifdef HAVE_SYS_TYPES_H
# include <sys/types.h>
@@ -86,6 +86,8 @@
/** Size of an UDP datagram */
#define NORMAL_UDP_SIZE 512 /* bytes */
+/** ratelimit for error responses */
+#define ERROR_RATELIMIT 100 /* qps */
/**
* seconds to add to prefetch leeway. This is a TTL that expires old rrsets
@@ -291,6 +293,26 @@ worker_handle_service_reply(struct comm_point* c, void* arg, int error,
return 0;
}
+/** ratelimit error replies
+ * @param worker: the worker struct with ratelimit counter
+ * @param err: error code that would be wanted.
+ * @return value of err if okay, or -1 if it should be discarded instead.
+ */
+static int
+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;
+ } else {
+ /* new second, new limits */
+ worker->err_limit_time = *worker->env.now;
+ worker->err_limit_count = 1;
+ }
+ return err;
+}
+
/** check request sanity.
* @param pkt: the wire packet to examine for sanity.
* @param worker: parameters for checking.
@@ -315,32 +337,32 @@ worker_check_request(sldns_buffer* pkt, struct worker* worker)
if(LDNS_TC_WIRE(sldns_buffer_begin(pkt))) {
LDNS_TC_CLR(sldns_buffer_begin(pkt));
verbose(VERB_QUERY, "request bad, has TC bit on");
- return LDNS_RCODE_FORMERR;
+ return worker_err_ratelimit(worker, LDNS_RCODE_FORMERR);
}
if(LDNS_OPCODE_WIRE(sldns_buffer_begin(pkt)) != LDNS_PACKET_QUERY) {
verbose(VERB_QUERY, "request unknown opcode %d",
LDNS_OPCODE_WIRE(sldns_buffer_begin(pkt)));
- return LDNS_RCODE_NOTIMPL;
+ return worker_err_ratelimit(worker, LDNS_RCODE_NOTIMPL);
}
if(LDNS_QDCOUNT(sldns_buffer_begin(pkt)) != 1) {
verbose(VERB_QUERY, "request wrong nr qd=%d",
LDNS_QDCOUNT(sldns_buffer_begin(pkt)));
- return LDNS_RCODE_FORMERR;
+ return worker_err_ratelimit(worker, LDNS_RCODE_FORMERR);
}
if(LDNS_ANCOUNT(sldns_buffer_begin(pkt)) != 0) {
verbose(VERB_QUERY, "request wrong nr an=%d",
LDNS_ANCOUNT(sldns_buffer_begin(pkt)));
- return LDNS_RCODE_FORMERR;
+ return worker_err_ratelimit(worker, LDNS_RCODE_FORMERR);
}
if(LDNS_NSCOUNT(sldns_buffer_begin(pkt)) != 0) {
verbose(VERB_QUERY, "request wrong nr ns=%d",
LDNS_NSCOUNT(sldns_buffer_begin(pkt)));
- return LDNS_RCODE_FORMERR;
+ return worker_err_ratelimit(worker, LDNS_RCODE_FORMERR);
}
if(LDNS_ARCOUNT(sldns_buffer_begin(pkt)) > 1) {
verbose(VERB_QUERY, "request wrong nr ar=%d",
LDNS_ARCOUNT(sldns_buffer_begin(pkt)));
- return LDNS_RCODE_FORMERR;
+ return worker_err_ratelimit(worker, LDNS_RCODE_FORMERR);
}
return 0;
}
@@ -546,7 +568,7 @@ answer_from_cache(struct worker* worker, struct query_info* qinfo,
if(rep->an_numrrsets > 0 && (rep->rrsets[0]->rk.type ==
htons(LDNS_RR_TYPE_CNAME) || rep->rrsets[0]->rk.type ==
htons(LDNS_RR_TYPE_DNAME))) {
- if(!reply_check_cname_chain(rep)) {
+ if(!reply_check_cname_chain(qinfo, rep)) {
/* cname chain invalid, redo iterator steps */
verbose(VERB_ALGO, "Cache reply: cname chain broken");
bail_out:
@@ -813,6 +835,10 @@ worker_handle_request(struct comm_point* c, void* arg, int error,
if(!query_info_parse(&qinfo, c->buffer)) {
verbose(VERB_ALGO, "worker parse request: formerror.");
log_addr(VERB_CLIENT,"from",&repinfo->addr, repinfo->addrlen);
+ if(worker_err_ratelimit(worker, LDNS_RCODE_FORMERR) == -1) {
+ comm_point_drop_reply(repinfo);
+ return 0;
+ }
sldns_buffer_rewind(c->buffer);
LDNS_QR_SET(sldns_buffer_begin(c->buffer));
LDNS_RCODE_SET(sldns_buffer_begin(c->buffer),