*/
uint16_t udpsize = edns->udp_size;
int secure = 0;
- uint32_t timenow = (uint32_t)time(0);
+ uint32_t timenow = *worker->env.now;
int must_validate = !(flags&BIT_CD) && worker->env.need_to_validate;
struct dns_msg *msg = NULL;
struct delegpt *dp;
{
struct msgreply_entry* mrentry = (struct msgreply_entry*)e->key;
struct reply_info* rep = (struct reply_info*)e->data;
- uint32_t timenow = time(0);
+ uint32_t timenow = *worker->env.now;
uint16_t udpsize = edns->udp_size;
int secure;
int must_validate = !(flags&BIT_CD) && worker->env.need_to_validate;
worker->thread_num);
alloc_set_id_cleanup(&worker->alloc, &worker_alloc_cleanup, worker);
worker->env = *worker->daemon->env;
+ comm_base_timept(worker->base, &worker->env.now, &worker->env.now_tv);
+ if(worker->thread_num == 0)
+ log_set_time(worker->env.now);
worker->env.worker = worker;
worker->env.send_packet = &worker_send_packet;
worker->env.send_query = &worker_send_query;
comm_signal_delete(worker->comsig);
comm_point_delete(worker->cmd_com);
comm_timer_delete(worker->stat_timer);
+ if(worker->thread_num == 0)
+ log_set_time(NULL);
comm_base_delete(worker->base);
ub_randfree(worker->rndstate);
/* close fds after deleting commpoints, to be sure.
- applied patch to unbound-host man page from Jan-Piet Mens.
- fix donotquery-localhost: yes default (it erroneously was switched
to default 'no').
+ - time is only gotten once and the value is shared across unbound.
+ - unittest cleans up crypto, so that it has no memory leaks.
+ - mini_event shares the time value with unbound this results in
+ +3% speed for cache responses and +9% for recursions.
18 February 2008: Wouter
- patch to unbound-host from Jan-Piet Mens.
struct ub_packed_rrset_key* k;
struct packed_rrset_data* d;
struct rrset_ref ref;
- uint32_t now = time(NULL);
+ uint32_t now = *env->now;
k = alloc_special_obtain(env->alloc);
if(!k)
/** filter out unsuitable targets, return rtt or -1 */
static int
iter_filter_unsuitable(struct iter_env* iter_env, struct module_env* env,
- uint8_t* name, size_t namelen, time_t now, struct delegpt_addr* a)
+ uint8_t* name, size_t namelen, uint32_t now, struct delegpt_addr* a)
{
int rtt;
int lame;
* returns number of best targets (or 0, no suitable targets) */
static int
iter_filter_order(struct iter_env* iter_env, struct module_env* env,
- uint8_t* name, size_t namelen, time_t now, struct delegpt* dp,
+ uint8_t* name, size_t namelen, uint32_t now, struct delegpt* dp,
int* best_rtt)
{
int got_num = 0, got_rtt = 0, thisrtt, swap_to_front;
struct module_env* env, struct delegpt* dp,
uint8_t* name, size_t namelen, int* dnssec_expected)
{
- time_t now = time(NULL);
int sel;
int selrtt;
struct delegpt_addr* a, *prev;
- int num = iter_filter_order(iter_env, env, name, namelen, now, dp,
- &selrtt);
+ int num = iter_filter_order(iter_env, env, name, namelen,
+ *env->now, dp, &selrtt);
if(num == 0)
return NULL;
* cache needs to be primed for the qclass. */
iq->dp = dns_cache_find_delegation(qstate->env, delname,
delnamelen, iq->qchase.qtype, iq->qchase.qclass,
- qstate->region, &iq->deleg_msg, (uint32_t)time(NULL));
+ qstate->region, &iq->deleg_msg, *qstate->env->now);
/* If the cache has returned nothing, then we have a
* root priming situation. */
* gotten this from cache, so test to be sure */
if(!infra_set_lame(qstate->env->infra_cache,
&qstate->reply->addr, qstate->reply->addrlen,
- iq->dp->name, iq->dp->namelen, time(NULL),
- dnsseclame))
+ iq->dp->name, iq->dp->namelen,
+ *qstate->env->now, dnsseclame))
log_err("mark host lame: out of memory");
} else log_err("%slame response from cache",
dnsseclame?"DNSSEC ":"");
w->env->attach_sub = &mesh_attach_sub;
w->env->kill_sub = &mesh_state_delete;
w->env->detect_cycle = &mesh_detect_cycle;
+ comm_base_timept(w->base, &w->env->now, &w->env->now_tv);
return w;
}
/** store rrsets in the rrset cache.
* @param env: module environment with caches.
* @param rep: contains list of rrsets to store.
- * @param now: current time(NULL).
+ * @param now: current time.
*/
static void
store_rrsets(struct module_env* env, struct reply_info* rep, uint32_t now)
hashvalue_t hash, struct reply_info* rep)
{
struct msgreply_entry* e;
- uint32_t now = time(NULL), ttl = rep->ttl;
+ uint32_t ttl = rep->ttl;
size_t i;
/* store RRsets */
rep->ref[i].id = rep->rrsets[i]->id;
}
reply_info_sortref(rep);
- reply_info_set_ttls(rep, now);
- store_rrsets(env, rep, now);
+ reply_info_set_ttls(rep, *env->now);
+ store_rrsets(env, rep, *env->now);
if(ttl == 0) {
/* we do not store the message, but we did store the RRs,
* which could be useful for delegation information */
{
struct delegpt_ns* ns;
struct ub_packed_rrset_key* akey;
- uint32_t now = time(NULL);
+ uint32_t now = *env->now;
for(ns = dp->nslist; ns; ns = ns->next) {
if(ns->resolved)
continue;
struct lruhash_entry* e;
struct query_info k;
hashvalue_t h;
- uint32_t now = (uint32_t)time(NULL);
+ uint32_t now = *env->now;
struct ub_packed_rrset_key* rrset;
/* lookup first, this has both NXdomains and ANSWER responses */
if(is_referral) {
/* store rrsets */
struct rrset_ref ref;
- uint32_t now = time(NULL);
size_t i;
for(i=0; i<rep->rrset_count; i++) {
packed_rrset_ttl_add((struct packed_rrset_data*)
- rep->rrsets[i]->entry.data, now);
+ rep->rrsets[i]->entry.data, *env->now);
ref.key = rep->rrsets[i];
ref.id = rep->rrsets[i]->id;
/*ignore ret: it was in the cache, ref updated */
(void)rrset_cache_update(env->rrset_cache, &ref,
- env->alloc, now);
+ env->alloc, *env->now);
}
free(rep);
return 1;
struct infra_host_data*
infra_lookup_host(struct infra_cache* infra,
struct sockaddr_storage* addr, socklen_t addrlen, int wr,
- time_t timenow, struct infra_host_key** key)
+ uint32_t timenow, struct infra_host_key** key)
{
struct infra_host_data* data;
struct lruhash_entry* e = infra_lookup_host_nottl(infra, addr,
*/
static struct lruhash_entry*
new_host_entry(struct infra_cache* infra, struct sockaddr_storage* addr,
- socklen_t addrlen, time_t tm)
+ socklen_t addrlen, uint32_t tm)
{
struct infra_host_data* data;
struct infra_host_key* key = (struct infra_host_key*)malloc(
int
infra_host(struct infra_cache* infra, struct sockaddr_storage* addr,
- socklen_t addrlen, time_t timenow, int* edns_vs, int* to)
+ socklen_t addrlen, uint32_t timenow, int* edns_vs, int* to)
{
struct lruhash_entry* e = infra_lookup_host_nottl(infra, addr,
addrlen, 0);
int
infra_lookup_lame(struct infra_host_data* host,
- uint8_t* name, size_t namelen, time_t timenow)
+ uint8_t* name, size_t namelen, uint32_t timenow)
{
struct lruhash_entry* e;
struct infra_lame_key k;
int
infra_set_lame(struct infra_cache* infra,
struct sockaddr_storage* addr, socklen_t addrlen,
- uint8_t* name, size_t namelen, time_t timenow, int dnsseclame)
+ uint8_t* name, size_t namelen, uint32_t timenow, int dnsseclame)
{
struct infra_host_data* data;
struct lruhash_entry* e;
int
infra_rtt_update(struct infra_cache* infra,
struct sockaddr_storage* addr, socklen_t addrlen,
- int roundtrip, time_t timenow)
+ int roundtrip, uint32_t timenow)
{
struct lruhash_entry* e = infra_lookup_host_nottl(infra, addr,
addrlen, 1);
int
infra_edns_update(struct infra_cache* infra,
struct sockaddr_storage* addr, socklen_t addrlen,
- int edns_version, time_t timenow)
+ int edns_version, uint32_t timenow)
{
struct lruhash_entry* e = infra_lookup_host_nottl(infra, addr,
addrlen, 1);
infra_get_lame_rtt(struct infra_cache* infra,
struct sockaddr_storage* addr, socklen_t addrlen,
uint8_t* name, size_t namelen, int* lame, int* dnsseclame,
- int* rtt, time_t timenow)
+ int* rtt, uint32_t timenow)
{
struct infra_host_data* host;
struct lruhash_entry* e = infra_lookup_host_nottl(infra, addr,
*/
struct infra_host_data {
/** TTL value for this entry. absolute time. */
- time_t ttl;
+ uint32_t ttl;
/** round trip times for timeout calculation */
struct rtt_info rtt;
/** Names of the zones that are lame. NULL=no lame zones. */
*/
struct infra_lame_data {
/** TTL of this entry. absolute time. */
- time_t ttl;
+ uint32_t ttl;
/** is the host lame (does not serve the zone authoritatively),
* or is the host dnssec lame (does not serve DNSSEC data) */
int isdnsseclame;
*/
struct infra_host_data* infra_lookup_host(struct infra_cache* infra,
struct sockaddr_storage* addr, socklen_t addrlen, int wr,
- time_t timenow, struct infra_host_key** key);
+ uint32_t timenow, struct infra_host_key** key);
/**
* Find host information to send a packet. Creates new entry if not found.
* @return: 0 on error.
*/
int infra_host(struct infra_cache* infra, struct sockaddr_storage* addr,
- socklen_t addrlen, time_t timenow, int* edns_vs, int* to);
+ socklen_t addrlen, uint32_t timenow, int* edns_vs, int* to);
/**
* Check for lameness of this server for a particular zone.
* @return: 0 if not lame or unknown or timed out, 1 if lame, 2 if dnsseclame.
*/
int infra_lookup_lame(struct infra_host_data* host,
- uint8_t* name, size_t namelen, time_t timenow);
+ uint8_t* name, size_t namelen, uint32_t timenow);
/**
* Set a host to be lame for the given zone.
*/
int infra_set_lame(struct infra_cache* infra,
struct sockaddr_storage* addr, socklen_t addrlen,
- uint8_t* name, size_t namelen, time_t timenow, int dnsseclame);
+ uint8_t* name, size_t namelen, uint32_t timenow, int dnsseclame);
/**
* Update rtt information for the host.
*/
int infra_rtt_update(struct infra_cache* infra,
struct sockaddr_storage* addr, socklen_t addrlen,
- int roundtrip, time_t timenow);
+ int roundtrip, uint32_t timenow);
/**
* Update information for the host, store that a TCP transaction works.
*/
int infra_edns_update(struct infra_cache* infra,
struct sockaddr_storage* addr, socklen_t addrlen,
- int edns_version, time_t timenow);
+ int edns_version, uint32_t timenow);
/**
* Get Lameness information and average RTT if host is in the cache.
int infra_get_lame_rtt(struct infra_cache* infra,
struct sockaddr_storage* addr, socklen_t addrlen,
uint8_t* name, size_t namelen, int* lame, int* dnsseclame,
- int* rtt, time_t timenow);
+ int* rtt, uint32_t timenow);
/**
* Get memory used by the infra cache.
void
rrset_update_sec_status(struct rrset_cache* r,
- struct ub_packed_rrset_key* rrset)
+ struct ub_packed_rrset_key* rrset, uint32_t now)
{
- uint32_t now = (uint32_t)time(0);
struct packed_rrset_data* updata =
(struct packed_rrset_data*)rrset->entry.data;
struct lruhash_entry* e;
void
rrset_check_sec_status(struct rrset_cache* r,
- struct ub_packed_rrset_key* rrset)
+ struct ub_packed_rrset_key* rrset, uint32_t now)
{
- uint32_t now = (uint32_t)time(0);
struct packed_rrset_data* updata =
(struct packed_rrset_data*)rrset->entry.data;
struct lruhash_entry* e;
* @param r: the rrset cache.
* @param rrset: which rrset to attempt to update. This rrset is left
* untouched. The rrset in the cache is updated in-place.
+ * @param now: current time.
*/
void rrset_update_sec_status(struct rrset_cache* r,
- struct ub_packed_rrset_key* rrset);
+ struct ub_packed_rrset_key* rrset, uint32_t now);
/**
* Looks up security status of an rrset. Looks up the rrset.
* @param r: the rrset cache.
* @param rrset: This rrset may change security status due to the cache.
* But its status will only improve, towards secure.
+ * @param now: current time.
*/
void rrset_check_sec_status(struct rrset_cache* r,
- struct ub_packed_rrset_key* rrset);
+ struct ub_packed_rrset_key* rrset, uint32_t now);
/** mark rrset to be deleted, set id=0 */
void rrset_markdel(void* key);
struct mesh_reply* r)
{
struct timeval end_time;
+ struct timeval duration;
int secure;
/* examine security status */
if(m->s.env->need_to_validate && !(r->qflags&BIT_CD) && rep &&
}
/* account */
m->s.env->mesh->num_reply_addrs--;
- if(gettimeofday(&end_time, NULL) < 0) {
- log_err("gettimeofday: %s", strerror(errno));
- return;
- } else {
- struct timeval duration;
- timeval_subtract(&duration, &end_time, &r->start_time);
- verbose(VERB_ALGO, "query took %d.%6.6d sec",
- (int)duration.tv_sec, (int)duration.tv_usec);
- m->s.env->mesh->replies_sent++;
- timeval_add(&m->s.env->mesh->replies_sum_wait, &duration);
- timehist_insert(m->s.env->mesh->histogram, &duration);
- }
+ end_time = *m->s.env->now_tv;
+ timeval_subtract(&duration, &end_time, &r->start_time);
+ verbose(VERB_ALGO, "query took %d.%6.6d sec",
+ (int)duration.tv_sec, (int)duration.tv_usec);
+ m->s.env->mesh->replies_sent++;
+ timeval_add(&m->s.env->mesh->replies_sum_wait, &duration);
+ timehist_insert(m->s.env->mesh->histogram, &duration);
}
void mesh_query_done(struct mesh_state* mstate)
r->edns = *edns;
r->qid = qid;
r->qflags = qflags;
- if(gettimeofday(&r->start_time, NULL) < 0) {
- log_err("addrep: gettimeofday: %s", strerror(errno));
- memset(&r->start_time, 0, sizeof(r->start_time));
- }
+ r->start_time = *s->s.env->now_tv;
r->next = s->reply_list;
s->reply_list = r;
return 1;
log_err("malloc failed");
return NULL;
}
+ comm_base_timept(base, &outnet->now_secs, &outnet->now_tv);
outnet->base = base;
outnet->num_tcp = num_tcp;
outnet->infra = infra;
serviced_udp_send(struct serviced_query* sq, ldns_buffer* buff)
{
int rtt, vs;
- time_t now = time(0);
+ uint32_t now = *sq->outnet->now_secs;
if(!infra_host(sq->outnet->infra, &sq->addr, sq->addrlen, now, &vs,
&rtt))
else sq->status = serviced_query_UDP;
}
serviced_encode(sq, buff, sq->status == serviced_query_UDP_EDNS);
- if(gettimeofday(&sq->last_sent_time, NULL) < 0) {
- log_err("gettimeofday: %s", strerror(errno));
- return 0;
- }
+ sq->last_sent_time = *sq->outnet->now_tv;
verbose(VERB_ALGO, "serviced query UDP timeout=%d msec", rtt);
sq->pending = pending_udp_query(sq->outnet, buff, &sq->addr,
sq->addrlen, rtt, serviced_udp_callback, sq, sq->outnet->rnd);
LDNS_RCODE_FORMERR || LDNS_RCODE_WIRE(ldns_buffer_begin(
c->buffer)) == LDNS_RCODE_NOTIMPL) ) {
if(!infra_edns_update(sq->outnet->infra, &sq->addr,
- sq->addrlen, -1, time(0)))
+ sq->addrlen, -1, *sq->outnet->now_secs))
log_err("Out of memory caching no edns for host");
sq->status = serviced_query_TCP;
serviced_tcp_initiate(sq->outnet, sq, c->buffer);
{
struct serviced_query* sq = (struct serviced_query*)arg;
struct outside_network* outnet = sq->outnet;
- struct timeval now;
+ struct timeval now = *sq->outnet->now_tv;
int fallback_tcp = 0;
- if(gettimeofday(&now, NULL) < 0) {
- log_err("gettimeofday: %s", strerror(errno));
- /* this option does not need current time */
- error = NETEVENT_CLOSED;
- }
+
sq->pending = NULL; /* removed after callback */
if(error == NETEVENT_TIMEOUT) {
int rto = 0;
sq->retry++;
if(!(rto=infra_rtt_update(outnet->infra, &sq->addr, sq->addrlen,
- -1, (time_t)now.tv_sec)))
+ -1, (uint32_t)now.tv_sec)))
log_err("out of memory in UDP exponential backoff");
if(sq->retry < OUTBOUND_UDP_RETRY) {
log_name_addr(VERB_ALGO, "retry query", sq->qbuf+10,
ldns_buffer_begin(c->buffer)) == LDNS_RCODE_NOTIMPL)) {
/* note no EDNS, fallback without EDNS */
if(!infra_edns_update(outnet->infra, &sq->addr, sq->addrlen,
- -1, (time_t)now.tv_sec)) {
+ -1, (uint32_t)now.tv_sec)) {
log_err("Out of memory caching no edns for host");
}
sq->status = serviced_query_UDP;
verbose(VERB_ALGO, "measured roundtrip at %d msec", roundtime);
log_assert(roundtime >= 0);
if(!infra_rtt_update(outnet->infra, &sq->addr, sq->addrlen,
- roundtime, (time_t)now.tv_sec))
+ roundtime, (uint32_t)now.tv_sec))
log_err("out of memory noting rtt.");
}
serviced_callbacks(sq, error, c, rep);
struct outside_network {
/** Base for select calls */
struct comm_base* base;
+ /** pointer to time in seconds */
+ uint32_t* now_secs;
+ /** pointer to time in microseconds */
+ struct timeval* now_tv;
/** buffer shared by UDP connections, since there is only one
datagram at any time. */
free(runtime);
}
+void
+comm_base_timept(struct comm_base* b, uint32_t** tt, struct timeval** tv)
+{
+ struct replay_runtime* runtime = (struct replay_runtime*)b;
+ *tt = &runtime->now_secs;
+ *tv = &runtime->now_tv;
+}
+
void
comm_base_dispatch(struct comm_base* b)
{
/** user argument for incoming query callback */
void *cb_arg;
+ /** the current time in seconds */
+ uint32_t now_secs;
+ /** the current time in microseconds */
+ struct timeval now_tv;
+
/** signal handler callback */
void (*sig_cb)(int, void*);
/** signal handler user arg */
size_t zonelen = 13;
struct infra_cache* slab;
struct config_file* cfg = config_create();
- time_t now = 0;
+ uint32_t now = 0;
int vs, to;
struct infra_host_key* k;
struct infra_host_data* d;
msgparse_test();
checklock_stop();
printf("%d checks ok.\n", testcount);
+ EVP_cleanup();
+ CRYPTO_cleanup_all_ex_data();
return 0;
}
struct entry* list = read_datafile(fname);
struct module_env env;
struct val_env ve;
+ uint32_t now = time(NULL);
if(!list)
fatal_exit("could not read %s: %s", fname, strerror(errno));
memset(&ve, 0, sizeof(ve));
env.scratch = region;
env.scratch_buffer = buf;
+ env.now = &now;
ve.date_override = cfg_convert_timeval(at_date);
unit_assert(region && buf);
dnskey = extract_keys(list, &alloc, region, buf);
/** are we using syslog(3) to log to */
static int log_to_syslog = 0;
#endif /* HAVE_SYSLOG_H */
+/** time to print in log, if NULL, use time(2) */
+static uint32_t* log_now = NULL;
void
log_init(const char* filename, int use_syslog, const char* chrootdir)
ident = id;
}
+void log_set_time(uint32_t* t)
+{
+ log_now = t;
+}
+
void
log_vmsg(int pri, const char* type,
const char *format, va_list args)
{
char message[MAXSYSLOGMSGLEN];
unsigned int* tid = (unsigned int*)ub_thread_key_get(logkey);
+ uint32_t now;
(void)pri;
vsnprintf(message, sizeof(message), format, args);
#ifdef HAVE_SYSLOG_H
}
#endif /* HAVE_SYSLOG_H */
if(!logfile) return;
- fprintf(logfile, "[%d] %s[%d:%x] %s: %s\n", (int)time(NULL),
+ if(log_now)
+ now = *log_now;
+ else now = (uint32_t)time(NULL);
+ fprintf(logfile, "[%u] %s[%d:%x] %s: %s\n", (unsigned)now,
ident, (int)getpid(), tid?*tid:0, type, message);
fflush(logfile);
}
*/
void log_ident_set(const char* id);
+/**
+ * Set the time value to print in log entries.
+ * @param t: the point is copied and used to find the time.
+ * if NULL, time(2) is used.
+ */
+void log_set_time(uint32_t* t);
+
/**
* Log informational message.
* Pass printf formatted arguments. No trailing newline is needed.
return 0;
}
+/** set time */
+static int
+settime(struct event_base* base)
+{
+ if(gettimeofday(base->time_tv, NULL) < 0) {
+ return -1;
+ }
+#ifndef S_SPLINT_S
+ *base->time_secs = (uint32_t)base->time_tv->tv_sec;
+#endif
+ return 0;
+}
+
/** create event base */
-void *event_init(void)
+void *event_init(uint32_t* time_secs, struct timeval* time_tv)
{
struct event_base* base = (struct event_base*)malloc(
sizeof(struct event_base));
if(!base)
return NULL;
memset(base, 0, sizeof(*base));
+ base->time_secs = time_secs;
+ base->time_tv = time_tv;
base->times = rbtree_create(mini_ev_cmp);
if(!base->times) {
event_base_free(base);
memmove(&w, &base->writes, sizeof(fd_set));
if((ret = select(base->maxfd+1, &r, &w, NULL, wait)) == -1) {
- if(errno == EAGAIN || errno == EINTR)
+ ret = errno;
+ if(settime(base) < 0)
+ return -1;
+ errno = ret;
+ if(ret == EAGAIN || ret == EINTR)
return 0;
return -1;
}
+ if(settime(base) < 0)
+ return -1;
for(i=0; i<base->maxfd+1; i++) {
short bits = 0;
/** run select in a loop */
int event_base_dispatch(struct event_base* base)
{
- struct timeval now, wait;
+ struct timeval wait;
+ if(settime(base) < 0)
+ return -1;
while(!base->need_to_exit)
{
- if(gettimeofday(&now, NULL) < 0)
- return -1;
/* see if timeouts need handling */
- handle_timeouts(base, &now, &wait);
+ handle_timeouts(base, base->time_tv, &wait);
if(base->need_to_exit)
return 0;
/* do select */
}
if(tv && (ev->ev_events&EV_TIMEOUT)) {
#ifndef S_SPLINT_S
- struct timeval now;
- if(gettimeofday(&now, NULL) < 0)
- return -1;
- ev->ev_timeout.tv_sec = tv->tv_sec + now.tv_sec;
- ev->ev_timeout.tv_usec = tv->tv_usec + now.tv_usec;
+ struct timeval *now = ev->ev_base->time_tv;
+ ev->ev_timeout.tv_sec = tv->tv_sec + now->tv_sec;
+ ev->ev_timeout.tv_usec = tv->tv_usec + now->tv_usec;
while(ev->ev_timeout.tv_usec > 1000000) {
ev->ev_timeout.tv_usec -= 1000000;
ev->ev_timeout.tv_sec++;
struct event** signals;
/** if we need to exit */
int need_to_exit;
+ /** where to store time in seconds */
+ uint32_t* time_secs;
+ /** where to store time in microseconds */
+ struct timeval* time_tv;
};
/**
/* function prototypes as they appear in event.h */
/** create event base */
-void *event_init(void);
+void *event_init(uint32_t* time_secs, struct timeval* time_tv);
/** get version */
const char *event_get_version(void);
/** get polling method, select */
struct alloc_cache* alloc;
/** random table to generate random numbers */
struct ub_randstate* rnd;
+ /** time in seconds, converted to integer */
+ uint32_t* now;
+ /** time in microseconds. Relatively recent. */
+ struct timeval* now_tv;
/** is validation required for messages, controls client-facing
* validation status (AD bits) and servfails */
int need_to_validate;
* Possibly other structures (list, tree) this is part of.
*/
struct internal_event {
+ /** the comm base */
+ struct comm_base* base;
/** libevent event type, alloced here */
struct event ev;
};
struct internal_base {
/** libevent event_base type. */
struct event_base* base;
+ /** seconds time pointer points here */
+ uint32_t secs;
+ /** timeval with current time */
+ struct timeval now;
};
/**
* Internal timer structure, to store timer event in.
*/
struct internal_timer {
+ /** the comm base */
+ struct comm_base* base;
/** libevent event type, alloced here */
struct event ev;
/** is timer enabled */
/* -------- End of local definitions -------- */
+#ifdef USE_MINI_EVENT
+#define comm_base_now(x) /* nothing to do */
+#else /* !USE_MINI_EVENT */
+/** fillup the time values in the event base */
+static void
+comm_base_now(struct comm_base* b)
+{
+ if(gettimeofday(&b->eb->now, NULL) < 0) {
+ log_err("gettimeofday: %s", strerror(errno));
+ }
+ b->eb->secs = (uint32_t)b->eb->now.tv_sec;
+}
+#endif /* USE_MINI_EVENT */
+
struct comm_base*
comm_base_create()
{
free(b);
return NULL;
}
+#ifdef USE_MINI_EVENT
+ /* use mini event time-sharing feature */
+ b->eb->base = event_init(&b->eb->secs, &b->eb->now);
+#else
b->eb->base = event_init();
+#endif
if(!b->eb->base) {
free(b->eb);
free(b);
return NULL;
}
+ comm_base_now(b);
verbose(VERB_ALGO, "libevent %s uses %s method.",
event_get_version(), event_get_method());
return b;
free(b);
}
+void
+comm_base_timept(struct comm_base* b, uint32_t** tt, struct timeval** tv)
+{
+ *tt = &b->eb->secs;
+ *tv = &b->eb->now;
+}
+
void
comm_base_dispatch(struct comm_base* b)
{
if(!(event&EV_READ))
return;
log_assert(rep.c && rep.c->buffer && rep.c->fd == fd);
+ comm_base_now(rep.c->ev->base);
ldns_buffer_clear(rep.c->buffer);
rep.addrlen = (socklen_t)sizeof(rep.addr);
log_assert(fd != -1);
if(!(event&EV_READ))
return;
log_assert(rep.c && rep.c->buffer && rep.c->fd == fd);
+ comm_base_now(rep.c->ev->base);
ldns_buffer_clear(rep.c->buffer);
rep.addrlen = (socklen_t)sizeof(rep.addr);
log_assert(fd != -1);
log_info("ignoring tcp accept event %d", (int)event);
return;
}
+ comm_base_now(c->ev->base);
/* find free tcp handler. */
if(!c->tcp_free) {
log_warn("accepted too many tcp, connections full");
{
struct comm_point* c = (struct comm_point*)arg;
log_assert(c->type == comm_tcp);
+ comm_base_now(c->ev->base);
if(event&EV_READ) {
if(!comm_point_tcp_handle_read(fd, c, 0)) {
{
struct comm_point* c = (struct comm_point*)arg;
log_assert(c->type == comm_local);
+ comm_base_now(c->ev->base);
if(event&EV_READ) {
if(!comm_point_tcp_handle_read(fd, c, 1)) {
{
struct comm_point* c = (struct comm_point*)arg;
log_assert(c->type == comm_raw);
+ comm_base_now(c->ev->base);
(void)(*c->callback)(c, c->cb_arg, NETEVENT_NOERROR, NULL);
}
free(c);
return NULL;
}
+ c->ev->base = base;
c->fd = fd;
c->buffer = buffer;
c->timeout = NULL;
free(c);
return NULL;
}
+ c->ev->base = base;
c->fd = fd;
c->buffer = buffer;
c->timeout = NULL;
free(c);
return NULL;
}
+ c->ev->base = base;
c->fd = -1;
c->buffer = ldns_buffer_new(bufsize);
if(!c->buffer) {
free(c);
return NULL;
}
+ c->ev->base = base;
c->fd = fd;
c->buffer = NULL;
c->timeout = NULL;
free(c);
return NULL;
}
+ c->ev->base = base;
c->fd = -1;
c->buffer = ldns_buffer_new(bufsize);
if(!c->buffer) {
free(c);
return NULL;
}
+ c->ev->base = base;
c->fd = fd;
c->buffer = ldns_buffer_new(bufsize);
if(!c->buffer) {
free(c);
return NULL;
}
+ c->ev->base = base;
c->fd = fd;
c->buffer = NULL;
c->timeout = NULL;
free(tm);
return NULL;
}
+ tm->ev_timer->base = base;
tm->callback = cb;
tm->cb_arg = cb_arg;
event_set(&tm->ev_timer->ev, -1, EV_PERSIST|EV_TIMEOUT,
struct comm_timer* tm = (struct comm_timer*)arg;
if(!(event&EV_TIMEOUT))
return;
+ comm_base_now(tm->ev_timer->base);
tm->ev_timer->enabled = 0;
log_assert(fptr_whitelist_comm_timer(tm->callback));
(*tm->callback)(tm->cb_arg);
struct comm_signal* comsig = (struct comm_signal*)arg;
if(!(event & EV_SIGNAL))
return;
+ comm_base_now(comsig->base);
log_assert(fptr_whitelist_comm_signal(comsig->callback));
(*comsig->callback)(sig, comsig->cb_arg);
}
*/
void comm_base_delete(struct comm_base* b);
+/**
+ * Obtain two pointers. The pointers never change (until base_delete()).
+ * The pointers point to time values that are updated regularly.
+ * @param b: the communication base that will update the time values.
+ * @param tt: pointer to time in seconds is returned.
+ * @param tv: pointer to time in microseconds is returned.
+ */
+void comm_base_timept(struct comm_base* b, uint32_t** tt, struct timeval** tv);
+
/**
* Dispatch the comm base events.
* @param b: the communication to perform.
struct key_entry_key*
key_cache_obtain(struct key_cache* kcache, uint8_t* name, size_t namelen,
- uint16_t key_class, struct regional* region)
+ uint16_t key_class, struct regional* region, uint32_t now)
{
- uint32_t now = time(NULL);
/* keep looking until we find a nonexpired entry */
while(1) {
struct key_entry_key* k = key_cache_search(kcache, name,
* @param namelen: length of the name.
* @param key_class: class of the key.
* @param region: a copy of the key_entry is allocated in this region.
+ * @param now: current time.
* @return pointer to a newly allocated key_entry copy in the region, if
* a key entry could be found, and allocation succeeded and TTL was OK.
* Otherwise, NULL is returned.
*/
struct key_entry_key* key_cache_obtain(struct key_cache* kcache,
uint8_t* name, size_t namelen, uint16_t key_class,
- struct regional* region);
+ struct regional* region, uint32_t now);
/**
* Get memory in use by the key cache.
struct key_entry_key*
key_entry_create_null(struct regional* region,
- uint8_t* name, size_t namelen, uint16_t dclass, uint32_t ttl)
+ uint8_t* name, size_t namelen, uint16_t dclass, uint32_t ttl,
+ uint32_t now)
{
struct key_entry_key* k;
struct key_entry_data* d;
if(!key_entry_setup(region, name, namelen, dclass, &k, &d))
return NULL;
- d->ttl = time(0) + ttl;
+ d->ttl = now + ttl;
d->isbad = 0;
d->rrset_type = LDNS_RR_TYPE_DNSKEY;
d->rrset_data = NULL;
struct key_entry_key*
key_entry_create_rrset(struct regional* region,
uint8_t* name, size_t namelen, uint16_t dclass,
- struct ub_packed_rrset_key* rrset)
+ struct ub_packed_rrset_key* rrset, uint32_t now)
{
struct key_entry_key* k;
struct key_entry_data* d;
rrset->entry.data;
if(!key_entry_setup(region, name, namelen, dclass, &k, &d))
return NULL;
- d->ttl = rd->ttl + time(NULL);
+ d->ttl = rd->ttl + now;
d->isbad = 0;
d->rrset_type = ntohs(rrset->rk.type);
d->rrset_data = (struct packed_rrset_data*)regional_alloc_init(region,
* @param namelen: length of name
* @param dclass: class of key entry. (host order);
* @param ttl: what ttl should the key have. relative.
+ * @param now: current time (added to ttl).
* @return new key entry or NULL on alloc failure
*/
struct key_entry_key* key_entry_create_null(struct regional* region,
- uint8_t* name, size_t namelen, uint16_t dclass, uint32_t ttl);
+ uint8_t* name, size_t namelen, uint16_t dclass, uint32_t ttl,
+ uint32_t now);
/**
* Create a key entry from an rrset, in the given region.
* @param namelen: length of name
* @param dclass: class of key entry. (host order);
* @param rrset: data for key entry. This is copied to the region.
+ * @param now: current time (added to ttl of rrset)
* @return new key entry or NULL on alloc failure
*/
struct key_entry_key* key_entry_create_rrset(struct regional* region,
uint8_t* name, size_t namelen, uint16_t dclass,
- struct ub_packed_rrset_key* rrset);
+ struct ub_packed_rrset_key* rrset, uint32_t now);
/**
* Create a bad entry, in the given region.
enum sec_status
dnskeyset_verify_rrset(struct module_env* env, struct val_env* ve,
- struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* dnskey)
+ struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* dnskey)
{
enum sec_status sec;
size_t i, num;
return sec_status_bogus;
}
for(i=0; i<num; i++) {
- sec = dnskeyset_verify_rrset_sig(env, ve, rrset, dnskey, i,
- &sortree);
+ sec = dnskeyset_verify_rrset_sig(env, ve, *env->now, rrset,
+ dnskey, i, &sortree);
if(sec == sec_status_secure)
return sec;
}
continue;
buf_canon = 0;
sec = dnskey_verify_rrset_sig(env->scratch,
- env->scratch_buffer, ve, rrset, dnskey, dnskey_idx, i,
- &sortree, &buf_canon);
+ env->scratch_buffer, ve, *env->now, rrset,
+ dnskey, dnskey_idx, i, &sortree, &buf_canon);
if(sec == sec_status_secure)
return sec;
}
}
enum sec_status
-dnskeyset_verify_rrset_sig(struct module_env* env, struct val_env* ve,
- struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* dnskey,
- size_t sig_idx, struct rbtree_t** sortree)
+dnskeyset_verify_rrset_sig(struct module_env* env, struct val_env* ve,
+ uint32_t now, struct ub_packed_rrset_key* rrset,
+ struct ub_packed_rrset_key* dnskey, size_t sig_idx,
+ struct rbtree_t** sortree)
{
/* find matching keys and check them */
enum sec_status sec = sec_status_bogus;
numchecked ++;
/* see if key verifies */
sec = dnskey_verify_rrset_sig(env->scratch,
- env->scratch_buffer, ve, rrset, dnskey, i, sig_idx,
- sortree, &buf_canon);
+ env->scratch_buffer, ve, now, rrset, dnskey, i,
+ sig_idx, sortree, &buf_canon);
if(sec == sec_status_secure)
return sec;
}
/** check rrsig dates */
static int
-check_dates(struct val_env* ve, uint8_t* expi_p, uint8_t* incep_p)
+check_dates(struct val_env* ve, uint32_t unow,
+ uint8_t* expi_p, uint8_t* incep_p)
{
/* read out the dates */
int32_t expi, incep, now;
if(ve->date_override) {
now = ve->date_override;
verbose(VERB_ALGO, "date override option %d", (int)now);
- } else now = (int32_t)time(0);
+ } else now = (int32_t)unow;
/* check them */
if(incep - expi > 0) {
/** adjust rrset TTL for verified rrset, compare to original TTL and expi */
static void
-adjust_ttl(struct val_env* ve, struct ub_packed_rrset_key* rrset,
- uint8_t* orig_p, uint8_t* expi_p, uint8_t* incep_p)
+adjust_ttl(struct val_env* ve, uint32_t unow,
+ struct ub_packed_rrset_key* rrset, uint8_t* orig_p,
+ uint8_t* expi_p, uint8_t* incep_p)
{
struct packed_rrset_data* d =
(struct packed_rrset_data*)rrset->entry.data;
/* get current date */
if(ve->date_override) {
now = ve->date_override;
- } else now = (int32_t)time(0);
+ } else now = (int32_t)unow;
expittl = expi - now;
/* so now:
enum sec_status
dnskey_verify_rrset_sig(struct regional* region, ldns_buffer* buf,
- struct val_env* ve,
+ struct val_env* ve, uint32_t now,
struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* dnskey,
size_t dnskey_idx, size_t sig_idx,
struct rbtree_t** sortree, int* buf_canon)
/* original ttl, always ok */
/* verify inception, expiration dates */
- if(!check_dates(ve, sig+2+8, sig+2+12)) {
+ if(!check_dates(ve, now, sig+2+8, sig+2+12)) {
return sec_status_bogus;
}
/* check if TTL is too high - reduce if so */
if(sec == sec_status_secure) {
- adjust_ttl(ve, rrset, sig+2+4, sig+2+8, sig+2+12);
+ adjust_ttl(ve, now, rrset, sig+2+4, sig+2+8, sig+2+12);
}
return sec;
* verify rrset, with dnskey rrset, for a specific rrsig in rrset
* @param env: module environment, scratch space is used.
* @param ve: validator environment, date settings.
+ * @param now: current time for validation (can be overridden).
* @param rrset: to be validated.
* @param dnskey: DNSKEY rrset, keyset to try.
* @param sig_idx: which signature to try to validate.
* or unchecked on error.
*/
enum sec_status dnskeyset_verify_rrset_sig(struct module_env* env,
- struct val_env* ve, struct ub_packed_rrset_key* rrset,
+ struct val_env* ve, uint32_t now, struct ub_packed_rrset_key* rrset,
struct ub_packed_rrset_key* dnskey, size_t sig_idx,
struct rbtree_t** sortree);
* @param region: scratch region used for temporary allocation.
* @param buf: scratch buffer used for canonicalized rrset data.
* @param ve: validator environment, date settings.
+ * @param now: current time for validation (can be overridden).
* @param rrset: to be validated.
* @param dnskey: DNSKEY rrset, keyset.
* @param dnskey_idx: which key from the rrset to try.
* bogus if it did not validate.
*/
enum sec_status dnskey_verify_rrset_sig(struct regional* region,
- ldns_buffer* buf, struct val_env* ve,
+ ldns_buffer* buf, struct val_env* ve, uint32_t now,
struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* dnskey,
size_t dnskey_idx, size_t sig_idx,
struct rbtree_t** sortree, int* buf_canon);
return d->security;
}
/* check in the cache if verification has already been done */
- rrset_check_sec_status(env->rrset_cache, rrset);
+ rrset_check_sec_status(env->rrset_cache, rrset, *env->now);
if(d->security == sec_status_secure) {
log_nametypeclass(VERB_ALGO, "verify rrset from cache",
rrset->rk.dname, ntohs(rrset->rk.type),
* if RRset timed out and clients see proper value. */
}
/* if status updated - store in cache for reuse */
- rrset_update_sec_status(env->rrset_cache, rrset);
+ rrset_update_sec_status(env->rrset_cache, rrset, *env->now);
}
return sec;
verbose(VERB_ALGO, "DS matched DNSKEY.");
return key_entry_create_rrset(region,
ds_rrset->rk.dname, ds_rrset->rk.dname_len,
- ntohs(ds_rrset->rk.rrset_class), dnskey_rrset);
+ ntohs(ds_rrset->rk.rrset_class), dnskey_rrset,
+ *env->now);
}
}
return key_entry_create_null(region, ds_rrset->rk.dname,
ds_rrset->rk.dname_len,
ntohs(ds_rrset->rk.rrset_class),
- rrset_get_ttl(ds_rrset));
+ rrset_get_ttl(ds_rrset), *env->now);
}
/* If any were understandable, then it is bad. */
verbose(VERB_QUERY, "Failed to match any usable DS to a DNSKEY.");
void
val_mark_indeterminate(struct reply_info* rep, struct val_anchors* anchors,
- struct rrset_cache* r)
+ struct rrset_cache* r, struct module_env* env)
{
size_t i;
struct packed_rrset_data* d;
{
/* mark as indeterminate */
d->security = sec_status_indeterminate;
- rrset_update_sec_status(r, rep->rrsets[i]);
+ rrset_update_sec_status(r, rep->rrsets[i], *env->now);
}
}
}
void
val_mark_insecure(struct reply_info* rep, struct key_entry_key* kkey,
- struct rrset_cache* r)
+ struct rrset_cache* r, struct module_env* env)
{
size_t i;
struct packed_rrset_data* d;
dname_subdomain_c(rep->rrsets[i]->rk.dname, kkey->name)) {
/* mark as insecure */
d->security = sec_status_insecure;
- rrset_update_sec_status(r, rep->rrsets[i]);
+ rrset_update_sec_status(r, rep->rrsets[i], *env->now);
}
}
}
* @param rep: the reply with rrsets.
* @param anchors: the trust anchors.
* @param r: rrset cache to store updated security status into.
+ * @param env: module environment
*/
void val_mark_indeterminate(struct reply_info* rep,
- struct val_anchors* anchors, struct rrset_cache* r);
+ struct val_anchors* anchors, struct rrset_cache* r,
+ struct module_env* env);
/**
* Mark all unchecked rrset entries below a NULL key entry as insecure.
* @param kkey: key entry, key_entry_isnull() for it. A key entry that marks
* the end of secure space into insecure space.
* @param r: rrset cache to store updated security status into.
+ * @param env: module environment
*/
void val_mark_insecure(struct reply_info* rep, struct key_entry_key* kkey,
- struct rrset_cache* r);
+ struct rrset_cache* r, struct module_env* env);
/**
* Find next unchecked rrset position, return it for skip.
}
val_mark_indeterminate(vq->chase_reply, qstate->env->anchors,
- qstate->env->rrset_cache);
+ qstate->env->rrset_cache, qstate->env);
vq->key_entry = NULL;
vq->empty_DS_name = NULL;
vq->ds_rrset = 0;
}
vq->key_entry = key_cache_obtain(ve->kcache, lookup_name, lookup_len,
- vq->qchase.qclass, qstate->region);
+ vq->qchase.qclass, qstate->region, *qstate->env->now);
/* if not key, or if keyentry is *above* the trustanchor, i.e.
* the keyentry is based on another (higher) trustanchor */
* essentially proven insecure. */
vq->chase_reply->security = sec_status_insecure;
val_mark_insecure(vq->chase_reply, vq->key_entry,
- qstate->env->rrset_cache);
+ qstate->env->rrset_cache, qstate->env);
/* go to finished state to cache this result */
vq->state = VAL_FINISHED_STATE;
return 1;
vq->signer_name?"":"unsigned ");
vq->chase_reply->security = sec_status_insecure;
val_mark_insecure(vq->chase_reply, vq->key_entry,
- qstate->env->rrset_cache);
+ qstate->env->rrset_cache, qstate->env);
return 1;
}
/* if the result is bogus - set message ttl to bogus ttl to avoid
* endless bogus revalidation */
if(vq->orig_msg->rep->security == sec_status_bogus) {
- vq->orig_msg->rep->ttl = time(0) + ve->bogus_ttl;
+ vq->orig_msg->rep->ttl = *qstate->env->now + ve->bogus_ttl;
/* If we are in permissive mode, bogus gets indeterminate */
if(ve->permissive_mode)
vq->orig_msg->rep->security = sec_status_indeterminate;
kkey = key_entry_create_bad(qstate->region, ta->name,
ta->namelen, ta->dclass);
else kkey = key_entry_create_null(qstate->region, ta->name,
- ta->namelen, ta->dclass, NULL_KEY_TTL);
+ ta->namelen, ta->dclass, NULL_KEY_TTL,
+ *qstate->env->now);
if(!kkey) {
log_err("out of memory: allocate fail prime key");
return NULL;
if(sec == sec_status_secure) {
kkey = key_entry_create_rrset(qstate->region,
ta->name, ta->namelen, ta->dclass,
- dnskey_rrset);
+ dnskey_rrset, *qstate->env->now);
if(!kkey) {
log_err("out of memory: allocate primed key");
return NULL;
kkey = key_entry_create_bad(qstate->region, ta->name,
ta->namelen, ta->dclass);
else kkey = key_entry_create_null(qstate->region, ta->name,
- ta->namelen, ta->dclass, NULL_KEY_TTL);
+ ta->namelen, ta->dclass, NULL_KEY_TTL,
+ *qstate->env->now);
if(!kkey) {
log_err("out of memory: allocate null prime key");
return NULL;
* there was no DS. */
*ke = key_entry_create_null(qstate->region,
qinfo->qname, qinfo->qname_len, qinfo->qclass,
- ub_packed_rrset_ttl(ds));
+ ub_packed_rrset_ttl(ds), *qstate->env->now);
return (*ke) != NULL;
}
/* Otherwise, we return the positive response. */
log_query_info(VERB_DETAIL, "validated DS", qinfo);
*ke = key_entry_create_rrset(qstate->region,
- qinfo->qname, qinfo->qname_len, qinfo->qclass, ds);
+ qinfo->qname, qinfo->qname_len, qinfo->qclass, ds,
+ *qstate->env->now);
return (*ke) != NULL;
} else if(subtype == VAL_CLASS_NODATA ||
subtype == VAL_CLASS_NAMEERROR) {
"referral proved no DS.");
*ke = key_entry_create_null(qstate->region,
qinfo->qname, qinfo->qname_len,
- qinfo->qclass, proof_ttl);
+ qinfo->qclass, proof_ttl,
+ *qstate->env->now);
return (*ke) != NULL;
case sec_status_insecure:
verbose(VERB_DETAIL, "NSEC RRset for the "
"referral proved no DS.");
*ke = key_entry_create_null(qstate->region,
qinfo->qname, qinfo->qname_len,
- qinfo->qclass, proof_ttl);
+ qinfo->qclass, proof_ttl,
+ *qstate->env->now);
return (*ke) != NULL;
case sec_status_indeterminate:
verbose(VERB_DETAIL, "NSEC3s for the "