* New adnslogres, ~100x faster replacement for Apache logresolve;
Thanks to Tony Finch for the program and the performance figure.
* New adnshost utility - currently only a usage message :-).
+ * New internal consistency checking with assert if right options set.
Incompatible changes:
* RRs with mailboxes never rejected due to strange chars if _raw.
if (initstring) {
r= adns_init_strcfg(&ads,
- (adns_if_debug|adns_if_noautosys)^initflagsnum,
+ (adns_if_debug|adns_if_noautosys|adns_if_checkc_freq)
+ ^initflagsnum,
stdout,initstring);
} else {
r= adns_init(&ads,
adns_if_noautosys= 0x0010, /* do not make syscalls at every opportunity */
adns_if_eintr= 0x0020, /* allow _wait and _synchronous to return EINTR */
adns_if_nosigpipe= 0x0040, /* applic has SIGPIPE set to SIG_IGN, do not protect */
+ adns_if_checkc_entex= 0x0100, /* do consistency checks on entry/exit to adns funcs */
+ adns_if_checkc_freq= 0x0300, /* do consistency checks very frequently (slow!) */
} adns_initflags;
typedef enum {
* context_r may be 0. *context_r may not be set when _next returns 0.
*/
+void adns_checkconsistency(adns_state ads);
+/* Checks the consistency of adns's internal data structures.
+ * If any error is found, the program will abort().
+ */
+
/*
* Example expected/legal calling sequence for submit/check/wait:
* adns_init
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
LIBOBJS= types.o event.o query.o reply.o general.o setup.o transmit.o \
- parse.o poll.o
+ parse.o poll.o check.o
--- /dev/null
+/*
+ * check.c
+ * - consistency checks
+ */
+/*
+ * This file is part of adns, which is Copyright (C) 1997-1999 Ian Jackson
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "internal.h"
+
+void adns_checkconsistency(adns_state ads) { adns__consistency(ads,cc_user); }
+
+#define DLIST_CHECK(list, nodevar, part, body) \
+ if ((list).head) { \
+ assert(! (list).head->part back); \
+ for ((nodevar)= (list).head; (nodevar); (nodevar)= (nodevar)->part next) { \
+ assert((nodevar)->part next \
+ ? (nodevar) == (nodevar)->part next->part back \
+ : (nodevar) == (list).tail); \
+ body \
+ } \
+ }
+
+static void checkc_query_alloc(adns_state ads, adns_query qu) {
+ allocnode *an;
+
+ DLIST_CHECK(qu->allocations, an, , {
+ });
+}
+
+static void checkc_query(adns_state ads, adns_query qu) {
+ assert(qu->udpnextserver < ads->nservers);
+ assert(!(qu->udpsent & (~0UL << ads->nservers)));
+ assert(!(qu->tcpfailed & (~0UL << ads->nservers)));
+ assert(qu->udpretries <= UDPMAXRETRIES);
+ assert(qu->search_pos <= ads->nsearchlist);
+}
+
+static void checkc_global(adns_state ads) {
+ int i;
+
+ assert(ads->udpsocket >= 0);
+
+ for (i=0; i<ads->nsortlist; i++)
+ assert(!(ads->sortlist[i].base.s_addr & ~ads->sortlist[i].mask.s_addr));
+
+ assert(ads->tcpserver >= 0 && ads->tcpserver < ads->nservers);
+
+ switch (ads->tcpstate) {
+ case server_connecting:
+ assert(ads->tcpsocket >= 0);
+ case server_disconnected: /* fall through */
+ assert(!ads->tcprecv.used);
+ assert(!ads->tcpsend.used);
+ break;
+ case server_ok:
+ assert(ads->tcpsocket >= 0);
+ break;
+ default:
+ assert(!"ads->tcpstate value");
+ }
+
+ assert(ads->searchlist || !ads->nsearchlist);
+}
+
+static void checkc_queue_timew(adns_state ads) {
+ adns_query qu;
+
+ DLIST_CHECK(ads->timew, qu, , {
+ switch (qu->state) {
+ case query_tosend:
+ assert(qu->udpsent);
+ assert(!qu->tcpfailed);
+ break;
+ case query_tcpwait:
+ assert(ads->tcpstate != server_ok);
+ break;
+ case query_tcpsent:
+ break;
+ default:
+ assert(!"timew state");
+ }
+ assert(!qu->children.head && !qu->children.tail);
+ checkc_query(ads,qu);
+ checkc_query_alloc(ads,qu);
+ });
+}
+
+static void checkc_queue_childw(adns_state ads) {
+ adns_query parent, child;
+
+ DLIST_CHECK(ads->childw, parent, , {
+ assert(parent->state == query_child);
+ assert(parent->children.head);
+ DLIST_CHECK(parent->children, child, siblings., {
+ assert(child->parent == parent);
+ assert(child->state != query_done);
+ });
+ checkc_query(ads,parent);
+ checkc_query_alloc(ads,parent);
+ });
+}
+
+static void checkc_queue_output(adns_state ads) {
+ adns_query qu;
+
+ DLIST_CHECK(ads->output, qu, , {
+ assert(qu->state == query_done);
+ assert(!qu->children.head && !qu->children.tail);
+ assert(!qu->parent);
+ checkc_query(ads,qu);
+ });
+}
+
+void adns__consistency(adns_state ads, consistency_checks cc) {
+ switch (cc) {
+ case cc_user:
+ break;
+ case cc_entex:
+ if (!(ads->iflags & adns_if_checkc_entex)) return;
+ break;
+ case cc_freq:
+ if ((ads->iflags & adns_if_checkc_freq) != adns_if_checkc_freq) return;
+ break;
+ default:
+ abort();
+ }
+
+ checkc_global(ads);
+ checkc_queue_timew(ads);
+ checkc_queue_childw(ads);
+ checkc_queue_output(ads);
+}
void adns_firsttimeout(adns_state ads,
struct timeval **tv_io, struct timeval *tvbuf,
struct timeval now) {
+ adns__consistency(ads,cc_entex);
adns__timeouts(ads, 0, tv_io,tvbuf, now);
+ adns__consistency(ads,cc_entex);
}
void adns_processtimeouts(adns_state ads, const struct timeval *now) {
struct timeval tv_buf;
- adns__must_gettimeofday(ads,&now,&tv_buf); if (!now) return;
- adns__timeouts(ads, 1, 0,0, *now);
+ adns__consistency(ads,cc_entex);
+ adns__must_gettimeofday(ads,&now,&tv_buf);
+ if (now) adns__timeouts(ads, 1, 0,0, *now);
+ adns__consistency(ads,cc_entex);
}
/* fd handling functions. These are the top-level of the real work of
byte udpbuf[DNS_MAXUDP];
struct sockaddr_in udpaddr;
+ adns__consistency(ads,cc_entex);
+
switch (ads->tcpstate) {
case server_disconnected:
case server_connecting:
ads->tcprecv.used -= skip;
memmove(ads->tcprecv.buf,ads->tcprecv.buf+skip,ads->tcprecv.used);
skip= 0;
- if (!adns__vbuf_ensure(&ads->tcprecv,want)) return ENOMEM;
+ if (!adns__vbuf_ensure(&ads->tcprecv,want)) { r= ENOMEM; goto xit; }
assert(ads->tcprecv.used <= ads->tcprecv.avail);
if (ads->tcprecv.used == ads->tcprecv.avail) continue;
r= read(ads->tcpsocket,
ads->tcprecv.used+= r;
} else {
if (r) {
- if (errno==EAGAIN || errno==EWOULDBLOCK) return 0;
+ if (errno==EAGAIN || errno==EWOULDBLOCK) { r= 0; goto xit; }
if (errno==EINTR) continue;
- if (errno_resources(errno)) return errno;
+ if (errno_resources(errno)) { r= errno; goto xit; }
}
adns__tcp_broken(ads,"read",r?strerror(errno):"closed");
- return 0;
+ r= 0; goto xit;
}
} /* never reached */
default:
r= recvfrom(ads->udpsocket,udpbuf,sizeof(udpbuf),0,
(struct sockaddr*)&udpaddr,&udpaddrlen);
if (r<0) {
- if (errno == EAGAIN || errno == EWOULDBLOCK) return 0;
+ if (errno == EAGAIN || errno == EWOULDBLOCK) { r= 0; goto xit; }
if (errno == EINTR) continue;
- if (errno_resources(errno)) return errno;
+ if (errno_resources(errno)) { r= errno; goto xit; }
adns__warn(ads,-1,0,"datagram receive error: %s",strerror(errno));
- return 0;
+ r= 0; goto xit;
}
if (udpaddrlen != sizeof(udpaddr)) {
adns__diag(ads,-1,0,"datagram received with wrong address length %d"
adns__procdgram(ads,udpbuf,r,serv,0,*now);
}
}
- return 0;
+ r= 0;
+xit:
+ adns__consistency(ads,cc_entex);
+ return r;
}
int adns_processwriteable(adns_state ads, int fd, const struct timeval *now) {
int r;
+ adns__consistency(ads,cc_entex);
+
switch (ads->tcpstate) {
case server_disconnected:
break;
if (fd != ads->tcpsocket) break;
assert(ads->tcprecv.used==0);
for (;;) {
- if (!adns__vbuf_ensure(&ads->tcprecv,1)) return ENOMEM;
+ if (!adns__vbuf_ensure(&ads->tcprecv,1)) { r= ENOMEM; goto xit; }
r= read(ads->tcpsocket,&ads->tcprecv.buf,1);
if (r==0 || (r<0 && (errno==EAGAIN || errno==EWOULDBLOCK))) {
tcp_connected(ads,*now);
- return 0;
+ r= 0; goto xit;
}
if (r>0) {
adns__tcp_broken(ads,"connect/read","sent data before first request");
- return 0;
+ r= 0; goto xit;
}
if (errno==EINTR) continue;
- if (errno_resources(errno)) return errno;
+ if (errno_resources(errno)) { r= errno; goto xit; }
adns__tcp_broken(ads,"connect/read",strerror(errno));
- return 0;
+ r= 0; goto xit;
} /* not reached */
case server_ok:
if (!(ads->tcpsend.used && fd == ads->tcpsocket)) break;
adns__sigpipe_unprotect(ads);
if (r<0) {
if (errno==EINTR) continue;
- if (errno==EAGAIN || errno==EWOULDBLOCK) return 0;
- if (errno_resources(errno)) return errno;
+ if (errno==EAGAIN || errno==EWOULDBLOCK) { r= 0; goto xit; }
+ if (errno_resources(errno)) { r= errno; goto xit; }
adns__tcp_broken(ads,"write",strerror(errno));
- return 0;
+ r= 0; goto xit;
} else if (r>0) {
ads->tcpsend.used -= r;
memmove(ads->tcpsend.buf,ads->tcpsend.buf+r,ads->tcpsend.used);
default:
abort();
}
- return 0;
+ r= 0;
+xit:
+ adns__consistency(ads,cc_entex);
+ return r;
}
int adns_processexceptional(adns_state ads, int fd, const struct timeval *now) {
+ adns__consistency(ads,cc_entex);
switch (ads->tcpstate) {
case server_disconnected:
break;
case server_ok:
if (fd != ads->tcpsocket) break;
adns__tcp_broken(ads,"poll/select","exceptional condition detected");
- return 0;
+ break;
default:
abort();
}
+ adns__consistency(ads,cc_entex);
return 0;
}
struct pollfd pollfds[MAX_POLLFDS];
int i, fd, maxfd, npollfds;
+ adns__consistency(ads,cc_entex);
+
if (tv_mod && (!*tv_mod || (*tv_mod)->tv_sec || (*tv_mod)->tv_usec)) {
/* The caller is planning to sleep. */
adns__must_gettimeofday(ads,&now,&tv_nowbuf);
- if (!now) return;
+ if (!now) goto xit;
adns__timeouts(ads, 1, tv_mod,tv_tobuf, *now);
}
if (pollfds[i].events & POLLPRI) FD_SET(fd,exceptfds_io);
}
*maxfd_io= maxfd;
+
+xit:
+ adns__consistency(ads,cc_entex);
}
void adns_afterselect(adns_state ads, int maxfd, const fd_set *readfds,
struct pollfd pollfds[MAX_POLLFDS];
int npollfds, i;
+ adns__consistency(ads,cc_entex);
adns__must_gettimeofday(ads,&now,&tv_buf);
- if (!now) return;
+ if (!now) goto xit;
adns_processtimeouts(ads,now);
npollfds= adns__pollfds(ads,pollfds);
pollfds,npollfds,
maxfd,readfds,writefds,exceptfds,
*now, 0);
+xit:
+ adns__consistency(ads,cc_entex);
}
/* General helpful functions. */
void adns_globalsystemfailure(adns_state ads) {
+ adns__consistency(ads,cc_entex);
+
while (ads->timew.head) {
adns__query_fail(ads->timew.head, adns_s_systemfail);
}
default:
abort();
}
+ adns__consistency(ads,cc_entex);
}
int adns_processany(adns_state ads) {
struct pollfd pollfds[MAX_POLLFDS];
int npollfds;
+ adns__consistency(ads,cc_entex);
+
r= gettimeofday(&now,0);
if (!r) adns_processtimeouts(ads,&now);
pollfds,npollfds,
0,0,0,0,
now,&r);
+
+ adns__consistency(ads,cc_entex);
return r;
}
fd_set readfds, writefds, exceptfds;
struct timeval tvbuf, *tvp;
+ adns__consistency(ads,cc_entex);
for (;;) {
r= internal_check(ads,query_io,answer_r,context_r);
- if (r != EWOULDBLOCK) return r;
+ if (r != EWOULDBLOCK) break;
maxfd= 0; tvp= 0;
FD_ZERO(&readfds); FD_ZERO(&writefds); FD_ZERO(&exceptfds);
adns_beforeselect(ads,&maxfd,&readfds,&writefds,&exceptfds,&tvp,&tvbuf,0);
rsel= select(maxfd,&readfds,&writefds,&exceptfds,tvp);
if (rsel==-1) {
if (errno == EINTR) {
- if (ads->iflags & adns_if_eintr) return EINTR;
+ if (ads->iflags & adns_if_eintr) { r= EINTR; break; }
} else {
adns__diag(ads,-1,0,"select failed in wait: %s",strerror(errno));
adns_globalsystemfailure(ads);
adns_afterselect(ads,maxfd,&readfds,&writefds,&exceptfds,0);
}
}
+ adns__consistency(ads,cc_entex);
+ return r;
}
int adns_check(adns_state ads,
struct timeval now;
int r;
+ adns__consistency(ads,cc_entex);
r= gettimeofday(&now,0);
if (!r) adns__autosys(ads,now);
-
- return internal_check(ads,query_io,answer_r,context_r);
+
+ r= internal_check(ads,query_io,answer_r,context_r);
+ adns__consistency(ads,cc_entex);
+ return r;
}
#define MAX_POLLFDS ADNS_POLLFDS_RECOMMENDED
+typedef enum {
+ cc_user,
+ cc_entex,
+ cc_freq
+} consistency_checks;
+
typedef enum {
rcode_noerror,
rcode_formaterror,
/* Possible states:
*
- * state Queue child id nextudpserver sentudp failedtcp
+ * state Queue child id nextudpserver udpsent tcpfailed
*
* tosend NONE null >=0 0 zero zero
* tosend timew null >=0 any nonzero zero
* tosend NONE null >=0 any nonzero zero
*
- * tcpwait timew null >=0 irrelevant zero any
- * tcpsent timew null >=0 irrelevant zero any
+ * tcpwait timew null >=0 irrelevant any any
+ * tcpsent timew null >=0 irrelevant any any
*
* child childw set >=0 irrelevant irrelevant irrelevant
* child NONE null >=0 irrelevant irrelevant irrelevant
const fd_set *writefds, const fd_set *exceptfds,
struct timeval now, int *r_r);
+/* From check.c: */
+
+void adns__consistency(adns_state ads, consistency_checks cc);
+
/* Useful static inline functions: */
static inline void timevaladd(struct timeval *tv_io, long ms) {
int adns_beforepoll(adns_state ads, struct pollfd *fds, int *nfds_io, int *timeout_io,
const struct timeval *now) {
struct timeval tv_nowbuf, tv_tobuf, *tv_to;
- int space, found, timeout_ms;
+ int space, found, timeout_ms, r;
struct pollfd fds_tmp[MAX_POLLFDS];
+ adns__consistency(ads,cc_entex);
+
if (timeout_io) {
adns__must_gettimeofday(ads,&now,&tv_nowbuf);
- if (!now) { *nfds_io= 0; return 0; }
+ if (!now) { *nfds_io= 0; r= 0; goto xit; }
timeout_ms= *timeout_io;
if (timeout_ms == -1) {
} else {
found= adns__pollfds(ads,fds_tmp);
*nfds_io= found;
- if (space < found) return ERANGE;
+ if (space < found) { r= ERANGE; goto xit; }
memcpy(fds,fds_tmp,sizeof(struct pollfd)*found);
}
- return 0;
+ r= 0;
+xit:
+ adns__consistency(ads,cc_entex);
+ return r;
}
void adns_afterpoll(adns_state ads, const struct pollfd *fds, int nfds,
const struct timeval *now) {
struct timeval tv_buf;
+ adns__consistency(ads,cc_entex);
adns__must_gettimeofday(ads,&now,&tv_buf);
- if (!now) return;
-
- adns__timeouts(ads, 1, 0,0, *now);
- adns__fdevents(ads, fds,nfds, 0,0,0,0, *now,0);
+ if (now) {
+ adns__timeouts(ads, 1, 0,0, *now);
+ adns__fdevents(ads, fds,nfds, 0,0,0,0, *now,0);
+ }
+ adns__consistency(ads,cc_entex);
}
#endif
adns_query qu;
const char *p;
+ adns__consistency(ads,cc_entex);
+
typei= adns__findtype(type);
if (!typei) return ENOSYS;
}
query_simple(ads,qu, owner,ol, typei,flags, now);
}
+ adns__consistency(ads,cc_entex);
return 0;
x_adnsfail:
adns__query_fail(qu,stat);
+ adns__consistency(ads,cc_entex);
return 0;
x_errno:
r= errno;
assert(r);
+ adns__consistency(ads,cc_entex);
return r;
}
}
void adns_cancel(adns_query qu) {
+ adns_state ads;
+
+ ads= qu->ads;
+ adns__consistency(ads,cc_entex);
switch (qu->state) {
case query_tosend: case query_tcpwait: case query_tcpsent:
- LIST_UNLINK(qu->ads->timew,qu);
+ LIST_UNLINK(ads->timew,qu);
break;
case query_child:
- LIST_UNLINK(qu->ads->childw,qu);
+ LIST_UNLINK(ads->childw,qu);
break;
case query_done:
- LIST_UNLINK(qu->ads->output,qu);
+ LIST_UNLINK(ads->output,qu);
break;
default:
abort();
free_query_allocs(qu);
free(qu->answer);
free(qu);
+ adns__consistency(ads,cc_entex);
}
void adns__update_expires(adns_query qu, unsigned long ttl, struct timeval now) {
makefinal_query(qu);
LIST_LINK_TAIL(qu->ads->output,qu);
}
+ qu->state= query_done;
}
void adns__query_fail(adns_query qu, adns_status stat) {
ads->searchndots= v;
continue;
}
+ if (l>=12 && !memcmp(word,"adns_checkc:",12)) {
+ if (!strcmp(word+12,"none")) {
+ ads->iflags &= ~adns_if_checkc_freq;
+ ads->iflags |= adns_if_checkc_entex;
+ } else if (!strcmp(word+12,"entex")) {
+ ads->iflags &= ~adns_if_checkc_freq;
+ ads->iflags |= adns_if_checkc_entex;
+ } else if (!strcmp(word+12,"freq")) {
+ ads->iflags |= adns_if_checkc_freq;
+ } else {
+ configparseerr(ads,fn,lno, "option adns_checkc has bad value `%s' "
+ "(must be none, entex or freq", word+12);
+ }
+ continue;
+ }
adns__diag(ads,-1,0,"%s:%d: unknown option `%.*s'", fn,lno, l,word);
}
}
r= init_finish(ads);
if (r) return r;
+ adns__consistency(ads,cc_entex);
*ads_r= ads;
return 0;
}
}
r= init_finish(ads); if (r) return r;
+ adns__consistency(ads,cc_entex);
*ads_r= ads;
return 0;
}
+
void adns_finish(adns_state ads) {
+ adns__consistency(ads,cc_entex);
for (;;) {
if (ads->timew.head) adns_cancel(ads->timew.head);
else if (ads->childw.head) adns_cancel(ads->childw.head);
}
void adns_forallqueries_begin(adns_state ads) {
+ adns__consistency(ads,cc_entex);
ads->forallnext=
ads->timew.head ? ads->timew.head :
ads->childw.head ? ads->childw.head :
adns_query adns_forallqueries_next(adns_state ads, void **context_r) {
adns_query qu, nqu;
+ adns__consistency(ads,cc_entex);
nqu= ads->forallnext;
for (;;) {
qu= nqu;