}
void libworker_fg_done_cb(void* ATTR_UNUSED(arg), int ATTR_UNUSED(rcode),
- sldns_buffer* ATTR_UNUSED(buf), enum sec_status ATTR_UNUSED(s),
- char* ATTR_UNUSED(why_bogus))
+ sldns_buffer* ATTR_UNUSED(buf), enum sec_status ATTR_UNUSED(s),
+ char* ATTR_UNUSED(why_bogus), int ATTR_UNUSED(was_ratelimited))
{
log_assert(0);
}
void libworker_bg_done_cb(void* ATTR_UNUSED(arg), int ATTR_UNUSED(rcode),
- sldns_buffer* ATTR_UNUSED(buf), enum sec_status ATTR_UNUSED(s),
- char* ATTR_UNUSED(why_bogus))
+ sldns_buffer* ATTR_UNUSED(buf), enum sec_status ATTR_UNUSED(s),
+ char* ATTR_UNUSED(why_bogus), int ATTR_UNUSED(was_ratelimited))
{
log_assert(0);
}
void libworker_event_done_cb(void* ATTR_UNUSED(arg), int ATTR_UNUSED(rcode),
- sldns_buffer* ATTR_UNUSED(buf), enum sec_status ATTR_UNUSED(s),
- char* ATTR_UNUSED(why_bogus))
+ sldns_buffer* ATTR_UNUSED(buf), enum sec_status ATTR_UNUSED(s),
+ char* ATTR_UNUSED(why_bogus), int ATTR_UNUSED(was_ratelimited))
{
log_assert(0);
}
int order_lock_cmp(const void* ATTR_UNUSED(e1), const void* ATTR_UNUSED(e2))
{
- log_assert(0);
- return 0;
+ log_assert(0);
+ return 0;
}
int codeline_cmp(const void* ATTR_UNUSED(a), const void* ATTR_UNUSED(b))
{
- log_assert(0);
- return 0;
+ log_assert(0);
+ return 0;
}
+3 August 2018: George
+ - Expose if a query (or a subquery) was ratelimited (not src IP
+ ratelimiting) to libunbound under 'ub_result.was_ratelimited'.
+ This also introduces a change to 'ub_event_callback_type' in
+ libunbound/unbound-event.h.
+ - Tidy pylib tests.
+
3 August 2018: Wouter
- Revert previous change for #4136: because it introduces build
problems.
log_nametypeclass(VERB_ALGO, "ratelimit exceeded with "
"delegation point", iq->dp->name,
LDNS_RR_TYPE_NS, LDNS_RR_CLASS_IN);
+ qstate->was_ratelimited = 1;
return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
}
}
ie->num_queries_ratelimited++;
lock_basic_unlock(&ie->queries_ratelimit_lock);
verbose(VERB_ALGO, "query exceeded ratelimits");
+ qstate->was_ratelimited = 1;
return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
}
}
* o uint32 id
* o uint32 error_code
* o uint32 msg_security
+ * o uint32 was_ratelimited
* o uint32 length of why_bogus string (+1 for eos); 0 absent.
* o why_bogus_string
* o the remainder is the answer msg from resolver lookup.
* remainder can be length 0.
*/
+ size_t size_of_uint32s = 6 * sizeof(uint32_t);
size_t pkt_len = pkt?sldns_buffer_remaining(pkt):0;
size_t wlen = (pkt&&q->res->why_bogus)?strlen(q->res->why_bogus)+1:0;
uint8_t* p;
- *len = sizeof(uint32_t)*5 + pkt_len + wlen;
+ *len = size_of_uint32s + pkt_len + wlen;
p = (uint8_t*)malloc(*len);
if(!p) return NULL;
sldns_write_uint32(p, UB_LIBCMD_ANSWER);
sldns_write_uint32(p+sizeof(uint32_t), (uint32_t)q->querynum);
sldns_write_uint32(p+2*sizeof(uint32_t), (uint32_t)err);
sldns_write_uint32(p+3*sizeof(uint32_t), (uint32_t)q->msg_security);
- sldns_write_uint32(p+4*sizeof(uint32_t), (uint32_t)wlen);
+ sldns_write_uint32(p+4*sizeof(uint32_t), (uint32_t)q->res->was_ratelimited);
+ sldns_write_uint32(p+5*sizeof(uint32_t), (uint32_t)wlen);
if(wlen > 0)
- memmove(p+5*sizeof(uint32_t), q->res->why_bogus, wlen);
+ memmove(p+size_of_uint32s, q->res->why_bogus, wlen);
if(pkt_len > 0)
- memmove(p+5*sizeof(uint32_t)+wlen,
+ memmove(p+size_of_uint32s+wlen,
sldns_buffer_begin(pkt), pkt_len);
return p;
}
context_deserialize_answer(struct ub_ctx* ctx,
uint8_t* p, uint32_t len, int* err)
{
+ size_t size_of_uint32s = 6 * sizeof(uint32_t);
struct ctx_query* q = NULL ;
int id;
size_t wlen;
- if(len < 5*sizeof(uint32_t)) return NULL;
+ if(len < size_of_uint32s) return NULL;
log_assert( sldns_read_uint32(p) == UB_LIBCMD_ANSWER);
id = (int)sldns_read_uint32(p+sizeof(uint32_t));
q = (struct ctx_query*)rbtree_search(&ctx->queries, &id);
if(!q) return NULL;
*err = (int)sldns_read_uint32(p+2*sizeof(uint32_t));
q->msg_security = sldns_read_uint32(p+3*sizeof(uint32_t));
- wlen = (size_t)sldns_read_uint32(p+4*sizeof(uint32_t));
- if(len > 5*sizeof(uint32_t) && wlen > 0) {
- if(len >= 5*sizeof(uint32_t)+wlen)
+ q->res->was_ratelimited = sldns_read_uint32(p+4*sizeof(uint32_t));
+ wlen = (size_t)sldns_read_uint32(p+5*sizeof(uint32_t));
+ if(len > size_of_uint32s && wlen > 0) {
+ if(len >= size_of_uint32s+wlen)
q->res->why_bogus = (char*)memdup(
- p+5*sizeof(uint32_t), wlen);
+ p+size_of_uint32s, wlen);
if(!q->res->why_bogus) {
/* pass malloc failure to the user callback */
q->msg_len = 0;
}
q->res->why_bogus[wlen-1] = 0; /* zero terminated for sure */
}
- if(len > 5*sizeof(uint32_t)+wlen) {
- q->msg_len = len - 5*sizeof(uint32_t) - wlen;
- q->msg = (uint8_t*)memdup(p+5*sizeof(uint32_t)+wlen,
+ if(len > size_of_uint32s+wlen) {
+ q->msg_len = len - size_of_uint32s - wlen;
+ q->msg = (uint8_t*)memdup(p+size_of_uint32s+wlen,
q->msg_len);
if(!q->msg) {
/* pass malloc failure to the user callback */
/** fillup fg results */
static void
libworker_fillup_fg(struct ctx_query* q, int rcode, sldns_buffer* buf,
- enum sec_status s, char* why_bogus)
+ enum sec_status s, char* why_bogus, int was_ratelimited)
{
+ q->res->was_ratelimited = was_ratelimited;
if(why_bogus)
q->res->why_bogus = strdup(why_bogus);
if(rcode != 0) {
void
libworker_fg_done_cb(void* arg, int rcode, sldns_buffer* buf, enum sec_status s,
- char* why_bogus)
+ char* why_bogus, int was_ratelimited)
{
struct ctx_query* q = (struct ctx_query*)arg;
/* fg query is done; exit comm base */
comm_base_exit(q->w->base);
- libworker_fillup_fg(q, rcode, buf, s, why_bogus);
+ libworker_fillup_fg(q, rcode, buf, s, why_bogus, was_ratelimited);
}
/** setup qinfo and edns */
NULL, 0, NULL, 0, NULL)) {
regional_free_all(w->env->scratch);
libworker_fillup_fg(q, LDNS_RCODE_NOERROR,
- w->back->udp_buff, sec_status_insecure, NULL);
+ w->back->udp_buff, sec_status_insecure, NULL, 0);
libworker_delete(w);
free(qinfo.qname);
return UB_NOERROR;
w->env, &qinfo, &edns, w->back->udp_buff, w->env->scratch)) {
regional_free_all(w->env->scratch);
libworker_fillup_fg(q, LDNS_RCODE_NOERROR,
- w->back->udp_buff, sec_status_insecure, NULL);
+ w->back->udp_buff, sec_status_insecure, NULL, 0);
libworker_delete(w);
free(qinfo.qname);
return UB_NOERROR;
void
libworker_event_done_cb(void* arg, int rcode, sldns_buffer* buf,
- enum sec_status s, char* why_bogus)
+ enum sec_status s, char* why_bogus, int was_ratelimited)
{
struct ctx_query* q = (struct ctx_query*)arg;
ub_event_callback_type cb = q->cb_event;
else if(s == sec_status_secure)
sec = 2;
(*cb)(cb_arg, rcode, (void*)sldns_buffer_begin(buf),
- (int)sldns_buffer_limit(buf), sec, why_bogus);
+ (int)sldns_buffer_limit(buf), sec, why_bogus, was_ratelimited);
}
}
regional_free_all(w->env->scratch);
free(qinfo.qname);
libworker_event_done_cb(q, LDNS_RCODE_NOERROR,
- w->back->udp_buff, sec_status_insecure, NULL);
+ w->back->udp_buff, sec_status_insecure, NULL, 0);
return UB_NOERROR;
}
if(ctx->env->auth_zones && auth_zones_answer(ctx->env->auth_zones,
regional_free_all(w->env->scratch);
free(qinfo.qname);
libworker_event_done_cb(q, LDNS_RCODE_NOERROR,
- w->back->udp_buff, sec_status_insecure, NULL);
+ w->back->udp_buff, sec_status_insecure, NULL, 0);
return UB_NOERROR;
}
/* process new query */
/** add result to the bg worker result queue */
static void
add_bg_result(struct libworker* w, struct ctx_query* q, sldns_buffer* pkt,
- int err, char* reason)
+ int err, char* reason, int was_ratelimited)
{
uint8_t* msg = NULL;
uint32_t len = 0;
lock_basic_lock(&w->ctx->cfglock);
if(reason)
q->res->why_bogus = strdup(reason);
+ q->res->was_ratelimited = was_ratelimited;
if(pkt) {
q->msg_len = sldns_buffer_remaining(pkt);
q->msg = memdup(sldns_buffer_begin(pkt), q->msg_len);
- if(!q->msg)
- msg = context_serialize_answer(q, UB_NOMEM,
- NULL, &len);
- else msg = context_serialize_answer(q, err,
- NULL, &len);
- } else msg = context_serialize_answer(q, err, NULL, &len);
+ if(!q->msg) {
+ msg = context_serialize_answer(q, UB_NOMEM, NULL, &len);
+ } else {
+ msg = context_serialize_answer(q, err, NULL, &len);
+ }
+ } else {
+ msg = context_serialize_answer(q, err, NULL, &len);
+ }
lock_basic_unlock(&w->ctx->cfglock);
} else {
if(reason)
q->res->why_bogus = strdup(reason);
+ q->res->was_ratelimited = was_ratelimited;
msg = context_serialize_answer(q, err, pkt, &len);
(void)rbtree_delete(&w->ctx->queries, q->node.key);
w->ctx->num_async--;
void
libworker_bg_done_cb(void* arg, int rcode, sldns_buffer* buf, enum sec_status s,
- char* why_bogus)
+ char* why_bogus, int was_ratelimited)
{
struct ctx_query* q = (struct ctx_query*)arg;
return;
}
q->msg_security = s;
- if(!buf)
+ if(!buf) {
buf = q->w->env->scratch_buffer;
+ }
if(rcode != 0) {
error_encode(buf, rcode, NULL, 0, BIT_RD, NULL);
}
- add_bg_result(q->w, q, buf, UB_NOERROR, why_bogus);
+ add_bg_result(q->w, q, buf, UB_NOERROR, why_bogus, was_ratelimited);
}
return;
}
if(!setup_qinfo_edns(w, q, &qinfo, &edns)) {
- add_bg_result(w, q, NULL, UB_SYNTAX, NULL);
+ add_bg_result(w, q, NULL, UB_SYNTAX, NULL, 0);
return;
}
qid = 0;
NULL, 0, NULL, 0, NULL)) {
regional_free_all(w->env->scratch);
q->msg_security = sec_status_insecure;
- add_bg_result(w, q, w->back->udp_buff, UB_NOERROR, NULL);
+ add_bg_result(w, q, w->back->udp_buff, UB_NOERROR, NULL, 0);
free(qinfo.qname);
return;
}
w->env, &qinfo, &edns, w->back->udp_buff, w->env->scratch)) {
regional_free_all(w->env->scratch);
q->msg_security = sec_status_insecure;
- add_bg_result(w, q, w->back->udp_buff, UB_NOERROR, NULL);
+ add_bg_result(w, q, w->back->udp_buff, UB_NOERROR, NULL, 0);
free(qinfo.qname);
return;
}
/* process new query */
if(!mesh_new_callback(w->env->mesh, &qinfo, qflags, &edns,
w->back->udp_buff, qid, libworker_bg_done_cb, q)) {
- add_bg_result(w, q, NULL, UB_NOMEM, NULL);
+ add_bg_result(w, q, NULL, UB_NOMEM, NULL, 0);
}
free(qinfo.qname);
}
struct ub_event_vmt* vmt;
};
-typedef void (*ub_event_callback_type)(void*, int, void*, int, int, char*);
+typedef void (*ub_event_callback_type)(void*, int, void*, int, int, char*, int);
/**
* Create a resolving and validation context.
*/
char* why_bogus;
+ /**
+ * If the query or one of its subqueries was ratelimited. Useful if
+ * ratelimiting is enabled and answer is SERVFAIL.
+ */
+ int was_ratelimited;
+
/**
* TTL for the result, in seconds. If the security is bogus, then
* you also cannot trust this value.
/** mesh callback with fg results */
void libworker_fg_done_cb(void* arg, int rcode, sldns_buffer* buf,
- enum sec_status s, char* why_bogus);
+ enum sec_status s, char* why_bogus, int was_ratelimited);
/** mesh callback with bg results */
void libworker_bg_done_cb(void* arg, int rcode, sldns_buffer* buf,
- enum sec_status s, char* why_bogus);
+ enum sec_status s, char* why_bogus, int was_ratelimited);
/** mesh callback with event results */
void libworker_event_done_cb(void* arg, int rcode, struct sldns_buffer* buf,
- enum sec_status s, char* why_bogus);
+ enum sec_status s, char* why_bogus, int was_ratelimited);
/**
* Worker signal handler function. User argument is the worker itself.
/** callback for task_transfer lookup of host name, of A or AAAA */
void auth_xfer_transfer_lookup_callback(void* arg, int rcode, sldns_buffer* buf,
- enum sec_status ATTR_UNUSED(sec), char* ATTR_UNUSED(why_bogus))
+ enum sec_status ATTR_UNUSED(sec), char* ATTR_UNUSED(why_bogus),
+ int ATTR_UNUSED(was_ratelimited))
{
struct auth_xfer* xfr = (struct auth_xfer*)arg;
struct module_env* env;
/** callback for task_probe lookup of host name, of A or AAAA */
void auth_xfer_probe_lookup_callback(void* arg, int rcode, sldns_buffer* buf,
- enum sec_status ATTR_UNUSED(sec), char* ATTR_UNUSED(why_bogus))
+ enum sec_status ATTR_UNUSED(sec), char* ATTR_UNUSED(why_bogus),
+ int ATTR_UNUSED(was_ratelimited))
{
struct auth_xfer* xfr = (struct auth_xfer*)arg;
struct module_env* env;
void auth_xfer_probe_timer_callback(void* arg);
/** mesh callback for task_probe on lookup of host names */
void auth_xfer_probe_lookup_callback(void* arg, int rcode,
- struct sldns_buffer* buf, enum sec_status sec, char* why_bogus);
+ struct sldns_buffer* buf, enum sec_status sec, char* why_bogus,
+ int was_ratelimited);
/** mesh callback for task_transfer on lookup of host names */
void auth_xfer_transfer_lookup_callback(void* arg, int rcode,
- struct sldns_buffer* buf, enum sec_status sec, char* why_bogus);
+ struct sldns_buffer* buf, enum sec_status sec, char* why_bogus,
+ int was_ratelimited);
/*
* Compares two 32-bit serial numbers as defined in RFC1982. Returns
mesh_run(mesh, e->qstate->mesh_info, event, e);
}
-struct mesh_state*
-mesh_state_create(struct module_env* env, struct query_info* qinfo,
+struct mesh_state*
+mesh_state_create(struct module_env* env, struct query_info* qinfo,
struct respip_client_info* cinfo, uint16_t qflags, int prime,
int valrec)
{
mstate->s.no_cache_lookup = 0;
mstate->s.no_cache_store = 0;
mstate->s.need_refetch = 0;
+ mstate->s.was_ratelimited = 0;
/* init modules */
for(i=0; i<env->mesh->mods.num; i++) {
mstate->cb_list = cb->next;
fptr_ok(fptr_whitelist_mesh_cb(cb->cb));
(*cb->cb)(cb->cb_arg, LDNS_RCODE_SERVFAIL, NULL,
- sec_status_unchecked, NULL);
+ sec_status_unchecked, NULL, 0);
mesh->num_reply_addrs--;
}
}
{
int secure;
char* reason = NULL;
- /* bogus messages are not made into servfail, sec_status passed
+ int was_ratelimited = m->s.was_ratelimited;
+ /* bogus messages are not made into servfail, sec_status passed
* to the callback function */
if(rep && rep->security == sec_status_secure)
secure = 1;
r->edns.opt_list = NULL;
}
fptr_ok(fptr_whitelist_mesh_cb(r->cb));
- (*r->cb)(r->cb_arg, rcode, r->buf, sec_status_unchecked, NULL);
+ (*r->cb)(r->cb_arg, rcode, r->buf, sec_status_unchecked, NULL,
+ was_ratelimited);
} else {
size_t udp_size = r->edns.udp_size;
sldns_buffer_clear(r->buf);
{
fptr_ok(fptr_whitelist_mesh_cb(r->cb));
(*r->cb)(r->cb_arg, LDNS_RCODE_SERVFAIL, r->buf,
- sec_status_unchecked, NULL);
+ sec_status_unchecked, NULL, 0);
} else {
fptr_ok(fptr_whitelist_mesh_cb(r->cb));
(*r->cb)(r->cb_arg, LDNS_RCODE_NOERROR, r->buf,
- rep->security, reason);
+ rep->security, reason, was_ratelimited);
}
}
free(reason);
mesh->mods.mod[ref->s->s.curmod]->inform_super));
(*mesh->mods.mod[ref->s->s.curmod]->inform_super)(&mstate->s,
ref->s->s.curmod, &ref->s->s);
+ /* copy state that is always relevant to super */
+ copy_state_to_super(&mstate->s, ref->s->s.curmod, &ref->s->s);
}
}
/**
* Mesh result callback func.
- * called as func(cb_arg, rcode, buffer_with_reply, security, why_bogus);
+ * called as func(cb_arg, rcode, buffer_with_reply, security, why_bogus,
+ * was_ratelimited);
*/
-typedef void (*mesh_cb_func_type)(void*, int, struct sldns_buffer*, enum sec_status,
- char*);
+typedef void (*mesh_cb_func_type)(void* cb_arg, int rcode, struct sldns_buffer*,
+ enum sec_status, char* why_bogus, int was_ratelimited);
/**
* Callback to result routine
uint16_t qflags;
/** buffer for reply */
struct sldns_buffer* buf;
-
/** callback routine for results. if rcode != 0 buf has message.
- * called as cb(cb_arg, rcode, buf, sec_state);
+ * called as cb(cb_arg, rcode, buf, sec_state, why_bogus, was_ratelimited);
*/
mesh_cb_func_type cb;
/** user arg for callback */
void libworker_fg_done_cb(void* ATTR_UNUSED(arg), int ATTR_UNUSED(rcode),
struct sldns_buffer* ATTR_UNUSED(buf), enum sec_status ATTR_UNUSED(s),
- char* ATTR_UNUSED(why_bogus))
+ char* ATTR_UNUSED(why_bogus), int ATTR_UNUSED(was_ratelimited))
{
log_assert(0);
}
void libworker_bg_done_cb(void* ATTR_UNUSED(arg), int ATTR_UNUSED(rcode),
struct sldns_buffer* ATTR_UNUSED(buf), enum sec_status ATTR_UNUSED(s),
- char* ATTR_UNUSED(why_bogus))
+ char* ATTR_UNUSED(why_bogus), int ATTR_UNUSED(was_ratelimited))
{
log_assert(0);
}
void libworker_event_done_cb(void* ATTR_UNUSED(arg), int ATTR_UNUSED(rcode),
struct sldns_buffer* ATTR_UNUSED(buf), enum sec_status ATTR_UNUSED(s),
- char* ATTR_UNUSED(why_bogus))
+ char* ATTR_UNUSED(why_bogus), int ATTR_UNUSED(was_ratelimited))
{
log_assert(0);
}
server:
verbosity: 2
- # num-threads: 1
- #port: @PORT@
use-syslog: no
directory: ""
pidfile: "unbound.pid"
do-not-query-localhost: no
module-config: "validator iterator"
-#python:
- #python-script: "pylib.py"
-
-forward-zone:
- name: "."
- forward-addr: "127.0.0.1@@TOPORT@"
+stub-zone:
+ name: "example.com."
+ stub-addr: "127.0.0.1@@TOPORT@"
#!/usr/bin/env python
-'''
-Test for unbound lookup.
-BSD licensed.
-'''
+#
+# Test for pyunbound lookup.
+# BSD licensed.
+#
+import sys
+import time
+
import unbound
-ctx = unbound.ub_ctx()
-status = ctx.config("ub.conf")
-if status != 0:
- print "read config failed ", status
- exit(1)
+qname = "www.example.com"
+qtype = unbound.RR_TYPE_A
+qclass = unbound.RR_CLASS_IN
+
+def create_context(config_file="ub.lookup.conf", async=False):
+ """
+ Create an unbound context to use for testing.
+
+ """
+ ctx = unbound.ub_ctx()
+ status = ctx.config(config_file)
+ if status != 0:
+ print("read config failed with status: {}".format(status))
+ sys.exit(1)
+ ctx.set_async(async)
+ return ctx
+
+
+def callback(data, status, result):
+ """
+ Callback for background workers.
+
+ """
+ if status == 0:
+ data['rcode'] = result.rcode
+ data['secure'] = result.secure
+ if result.havedata:
+ data['data'] = result.data
+ data['was_ratelimited'] = result.was_ratelimited
+ data['done'] = True
+
+
+def test_resolve(ctx):
+ """
+ Test resolving a domain with a foreground worker.
+
+ """
+ status, result = ctx.resolve(qname, qtype, qclass)
+ if status == 0 and result.havedata:
+ print("Resolve: {}".format(result.data.address_list))
+ else:
+ print("Failed resolve with: {}".format(status))
+
+
+def test_async_resolve(ctx):
+ """
+ Test resolving a domain with a background worker.
+
+ """
+ cb_data = dict(done=False)
+ retval, async_id = ctx.resolve_async(qname, cb_data, callback, qtype, qclass)
+ while retval == 0 and not cb_data['done']:
+ time.sleep(0.1)
+ retval = ctx.process()
+
+ if cb_data.get('data'):
+ print("Async resolve: {}".format(cb_data['data'].address_list))
+ else:
+ print("Failed async resolve with: {}".format(retval))
+
+
+def test_ratelimit_fg_on(ctx):
+ """
+ Test resolving a ratelimited domain with a foreground worker.
+
+ """
+ ctx.set_option("ratelimit:", "1")
+ status, result = ctx.resolve(qname, qtype, qclass)
+ if status == 0 and result.was_ratelimited:
+ print("Ratelimit-fg-on: pass")
+ else:
+ print("Failed ratelimit-fg-on with: {}".format(status))
+
+
+def test_ratelimit_fg_off(ctx):
+ """
+ Test resolving a non-ratelimited domain with a foreground worker.
+
+ """
+ status, result = ctx.resolve(qname, qtype, qclass)
+ if status == 0 and result.havedata:
+ print("Ratelimit-fg-off: {}".format(result.data.address_list))
+ else:
+ print("Failed ratelimit-fg-off with: {}".format(status))
+
+
+def test_ratelimit_bg_on(ctx):
+ """
+ Test resolving a ratelimited domain with a background worker.
+
+ """
+ ctx.set_option("ratelimit:", "1")
+ cb_data = dict(done=False)
+ retval, async_id = ctx.resolve_async(qname, cb_data, callback, qtype, qclass)
+ while retval == 0 and not cb_data['done']:
+ time.sleep(0.1)
+ retval = ctx.process()
+
+ if cb_data.get('was_ratelimited'):
+ print("Ratelimit-bg-on: pass")
+ else:
+ print("Failed ratelimit-bg-on with: {}".format(status))
+
+
+def test_ratelimit_bg_off(ctx):
+ """
+ Test resolving a non-ratelimited domain with a background worker.
+
+ """
+ cb_data = dict(done=False)
+ retval, async_id = ctx.resolve_async(qname, cb_data, callback, qtype, qclass)
+ while retval == 0 and not cb_data['done']:
+ time.sleep(0.1)
+ retval = ctx.process()
-print "config created"
+ if cb_data.get('data'):
+ print("Ratelimit-bg-off: {}".format(cb_data['data'].address_list))
+ else:
+ print("Failed ratelimit-bg-off with: {}".format(status))
-status, result = ctx.resolve("www.example.com", unbound.RR_TYPE_A, unbound.RR_CLASS_IN);
-if status == 0 and result.havedata:
- print "Result: ", result.data.address_list
-else:
- print "Failed ", status, " and data ", result
-ctx = None
+test_resolve(create_context())
+test_async_resolve(create_context(async=True))
+test_ratelimit_fg_on(create_context())
+test_ratelimit_fg_off(create_context())
+test_ratelimit_bg_on(create_context(async=True))
+test_ratelimit_bg_off(create_context(async=True))
-exit(0)
+sys.exit(0)
. ../common.sh
# if no python; exit
if grep "define WITH_PYUNBOUND 1" $PRE/config.h; then
- echo "have python module"
+ echo "have pyunbound"
else
- echo "no python module"
+ echo "no pyunbound"
exit 0
fi
. ../common.sh
# if no python; exit
if grep "define WITH_PYUNBOUND 1" $PRE/config.h; then
- echo "have python module"
+ echo "have pyunbound"
else
- echo "no python module"
+ echo "no pyunbound"
exit 0
fi
-# get module python local
-cp $PRE/pythonmod/unboundmodule.py .
+
+# Copy the required libraries
+cp $PRE/libunbound/python/unbound.py .
+cp $PRE/.libs/_unbound* .
+cp $PRE/.libs/libunbound* .
get_random_port 2
UNBOUND_PORT=$RND_PORT
echo "FWD_PID=$FWD_PID" >> .tpkg.var.test
# modify config file
-sed -e 's/@PORT\@/'$UNBOUND_PORT'/' -e 's/@TOPORT\@/'$FWD_PORT'/' < pylib.conf > ub.conf
+sed -e 's/@PORT\@/'$UNBOUND_PORT'/' -e 's/@TOPORT\@/'$FWD_PORT'/' < pylib.lookup.conf > ub.lookup.conf
cat .tpkg.var.test
wait_ldns_testns_up fwd.log
+++ /dev/null
-# -*- coding: utf-8 -*-
-'''
- ubmodule-msg.py: simple response packet logger
-
- Authors: Zdenek Vasicek (vasicek AT fit.vutbr.cz)
- Marek Vavrusa (xvavru00 AT stud.fit.vutbr.cz)
-
- Copyright (c) 2008. All rights reserved.
-
- This software is open source.
-
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions
- are met:
-
- Redistributions of source code must retain the above copyright notice,
- this list of conditions and the following disclaimer.
-
- Redistributions in binary form must reproduce the above copyright notice,
- this list of conditions and the following disclaimer in the documentation
- and/or other materials provided with the distribution.
-
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
- TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- POSSIBILITY OF SUCH DAMAGE.
-
- Modified for unit test by Wouter Wijngaards, NLnet Labs, 2009.
-'''
-import os
-
-def init(id, cfg):
- log_info("pythonmod: init called, module id is %d port: %d script: %s" % (id, cfg.port, cfg.python_script))
- return True
-
-def deinit(id):
- log_info("pythonmod: deinit called, module id is %d" % id)
- return True
-
-def inform_super(id, qstate, superqstate, qdata):
- return True
-
-def setTTL(qstate, ttl):
- """Sets return_msg TTL and all the RRs TTL"""
- if qstate.return_msg:
- qstate.return_msg.rep.ttl = ttl
- if (qstate.return_msg.rep):
- for i in range(0,qstate.return_msg.rep.rrset_count):
- d = qstate.return_msg.rep.rrsets[i].entry.data
- for j in range(0,d.count+d.rrsig_count):
- d.rr_ttl[j] = ttl
-
-def dataHex(data, prefix=""):
- res = ""
- for i in range(0, (len(data)+15)/16):
- res += "%s0x%02X | " % (prefix, i*16)
- d = map(lambda x:ord(x), data[i*16:i*16+17])
- for ch in d:
- res += "%02X " % ch
- for i in range(0,17-len(d)):
- res += " "
- res += "| "
- for ch in d:
- if (ch < 32) or (ch > 127):
- res += ". "
- else:
- res += "%c " % ch
- res += "\n"
- return res
-
-def printReturnMsg(qstate):
- print "Return MSG rep :: flags: %04X, QDcount: %d, Security:%d, TTL=%d" % (qstate.return_msg.rep.flags, qstate.return_msg.rep.qdcount,qstate.return_msg.rep.security, qstate.return_msg.rep.ttl)
- print " qinfo :: qname:",qstate.return_msg.qinfo.qname_list, qstate.return_msg.qinfo.qname_str, "type:",qstate.return_msg.qinfo.qtype_str, "class:",qstate.return_msg.qinfo.qclass_str
- if (qstate.return_msg.rep):
- print "RRSets:",qstate.return_msg.rep.rrset_count
- prevkey = None
- for i in range(0,qstate.return_msg.rep.rrset_count):
- r = qstate.return_msg.rep.rrsets[i]
- rk = r.rk
- print i,":",rk.dname_list, rk.dname_str, "flags: %04X" % rk.flags,
- print "type:",rk.type_str,"(%d)" % ntohs(rk.type), "class:",rk.rrset_class_str,"(%d)" % ntohs(rk.rrset_class)
-
- d = r.entry.data
- print " RRDatas:",d.count+d.rrsig_count
- for j in range(0,d.count+d.rrsig_count):
- print " ",j,":","TTL=",d.rr_ttl[j],"RR data:"
- print dataHex(d.rr_data[j]," ")
-
-
-def operate(id, event, qstate, qdata):
- log_info("pythonmod: operate called, id: %d, event:%s" % (id, strmodulevent(event)))
- #print "pythonmod: per query data", qdata
-
- print "Query:", ''.join(map(lambda x:chr(max(32,ord(x))),qstate.qinfo.qname)), qstate.qinfo.qname_list, qstate.qinfo.qname_str,
- print "Type:",qstate.qinfo.qtype_str,"(%d)" % qstate.qinfo.qtype,
- print "Class:",qstate.qinfo.qclass_str,"(%d)" % qstate.qinfo.qclass
- print
-
- if (event == MODULE_EVENT_NEW or event == MODULE_EVENT_PASS) and (qstate.qinfo.qname_str.endswith("www2.example.com.")):
- print qstate.qinfo.qname_str
-
- qstate.ext_state[id] = MODULE_FINISHED
-
- msg = DNSMessage(qstate.qinfo.qname_str, RR_TYPE_A, RR_CLASS_IN, PKT_QR | PKT_RA | PKT_AA) #, 300)
- #msg.authority.append("xxx.seznam.cz. 10 IN A 192.168.1.1")
- #msg.additional.append("yyy.seznam.cz. 10 IN A 1.1.1.2.")
-
- # answer can be returned to the client without further checking.
-
- if qstate.qinfo.qtype == RR_TYPE_A:
- msg.answer.append("%s 10 IN A 192.168.1.1" % qstate.qinfo.qname_str)
- if (qstate.qinfo.qtype == RR_TYPE_SRV) or (qstate.qinfo.qtype == RR_TYPE_ANY):
- msg.answer.append("%s 10 IN SRV 0 0 80 neinfo.example.com." % qstate.qinfo.qname_str)
- if (qstate.qinfo.qtype == RR_TYPE_TXT) or (qstate.qinfo.qtype == RR_TYPE_ANY):
- msg.answer.append("%s 10 IN TXT path=/" % qstate.qinfo.qname_str)
-
- if not msg.set_return_msg(qstate):
- qstate.ext_state[id] = MODULE_ERROR
- return True
-
- #qstate.return_msg.rep.security = 2 #pokud nebude nasledovat validator, je zapotrebi nastavit security, aby nebyl paket zahozen v mesh_send_reply
- printReturnMsg(qstate)
-
- #Authoritative result can't be stored in cache
- #if (not storeQueryInCache(qstate, qstate.return_msg.qinfo, qstate.return_msg.rep, 0)):
- # print "Can't store in cache"
- # qstate.ext_state[id] = MODULE_ERROR
- # return False
- #print "Store OK"
-
- qstate.return_rcode = RCODE_NOERROR
- return True
-
- if event == MODULE_EVENT_NEW:
- qstate.ext_state[id] = MODULE_WAIT_MODULE
- return True
-
- if event == MODULE_EVENT_MODDONE:
- log_info("pythonmod: previous module done")
- qstate.ext_state[id] = MODULE_FINISHED
- return True
-
- if event == MODULE_EVENT_PASS:
- log_info("pythonmod: event_pass")
- qstate.ext_state[id] = MODULE_WAIT_MODULE
- return True
-
- log_err("pythonmod: BAD event")
- qstate.ext_state[id] = MODULE_ERROR
- return True
-
-log_info("pythonmod: script loaded.")
PRE="../.."
if grep "define WITH_PYUNBOUND 1" $PRE/config.h; then
- echo "have python module"
+ echo "have pyunbound"
else
- echo "no python module"
+ echo "no pyunbound"
exit 0
fi
#echo export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:../../.libs:."
#export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:../../.libs:."
-cp $PRE/libunbound/python/unbound.py .
-cp $PRE/.libs/_unbound* .
-cp $PRE/.libs/libunbound* .
-
# do the test
echo "> pylib.lookup.py www.example.com."
./pylib.lookup.py www.example.com. | tee outfile
echo "> cat logfiles"
-cat fwd.log
+cat fwd.log
echo "> check answer"
-if grep "10.20.30.40" outfile; then
- echo "OK"
+if grep "Resolve: \[.\?10.20.30.40.\?\]" outfile; then
+ :
else
- echo "Not OK"
+ echo "Not OK (resolve)"
exit 1
fi
+if grep "Async resolve: \[.\?10.20.30.40.\?\]" outfile; then
+ :
+else
+ echo "Not OK (async resolve)"
+ exit 1
+fi
+if grep "Ratelimit-fg-on: pass" outfile; then
+ :
+else
+ echo "Not OK (ratelimit-fg-on)"
+ exit 1
+fi
+if grep "Ratelimit-fg-off: \[.\?10.20.30.40.\?\]" outfile; then
+ :
+else
+ echo "Not OK (ratelimit-fg-off)"
+ exit 1
+fi
+if grep "Ratelimit-bg-on: pass" outfile; then
+ :
+else
+ echo "Not OK (ratelimit-bg-on)"
+ exit 1
+fi
+if grep "Ratelimit-bg-off: \[.\?10.20.30.40.\?\]" outfile; then
+ :
+else
+ echo "Not OK (ratelimit-bg-off)"
+ exit 1
+fi
+
+echo "OK"
exit 0
}
memset(msg, 0, sizeof(*msg));
sldns_buffer_set_position(pkt, 0);
- if(parse_packet(pkt, msg, region) != 0)
+ if(parse_packet(pkt, msg, region) != 0){
return 0;
+ }
if(!parse_create_msg(pkt, msg, NULL, qi, &rep, region)) {
return 0;
}
}
}
}
+
+void
+copy_state_to_super(struct module_qstate* qstate, int ATTR_UNUSED(id),
+ struct module_qstate* super)
+{
+ /* Overwrite super's was_ratelimited only when it was not set */
+ if(!super->was_ratelimited) {
+ super->was_ratelimited = qstate->was_ratelimited;
+ }
+}
int no_cache_store;
/** whether to refetch a fresh answer on finishing this state*/
int need_refetch;
+ /** whether the query (or a subquery) was ratelimited */
+ int was_ratelimited;
/**
* Attributes of clients that share the qstate that may affect IP-based
void log_edns_known_options(enum verbosity_value level,
struct module_env* env);
+/**
+ * Copy state that may have happened in the subquery and is always relevant to
+ * the super.
+ * @param qstate: query state that finished.
+ * @param id: module id.
+ * @param super: the qstate to inform.
+ */
+void copy_state_to_super(struct module_qstate* qstate, int id,
+ struct module_qstate* super);
+
#endif /* UTIL_MODULE_H */
void probe_answer_cb(void* arg, int ATTR_UNUSED(rcode),
sldns_buffer* ATTR_UNUSED(buf), enum sec_status ATTR_UNUSED(sec),
- char* ATTR_UNUSED(why_bogus))
+ char* ATTR_UNUSED(why_bogus), int ATTR_UNUSED(was_ratelimited))
{
/* retry was set before the query was done,
* re-querytime is set when query succeeded, but that may not
/** callback for query answer to 5011 probe */
void probe_answer_cb(void* arg, int rcode, struct sldns_buffer* buf,
- enum sec_status sec, char* errinf);
+ enum sec_status sec, char* errinf, int was_ratelimited);
#endif /* VALIDATOR_AUTOTRUST_H */