summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorian <ian>1998-10-04 14:52:08 +0000
committerian <ian>1998-10-04 14:52:08 +0000
commitddfda8613e6fd8b49d8532a6d34d26df29add034 (patch)
treef4e047142e6807af0225414ecf8eb5674defc8cb
parent4bec51a44bf61bca32e7b947f20b1e7d0f971b94 (diff)
Event loop stuff complete ?
-rw-r--r--src/adns.h52
-rw-r--r--src/event.c168
-rw-r--r--src/internal.h79
-rw-r--r--src/query.c118
-rw-r--r--src/setup.c36
-rw-r--r--src/submit.c8
6 files changed, 302 insertions, 159 deletions
diff --git a/src/adns.h b/src/adns.h
index 32e442a..f749f6e 100644
--- a/src/adns.h
+++ b/src/adns.h
@@ -13,36 +13,61 @@ typedef enum {
adns_if_noenv= 0x0001, /* do not look at environment */
adns_if_noerrprint= 0x0002, /* never print output to stderr (_debug overrides) */
adns_if_noserverwarn= 0x0004, /* do not warn to stderr about duff nameservers etc */
- adns_if_debug= 0x0008, /* enable all output to stderr plus debug msgs*/
+ adns_if_debug= 0x0008, /* enable all output to stderr plus debug msgs */
adns_if_noautosys= 0x0010, /* do not make syscalls at every opportunity */
} adns_initflags;
typedef enum {
- adns_f_search= 0x0001, /* use the searchlist */
- adns_f_usevc= 0x0002, /* use a virtual circuit (TCP connection) */
- adns_f_anyquote= 0x0004,
+ adns_qf_search= 0x0001, /* use the searchlist */
+ adns_qf_usevc= 0x0002, /* use a virtual circuit (TCP connection) */
+ adns_qf_anyquote= 0x0004,
+ adns_qf_loosecname= 0x0008, /* allow refs to CNAMEs - without, get _s_cname */
+ adns_qf_nocname= 0x0010, /* don't follow CNAMEs, instead give _s_cname */
} adns_queryflags;
typedef enum {
adns__rrt_typemask= 0x0ffff,
- adns__qtf_deref= 0x10000, /* dereference domains and produce extra data */
- adns__qtf_mailconv= 0x20000, /* put @ between first and second labels */
+ adns__qtf_deref= 0x10000, /* dereference domains and perhaps produce extra data */
+ adns__qtf_mail822= 0x20000, /* make mailboxes be in RFC822 rcpt field format */
+ adns__qtf_masterfmt= 0x80000, /* convert RRs to master file format, return as str */
+
adns_r_none= 0,
+
adns_r_a= 1,
+ adns_r_a_mf= adns_r_a|adns__qtf_mf,
+
adns_r_ns_raw= 2,
adns_r_ns= adns_r_ns_raw|adns__qtf_deref,
+ adns_r_ns_mf= adns_r_ns_raw|adns__qtf_mf,
+
adns_r_cname= 5,
+ adns_r_cname_mf= adns_r_cname|adns__qtf_mf,
+
adns_r_soa_raw= 6,
- adns_r_soa= adns_r_soa_raw|adns__qtf_mailconv,
+ adns_r_soa= adns_r_soa_raw|adns__qtf_mail822,
+ adns_r_soa_mf= adns_r_soa_raw|adns__qtf_mf,
+
adns_r_null= 10,
+ adns_r_null_mf= adns_r_null|adns__qtf_mf,
+
adns_r_ptr_raw= 12,
adns_r_ptr= adns_r_ptr_raw|adns__qtf_deref,
+ adns_r_ptr_mf= adns_r_ptr_raw|adns__qtf_mf,
+
adns_r_hinfo= 13,
+ adns_r_hinfo_mf= adns_r_hinfo|adns__qtf_mf,
+
adns_r_mx_raw= 15,
adns_r_mx= adns_r_mx_raw|adns__qtf_deref,
+ adns_r_mx_mf= adns_r_mx_raw|adns__qtf_mf,
+
adns_r_txt= 16,
+ adns_r_txt_mf= adns_r_txt|adns__qtf_mf,
+
adns_r_rp_raw= 17,
- adns_r_rp= adns_r_rp_raw|adns__qtf_mailconv
+ adns_r_rp= adns_r_rp_raw|adns__qtf_mail822
+ adns_r_rp_mf= adns_r_rp_raw|adns__qtf_mf,
+
} adns_rrtype;
/* In queries without qtf_anyquote, all domains must have standard
@@ -55,8 +80,10 @@ typedef enum {
* not usually legal in domain names will be quoted as \X
* (if the character is 33-126 except \ and ") or \DDD.
*
- * Do not ask for records containing mailboxes without
- * specifying qtf_mailconv or qtf_anyquote.
+ * _qtf_anyquote is ignored for _mf queries.
+ *
+ * Do not ask for _raw records containing mailboxes without
+ * specifying _qf_anyquote.
*/
typedef enum {
@@ -64,11 +91,10 @@ typedef enum {
adns_s_timeout,
adns_s_unknownqtype,
adns_s_nolocalmem,
- adns_s_connlost,
adns_s_allservfail,
adns_s_max_tempfail= 99,
adns_s_inconsistent, /* PTR gives domain whose A does not match */
- adns_s_badcname, /* CNAME found where actual record expected */
+ adns_s_cname, /* CNAME found where data eg A expected (not if _qf_loosecname) */
adns_s_max_misconfig= 199;
adns_s_nxdomain,
adns_s_norecord,
@@ -89,7 +115,7 @@ typedef struct {
int nrrs;
union {
struct in_addr inaddr[1]; /* a */
- char (*str)[1]; /* ns_raw, cname, ptr, ptr_raw, txt */
+ char (*str)[1]; /* ns_raw, cname, ptr, ptr_raw, txt, <any>_mf */
adns_dmaddr dmaddr[1]; /* ns */
struct { char *a, *b; } strpair[1]; /* hinfo, rp, rp_raw */
struct { int pref; adns_dmaddrs dmaddr; } intdmaddr[1]; /* mx */
diff --git a/src/event.c b/src/event.c
index 3007a95..81e8d4a 100644
--- a/src/event.c
+++ b/src/event.c
@@ -2,41 +2,48 @@
#include "adns-internal.h"
-static void autosys(adns_state ads, struct timeval now) {
- if (ads->iflags & adns_if_noautosys) return;
- adns_callback(ads,-1,0,0,0);
-}
-
-static int callb_checkfd(int maxfd, const fd_set *fds, int fd) {
- return maxfd<0 || !fds ? 1 :
- fd<maxfd && FD_ISSET(fd,fds);
-}
+/* TCP connection management */
void adns__tcp_broken(adns_state ads, const char *what, const char *why) {
int serv;
assert(ads->tcpstate == server_connecting || ads->tcpstate == server_ok);
- warn("nameserver %s TCP connection lost: %s: %s",
- inet_ntoa(ads->servers[tcpserver].addr,what,why));
+ serv= ads->tcpserver;
+ warn("TCP connection lost: %s: %s",serv,why);
close(ads->tcpsocket);
ads->tcpstate= server_disconnected;
- serv= ads->tcpserver;
for (qu= ads->timew; qu; qu= nqu) {
nqu= qu->next;
- if (qu->senttcpserver == -1) continue;
- assert(qu->senttcpserver == serv);
- DLIST_UNLINK(ads->timew,qu);
- adns__query_fail(ads,qu,adns_s_connlost); /* wishlist: send to other servers ? */
+ if (qu->state == query_udp) continue;
+ assert(qu->state == query_tcpwait || qu->state == query_tcpsent);
+ qu->state= query_tcpwait;
+ qu->tcpfailed |= (1<<serv);
+ if (qu->tcpfailed == (1<<ads->nservers)-1) {
+ DLIST_UNLINK(ads->timew,qu);
+ adns__query_fail(ads,qu,adns_s_allservfail);
+ }
}
ads->tcpbuf.used= 0;
ads->tcpserver= (serv+1)%ads->nservers;
}
-void adns__tcp_tryconnect(adns_state ads) {
+static void tcp_connected(adns_state ads, struct timeval now) {
+ debug("TCP connected",ads->tcpserver);
+ ads->tcpstate= server_connected;
+ for (qu= ads->timew.head; qu; qu= nqu) {
+ nqu= qu->next;
+ if (qu->state == query_udp) continue;
+ assert (qu->state == query_tcpwait);
+ adns__query_tcp(ads,qu,now);
+ }
+}
+
+void adns__tcp_tryconnect(adns_state ads, struct timeval now) {
int r, fd, tries;
sockaddr_in addr;
+ /* fixme: single TCP timeout, not once per server */
for (tries=0; tries<ads->nservers; tries++) {
if (ads->tcpstate == server_connecting || ads->tcpstate == server_ok) return;
@@ -55,24 +62,33 @@ void adns__tcp_tryconnect(adns_state ads) {
r= connect(fd,&addr,sizeof(addr));
ads->tcpsocket= fd;
ads->tcpstate= server_connecting;
- if (r==0) { ads->tcpstate= server_ok; return; }
+ if (r==0) { tcp_connected(ads); continue; }
if (errno == EWOULDBLOCK || errno == EINPROGRESS) return;
- tcpserver_broken(ads,"connect",strerror(errno));
+ adns__tcp_broken(ads,"connect",strerror(errno));
}
}
-int adns_callback(adns_state ads, int maxfd,
- const fd_set *readfds, const fd_set *writefds,
- const fd_set *exceptfds) {
- int skip, dgramlen, count, udpaddrlen;
+/* Callback procedures - these do the real work of reception and timeout, etc. */
+
+static int callb_checkfd(int maxfd, const fd_set *fds, int fd) {
+ return maxfd<0 || !fds ? 1 :
+ fd<maxfd && FD_ISSET(fd,fds);
+}
+
+static int internal_callback(adns_state ads, int maxfd,
+ const fd_set *readfds, const fd_set *writefds,
+ const fd_set *exceptfds) {
+ int skip, dgramlen, count, udpaddrlen, oldtcpsocket;
enum adns__tcpstate oldtcpstate;
unsigned char udpbuf[UDPMAXDGRAM];
struct sockaddr_in udpaddr;
count= 0;
- oldtcpstate= ads->tcpstate;
-
- if (ads->tcpstate == server_connecting) {
+
+ switch (ads->tcpstate) {
+ case server_disconnected:
+ break;
+ case server_connecting:
if (callb_checkfd(maxfd,writefds,ads->tcpsocket)) {
count++;
assert(ads->tcprecv.used==0);
@@ -80,9 +96,7 @@ int adns_callback(adns_state ads, int maxfd,
if (ads->tcprecv.buf) {
r= read(ads->tcpsocket,&ads->tcprecv.buf,1);
if (r==0 || (r<0 && (errno==EAGAIN || errno==EWOULDBLOCK))) {
- debug("nameserver %s TCP connected",
- inet_ntoa(ads->servers[ads->tcpserver].addr));
- ads->tcpstate= server_connected;
+ tcpserver_connected(ads);
} else if (r>0) {
tcpserver_broken(ads,"connect/read","sent data before first request");
} else if (errno!=EINTR) {
@@ -90,13 +104,12 @@ int adns_callback(adns_state ads, int maxfd,
}
}
}
- }
- if (ads->tcpstate == server_connected) {
- if (oldtcpstate == server_connected)
- count+= callb_checkfd(maxfd,readfds,ads->tcpsocket) +
- callb_checkfd(maxfd,exceptfds,ads->tcpsocket) +
- (ads->tcpsend.used && callb_checkfd(maxfd,writefds,ads->tcpsocket));
- if (oldtcpstate != server_connected || callb_checkfd(maxfd,readfds,ads->tcpsocket)) {
+ break;
+ case server_ok:
+ count+= callb_checkfd(maxfd,readfds,ads->tcpsocket) +
+ callb_checkfd(maxfd,exceptfds,ads->tcpsocket) +
+ (ads->tcpsend.used && callb_checkfd(maxfd,writefds,ads->tcpsocket));
+ if (callb_checkfd(maxfd,readfds,ads->tcpsocket)) {
skip= 0;
for (;;) {
if (ads->tcprecv.used<skip+2) {
@@ -141,6 +154,8 @@ int adns_callback(adns_state ads, int maxfd,
memmove(ads->tcpsend.buf,ads->tcpsend.buf+r,ads->tcpsend.used);
}
}
+ default:
+ abort();
}
if (callb_checkfd(maxfd,readfds,ads->udpsocket)) {
@@ -182,10 +197,42 @@ int adns_callback(adns_state ads, int maxfd,
}
}
+static void checktimeouts(adns_state ads, struct timeval now,
+ struct timeval **tv_io, struct timeval *tvbuf) {
+ for (qu= ads->timew; qu; qu= nqu) {
+ nqu= qu->next;
+ if (timercmp(&now,qu->timeout,>)) {
+ DLIST_UNLINK(ads->timew,qu);
+ if (qu->state != state_udp) {
+ query_fail(ads,qu,adns_s_notresponding);
+ } else {
+ adns__query_udp(ads,qu,now);
+ }
+ } else {
+ inter_maxtoabs(tv_io,tvbuf,now,qu->timeout);
+ }
+ }
+}
+
+int adns_callback(adns_state ads, int maxfd,
+ const fd_set *readfds, const fd_set *writefds,
+ const fd_set *exceptfds) {
+ struct timeval now;
+
+ r= gettimeofday(&now,0);
+ if (!r) checktimeouts(ads,now,0,0);
+ return internal_callback(ads,maxfd,readfds,writefds,exceptfds);
+}
+
+/* `Interest' functions - find out which fd's we might be interested in,
+ * and when we want to be called back for a timeout.
+ */
+
static void inter_maxto(struct timeval **tv_io, struct timeval *tvbuf,
struct timeval maxto) {
struct timeval rbuf;
+ if (!tv_io) return;
rbuf= *tv_io;
if (!rbuf) { *tvbuf= maxto; *tv_io= tvbuf; return; }
if (timercmp(rbuf,&maxto,>)) *rbuf= maxto;
@@ -194,7 +241,8 @@ static void inter_maxto(struct timeval **tv_io, struct timeval *tvbuf,
static void inter_maxtoabs(struct timeval **tv_io, struct timeval *tvbuf,
struct timeval now, struct timeval maxtime) {
ldiv_t dr;
-
+
+ if (!tv_io) return;
maxtime.tv_sec -= (now.tv_sec-1);
maxtime.tv_usec += (1000-now.tv_usec);
dr= ldiv(maxtime.tv_usec,1000);
@@ -203,17 +251,8 @@ static void inter_maxtoabs(struct timeval **tv_io, struct timeval *tvbuf,
inter_maxto(tv_io,tvbuf,maxtime);
}
-static void localresourcerr(struct timeval **tv_io, struct timeval *tvbuf,
- const char *syscall) {
- struct timeval tvto_lr;
-
- warn(ads,"local system resources scarce (during %s): %s",syscall,strerror(errno));
- timerclear(&tvto_lr); timevaladd(&tvto_lr,LOCALRESOURCEMS);
- inter_maxto(tv_io, tvbuf, tvto_lr);
- return;
-}
-
static void inter_addfd(int *maxfd, fd_set *fds, int fd) {
+ if (!maxfd || !fds) return;
if (fd>=*maxfd) *maxfd= fd+1;
FD_SET(fd,fds);
}
@@ -222,31 +261,19 @@ void adns_interest(adns_state ads, int *maxfd,
fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
struct timeval **tv_io, struct timeval *tvbuf) {
struct timeval now;
+ struct timeval tvto_lr;
adns_query qu;
int r;
r= gettimeofday(&now,0);
- if (r) { localresourcerr(tv_io,tvbuf,"gettimeofday"); return; }
-
- for (qu= ads->timew; qu; qu= nqu) {
- nqu= qu->next;
- if (timercmp(&now,qu->timeout,>)) {
- DLIST_UNLINK(ads->timew,qu);
- if (qu->nextudpserver == -1) {
- query_fail(ads,qu,adns_s_notresponding);
- } else {
- DLIST_LINKTAIL(ads->tosend,qu);
- }
- } else {
- inter_maxtoabs(tv_io,tvbuf,now,qu->timeout);
- }
+ if (r) {
+ warn(ads,"gettimeofday failed - will sleep for a bit: %s",-1,strerror(errno));
+ timerclear(&tvto_lr); timevaladd(&tvto_lr,LOCALRESOURCEMS);
+ inter_maxto(tv_io, tvbuf, tvto_lr);
+ } else {
+ checktimeouts(ads,now,tv_io,tvbuf);
}
- for (qu= ads->tosend; qu; qu= nqu) {
- nqu= qu->next;
- quproc_tosend(ads,qu,now);
- }
-
inter_addfd(maxfd,readfds,ads->udpsocket);
switch (ads->tcpstate) {
@@ -264,6 +291,13 @@ void adns_interest(adns_state ads, int *maxfd,
}
}
+/* User-visible functions and their implementation. */
+
+static void autosys(adns_state ads, struct timeval now) {
+ if (ads->iflags & adns_if_noautosys) return;
+ adns_callback(ads,-1,0,0,0);
+}
+
static int internal_check(adns_state ads,
adns_query *query_io,
adns_answer **answer,
@@ -300,7 +334,7 @@ int adns_wait(adns_state ads,
adns_interest(ads,&maxfd,&readfds,&writefds,&exceptfds,&tvp,&tvbuf);
rsel= select(maxfd,&readfds,&writefds,&exceptfds,tvp);
if (rsel==-1) return r;
- rcb= adns_callback(ads,maxfd,&readfds,&writefds,&exceptfds);
+ rcb= internal_callback(ads,maxfd,&readfds,&writefds,&exceptfds);
assert(rcb==rsel);
}
}
diff --git a/src/internal.h b/src/internal.h
index b8f881d..eaa3a6d 100644
--- a/src/internal.h
+++ b/src/internal.h
@@ -30,6 +30,7 @@ union adns__align {
struct adns__query {
/* FIXME: make sure this is all init'd properly */
+ enum { query_udp, query_tcpwait, query_tcpsent, query_child, query_done } state;
adns_query back, next;
adns_query parent;
struct { adns_query head, tail; } children;
@@ -37,33 +38,79 @@ struct adns__query {
adns_rrtype type;
adns_answer *answer;
size_t ansalloc; ansused;
- int id, flags, udpretries; /* udpretries==-1 => _f_usevc or too big for UDP */
- int nextudpserver, senttcpserver;
- unsigned long sentudp; /* bitmap indexed by server */
+ int id, flags, udpretries;
+ int nextudpserver;
+ unsigned long sentudp, failedtcp; /* bitmap indexed by server */
struct timeval timeout;
void *context;
unsigned char *querymsg;
int querylen;
char owner[1];
+ /* After the owner name and nul comes the query message */
+
/* Possible states:
- * Queue child id answer nextserver sentudp senttcp
- * tosend null >=0 null any any any
- * timew null >=0 null any at least 1 bit set any
- * childw set >=0 partial any any any
- * output null -1 set/null any any any
+ *
+ * state Queue child id answer nextudpserver sentudp failedtcp
+ *
+ * udp NONE null >=0 null 0 zero zero
+ * udp timew null >=0 null any nonzero zero
+ * udp NONE null >=0 null any nonzero zero
+ *
+ * tcpwait timew null >=0 null irrelevant zero any
+ * tcpsent timew null >=0 null irrelevant zero any
+ *
+ * child childw set >=0 partial irrelevant irrelevant irrelevant
+ * done output null -1 set/null irrelevant irrelevant irrelevant
+ *
+ * +------------------------+
+ * START -----> | udp/NONE |
+ * +------------------------+
+ * / |\ \
+ * too big for UDP / UDP timeout \ \ send via UDP
+ * do this ASAP! / more retries \ \ do this ASAP!
+ * |_ desired \ _|
+ * +---------------+ +-----------+
+ * | tcpwait/timew | ____ | udp/timew |
+ * +---------------+ \ +-----------+
+ * | ^ | | |
+ * TCP conn'd; | | TCP died | | |
+ * send via TCP | | more | UDP timeout | |
+ * do this ASAP! | | servers | no more | |
+ * v | to try | retries | |
+ * +---------------+ | desired | |
+ * | tcpsent/timew | ____ | | |
+ * +---------------+ \| | |
+ * \ \ TCP died | TCP | |
+ * \ \ no more | timeout / |
+ * \ \ servers | / |
+ * \ \ to try | / |
+ * got \ \ v |_ / got
+ * reply \ _| +------------------+ / reply
+ * \ | done/output FAIL | /
+ * \ +------------------+ /
+ * \ /
+ * _| |_
+ * (..... got reply ....)
+ * / \
+ * need child query/ies / \ no child query
+ * / \
+ * |_ _|
+ * +--------------+ +----------------+
+ * | child/childw | ----------------> | done/output OK |
+ * +--------------+ children done +----------------+
*/
};
-struct adns__vbuf {
+typedef struct {
size_t used, avail;
unsigned char *buf;
-};
+} adns__vbuf;
struct adns__state {
/* FIXME: make sure this is all init'd properly */
adns_initflags iflags;
FILE *diagfile;
- struct { adns_query head, tail; } tosend, timew, childw, output;
+ struct { adns_query head, tail; } timew, childw, output;
int nextid, udpsocket;
adns_vbuf rqbuf, tcpsend, tcprecv;
int nservers, tcpserver;
@@ -83,13 +130,19 @@ void adns__debug(adns_state ads, int serv, const char *fmt, ...) PRINTFFORMAT(3,
void adns__warn(adns_state ads, int serv, const char *fmt, ...) PRINTFFORMAT(3,4);
void adns__diag(adns_state ads, int serv, const char *fmt, ...) PRINTFFORMAT(3,4);
+static inline int adns__vbuf_ensure(adns__vbuf *vb, size_t want);
+int adns__vbuf_append(adns__vbuf *vb, const byte *data, size_t len);
+int adns__vbuf_appendq(adns__vbuf *vb, const byte *data, size_t len);
+/* 1=>success, 0=>realloc failed */
+
/* From submit.c: */
void adns__query_fail(adns_state ads, adns_query qu, adns_status stat);
/* From query.c: */
-void adns__quproc_tosend(adns_state ads, adns_query qu, struct timeval now);
+void adns__query_udp(adns_state ads, adns_query qu, struct timeval now);
+void adns__query_tcp(adns_state ads, adns_query qu, struct timeval now);
adns_status adns__mkquery(adns_state ads, const char *owner, int ol, int id,
adns_rrtype type, adns_queryflags flags, int *qml_r);
@@ -107,7 +160,7 @@ static inline void timevaladd(struct timeval *tv_io, long ms) {
tmp.tv_sec += ms/1000;
if (tmp.tv_usec >= 1000) { tmp.tv_sec++; tmp.tv_usec -= 1000; }
*tv_io= tmp;
-}
+}
static inline int ctype_whitespace(int c) { return c==' ' || c=='\n' || c=='\t'; }
static inline int ctype_digit(int c) { return c>='0' && c<='9'; }
diff --git a/src/query.c b/src/query.c
index f12e1a3..bd7af8a 100644
--- a/src/query.c
+++ b/src/query.c
@@ -3,22 +3,18 @@
#include "internal.h"
adns_status adns__mkquery(adns_state ads, const char *owner, int ol, int id,
- adns_rrtype type, adns_queryflags flags, int *qml_r) {
- int ll, c, nlabs, qbufreq;
- unsigned char label[255], *nqbuf;
+ adns_rrtype type, adns_queryflags flags) {
+ /* Assembles a query packet in ads->rqbuf. */
+ int ll, c, nlabs;
+ unsigned char label[255], *rqp;
const char *p, *pe;
-#define MKQUERY_ADDB(b) *nqbuf++= (b)
+#define MKQUERY_ADDB(b) *rqp++= (b)
#define MKQUERY_ADDW(w) (MKQUERY_ADDB(((w)>>8)&0x0ff), MKQUERY_ADDB((w)&0x0ff))
- qbufreq= 12+strlen(owner)+3;
- if (ads->qbufavail < qbufreq) {
- nqbuf= realloc(ads->qbuf,qbufreq);
- if (!nqbuf) return adns_s_nolocalmem;
- ads->qbuf= nqbuf; ads->qbufavail= qbufreq;
- }
- nqbuf= ads->qbuf;
-
+ if (!vbuf_ensure(&ads->rqbuf,12+strlen(owner)+3)) return adns_s_nolocalmem;
+ rqp= ads->rqbuf.buf;
+
MKQUERY_ADDW(id);
MKQUERY_ADDB(0x01); /* QR=Q(0), OPCODE=QUERY(0000), !AA, !TC, RD */
MKQUERY_ADDB(0x00); /* !RA, Z=000, RCODE=NOERROR(0000) */
@@ -58,94 +54,100 @@ adns_status adns__mkquery(adns_state ads, const char *owner, int ol, int id,
if (!ll) return adns_s_invaliddomain;
if (nlabs++ > 63) return adns_s_invaliddomain;
MKQUERY_ADDB(ll);
- memcpy(nqbuf,label,ll); nqbuf+= ll;
+ memcpy(rqp,label,ll); rqp+= ll;
} while (p!=pe);
MKQUERY_ADDB(0);
MKQUERY_ADDW(type & adns__rrt_typemask); /* QTYPE */
MKQUERY_ADDW(1); /* QCLASS=IN */
- *qml_r= nqbuf - ads->qbuf;
+ ads->rqbuf.used= rqp - ads->rqbuf.used;
+ assert(ads->rqbuf.used < ads->rqbuf.avail);
return adns_s_ok;
}
-static void quproc_tosend_tcp(adns_state ads, adns_query qu, struct timeval now) {
+void adns__query_tcp(adns_state ads, adns_query qu) {
+ /* Query must be in state tcpwait/timew; it will be moved to a new state
+ * if possible and no further processing can be done on it for now.
+ * (Resulting state is one of tcpwait/timew (if server not connected),
+ * tcpsent/timew, child/childw or done/output.)
+ *
+ * adns__tcp_tryconnect should already have been called - _tcp
+ * will only use an existing connection (if there is one), which it
+ * may break. If the conn list lost then the caller is responsible for any
+ * reestablishment and retry.
+ */
unsigned char length[2];
struct iovec iov[2];
+ if (ads->tcpstate != server_ok) return;
+
length[0]= (qu->querylen&0x0ff00U) >>8;
length[1]= (qu->querylen&0x0ff);
- adns__tcp_tryconnect(ads);
- /* fixme: try sending queries as soon as server comes up */
- /* fixme: use vbuf_ensure, and preallocate buffer space */
- if (ads->tcpstate != server_ok) return;
+ if (!vbuf_ensure(&ads->tcpsend,ads->tcpsend.used+qu->querylen+2)) return;
- DLIST_UNLINK(ads->tosend,qu);
timevaladd(&now,TCPMS);
qu->timeout= now;
qu->senttcpserver= ads->tcpserver;
DLIST_LINKTAIL(ads->timew,qu);
if (ads->opbufused) {
- r= 0;
+ wr= 0;
} else {
iov[0].iovbase= length;
iov[0].iov_len= 2;
iov[1].iovbase= qu->querymsg;
iov[1].iov_len= qu->querylen;
- r= writev(ads->tcpsocket,iov,2);
- if (r < 0) {
+ wr= writev(ads->tcpsocket,iov,2);
+ if (wr < 0) {
if (!(errno == EAGAIN || errno == EINTR || errno == ENOSPC ||
errno == ENOBUFS || errno == ENOMEM)) {
adns__tcp_broken(ads,"write",strerror(errno));
return;
}
- r= 0;
+ wr= 0;
}
}
-
- if (r < qu->querylen+2) {
- newopbufused= qu->opbufused + qu->querylen + 2 - r;
- if (newopbufused > ads->opbufavail) {
- newopbufavail= ads->newopbufused<<1;
- newopbuf= realloc(newopbufavail);
- if (!newopbuf) {
- DLIST_UNLINK(ads->timew,qu);
- query_fail(ads,qu,adns_s_nolocalmem);
- return;
- }
- ads->opbuf= newopbuf;
- ads->opbufavail= newopbufavail;
- }
- if (r<2) {
- memcpy(ads->opbuf+ads->opbufused,length+r,2-r);
- ads->opbufused += (2-r);
- r= 0;
- } else {
- r -= 2;
- }
- memcpy(ads->opbuf+ads->opbufused,qu->querymsg+r,qu->querylen-r);
- ads->opbufused= newopbufused;
+
+ if (wr<2) {
+ r= vbuf_append(&ads->tcpsend,length,2-wr); assert(r);
+ wr= 0;
+ } esle {
+ wr-= 2;
+ }
+ if (wr<qu->querylen) {
+ r= vbuf_append(&ads->tcpsend,qu->querymsg+wr,qu->querylen-wr); assert(r);
}
}
-void adns__quproc_tosend(adns_state ads, adns_query qu, struct timeval now) {
- /* Query must be on the `tosend' queue, and we guarantee to remove it.
+static void query_usetcp(adns_state ads, adns_query qu, struct timeval now) {
+ timevaladd(&now,TCPMS);
+ qu->timeout= now;
+ qu->state= query_tcpwait;
+ DLIST_LINKTAIL(ads->timew,qu);
+ adns__query_tcp(ads,qu);
+ adns__tcp_tryconnect(ads);
+}
+
+void adns__query_udp(adns_state ads, adns_query qu, struct timeval now) {
+ /* Query must be in state udp/NONE; it will be moved to a new state,
+ * and no further processing can be done on it for now.
+ * (Resulting state is one of udp/timew, tcpwait/timew (if server not connected),
+ * tcpsent/timew, child/childw or done/output.)
*/
struct sockaddr_in servaddr;
int serv;
- if (qu->nextudpserver == -1) { quproc_tosend_tcp(ads,qu,now); return; }
- if (qu->querylen > UDPMAXDGRAM) {
- qu->nextudpserver= -1;
- quproc_tosend_tcp(ads,qu,now);
+ assert(qu->state == query_udp);
+ if ((qu->flags & adns_f_usevc) ||
+ (qu->querylen > UDPMAXDGRAM)) {
+ query_usetcp(ads,qu,now);
return;
}
-
+
if (qu->udpretries >= UDPMAXRETRIES) {
- DLIST_UNLINK(ads->tosend,qu);
query_fail(ads,qu,adns_s_notresponding);
return;
}
@@ -157,13 +159,9 @@ void adns__quproc_tosend(adns_state ads, adns_query qu, struct timeval now) {
servaddr.sin_port= htons(NSPORT);
r= sendto(ads->udpsocket,qu->querymsg,qu->querylen,0,&servaddr,sizeof(servaddr));
- if (r<0 && errno == EMSGSIZE) {
- qu->nextudpserver= -1;
- quproc_tosend_tcp(ads,qu,now); return;
- }
+ if (r<0 && errno == EMSGSIZE) { query_usetcp(ads,qu,now); return; }
if (r<0) warn("sendto %s failed: %s",inet_ntoa(servaddr.sin_addr),strerror(errno));
- DLIST_UNLINK(ads->tosend,qu);
timevaladd(&now,UDPRETRYMS);
qu->timeout= now;
qu->sentudp |= (1<<serv);
diff --git a/src/setup.c b/src/setup.c
index d7d9892..f1c2145 100644
--- a/src/setup.c
+++ b/src/setup.c
@@ -38,6 +38,41 @@ void adns__diag(adns_state ads, int serv, const char *fmt, ...) {
va_end(al);
}
+/* FIXME: unsigned char -> byte everywhere */
+
+
+int adns__vbuf_ensure(adns__vbuf *vb, size_t want) {
+ byte *nb;
+
+ if (vb->avail >= want) return;
+ nb= realloc(vb->buf,want); if (!nb) return 0;
+ vb->buf= nb;
+ vb->avail= want;
+ return 1;
+}
+
+void adns__vbuf_appendq(adns__vbuf *vb, const byte *data, size_t len) {
+ memcpy(vb->buf+vb->used,data,len);
+ vb->used+= len;
+}
+
+int adns__vbuf_append(adns__vbuf *vb, const byte *data, size_t len) {
+ size_t newlen, newalloc;
+ byte *nb;
+
+ newlen= vb->used+len;
+ if (vb->avail < newlen) {
+ newlen <<= 1;
+ nb= realloc(vb->buf,newlen);
+ if (!nb) { newlen >>= 1; nb= realloc(vb->buf,newlen); }
+ if (!nb) return 0;
+ vb->buf= nb;
+ vb->avail= newlen;
+ }
+ adns__vbuf_appendq(vb,data,len);
+ return 1;
+}
+
static void addserver(adns_state ads, struct in_addr addr) {
int i;
struct server *ss;
@@ -191,7 +226,6 @@ int adns_init(adns_state *ads_r, adns_initflags flags) {
int r;
ads= malloc(sizeof(*ads)); if (!ads) return errno;
- ads->tosend.head= ads->tosend.tail= 0;
ads->timew.head= ads->timew.tail= 0;
ads->childw.head= ads->childw.tail= 0;
ads->output.head= ads->output.tail= 0;
diff --git a/src/submit.c b/src/submit.c
index f2fd72c..b1fd51e 100644
--- a/src/submit.c
+++ b/src/submit.c
@@ -50,6 +50,8 @@ int adns_submit(adns_state ads,
id= ads->nextid++;
+ r= gettimeofday(&now,0); if (r) return errno;
+
ol= strlen(owner);
if (ol<=1 || ol>MAXDNAME+1)
return failsubmit(ads,context,query_r,type,flags,id,adns_s_invaliddomain);
@@ -59,11 +61,7 @@ int adns_submit(adns_state ads,
if (stat) return failsubmit(ads,context,query_r,type,flags,id,stat);
qu= allocquery(ads,owner,ol,qml,id,type,flags,context); if (!qu) return errno;
- if (qu->flags & adns_f_usevc) qu->udpretries= -1;
- LIST_LINK_TAIL(ads->tosend,qu);
-
- r= gettimeofday(&now,0); if (r) return;
- quproc_tosend(ads,qu,now);
+ adns__query_udp(ads,qu,now);
autosys(ads,now);
*query_r= qu;