mirror of
https://github.com/NLnetLabs/unbound.git
synced 2024-09-21 06:37:08 +00:00
DoS protection.
git-svn-id: file:///svn/unbound/trunk@1221 be551aaa-1e26-0410-a405-d3ace91eadb9
This commit is contained in:
parent
01cabbebc1
commit
960da40475
@ -826,13 +826,8 @@ worker_handle_request(struct comm_point* c, void* arg, int error,
|
||||
server_stats_querymiss(&worker->stats, worker);
|
||||
|
||||
/* grab a work request structure for this new request */
|
||||
if(worker->env.mesh->all.count > worker->request_size) {
|
||||
verbose(VERB_ALGO, "Too many requests active. "
|
||||
"dropping incoming query.");
|
||||
worker->stats.num_query_list_exceeded++;
|
||||
comm_point_drop_reply(repinfo);
|
||||
return 0;
|
||||
} else if(worker->env.mesh->num_reply_addrs>worker->request_size*16) {
|
||||
if(worker->env.mesh->num_reply_addrs>worker->request_size*16) {
|
||||
/* protect our memory usage from storing reply addresses */
|
||||
verbose(VERB_ALGO, "Too many requests queued. "
|
||||
"dropping incoming query.");
|
||||
worker->stats.num_query_list_exceeded++;
|
||||
|
@ -1,3 +1,7 @@
|
||||
2 September 2008: Wouter
|
||||
- DoS protection features. Queries are jostled out to make room.
|
||||
- testbound can pass time, increasing the internal timer.
|
||||
|
||||
1 September 2008: Wouter
|
||||
- disallow nonrecursive queries for cache snooping by default.
|
||||
You can allow is using access-control: <subnet> allow_snoop.
|
||||
|
12
doc/plan
12
doc/plan
@ -4,7 +4,7 @@ Plan for Unbound 1.1.
|
||||
- immediate attention: done
|
||||
- security issues: 1 week.
|
||||
- remote control: 2 week
|
||||
- requested: 1 week
|
||||
- improvements: 1 week
|
||||
- draft-mitigation: 2 week
|
||||
total 6 of 8 weeks; 2 weeks for maintenance activities.
|
||||
|
||||
@ -27,13 +27,13 @@ total 6 of 8 weeks; 2 weeks for maintenance activities.
|
||||
(done)
|
||||
|
||||
*** Security issues
|
||||
* current NS query retry is an option, default off, experimental on,
|
||||
+ current NS query retry is an option, default off, experimental on,
|
||||
because of the added load to 3rd parties.
|
||||
* block nonRD queries, acl like.
|
||||
+ block nonRD queries, acl like.
|
||||
what about our authority features, those are allowed.
|
||||
* DoS vector, flush more.
|
||||
+ DoS vector, flush more.
|
||||
50% of max is for run-to-completion
|
||||
50% rest is for lifo queue with 100 msec timeout.
|
||||
50% rest is for lifo queue with 100-200 msec timeout.
|
||||
* records in the additional section should not be marked bogus
|
||||
if they have no signer or a different signed. Validate if you can,
|
||||
otherwise leave unchecked.
|
||||
@ -61,7 +61,7 @@ like dnswall does. Allow certain subdomains to do it, config options.
|
||||
* remote control to see delegation; what servers would be used to get
|
||||
data for a name.
|
||||
|
||||
*** Requested
|
||||
*** Improvements
|
||||
* fallback to noEDNS if all queries are dropped.
|
||||
* dnssec lameness fixen. Check to make sure.
|
||||
* negative caching to avoid DS queries, NSEC, NSEC3 (w params).
|
||||
|
202
services/mesh.c
202
services/mesh.c
@ -54,6 +54,54 @@
|
||||
#include "util/timehist.h"
|
||||
#include "util/fptr_wlist.h"
|
||||
#include "util/alloc.h"
|
||||
#include "util/config_file.h"
|
||||
|
||||
/** subtract timers and the values do not overflow or become negative */
|
||||
static void
|
||||
timeval_subtract(struct timeval* d, struct timeval* end, struct timeval* start)
|
||||
{
|
||||
#ifndef S_SPLINT_S
|
||||
d->tv_sec = end->tv_sec - start->tv_sec;
|
||||
while(end->tv_usec < start->tv_usec) {
|
||||
end->tv_usec += 1000000;
|
||||
d->tv_sec--;
|
||||
}
|
||||
d->tv_usec = end->tv_usec - start->tv_usec;
|
||||
#endif
|
||||
}
|
||||
|
||||
/** add timers and the values do not overflow or become negative */
|
||||
static void
|
||||
timeval_add(struct timeval* d, struct timeval* add)
|
||||
{
|
||||
#ifndef S_SPLINT_S
|
||||
d->tv_sec += add->tv_sec;
|
||||
d->tv_usec += add->tv_usec;
|
||||
while(d->tv_usec > 1000000 ) {
|
||||
d->tv_usec -= 1000000;
|
||||
d->tv_sec++;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/** divide sum of timers to get average */
|
||||
static void
|
||||
timeval_divide(struct timeval* avg, struct timeval* sum, size_t d)
|
||||
{
|
||||
#ifndef S_SPLINT_S
|
||||
size_t leftover;
|
||||
if(d == 0) {
|
||||
avg->tv_sec = 0;
|
||||
avg->tv_usec = 0;
|
||||
return;
|
||||
}
|
||||
avg->tv_sec = sum->tv_sec / d;
|
||||
avg->tv_usec = sum->tv_usec / d;
|
||||
/* handle fraction from seconds divide */
|
||||
leftover = sum->tv_sec - avg->tv_sec*d;
|
||||
avg->tv_usec += (leftover*1000000)/d;
|
||||
#endif
|
||||
}
|
||||
|
||||
int
|
||||
mesh_state_compare(const void* ap, const void* bp)
|
||||
@ -108,6 +156,11 @@ mesh_create(struct module_stack* stack, struct module_env* env)
|
||||
mesh->num_reply_addrs = 0;
|
||||
mesh->num_reply_states = 0;
|
||||
mesh->num_detached_states = 0;
|
||||
mesh->num_forever_states = 0;
|
||||
mesh->stats_jostled = 0;
|
||||
mesh->stats_dropped = 0;
|
||||
mesh->max_reply_states = env->cfg->num_queries_per_thread;
|
||||
mesh->max_forever_states = (mesh->max_reply_states+1)/2;
|
||||
return mesh;
|
||||
}
|
||||
|
||||
@ -130,6 +183,42 @@ mesh_delete(struct mesh_area* mesh)
|
||||
free(mesh);
|
||||
}
|
||||
|
||||
int mesh_make_new_space(struct mesh_area* mesh)
|
||||
{
|
||||
struct mesh_state* m = mesh->jostle_last;
|
||||
/* free space is available */
|
||||
if(mesh->num_reply_states < mesh->max_reply_states)
|
||||
return 1;
|
||||
/* try to kick out a jostle-list item */
|
||||
if(m && m->reply_list && m->list_select == mesh_jostle_list) {
|
||||
/* how old is it? */
|
||||
struct timeval age;
|
||||
timeval_subtract(&age, mesh->env->now_tv,
|
||||
&m->reply_list->start_time);
|
||||
#ifndef S_SPLINT_S
|
||||
if(age.tv_sec > 0 || age.tv_usec > MESH_JOSTLE_USEC) {
|
||||
/* its a goner */
|
||||
log_nametypeclass(VERB_ALGO, "query jostled out to "
|
||||
"make space for a new one",
|
||||
m->s.qinfo.qname, m->s.qinfo.qtype,
|
||||
m->s.qinfo.qclass);
|
||||
/* notify supers */
|
||||
if(m->super_set.count > 0) {
|
||||
verbose(VERB_ALGO, "notify supers of failure");
|
||||
m->s.return_msg = NULL;
|
||||
m->s.return_rcode = LDNS_RCODE_SERVFAIL;
|
||||
mesh_walk_supers(mesh, m);
|
||||
}
|
||||
mesh->stats_jostled ++;
|
||||
mesh_state_delete(&m->s);
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
/* no space for new item */
|
||||
return 0;
|
||||
}
|
||||
|
||||
void mesh_new_client(struct mesh_area* mesh, struct query_info* qinfo,
|
||||
uint16_t qflags, struct edns_data* edns, struct comm_reply* rep,
|
||||
uint16_t qid)
|
||||
@ -138,6 +227,16 @@ void mesh_new_client(struct mesh_area* mesh, struct query_info* qinfo,
|
||||
int was_detached = 0;
|
||||
int was_noreply = 0;
|
||||
int added = 0;
|
||||
/* does this create a new reply state? */
|
||||
if(!s || s->list_select == mesh_no_list) {
|
||||
if(!mesh_make_new_space(mesh)) {
|
||||
verbose(VERB_ALGO, "Too many queries. dropping "
|
||||
"incoming query.");
|
||||
comm_point_drop_reply(rep);
|
||||
mesh->stats_dropped ++;
|
||||
return;
|
||||
}
|
||||
}
|
||||
/* see if it already exists, if not, create one */
|
||||
if(!s) {
|
||||
struct rbnode_t* n;
|
||||
@ -178,6 +277,19 @@ void mesh_new_client(struct mesh_area* mesh, struct query_info* qinfo,
|
||||
mesh->num_reply_states ++;
|
||||
}
|
||||
mesh->num_reply_addrs++;
|
||||
if(s->list_select == mesh_no_list) {
|
||||
/* move to either the forever or the jostle_list */
|
||||
if(mesh->num_forever_states < mesh->max_forever_states) {
|
||||
mesh->num_forever_states ++;
|
||||
mesh_list_insert(s, &mesh->forever_first,
|
||||
&mesh->forever_last);
|
||||
s->list_select = mesh_forever_list;
|
||||
} else {
|
||||
mesh_list_insert(s, &mesh->jostle_first,
|
||||
&mesh->jostle_last);
|
||||
s->list_select = mesh_jostle_list;
|
||||
}
|
||||
}
|
||||
if(added)
|
||||
mesh_run(mesh, s, module_event_new, NULL);
|
||||
}
|
||||
@ -191,6 +303,8 @@ mesh_new_callback(struct mesh_area* mesh, struct query_info* qinfo,
|
||||
int was_detached = 0;
|
||||
int was_noreply = 0;
|
||||
int added = 0;
|
||||
/* there are no limits on the number of callbacks */
|
||||
|
||||
/* see if it already exists, if not, create one */
|
||||
if(!s) {
|
||||
struct rbnode_t* n;
|
||||
@ -257,6 +371,7 @@ mesh_state_create(struct module_env* env, struct query_info* qinfo,
|
||||
mstate->node.key = mstate;
|
||||
mstate->run_node.key = mstate;
|
||||
mstate->reply_list = NULL;
|
||||
mstate->list_select = mesh_no_list;
|
||||
rbtree_init(&mstate->super_set, &mesh_state_ref_compare);
|
||||
rbtree_init(&mstate->sub_set, &mesh_state_ref_compare);
|
||||
mstate->num_activated = 0;
|
||||
@ -317,6 +432,14 @@ mesh_state_delete(struct module_qstate* qstate)
|
||||
mstate = qstate->mesh_info;
|
||||
mesh = mstate->s.env->mesh;
|
||||
mesh_detach_subs(&mstate->s);
|
||||
if(mstate->list_select == mesh_forever_list) {
|
||||
mesh->num_forever_states --;
|
||||
mesh_list_remove(mstate, &mesh->forever_first,
|
||||
&mesh->forever_last);
|
||||
} else if(mstate->list_select == mesh_jostle_list) {
|
||||
mesh_list_remove(mstate, &mesh->jostle_first,
|
||||
&mesh->jostle_last);
|
||||
}
|
||||
if(!mstate->reply_list && !mstate->cb_list
|
||||
&& mstate->super_set.count == 0) {
|
||||
log_assert(mesh->num_detached_states > 0);
|
||||
@ -414,53 +537,6 @@ int mesh_state_attachment(struct mesh_state* super, struct mesh_state* sub)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** subtract timers and the values do not overflow or become negative */
|
||||
static void
|
||||
timeval_subtract(struct timeval* d, struct timeval* end, struct timeval* start)
|
||||
{
|
||||
#ifndef S_SPLINT_S
|
||||
d->tv_sec = end->tv_sec - start->tv_sec;
|
||||
while(end->tv_usec < start->tv_usec) {
|
||||
end->tv_usec += 1000000;
|
||||
d->tv_sec--;
|
||||
}
|
||||
d->tv_usec = end->tv_usec - start->tv_usec;
|
||||
#endif
|
||||
}
|
||||
|
||||
/** add timers and the values do not overflow or become negative */
|
||||
static void
|
||||
timeval_add(struct timeval* d, struct timeval* add)
|
||||
{
|
||||
#ifndef S_SPLINT_S
|
||||
d->tv_sec += add->tv_sec;
|
||||
d->tv_usec += add->tv_usec;
|
||||
while(d->tv_usec > 1000000 ) {
|
||||
d->tv_usec -= 1000000;
|
||||
d->tv_sec++;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/** divide sum of timers to get average */
|
||||
static void
|
||||
timeval_divide(struct timeval* avg, struct timeval* sum, size_t d)
|
||||
{
|
||||
#ifndef S_SPLINT_S
|
||||
size_t leftover;
|
||||
if(d == 0) {
|
||||
avg->tv_sec = 0;
|
||||
avg->tv_usec = 0;
|
||||
return;
|
||||
}
|
||||
avg->tv_sec = sum->tv_sec / d;
|
||||
avg->tv_usec = sum->tv_usec / d;
|
||||
/* handle fraction from seconds divide */
|
||||
leftover = sum->tv_sec - avg->tv_sec*d;
|
||||
avg->tv_usec += (leftover*1000000)/d;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* callback results to mesh cb entry
|
||||
* @param m: mesh state to send it for.
|
||||
@ -785,11 +861,14 @@ mesh_stats(struct mesh_area* mesh, const char* str)
|
||||
{
|
||||
verbose(VERB_DETAIL, "%s %u recursion states (%u with reply, "
|
||||
"%u detached), %u waiting replies, %u recursion replies "
|
||||
"sent", str, (unsigned)mesh->all.count,
|
||||
"sent, %d replies dropped, %d states jostled out",
|
||||
str, (unsigned)mesh->all.count,
|
||||
(unsigned)mesh->num_reply_states,
|
||||
(unsigned)mesh->num_detached_states,
|
||||
(unsigned)mesh->num_reply_addrs,
|
||||
(unsigned)mesh->replies_sent);
|
||||
(unsigned)mesh->replies_sent,
|
||||
(unsigned)mesh->stats_dropped,
|
||||
(unsigned)mesh->stats_jostled);
|
||||
if(mesh->replies_sent > 0) {
|
||||
struct timeval avg;
|
||||
timeval_divide(&avg, &mesh->replies_sum_wait,
|
||||
@ -809,6 +888,8 @@ mesh_stats_clear(struct mesh_area* mesh)
|
||||
mesh->replies_sent = 0;
|
||||
mesh->replies_sum_wait.tv_sec = 0;
|
||||
mesh->replies_sum_wait.tv_usec = 0;
|
||||
mesh->stats_jostled = 0;
|
||||
mesh->stats_dropped = 0;
|
||||
timehist_clear(mesh->histogram);
|
||||
}
|
||||
|
||||
@ -850,3 +931,26 @@ mesh_detect_cycle(struct module_qstate* qstate, struct query_info* qinfo,
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void mesh_list_insert(struct mesh_state* m, struct mesh_state** fp,
|
||||
struct mesh_state** lp)
|
||||
{
|
||||
/* insert as last element */
|
||||
m->prev = *lp;
|
||||
m->next = NULL;
|
||||
if(*lp)
|
||||
(*lp)->next = m;
|
||||
else *fp = m;
|
||||
*lp = m;
|
||||
}
|
||||
|
||||
void mesh_list_remove(struct mesh_state* m, struct mesh_state** fp,
|
||||
struct mesh_state** lp)
|
||||
{
|
||||
if(m->next)
|
||||
m->next->prev = m->prev;
|
||||
else *lp = m->prev;
|
||||
if(m->prev)
|
||||
m->prev->next = m->next;
|
||||
else *fp = m->next;
|
||||
}
|
||||
|
@ -65,6 +65,12 @@ struct timehist;
|
||||
*/
|
||||
#define MESH_MAX_ACTIVATION 1000
|
||||
|
||||
/**
|
||||
* Maximum time to live in the jostle list. usec.
|
||||
* The entries older than this could be removed to make space for new ones.
|
||||
*/
|
||||
#define MESH_JOSTLE_USEC 200000 /* 0.200000 sec */
|
||||
|
||||
/**
|
||||
* Mesh of query states
|
||||
*/
|
||||
@ -89,13 +95,36 @@ struct mesh_area {
|
||||
* an empty set of super-states, thus are 'toplevel' or detached
|
||||
* internal opportunistic queries */
|
||||
size_t num_detached_states;
|
||||
/** number of reply states in the forever list */
|
||||
size_t num_forever_states;
|
||||
|
||||
/** max total number of reply states to have */
|
||||
size_t max_reply_states;
|
||||
/** max forever number of reply states to have */
|
||||
size_t max_forever_states;
|
||||
|
||||
/** stats, cumulative number of reply states jostled out */
|
||||
size_t stats_jostled;
|
||||
/** stats, cumulative number of incoming client msgs dropped */
|
||||
size_t stats_dropped;
|
||||
/** number of replies sent */
|
||||
size_t replies_sent;
|
||||
/** sum of waiting times for the replies */
|
||||
struct timeval replies_sum_wait;
|
||||
/** histogram of time values */
|
||||
struct timehist* histogram;
|
||||
|
||||
/** double linked list of the run-to-completion query states.
|
||||
* These are query states with a reply */
|
||||
struct mesh_state* forever_first;
|
||||
/** last entry in run forever list */
|
||||
struct mesh_state* forever_last;
|
||||
|
||||
/** double linked list of the query states that can be jostled out
|
||||
* by new queries if too old. These are query states with a reply */
|
||||
struct mesh_state* jostle_first;
|
||||
/** last entry in jostle list - this is the entry that is newest */
|
||||
struct mesh_state* jostle_last;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -127,6 +156,14 @@ struct mesh_state {
|
||||
rbtree_t sub_set;
|
||||
/** number of activations for the mesh state */
|
||||
size_t num_activated;
|
||||
|
||||
/** previous in linked list for reply states */
|
||||
struct mesh_state* prev;
|
||||
/** next in linked list for reply states */
|
||||
struct mesh_state* next;
|
||||
/** if this state is in the forever list, jostle list, or neither */
|
||||
enum mesh_list_select { mesh_no_list, mesh_forever_list,
|
||||
mesh_jostle_list } list_select;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -457,4 +494,29 @@ int mesh_state_compare(const void* ap, const void* bp);
|
||||
/** compare two mesh references */
|
||||
int mesh_state_ref_compare(const void* ap, const void* bp);
|
||||
|
||||
/**
|
||||
* Make space for another recursion state for a reply in the mesh
|
||||
* @param mesh: mesh area
|
||||
* @return false if no space is available.
|
||||
*/
|
||||
int mesh_make_new_space(struct mesh_area* mesh);
|
||||
|
||||
/**
|
||||
* Insert mesh state into a double linked list. Inserted at end.
|
||||
* @param m: mesh state.
|
||||
* @param fp: pointer to the first-elem-pointer of the list.
|
||||
* @param lp: pointer to the last-elem-pointer of the list.
|
||||
*/
|
||||
void mesh_list_insert(struct mesh_state* m, struct mesh_state** fp,
|
||||
struct mesh_state** lp);
|
||||
|
||||
/**
|
||||
* Remove mesh state from a double linked list. Remove from any position.
|
||||
* @param m: mesh state.
|
||||
* @param fp: pointer to the first-elem-pointer of the list.
|
||||
* @param lp: pointer to the last-elem-pointer of the list.
|
||||
*/
|
||||
void mesh_list_remove(struct mesh_state* m, struct mesh_state** fp,
|
||||
struct mesh_state** lp);
|
||||
|
||||
#endif /* SERVICES_MESH_H */
|
||||
|
@ -61,6 +61,20 @@
|
||||
/** Global variable: the scenario. Saved here for when event_init is done. */
|
||||
static struct replay_scenario* saved_scenario = NULL;
|
||||
|
||||
/** add timers and the values do not overflow or become negative */
|
||||
static void
|
||||
timeval_add(struct timeval* d, struct timeval* add)
|
||||
{
|
||||
#ifndef S_SPLINT_S
|
||||
d->tv_sec += add->tv_sec;
|
||||
d->tv_usec += add->tv_usec;
|
||||
while(d->tv_usec > 1000000 ) {
|
||||
d->tv_usec -= 1000000;
|
||||
d->tv_sec++;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
fake_event_init(struct replay_scenario* scen)
|
||||
{
|
||||
@ -98,6 +112,7 @@ repevt_string(enum replay_event_type t)
|
||||
case repevt_front_query: return "QUERY";
|
||||
case repevt_front_reply: return "CHECK_ANSWER";
|
||||
case repevt_timeout: return "TIMEOUT";
|
||||
case repevt_time_passes: return "TIME_PASSES";
|
||||
case repevt_back_reply: return "REPLY";
|
||||
case repevt_back_query: return "CHECK_OUT_QUERY";
|
||||
case repevt_error: return "ERROR";
|
||||
@ -408,6 +423,19 @@ fake_pending_callback(struct replay_runtime* runtime,
|
||||
ldns_buffer_free(c.buffer);
|
||||
}
|
||||
|
||||
/** pass time */
|
||||
static void
|
||||
time_passes(struct replay_runtime* runtime, struct replay_moment* mom)
|
||||
{
|
||||
timeval_add(&runtime->now_tv, &mom->elapse);
|
||||
runtime->now_secs = (uint32_t)runtime->now_tv.tv_sec;
|
||||
#ifndef S_SPLINT_S
|
||||
log_info("elapsed %d.%6.6d now %d.%6.6d",
|
||||
(int)mom->elapse.tv_sec, (int)mom->elapse.tv_usec,
|
||||
(int)runtime->now_tv.tv_sec, (int)runtime->now_tv.tv_usec);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Advance to the next moment.
|
||||
*/
|
||||
@ -471,6 +499,10 @@ do_moment_and_advance(struct replay_runtime* runtime)
|
||||
advance_moment(runtime);
|
||||
fake_pending_callback(runtime, mom, NETEVENT_CLOSED);
|
||||
break;
|
||||
case repevt_time_passes:
|
||||
time_passes(runtime, runtime->now);
|
||||
advance_moment(runtime);
|
||||
break;
|
||||
default:
|
||||
fatal_exit("testbound: unknown event type %d",
|
||||
runtime->now->evt_type);
|
||||
|
@ -221,6 +221,8 @@ replay_moment_read(char* remain, FILE* in, const char* name, int* lineno,
|
||||
readentry = 1;
|
||||
} else if(parse_keyword(&remain, "TIMEOUT")) {
|
||||
mom->evt_type = repevt_timeout;
|
||||
} else if(parse_keyword(&remain, "TIME_PASSES")) {
|
||||
mom->evt_type = repevt_time_passes;
|
||||
} else if(parse_keyword(&remain, "ERROR")) {
|
||||
mom->evt_type = repevt_error;
|
||||
} else {
|
||||
@ -242,6 +244,22 @@ replay_moment_read(char* remain, FILE* in, const char* name, int* lineno,
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
if(parse_keyword(&remain, "ELAPSE")) {
|
||||
double sec;
|
||||
errno = 0;
|
||||
sec = strtod(remain, &remain);
|
||||
if(sec == 0. && errno != 0) {
|
||||
log_err("line %d: could not parse ELAPSE: %s (%s)",
|
||||
*lineno, remain, strerror(errno));
|
||||
free(mom);
|
||||
return NULL;
|
||||
}
|
||||
#ifndef S_SPLINT_S
|
||||
mom->elapse.tv_sec = (int)sec;
|
||||
mom->elapse.tv_usec = (int)((sec - (double)mom->elapse.tv_sec)
|
||||
*1000000. + 0.5);
|
||||
#endif
|
||||
}
|
||||
|
||||
if(readentry) {
|
||||
mom->match = read_entry(in, name, lineno, ttl, or, prev);
|
||||
|
@ -58,6 +58,8 @@
|
||||
* o CHECK_OUT_QUERY - followed by entry (if copy-id it is also reply).
|
||||
* o REPLY - followed by entry
|
||||
* o TIMEOUT
|
||||
* o TIME_PASSES ELAPSE [seconds] - increase 'now' time counter, can be
|
||||
* a floating point number.
|
||||
* o ERROR
|
||||
* ; following entry starts on the next line, ENTRY_BEGIN.
|
||||
* ; more STEP items
|
||||
@ -149,6 +151,8 @@ struct replay_moment {
|
||||
repevt_front_reply,
|
||||
/** timeout */
|
||||
repevt_timeout,
|
||||
/** time passes */
|
||||
repevt_time_passes,
|
||||
/** reply arrives from the network */
|
||||
repevt_back_reply,
|
||||
/** test fails if query to the network does not match */
|
||||
@ -162,6 +166,9 @@ struct replay_moment {
|
||||
/** The sent packet must match this. Incoming events, the data. */
|
||||
struct entry* match;
|
||||
|
||||
/** the amount of time that passes */
|
||||
struct timeval elapse;
|
||||
|
||||
/** address that must be matched, or packet remote host address. */
|
||||
struct sockaddr_storage addr;
|
||||
/** length of addr, if 0, then any address will do */
|
||||
|
90
testdata/fwd_droptoomany.rpl
vendored
Normal file
90
testdata/fwd_droptoomany.rpl
vendored
Normal file
@ -0,0 +1,90 @@
|
||||
; config options go here.
|
||||
server:
|
||||
num-queries-per-thread: 1
|
||||
forward-zone:
|
||||
name: "."
|
||||
forward-addr: 216.0.0.1
|
||||
CONFIG_END
|
||||
SCENARIO_BEGIN Test too many queries asked, last is dropped.
|
||||
|
||||
; query responses from authority servers.
|
||||
RANGE_BEGIN 0 100
|
||||
ENTRY_BEGIN
|
||||
MATCH opcode qtype qname
|
||||
ADJUST copy_id
|
||||
REPLY QR RD RA NOERROR
|
||||
SECTION QUESTION
|
||||
www.example.net. IN A
|
||||
SECTION ANSWER
|
||||
www.example.net. IN A 10.20.30.40
|
||||
SECTION AUTHORITY
|
||||
www.example.net. IN NS ns.example.net.
|
||||
SECTION ADDITIONAL
|
||||
ns.example.net. IN A 10.20.30.50
|
||||
ENTRY_END
|
||||
RANGE_END
|
||||
|
||||
STEP 1 QUERY
|
||||
ENTRY_BEGIN
|
||||
REPLY RD
|
||||
SECTION QUESTION
|
||||
www.example.com. IN A
|
||||
ENTRY_END
|
||||
|
||||
STEP 2 CHECK_OUT_QUERY
|
||||
ENTRY_BEGIN
|
||||
MATCH qname qtype opcode
|
||||
SECTION QUESTION
|
||||
www.example.com. IN A
|
||||
ENTRY_END
|
||||
|
||||
; NO REPLY (this step is not needed)
|
||||
STEP 3 NOTHING
|
||||
|
||||
; another query
|
||||
STEP 4 QUERY
|
||||
ENTRY_BEGIN
|
||||
REPLY RD
|
||||
SECTION QUESTION
|
||||
www.example.net. IN A
|
||||
ENTRY_END
|
||||
|
||||
; reply from first query returns
|
||||
STEP 5 REPLY
|
||||
ENTRY_BEGIN
|
||||
MATCH opcode qtype qname
|
||||
ADJUST copy_id
|
||||
REPLY QR RD RA NOERROR
|
||||
SECTION QUESTION
|
||||
www.example.com. IN A
|
||||
SECTION ANSWER
|
||||
www.example.com. IN A 10.20.30.40
|
||||
SECTION AUTHORITY
|
||||
www.example.com. IN NS ns.example.com.
|
||||
SECTION ADDITIONAL
|
||||
ns.example.com. IN A 10.20.30.50
|
||||
ENTRY_END
|
||||
|
||||
STEP 10 CHECK_ANSWER
|
||||
ENTRY_BEGIN
|
||||
MATCH opcode qname qtype
|
||||
SECTION QUESTION
|
||||
www.example.com. IN A
|
||||
SECTION ANSWER
|
||||
www.example.com. IN A 10.20.30.40
|
||||
ENTRY_END
|
||||
|
||||
; This answer does not arrive, the query was dropped
|
||||
;STEP 11 CHECK_ANSWER
|
||||
;ENTRY_BEGIN
|
||||
;MATCH opcode qname qtype
|
||||
;SECTION QUESTION
|
||||
;www.example.net. IN A
|
||||
;SECTION ANSWER
|
||||
;www.example.net. IN A 10.20.30.40
|
||||
;ENTRY_END
|
||||
SCENARIO_END
|
||||
|
||||
; testbound checks before exit:
|
||||
; * no more pending queries outstanding.
|
||||
; * and no answers that have not been checked.
|
110
testdata/fwd_jostle.rpl
vendored
Normal file
110
testdata/fwd_jostle.rpl
vendored
Normal file
@ -0,0 +1,110 @@
|
||||
; config options go here.
|
||||
; This is one forever, one jostle.
|
||||
server:
|
||||
num-queries-per-thread: 2
|
||||
forward-zone:
|
||||
name: "."
|
||||
forward-addr: 216.0.0.1
|
||||
CONFIG_END
|
||||
SCENARIO_BEGIN Test too many queries asked, last is too recent to be jostled
|
||||
|
||||
; fill the forever slot.
|
||||
STEP 1 QUERY
|
||||
ENTRY_BEGIN
|
||||
REPLY RD
|
||||
SECTION QUESTION
|
||||
www.example.com. IN A
|
||||
ENTRY_END
|
||||
|
||||
STEP 2 CHECK_OUT_QUERY
|
||||
ENTRY_BEGIN
|
||||
MATCH qname qtype opcode
|
||||
SECTION QUESTION
|
||||
www.example.com. IN A
|
||||
ENTRY_END
|
||||
|
||||
; NO REPLY (this step is not needed)
|
||||
STEP 3 NOTHING
|
||||
|
||||
;something enters the jostle slot.
|
||||
STEP 4 QUERY
|
||||
ENTRY_BEGIN
|
||||
REPLY RD
|
||||
SECTION QUESTION
|
||||
www.example.net. IN A
|
||||
ENTRY_END
|
||||
|
||||
STEP 5 CHECK_OUT_QUERY
|
||||
ENTRY_BEGIN
|
||||
MATCH qname qtype opcode
|
||||
SECTION QUESTION
|
||||
www.example.net. IN A
|
||||
ENTRY_END
|
||||
|
||||
; something else tries to replace the entry in the jostle slot.
|
||||
; but the entry in the jostle slot is too recent.
|
||||
STEP 6 QUERY
|
||||
ENTRY_BEGIN
|
||||
REPLY RD
|
||||
SECTION QUESTION
|
||||
www.example.org. IN A
|
||||
ENTRY_END
|
||||
|
||||
; reply from latest query returns
|
||||
STEP 7 REPLY
|
||||
ENTRY_BEGIN
|
||||
MATCH opcode qtype qname
|
||||
ADJUST copy_id
|
||||
REPLY QR RD RA NOERROR
|
||||
SECTION QUESTION
|
||||
www.example.net. IN A
|
||||
SECTION ANSWER
|
||||
www.example.net. IN A 10.20.30.42
|
||||
SECTION AUTHORITY
|
||||
www.example.net. IN NS ns.example.net.
|
||||
SECTION ADDITIONAL
|
||||
ns.example.net. IN A 10.20.30.50
|
||||
ENTRY_END
|
||||
|
||||
; answer to last query
|
||||
STEP 8 CHECK_ANSWER
|
||||
ENTRY_BEGIN
|
||||
MATCH opcode qname qtype
|
||||
SECTION QUESTION
|
||||
www.example.net. IN A
|
||||
SECTION ANSWER
|
||||
www.example.net. IN A 10.20.30.42
|
||||
ENTRY_END
|
||||
|
||||
|
||||
; reply from first query returns
|
||||
STEP 10 REPLY
|
||||
ENTRY_BEGIN
|
||||
MATCH opcode qtype qname
|
||||
ADJUST copy_id
|
||||
REPLY QR RD RA NOERROR
|
||||
SECTION QUESTION
|
||||
www.example.com. IN A
|
||||
SECTION ANSWER
|
||||
www.example.com. IN A 10.20.30.40
|
||||
SECTION AUTHORITY
|
||||
www.example.com. IN NS ns.example.com.
|
||||
SECTION ADDITIONAL
|
||||
ns.example.com. IN A 10.20.30.50
|
||||
ENTRY_END
|
||||
|
||||
; answer to first query
|
||||
STEP 11 CHECK_ANSWER
|
||||
ENTRY_BEGIN
|
||||
MATCH opcode qname qtype
|
||||
SECTION QUESTION
|
||||
www.example.com. IN A
|
||||
SECTION ANSWER
|
||||
www.example.com. IN A 10.20.30.40
|
||||
ENTRY_END
|
||||
|
||||
SCENARIO_END
|
||||
|
||||
; testbound checks before exit:
|
||||
; * no more pending queries outstanding.
|
||||
; * and no answers that have not been checked.
|
113
testdata/fwd_jostle_out.rpl
vendored
Normal file
113
testdata/fwd_jostle_out.rpl
vendored
Normal file
@ -0,0 +1,113 @@
|
||||
; config options go here.
|
||||
; This is one forever, one jostle.
|
||||
server:
|
||||
num-queries-per-thread: 2
|
||||
forward-zone:
|
||||
name: "."
|
||||
forward-addr: 216.0.0.1
|
||||
CONFIG_END
|
||||
SCENARIO_BEGIN Test too many queries asked, last one jostled out to make space
|
||||
|
||||
; fill the forever slot.
|
||||
STEP 1 QUERY
|
||||
ENTRY_BEGIN
|
||||
REPLY RD
|
||||
SECTION QUESTION
|
||||
www.example.com. IN A
|
||||
ENTRY_END
|
||||
|
||||
STEP 2 CHECK_OUT_QUERY
|
||||
ENTRY_BEGIN
|
||||
MATCH qname qtype opcode
|
||||
SECTION QUESTION
|
||||
www.example.com. IN A
|
||||
ENTRY_END
|
||||
|
||||
; NO REPLY (this step is not needed)
|
||||
STEP 3 NOTHING
|
||||
|
||||
;something enters the jostle slot.
|
||||
STEP 4 QUERY
|
||||
ENTRY_BEGIN
|
||||
REPLY RD
|
||||
SECTION QUESTION
|
||||
www.example.net. IN A
|
||||
ENTRY_END
|
||||
|
||||
STEP 5 CHECK_OUT_QUERY
|
||||
ENTRY_BEGIN
|
||||
MATCH qname qtype opcode
|
||||
SECTION QUESTION
|
||||
www.example.net. IN A
|
||||
ENTRY_END
|
||||
|
||||
; 300 msec passes
|
||||
STEP 6 TIME_PASSES ELAPSE 0.300
|
||||
|
||||
; something else tries to replace the entry in the jostle slot.
|
||||
; and it works because the entry is now too old.
|
||||
STEP 8 QUERY
|
||||
ENTRY_BEGIN
|
||||
REPLY RD
|
||||
SECTION QUESTION
|
||||
www.example.org. IN A
|
||||
ENTRY_END
|
||||
|
||||
; reply from latest query returns
|
||||
STEP 9 REPLY
|
||||
ENTRY_BEGIN
|
||||
MATCH opcode qtype qname
|
||||
ADJUST copy_id
|
||||
REPLY QR RD RA NOERROR
|
||||
SECTION QUESTION
|
||||
www.example.org. IN A
|
||||
SECTION ANSWER
|
||||
www.example.org. IN A 10.20.30.42
|
||||
SECTION AUTHORITY
|
||||
www.example.org. IN NS ns.example.org.
|
||||
SECTION ADDITIONAL
|
||||
ns.example.org. IN A 10.20.30.50
|
||||
ENTRY_END
|
||||
|
||||
; answer to last query
|
||||
STEP 10 CHECK_ANSWER
|
||||
ENTRY_BEGIN
|
||||
MATCH opcode qname qtype
|
||||
SECTION QUESTION
|
||||
www.example.org. IN A
|
||||
SECTION ANSWER
|
||||
www.example.org. IN A 10.20.30.42
|
||||
ENTRY_END
|
||||
|
||||
|
||||
; reply from first query returns
|
||||
STEP 11 REPLY
|
||||
ENTRY_BEGIN
|
||||
MATCH opcode qtype qname
|
||||
ADJUST copy_id
|
||||
REPLY QR RD RA NOERROR
|
||||
SECTION QUESTION
|
||||
www.example.com. IN A
|
||||
SECTION ANSWER
|
||||
www.example.com. IN A 10.20.30.40
|
||||
SECTION AUTHORITY
|
||||
www.example.com. IN NS ns.example.com.
|
||||
SECTION ADDITIONAL
|
||||
ns.example.com. IN A 10.20.30.50
|
||||
ENTRY_END
|
||||
|
||||
; answer to first query
|
||||
STEP 12 CHECK_ANSWER
|
||||
ENTRY_BEGIN
|
||||
MATCH opcode qname qtype
|
||||
SECTION QUESTION
|
||||
www.example.com. IN A
|
||||
SECTION ANSWER
|
||||
www.example.com. IN A 10.20.30.40
|
||||
ENTRY_END
|
||||
|
||||
SCENARIO_END
|
||||
|
||||
; testbound checks before exit:
|
||||
; * no more pending queries outstanding.
|
||||
; * and no answers that have not been checked.
|
2
testdata/fwd_two.rpl
vendored
2
testdata/fwd_two.rpl
vendored
@ -1,6 +1,6 @@
|
||||
; config options go here.
|
||||
server:
|
||||
num-queries-per-thread: 1
|
||||
num-queries-per-thread: 2
|
||||
forward-zone:
|
||||
name: "."
|
||||
forward-addr: 216.0.0.1
|
||||
|
Loading…
Reference in New Issue
Block a user