]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
replay works.
authorWouter Wijngaards <wouter@nlnetlabs.nl>
Tue, 13 Feb 2007 14:00:58 +0000 (14:00 +0000)
committerWouter Wijngaards <wouter@nlnetlabs.nl>
Tue, 13 Feb 2007 14:00:58 +0000 (14:00 +0000)
git-svn-id: file:///svn/unbound/trunk@90 be551aaa-1e26-0410-a405-d3ace91eadb9

doc/Changelog
testcode/fake_event.c
testcode/replay.c
testcode/replay.h
testdata/replay.fwd [deleted file]

index 933bb56dfdc7af3129f6ecea64749b004b2504e8..083151ee38d5ee4ed1e26fd14bb5572a6e5318b7 100644 (file)
@@ -1,3 +1,6 @@
+13 February 2007: Wouter
+       - work on fake events, first fwd replay works.
+
 12 February 2007: Wouter
        - work on fake events.
 
index c590de622a68bb58d5c421f7f039c3754f352801..4b4536e16f0213232df59b6c4fde2bf51e1221c2 100644 (file)
@@ -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)) {
@@ -205,34 +318,66 @@ fake_front_query(struct replay_runtime* runtime)
 }
 
 /**
- * Perform actions or checks determined by the moment.
+ * Advance to the next moment.
  */
 static void
-do_moment(struct replay_runtime* runtime)
+advance_moment(struct replay_runtime* runtime)
 {
        if(!runtime->now)
+               runtime->now = runtime->scenario->mom_first;
+       else    runtime->now = runtime->now->mom_next;
+
+       if(runtime->now)
+               log_info("testbound: advancing to STEP %d %s",
+                       runtime->now->time_step, 
+                       repevt_string(runtime->now->evt_type));
+       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: 
-               /* call incoming query callback */
-               fake_front_query(runtime);
+               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:    
-               /* callback reply routine with timeout */
+               advance_moment(runtime);
+               /* TODO callback reply routine with timeout */
                break;
        case repevt_back_reply: 
-               /* callback the reply routine */
+               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:      
-               /* callback reply routine with error */
+               advance_moment(runtime);
+               /* TODO callback reply routine with error */
                break;
        default:
                fatal_exit("testbound: unknown event type %d", 
@@ -240,62 +385,47 @@ do_moment(struct replay_runtime* runtime)
        }
 }
 
-/**
- * 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.
- */
-static void
-advance_moment(struct replay_runtime* runtime)
-{
-       if(!runtime->now)
-               runtime->now = runtime->scenario->mom_first;
-       else    runtime->now = runtime->now->mom_next;
-
-       if(runtime->now)
-               log_info("testbound: advancing to STEP %d %s",
-                       runtime->now->time_step, 
-                       repevt_string(runtime->now->evt_type));
-       else    log_info("testbound: advancing to step: The End.");
-}
-
 /** 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 ***********/
index 6678cdef14267f44c020815cb6faca14a34ce7f6..4fe76698ded54824ab022c99bf7a0f97b1327039 100644 (file)
@@ -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;
 }
index 8a0d1296fbefd7e52b9d242ab3bd6fcc8891c878..3e40e78962fbe7ac94a1992aaab1ccc80aa7424b 100644 (file)
@@ -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 (file)
index 13618dc..0000000
+++ /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