--- /dev/null
+/**/
+
+#ifndef ADNS_INTERNAL_H_INCLUDED
+#define ADNS_INTERNAL_H_INCLUDED
+
+#include <sys/time.h>
+
+#include "adns.h"
+
+/* Configuration and constants */
+
+#define MAXSERVERS 5
+#define MAXUDPRETRIES 15
+#define UDPRETRYMS 2000
+#define TCPMS 30000
+#define LOCALRESOURCEMS 20
+#define UDPMAXDGRAM 512
+#define NSPORT 53
+
+/* Shared data structures */
+
+union adns__align {
+ adns_status status;
+ char *cp;
+ adns_rrtype type;
+ int int;
+ struct in_addr ia;
+ unsigned long ul;
+};
+
+struct adns__query {
+ /* FIXME: make sure this is all init'd properly */
+ adns_query back, next;
+ adns_query parent;
+ struct { adns_query head, tail; } children;
+ struct { adns_query back, next; } siblings;
+ 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;
+ unsigned long sentudp, senttcp; /* bitmaps indexed by server */
+ struct timeval timeout;
+ void *context;
+ unsigned char *querymsg;
+ int querylen;
+ char owner[1];
+ /* 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
+ */
+};
+
+struct adns__vbuf {
+ size_t used, avail;
+ unsigned char *buf;
+};
+
+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;
+ int nextid, udpsocket;
+ adns_vbuf rqbuf, tcpsend, tcprecv;
+ int nservers, tcpserver;
+ enum adns__tcpstate { server_disc, server_connecting, server_ok } tcpstate;
+ int tcpsocket;
+ struct timeval tcptimeout;
+ struct server {
+ struct in_addr addr;
+ } servers[MAXSERVERS];
+};
+
+/* From setup.c: */
+
+void adns__vdiag(adns_state ads, adns_initflags prevent, const char *pfx,
+ int serv, const char *fmt, va_list al);
+void adns__debug(adns_state ads, int serv, const char *fmt, ...) PRINTFFORMAT(2,3);
+void adns__warn(adns_state ads, int serv, const char *fmt, ...) PRINTFFORMAT(2,3);
+void adns__diag(adns_state ads, int serv, const char *fmt, ...) PRINTFFORMAT(2,3);
+
+/* 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) {
+
+/* Useful static inline functions: */
+
+static inline void timevaladd(struct timeval *tv_io, long ms) {
+ struct timeval tmp;
+ assert(ms>=0);
+ tmp= *tv_io;
+ tmp.tv_usec += (ms%1000)*1000;
+ 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'; }
+
+/* Useful macros */
+
+#define LIST_UNLINK_PART(list,node,part) \
+ do { \
+ if ((node)->back) (node)->back->part next= (node)->part next; \
+ else (list).head= (node)->part next; \
+ if ((node)->next) (node)->next->part back= (node)->part back; \
+ else (list).tail= (node)->part back; \
+ } while(0)
+
+#define LIST_LINK_TAIL_PART(list,node,part) \
+ do { \
+ (node)->part back= 0; \
+ (node)->part next= (list).tail; \
+ if ((list).tail) (list).tail->part back= (node); else (list).part head= (node); \
+ (list).tail= (node); \
+ } while(0)
+
+#define LIST_UNLINK(list,node) LIST_UNLINK_PART(list,node,)
+#define LIST_LINK_TAIL_PART(list,node) LIST_LINK_TAIL(list,node,)
+
+#endif
typedef struct adns__query *adns_query;
typedef enum {
- adns_if_noenv= 0x0001, /* do not look at environment */
- adns_if_noerrprint= 0x0002, /* never print output to stderr */
- adns_if_debug= 0x0004, /* print debugging output to stderr */
- adns_if_noautosys= 0x0008, /* do not do full flow-of-control whenever we can */
+ 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_noautosys= 0x0010, /* do not make syscalls at every opportunity */
} adns_initflags;
typedef enum {
typedef enum {
adns_s_ok,
- adns_s_notresponding,
- adns_s_serverfailure,
+ adns_s_timeout,
adns_s_unknownqtype,
- adns_s_remoteerror,
adns_s_nolocalmem,
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_max_misconfig= 199;
adns_s_nxdomain,
adns_s_norecord,
- adns_s_inconsistent, /* for bad PTR */
adns_s_invaliddomain
} adns_status;
-/* In dereferenced answers, multiple addresses show up as multiple
- * answers with all the dm pointers being the same, with ref= adns_s_ok.
- * If no address is available then INADDR_NONE is used, and ref indicates
- * the error.
- */
-
typedef struct {
char *dm;
adns_status astatus;
* ands_check and _wait set *answer to 0.
*/
-int adns_init(adns_state *newstate_r, adns_initflags flags);
+int adns_init(adns_state *newstate_r, adns_initflags flags, FILE *diagfile/*0=>stderr*/);
int adns_synchronous(adns_state ads,
const char *owner,
/**/
+#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);
fd<maxfd && FD_ISSET(fd,fds);
}
+static void tcpserver_broken(adns_state ads, const char *what, const char *why) {
+ assert(ads->tcpstate == server_connecting || ads->tcpstate == server_connected);
+ warn("nameserver %s TCP connection lost: %s: %s",
+ inet_ntoa(ads->servers[tcpserver].addr,what,why));
+ close(ads->tcpsocket);
+ ads->tcpstate= server_disconnected;
+
+
int adns_callback(adns_state ads, int maxfd,
const fd_set *readfds, const fd_set *writefds,
const fd_set *exceptfds) {
- int skip, dgramlen, count;
+ int skip, dgramlen, count, udpaddrlen;
enum adns__tcpstate oldtcpstate;
+ unsigned char udpbuf[UDPMAXDGRAM];
+ struct sockaddr_in udpaddr;
count= 0;
oldtcpstate= ads->tcpstate;
if (ads->tcprecv.buf) {
r= read(ads->tcpsocket,&ads->tcprecv.buf,1);
if (r==0 || (r<0 && (errno==EAGAIN || errno==EWOULDBLOCK))) {
- diag("nameserver %s TCP connection made",
- inet_ntoa(ads->servers[ads->tcpserver].addr));
+ debug("nameserver %s TCP connected",
+ inet_ntoa(ads->servers[ads->tcpserver].addr));
ads->tcpstate= server_connected;
} else if (r>0) {
tcpserver_broken(ads,"connect/read","sent data before first request");
if (ads->tcprecv.used<skip+2+dgramlen) {
want= 2+dgramlen;
} else {
- procdgram(ads,ads->tcprecv.buf+skip+2,dgramlen,-1);
+ procdgram(ads,ads->tcprecv.buf+skip+2,dgramlen,ads->tcpserver);
skip+= 2+dgramlen; continue;
}
}
- Ads->tcprecv.used -= skip;
+ ads->tcprecv.used -= skip;
memmove(ads->tcprecv.buf,ads->tcprecv.buf+skip,ads->tcprecv.used);
vbuf_ensure(&ads->tcprecv,want);
if (ads->tcprecv.used >= ads->tcprecv.avail) break;
}
}
- if (
- break;
-
-
- }
-
- tcpserver_broken(
-
- if (ads-
- used= 0;
- for (;;) {
- vbuf_ensure(&ads->tcprecv,2);
- vbuf_ensure(&ads->tcprecv,
- if (ads->tcprecv.avail<2) break;
- if (ads->tcprecv.used
-
- if (ads->tcprecv.used<2 && ads->tcprecv.avail
- if (ads->tcprecv.used<2 && ads->tcprecv.avail
- r= read(ads->tcpsocket,
- if (adns->tcprecv.used<2) {
- if (
-
- if (ads->tcpstate != server_disc) {
-
-
+ if (callb_checkfd(maxfd,readfds,ads->udpsocket)) {
+ count++;
+ for (;;) {
+ udpaddrlen= sizeof(udpaddr);
+ r= recvfrom(ads->udpsocket,udpbuf,sizeof(udpbuf),0,&udpaddr,&udpaddrlen);
+ if (r<0) {
+ if (!(errno == EAGAIN || errno == EWOULDBLOCK ||
+ errno == EINTR || errno == ENOMEM || errno == ENOBUFS))
+ warn("datagram receive error: %s",strerror(errno));
+ break;
+ }
+ if (udpaddrlen != sizeof(udpaddr)) {
+ diag("datagram received with wrong address length %d (expected %d)",
+ udpaddrlen,sizeof(udpaddr));
+ continue;
+ }
+ if (udpaddr.sin_family != AF_INET) {
+ diag("datagram received with wrong protocol family %u (expected %u)",
+ udpaddr.sin_family,AF_INET);
+ continue;
+ }
+ if (ntohs(udpaddr.sin_port) != NSPORT) {
+ diag("datagram received from wrong port %u (expected %u)",
+ ntohs(udpaddr.sin_port),NSPORT);
+ continue;
+ }
+ for (serv= 0;
+ serv < ads->nservers &&
+ ads->servers[serv].addr.s_addr != udpaddr.sin_addr.s_addr;
+ serv++);
+ if (serv >= ads->nservers) {
+ warn("datagram received from unknown nameserver %s",inet_ntoa(udpaddr.sin_addr));
+ continue;
+ }
+ procdgram(ads,udpbuf,r,serv);
}
- if (maxfd<0 || !readfds || (FD_ISSET
- ads->
-
- abort(); /* FIXME */
+ }
}
- diag("nameserver #%d (%s) TCP connection died: %s",
- inet_ntoa(ads->servers[tcpserver].addr),
static void inter_maxto(struct timeval **tv_io, struct timeval *tvbuf,
struct timeval maxto) {
const char *syscall) {
struct timeval tvto_lr;
- diag(ads,"local system resources scarce (during %s): %s",syscall,strerror(errno));
+ 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;
}
inter_addfd(maxfd,readfds,ads->udpsocket);
+
switch (ads->tcpstate) {
case server_disc:
break;
default:
abort();
}
-
}
static int internal_check(adns_state ads,
for (;;) {
r= internal_check(ads,query_io,answer_r,context_r);
if (r && r != EWOULDBLOCK) return r;
- FD_ZERO(&readfds); FD_ZERO(&writefds); FD_ZERO(&exceptfds);
maxfd= 0; tvp= 0;
+ FD_ZERO(&readfds); FD_ZERO(&writefds); FD_ZERO(&exceptfds);
adns_interest(ads,&maxfd,&readfds,&writefds,&exceptfds,&tvp,&tvbuf);
rsel= select(maxfd,&readfds,&writefds,&exceptfds,tvp);
if (rsel==-1) return r;
--- /dev/null
+/**/
+
+#ifndef ADNS_INTERNAL_H_INCLUDED
+#define ADNS_INTERNAL_H_INCLUDED
+
+#include <sys/time.h>
+
+#include "adns.h"
+
+/* Configuration and constants */
+
+#define MAXSERVERS 5
+#define MAXUDPRETRIES 15
+#define UDPRETRYMS 2000
+#define TCPMS 30000
+#define LOCALRESOURCEMS 20
+#define UDPMAXDGRAM 512
+#define NSPORT 53
+
+/* Shared data structures */
+
+union adns__align {
+ adns_status status;
+ char *cp;
+ adns_rrtype type;
+ int int;
+ struct in_addr ia;
+ unsigned long ul;
+};
+
+struct adns__query {
+ /* FIXME: make sure this is all init'd properly */
+ adns_query back, next;
+ adns_query parent;
+ struct { adns_query head, tail; } children;
+ struct { adns_query back, next; } siblings;
+ 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;
+ unsigned long sentudp, senttcp; /* bitmaps indexed by server */
+ struct timeval timeout;
+ void *context;
+ unsigned char *querymsg;
+ int querylen;
+ char owner[1];
+ /* 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
+ */
+};
+
+struct adns__vbuf {
+ size_t used, avail;
+ unsigned char *buf;
+};
+
+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;
+ int nextid, udpsocket;
+ adns_vbuf rqbuf, tcpsend, tcprecv;
+ int nservers, tcpserver;
+ enum adns__tcpstate { server_disc, server_connecting, server_ok } tcpstate;
+ int tcpsocket;
+ struct timeval tcptimeout;
+ struct server {
+ struct in_addr addr;
+ } servers[MAXSERVERS];
+};
+
+/* From setup.c: */
+
+void adns__vdiag(adns_state ads, adns_initflags prevent, const char *pfx,
+ int serv, const char *fmt, va_list al);
+void adns__debug(adns_state ads, int serv, const char *fmt, ...) PRINTFFORMAT(2,3);
+void adns__warn(adns_state ads, int serv, const char *fmt, ...) PRINTFFORMAT(2,3);
+void adns__diag(adns_state ads, int serv, const char *fmt, ...) PRINTFFORMAT(2,3);
+
+/* 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) {
+
+/* Useful static inline functions: */
+
+static inline void timevaladd(struct timeval *tv_io, long ms) {
+ struct timeval tmp;
+ assert(ms>=0);
+ tmp= *tv_io;
+ tmp.tv_usec += (ms%1000)*1000;
+ 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'; }
+
+/* Useful macros */
+
+#define LIST_UNLINK_PART(list,node,part) \
+ do { \
+ if ((node)->back) (node)->back->part next= (node)->part next; \
+ else (list).head= (node)->part next; \
+ if ((node)->next) (node)->next->part back= (node)->part back; \
+ else (list).tail= (node)->part back; \
+ } while(0)
+
+#define LIST_LINK_TAIL_PART(list,node,part) \
+ do { \
+ (node)->part back= 0; \
+ (node)->part next= (list).tail; \
+ if ((list).tail) (list).tail->part back= (node); else (list).part head= (node); \
+ (list).tail= (node); \
+ } while(0)
+
+#define LIST_UNLINK(list,node) LIST_UNLINK_PART(list,node,)
+#define LIST_LINK_TAIL_PART(list,node) LIST_LINK_TAIL(list,node,)
+
+#endif
}
void adns__quproc_tosend(adns_state ads, adns_query qu, struct timeval now) {
- /* Query must be on the `tosend' queue, and guarantees to remove it. */
+ /* Query must be on the `tosend' queue, and guarantees to remove it.
+ * fixme: Do not send more than 512-byte udp datagrams
+ */
struct sockaddr_in servaddr;
int serv;
memset(&servaddr,0,sizeof(servaddr));
servaddr.sin_family= AF_INET;
servaddr.sin_addr= ads->servers[serv].addr;
- servaddr.sin_port= htons(53);
+ 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;
} else {
if (r<0) {
- diag("sendto %s failed: %s",inet_ntoa(servaddr.sin_addr),strerror(errno));
+ warn("sendto %s failed: %s",inet_ntoa(servaddr.sin_addr),strerror(errno));
}
DLIST_UNLINK(ads->tosend,qu);
timevaladd(&now,UDPRETRYMS);
}
}
+ /* fixme: TCP queries preceded by length */
for (;;) {
serv= tcpserver_get(ads);
if (serv<0) { r=0; break; }
#include "adns-internal.h"
-static void vdebug(adns_state ads, const char *fmt, va_list al) {
- if (!(ads->iflags & adns_if_debug)) return;
- fputs("adns debug: ",stderr);
+void adns__vdiag(adns_state ads, adns_initflags prevent, const char *pfx,
+ int serv, const char *fmt, va_list al) {
+ if (!(ads->iflags & adns_if_debug) && (!prevent || (ads->iflags & prevent))) return;
+ if (serv>=0) {
+ fprintf(stderr,"adns%s: nameserver %s: ",pfx,inet_ntoa(ads->servers[serv].addr));
+ } else {
+ fprintf(stderr,"adns%s: ",pfx);
+ }
vfprintf(stderr,fmt,al);
fputc('\n',stderr);
}
-void adns__debug(adns_state ads, const char *fmt, ...) {
+void adns__debug(adns_state ads, int serv, const char *fmt, ...) {
va_list al;
va_start(al,fmt);
- vdebug(ads,fmt,al);
+ vdiag(ads," debug",0,serv,fmt,al);
va_end(al);
}
-static void vdiag(adns_state ads, const char *fmt, va_list al) {
- if (ads->iflags & adns_if_noerrprint) return;
- fputs("adns: ",stderr);
- vfprintf(stderr,fmt,al);
- fputc('\n',stderr);
+void adns__swarn(adns_state ads, int serv, const char *fmt, ...) {
+ va_list al;
+
+ va_start(al,fmt);
+ vdiag(ads," warning",adns_if_noerrprint|adns_if_noserverwarn,serv,fmt,al);
+ va_end(al);
}
-void adns__diag(adns_state ads, const char *fmt, ...) {
+void adns__diag(adns_state ads, int serv, const char *fmt, ...) {
va_list al;
va_start(al,fmt);
- vdiag(ads,fmt,al);
+ vdiag(ads,"",adns_if_noerrprint,serv,fmt,al);
va_end(al);
}
{ 0 }
};
-static int ctype_whitespace(int c) { return c==' ' || c=='\n' || c=='\t'; }
-static int ctype_digit(int c) { return c>='0' && c<='9'; }
-
static void readconfig(adns_state ads, const char *filename) {
char linebuf[2000], *p, *q;
FILE *file;