/** * Create commpoint (as return address) for a fake incoming query. */ static void fake_front_query(struct replay_runtime* runtime, struct replay_moment *todo) { struct comm_reply repinfo; memset(&repinfo, 0, sizeof(repinfo)); repinfo.c = (struct comm_point*)calloc(1, sizeof(struct comm_point)); repinfo.addrlen = (socklen_t)sizeof(struct sockaddr_in); if(todo->addrlen != 0) { repinfo.addrlen = todo->addrlen; memcpy(&repinfo.addr, &todo->addr, todo->addrlen); } repinfo.c->fd = -1; repinfo.c->ev = (struct internal_event*)runtime; repinfo.c->buffer = sldns_buffer_new(runtime->bufsize); if(todo->match->match_transport == transport_tcp) repinfo.c->type = comm_tcp; else repinfo.c->type = comm_udp; fill_buffer_with_reply(repinfo.c->buffer, todo->match, NULL, 0); log_info("testbound: incoming QUERY"); log_pkt("query pkt", todo->match->reply_list->reply_pkt, todo->match->reply_list->reply_len); /* call the callback for incoming queries */ if((*runtime->callback_query)(repinfo.c, runtime->cb_arg, NETEVENT_NOERROR, &repinfo)) { /* send immediate reply */ comm_point_send_reply(&repinfo); } /* clear it again, in case copy not done properly */ memset(&repinfo, 0, sizeof(repinfo)); }
/** * Find the range that matches this pending message. * @param runtime: runtime with current moment, and range list. * @param entry: returns the pointer to entry that matches. * @param pend: the pending that the entry must match. * @return: true if a match is found. */ static int pending_find_match(struct replay_runtime* runtime, struct entry** entry, struct fake_pending* pend) { int timenow = runtime->now->time_step; struct replay_range* p = runtime->scenario->range_list; while(p) { if(p->start_step <= timenow && timenow <= p->end_step && (p->addrlen == 0 || sockaddr_cmp(&p->addr, p->addrlen, &pend->addr, pend->addrlen) == 0) && (*entry = find_match(p->match, pend->pkt, pend->pkt_len, pend->transport))) { log_info("matched query time %d in range [%d, %d] " "with entry line %d", timenow, p->start_step, p->end_step, (*entry)->lineno); if(p->addrlen != 0) log_addr(0, "matched ip", &p->addr, p->addrlen); log_pkt("matched pkt: ", (*entry)->reply_list->reply_pkt, (*entry)->reply_list->reply_len); return 1; } p = p->next_range; } return 0; }
void comm_point_send_reply(struct comm_reply* repinfo) { 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!"); } log_pkt("reply pkt: ", ans->pkt); }
/** 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 = 5000; int rounds = 0; runtime->now = runtime->scenario->mom_first; 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, &entry, &pending)) { log_info("testbound: do STEP %d CHECK_OUT_QUERY", runtime->now->time_step); 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_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); if(runtime->pending_list) { struct fake_pending* p; log_err("testbound: there are still messages pending."); for(p = runtime->pending_list; p; p=p->next) { log_pkt("pending msg", p->pkt, p->pkt_len); log_addr(0, "pending to", &p->addr, p->addrlen); } fatal_exit("testbound: there are still messages pending."); } if(runtime->answer_list) { fatal_exit("testbound: there are unmatched answers."); } log_info("testbound: exiting fake runloop."); runtime->exit_cleanly = 1; }
struct waiting_tcp* pending_tcp_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) { struct replay_runtime* runtime = (struct replay_runtime*)outnet->base; struct fake_pending* pend = (struct fake_pending*)calloc(1, sizeof(struct fake_pending)); ldns_status status; log_assert(pend); pend->buffer = ldns_buffer_new(ldns_buffer_capacity(packet)); log_assert(pend->buffer); ldns_buffer_write(pend->buffer, ldns_buffer_begin(packet), ldns_buffer_limit(packet)); ldns_buffer_flip(pend->buffer); memcpy(&pend->addr, addr, addrlen); pend->addrlen = addrlen; pend->callback = callback; pend->cb_arg = callback_arg; pend->timeout = timeout; pend->transport = transport_tcp; pend->pkt = NULL; pend->runtime = runtime; pend->serviced = 0; status = ldns_buffer2pkt_wire(&pend->pkt, packet); if(status != LDNS_STATUS_OK) { log_err("ldns error parsing tcp output packet: %s", ldns_get_errorstr_by_id(status)); fatal_exit("Sending unparseable DNS packets to servers!"); } log_pkt("pending tcp pkt: ", pend->pkt); /* see if it matches the current moment */ if(runtime->now && runtime->now->evt_type == repevt_back_query && (runtime->now->addrlen == 0 || sockaddr_cmp( &runtime->now->addr, runtime->now->addrlen, &pend->addr, pend->addrlen) == 0) && find_match(runtime->now->match, pend->pkt, pend->transport)) { log_info("testbound: matched pending to event. " "advance time between events."); log_info("testbound: do STEP %d %s", runtime->now->time_step, repevt_string(runtime->now->evt_type)); advance_moment(runtime); /* still create the pending, because we need it to callback */ } log_info("testbound: created fake pending"); /* add to list */ pend->next = runtime->pending_list; runtime->pending_list = pend; return (struct waiting_tcp*)pend; }
struct waiting_tcp* pending_tcp_query(struct outside_network* outnet, sldns_buffer* packet, struct sockaddr_storage* addr, socklen_t addrlen, int timeout, comm_point_callback_t* callback, void* callback_arg, int ATTR_UNUSED(ssl_upstream)) { struct replay_runtime* runtime = (struct replay_runtime*)outnet->base; struct fake_pending* pend = (struct fake_pending*)calloc(1, sizeof(struct fake_pending)); log_assert(pend); pend->buffer = sldns_buffer_new(sldns_buffer_capacity(packet)); log_assert(pend->buffer); sldns_buffer_write(pend->buffer, sldns_buffer_begin(packet), sldns_buffer_limit(packet)); sldns_buffer_flip(pend->buffer); memcpy(&pend->addr, addr, addrlen); pend->addrlen = addrlen; pend->callback = callback; pend->cb_arg = callback_arg; pend->timeout = timeout; pend->transport = transport_tcp; pend->pkt = NULL; pend->zone = NULL; pend->runtime = runtime; pend->serviced = 0; pend->pkt_len = sldns_buffer_limit(packet); pend->pkt = memdup(sldns_buffer_begin(packet), pend->pkt_len); if(!pend->pkt) fatal_exit("out of memory"); log_pkt("pending tcp pkt: ", pend->pkt, pend->pkt_len); /* see if it matches the current moment */ if(runtime->now && runtime->now->evt_type == repevt_back_query && (runtime->now->addrlen == 0 || sockaddr_cmp( &runtime->now->addr, runtime->now->addrlen, &pend->addr, pend->addrlen) == 0) && find_match(runtime->now->match, pend->pkt, pend->pkt_len, pend->transport)) { log_info("testbound: matched pending to event. " "advance time between events."); log_info("testbound: do STEP %d %s", runtime->now->time_step, repevt_string(runtime->now->evt_type)); advance_moment(runtime); /* still create the pending, because we need it to callback */ } log_info("testbound: created fake pending"); /* add to list */ pend->next = runtime->pending_list; runtime->pending_list = pend; return (struct waiting_tcp*)pend; }
void comm_point_send_reply(struct comm_reply* repinfo) { struct replay_answer* ans = (struct replay_answer*)calloc(1, sizeof(struct replay_answer)); 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 */ ans->pkt = memdup(sldns_buffer_begin(ans->repinfo.c->buffer), sldns_buffer_limit(ans->repinfo.c->buffer)); ans->pkt_len = sldns_buffer_limit(ans->repinfo.c->buffer); if(!ans->pkt) fatal_exit("out of memory"); log_pkt("reply pkt: ", ans->pkt, ans->pkt_len); }