From 13022a096b20639960b0af56da75cc7e4592ba47 Mon Sep 17 00:00:00 2001 From: Wouter Wijngaards Date: Tue, 13 Feb 2007 14:00:58 +0000 Subject: [PATCH] replay works. git-svn-id: file:///svn/unbound/trunk@90 be551aaa-1e26-0410-a405-d3ace91eadb9 --- doc/Changelog | 3 + testcode/fake_event.c | 334 +++++++++++++++++++++++++++++++----------- testcode/replay.c | 18 ++- testcode/replay.h | 15 +- testdata/replay.fwd | 15 -- 5 files changed, 278 insertions(+), 107 deletions(-) delete mode 100644 testdata/replay.fwd diff --git a/doc/Changelog b/doc/Changelog index 933bb56df..083151ee3 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,3 +1,6 @@ +13 February 2007: Wouter + - work on fake events, first fwd replay works. + 12 February 2007: Wouter - work on fake events. diff --git a/testcode/fake_event.c b/testcode/fake_event.c index c590de622..4b4536e16 100644 --- a/testcode/fake_event.c +++ b/testcode/fake_event.c @@ -69,6 +69,24 @@ fake_event_cleanup() saved_scenario = NULL; } +/** + * Returns a string describing the event type. + */ +static const char* +repevt_string(enum replay_event_type t) +{ + switch(t) { + case repevt_nothing: return "NOTHING"; + case repevt_front_query:return "QUERY"; + case repevt_front_reply:return "CHECK_ANSWER"; + case repevt_timeout: return "TIMEOUT"; + case repevt_back_reply: return "REPLY"; + case repevt_back_query: return "CHECK_OUT_QUERY"; + case repevt_error: return "ERROR"; + default: return "UNKNOWN"; + }; +} + /** delete a fake pending */ static void delete_fake_pending(struct fake_pending* pend) @@ -86,7 +104,11 @@ delete_replay_answer(struct replay_answer* a) { if(!a) return; - ldns_buffer_free(a->buf); + if(a->repinfo.c) { + ldns_buffer_free(a->repinfo.c->buffer); + free(a->repinfo.c); + } + ldns_pkt_free(a->pkt); free(a); } @@ -94,16 +116,21 @@ delete_replay_answer(struct replay_answer* a) * return: true if pending query matches the now event. */ static int -pending_matches_current(struct replay_runtime* runtime) +pending_matches_current(struct replay_runtime* runtime, + struct entry** entry, struct fake_pending **pend) { struct fake_pending* p; + struct entry* e; if(!runtime->now || runtime->now->evt_type != repevt_back_query || !runtime->pending_list) return 0; /* see if any of the pending queries matches */ for(p = runtime->pending_list; p; p = p->next) { - if(find_match(runtime->now->match, p->pkt, p->transport)) + if((e=find_match(runtime->now->match, p->pkt, p->transport))) { + *entry = e; + *pend = p; return 1; + } } return 0; } @@ -124,8 +151,9 @@ pending_find_match(struct replay_runtime* runtime, struct entry** entry, while(p) { if(p->start_step <= timenow && timenow <= p->end_step && (*entry = find_match(p->match, pend->pkt, pend->transport))) { - log_info("matched query time %d in range [%d, %d]", - timenow, p->start_step, p->end_step); + log_info("matched query time %d in range [%d, %d] " + "with entry line %d", timenow, + p->start_step, p->end_step, (*entry)->lineno); return 1; } p = p->next_range; @@ -156,6 +184,27 @@ pending_matches_range(struct replay_runtime* runtime, return 0; } +/** + * Remove the item from the pending list. + */ +static void +pending_list_delete(struct replay_runtime* runtime, struct fake_pending* pend) +{ + struct fake_pending** prev = &runtime->pending_list; + struct fake_pending* p = runtime->pending_list; + + while(p) { + if(p == pend) { + *prev = p->next; + delete_fake_pending(pend); + return; + } + + prev = &p->next; + p = p->next; + } +} + /** * Perform range entry on pending message. * @param runtime: runtime, needed?. @@ -166,34 +215,98 @@ static void answer_callback_from_entry(struct replay_runtime* runtime, struct entry* entry, struct fake_pending* pend) { + struct comm_point c; + ldns_status status; + ldns_pkt* answer_pkt = NULL; + memset(&c, 0, sizeof(c)); + c.fd = -1; + c.buffer = ldns_buffer_new(runtime->bufsize); + c.type = comm_udp; + if(pend->transport == transport_tcp) + c.type = comm_tcp; + if(entry->reply_list->reply_from_hex) { + status = ldns_buffer2pkt_wire(&answer_pkt, + entry->reply_list->reply_from_hex); + if(status != LDNS_STATUS_OK) { + log_err("testbound: hex packet unparsable, sent."); + ldns_buffer_write(c.buffer, + ldns_buffer_begin(entry->reply_list->reply_from_hex), + ldns_buffer_limit(entry->reply_list->reply_from_hex)); + } + } else { + answer_pkt = ldns_pkt_clone(entry->reply_list->reply); + } + if(answer_pkt) { + adjust_packet(entry, answer_pkt, pend->pkt); + status = ldns_pkt2buffer_wire(c.buffer, answer_pkt); + if(status != LDNS_STATUS_OK) + fatal_exit("ldns: cannot pkt2buffer_wire parsed pkt"); + } + if((*pend->callback)(&c, pend->cb_arg, NETEVENT_NOERROR, NULL)) { + fatal_exit("testbound: unexpected: callback returned 1"); + } + ldns_pkt_free(answer_pkt); + ldns_buffer_free(c.buffer); + pending_list_delete(runtime, pend); +} + +/** Check the now moment answer check event */ +static void +answer_check_it(struct replay_runtime* runtime) +{ + struct replay_answer* ans = runtime->answer_list, + **prev = &runtime->answer_list; + log_assert(runtime && runtime->now && + runtime->now->evt_type == repevt_front_reply); + while(ans) { + enum transport_type tr = transport_tcp; + if(ans->repinfo.c->type == comm_udp) + tr = transport_udp; + if(find_match(runtime->now->match, ans->pkt, tr)) { + struct replay_answer *n = ans->next; + log_info("testbound matched event %s entry %d", + repevt_string(runtime->now->evt_type), + runtime->now->match->lineno); + *prev = ans->next; + delete_replay_answer(ans); + ans = n; + } else { + prev = &ans->next; + ans = ans->next; + } + } } /** * Create commpoint (as return address) for a fake incoming query. */ static void -fake_front_query(struct replay_runtime* runtime) +fake_front_query(struct replay_runtime* runtime, struct replay_moment *todo) { struct comm_reply repinfo; ldns_status status; memset(&repinfo, 0, sizeof(repinfo)); repinfo.c = (struct comm_point*)calloc(1, sizeof(struct comm_point)); repinfo.addrlen = (socklen_t)sizeof(struct sockaddr_in); + repinfo.c->ev = (struct internal_event*)runtime; repinfo.c->buffer = ldns_buffer_new(runtime->bufsize); - if(!runtime->now->match->reply_list->reply) { + repinfo.c->type = comm_udp; + if(!todo->match->reply_list->reply) { ldns_buffer_write(repinfo.c->buffer, - ldns_buffer_begin(runtime->now->match->reply_list-> + ldns_buffer_begin(todo->match->reply_list-> reply_from_hex), - ldns_buffer_limit(runtime->now->match->reply_list-> + ldns_buffer_limit(todo->match->reply_list-> reply_from_hex)); ldns_buffer_flip(repinfo.c->buffer); } else { status = ldns_pkt2buffer_wire(repinfo.c->buffer, - runtime->now->match->reply_list->reply); + todo->match->reply_list->reply); if(status != LDNS_STATUS_OK) { fatal_exit("could not parse incoming query pkt"); } } + log_info("testbound: incoming QUERY (event from time %d)", + todo->time_step); /* call the callback for incoming queries */ if((*runtime->callback_query)(repinfo.c, runtime->cb_arg, NETEVENT_NOERROR, &repinfo)) { @@ -204,60 +317,6 @@ fake_front_query(struct replay_runtime* runtime) memset(&repinfo, 0, sizeof(repinfo)); } -/** - * Perform actions or checks determined by the moment. - */ -static void -do_moment(struct replay_runtime* runtime) -{ - if(!runtime->now) - return; - switch(runtime->now->evt_type) { - case repevt_nothing: - break; - case repevt_front_query: - /* call incoming query callback */ - fake_front_query(runtime); - break; - case repevt_front_reply: - fatal_exit("testbound: query answer not matched"); - break; - case repevt_timeout: - /* callback reply routine with timeout */ - break; - case repevt_back_reply: - /* callback the reply routine */ - break; - case repevt_back_query: - fatal_exit("testbound: back query not matched"); - break; - case repevt_error: - /* callback reply routine with error */ - break; - default: - fatal_exit("testbound: unknown event type %d", - runtime->now->evt_type); - } -} - -/** - * Returns a string describing the event type. - */ -static const char* -repevt_string(enum replay_event_type t) -{ - switch(t) { - case repevt_nothing: return "NOTHING"; - case repevt_front_query: return "QUERY"; - case repevt_front_reply: return "CHECK_ANSWER"; - case repevt_timeout: return "TIMEOUT"; - case repevt_back_reply: return "REPLY"; - case repevt_back_query: return "CHECK_OUT_QUERY"; - case repevt_error: return "ERROR"; - default: return "UNKNOWN"; - }; -} - /** * Advance to the next moment. */ @@ -275,27 +334,98 @@ advance_moment(struct replay_runtime* runtime) else log_info("testbound: advancing to step: The End."); } +/** + * Perform actions or checks determined by the moment. + * Also advances the time by one step. + */ +static void +do_moment_and_advance(struct replay_runtime* runtime) +{ + struct replay_moment* mom; + log_info("testbound: do moment"); + if(!runtime->now) { + advance_moment(runtime); + return; + } + switch(runtime->now->evt_type) { + case repevt_nothing: + advance_moment(runtime); + break; + case repevt_front_query: + mom = runtime->now; + advance_moment(runtime); + fake_front_query(runtime, mom); + break; + case repevt_front_reply: + log_err("No query answer or query answer did not match."); + if(runtime->answer_list) + log_err("There are unmatched answers."); + fatal_exit("testbound: query answer not matched"); + break; + case repevt_timeout: + advance_moment(runtime); + /* TODO callback reply routine with timeout */ + break; + case repevt_back_reply: + advance_moment(runtime); + /* TODO callback the reply routine */ + break; + case repevt_back_query: + log_err("Back queries are matched when they are sent out."); + log_err("But no query matching the current moment was sent."); + fatal_exit("testbound: back query not matched"); + break; + case repevt_error: + advance_moment(runtime); + /* TODO callback reply routine with error */ + break; + default: + fatal_exit("testbound: unknown event type %d", + runtime->now->evt_type); + } +} + /** run the scenario in event callbacks */ static void run_scenario(struct replay_runtime* runtime) { struct entry* entry = NULL; struct fake_pending* pending = NULL; + int max_rounds = 50; + int rounds = 0; runtime->now = NULL; + log_info("testbound: entering fake runloop"); do { /* if moment matches pending query do it. */ + /* else if moment matches given answer, do it */ /* else if precoded_range matches pending, do it */ /* else do the current moment */ - if(pending_matches_current(runtime)) { + if(pending_matches_current(runtime, &entry, &pending)) { + advance_moment(runtime); + if(entry->copy_id) + answer_callback_from_entry(runtime, entry, + pending); + } else if(runtime->answer_list && runtime->now && + runtime->now->evt_type == repevt_front_reply) { + answer_check_it(runtime); advance_moment(runtime); } else if(pending_matches_range(runtime, &entry, &pending)) { answer_callback_from_entry(runtime, entry, pending); } else { - do_moment(runtime); - advance_moment(runtime); + do_moment_and_advance(runtime); } + log_info("testbound: end of event stage"); + rounds++; + if(rounds > max_rounds) + fatal_exit("testbound: too many rounds, it loops."); } while(runtime->now); log_info("testbound: exiting event loop (success)."); + if(runtime->pending_list) { + fatal_exit("testbound: there are still messages pending."); + } + if(runtime->answer_list) { + fatal_exit("testbound: there are unmatched answers."); + } } /*********** Dummy routines ***********/ @@ -333,7 +463,8 @@ listen_delete(struct listen_dnsport* listen) free(listen); } -struct comm_base* comm_base_create() +struct comm_base* +comm_base_create() { /* we return the runtime structure instead. */ struct replay_runtime* runtime = (struct replay_runtime*) @@ -342,7 +473,8 @@ struct comm_base* comm_base_create() return (struct comm_base*)runtime; } -void comm_base_delete(struct comm_base* b) +void +comm_base_delete(struct comm_base* b) { struct replay_runtime* runtime = (struct replay_runtime*)b; struct fake_pending* p, *np; @@ -365,19 +497,22 @@ void comm_base_delete(struct comm_base* b) free(runtime); } -void comm_base_dispatch(struct comm_base* b) +void +comm_base_dispatch(struct comm_base* b) { struct replay_runtime* runtime = (struct replay_runtime*)b; run_scenario(runtime); } -void comm_base_exit(struct comm_base* ATTR_UNUSED(b)) +void +comm_base_exit(struct comm_base* ATTR_UNUSED(b)) { /* some sort of failure */ - exit(1); + fatal_exit("testbound: comm_base_exit was called."); } -struct comm_signal* comm_signal_create(struct comm_base* base, +struct comm_signal* +comm_signal_create(struct comm_base* base, void (*callback)(int, void*), void* cb_arg) { struct replay_runtime* runtime = (struct replay_runtime*)base; @@ -386,13 +521,15 @@ struct comm_signal* comm_signal_create(struct comm_base* base, return calloc(1, sizeof(struct comm_signal)); } -int comm_signal_bind(struct comm_signal* ATTR_UNUSED(comsig), int +int +comm_signal_bind(struct comm_signal* ATTR_UNUSED(comsig), int ATTR_UNUSED(sig)) { return 1; } -void comm_signal_delete(struct comm_signal* comsig) +void +comm_signal_delete(struct comm_signal* comsig) { free(comsig); } @@ -400,15 +537,37 @@ void comm_signal_delete(struct comm_signal* comsig) void comm_point_send_reply(struct comm_reply* repinfo) { - /* TODO see if this is checked */ - log_info("comm_point_send_reply fake"); + struct replay_answer* ans = (struct replay_answer*)calloc(1, + sizeof(struct replay_answer)); + ldns_status status; + struct replay_runtime* runtime = (struct replay_runtime*)repinfo->c->ev; + log_info("testbound: comm_point_send_reply fake"); + /* dump it into the todo list */ + log_assert(ans); + memcpy(&ans->repinfo, repinfo, sizeof(struct comm_reply)); + ans->next = NULL; + if(runtime->answer_last) + runtime->answer_last->next = ans; + else runtime->answer_list = ans; + runtime->answer_last = ans; + + /* try to parse packet */ + status = ldns_buffer2pkt_wire(&ans->pkt, ans->repinfo.c->buffer); + if(status != LDNS_STATUS_OK) { + log_err("ldns error parsing packet: %s", + ldns_get_errorstr_by_id(status)); + fatal_exit("Sending unparseable DNS replies to clients!"); + } } void comm_point_drop_reply(struct comm_reply* repinfo) { - /* TODO */ log_info("comm_point_drop_reply fake"); + if(repinfo->c) { + ldns_buffer_free(repinfo->c->buffer); + free(repinfo->c); + } } struct outside_network* @@ -438,7 +597,8 @@ outside_network_delete(struct outside_network* outnet) free(outnet); } -void pending_udp_query(struct outside_network* outnet, ldns_buffer* packet, +void +pending_udp_query(struct outside_network* outnet, ldns_buffer* packet, struct sockaddr_storage* addr, socklen_t addrlen, int timeout, comm_point_callback_t* callback, void* callback_arg) { @@ -464,11 +624,19 @@ void pending_udp_query(struct outside_network* outnet, ldns_buffer* packet, ldns_get_errorstr_by_id(status)); fatal_exit("Sending unparseable DNS packets to servers!"); } - log_info("testbound: created fake pending"); - /* add to list */ - pend->next = runtime->pending_list; - runtime->pending_list = pend; + /* see if it matches the current moment */ + if(runtime->now && runtime->now->evt_type == repevt_back_query && + find_match(runtime->now->match, pend->pkt, transport_udp)) { + log_info("testbound: matched pending to event. " + "advance time between events."); + advance_moment(runtime); + } else { + log_info("testbound: created fake pending"); + /* add to list */ + pend->next = runtime->pending_list; + runtime->pending_list = pend; + } } /*********** End of Dummy routines ***********/ diff --git a/testcode/replay.c b/testcode/replay.c index 6678cdef1..4fe76698d 100644 --- a/testcode/replay.c +++ b/testcode/replay.c @@ -186,7 +186,7 @@ replay_moment_read(char* remain, FILE* in, int* lineno, } else if(parse_keyword(&remain, "TIMEOUT")) { mom->evt_type = repevt_timeout; } else if(parse_keyword(&remain, "ERROR")) { - mom->evt_type = repevt_back_query; + mom->evt_type = repevt_error; } else { log_err("%d: unknown event type %s", *lineno, remain); free(mom); @@ -195,9 +195,10 @@ replay_moment_read(char* remain, FILE* in, int* lineno, if(readentry) { mom->match = read_entry(in, "datafile", lineno, ttl, or, prev); - free(mom); - if(!mom->match) + if(!mom->match) { + free(mom); return NULL; + } } return mom; @@ -272,9 +273,20 @@ replay_scenario_read(FILE* in) else scen->mom_first = mom; scen->mom_last = mom; } else if(parse_keyword(&parse, "SCENARIO_END")) { + struct replay_moment *p = scen->mom_first; + int num = 0; + while(p) { + num++; + p = p->mom_next; + } + log_info("Scenario has %d steps", num); + ldns_rdf_deep_free(or); + ldns_rdf_deep_free(prev); return scen; } } + ldns_rdf_deep_free(or); + ldns_rdf_deep_free(prev); replay_scenario_delete(scen); return NULL; } diff --git a/testcode/replay.h b/testcode/replay.h index 8a0d1296f..3e40e7896 100644 --- a/testcode/replay.h +++ b/testcode/replay.h @@ -53,7 +53,7 @@ * o NOTHING - nothing * o QUERY - followed by entry * o CHECK_ANSWER - followed by entry - * o CHECK_OUT_QUERY - followed by entry + * o CHECK_OUT_QUERY - followed by entry (if copy-id it is also reply). * o REPLY - followed by entry * o TIMEOUT * o ERROR @@ -202,6 +202,9 @@ struct replay_runtime { * to the program. */ struct replay_answer* answer_list; + + /** last element in answer list. */ + struct replay_answer* answer_last; /** callback for incoming queries */ comm_point_callback_t* callback_query; @@ -243,15 +246,15 @@ struct fake_pending { }; /** - * An answer to a back-query, stored so it can be provided as an event. + * An answer that is pending to happen. */ struct replay_answer { /** Next in list */ struct replay_answer* next; - /** pending question this is an answer to */ - struct fake_pending* question; - /** The answer. */ - ldns_buffer* buf; + /** reply information */ + struct comm_reply repinfo; + /** the answer preparsed as ldns pkt */ + ldns_pkt* pkt; }; /** diff --git a/testdata/replay.fwd b/testdata/replay.fwd deleted file mode 100644 index 13618dca4..000000000 --- a/testdata/replay.fwd +++ /dev/null @@ -1,15 +0,0 @@ -; This is a comment. - -SCENARIO_BEGIN Sample of a valid query -RANGE_BEGIN 0 100 - ENTRY_BEGIN - ENTRY_END -RANGE_END -RANGE_BEGIN 200 300 -RANGE_END - -STEP 1 NOTHING -STEP 2 NOTHING -STEP 33 ERROR -STEP 34 TIMEOUT -SCENARIO_END