#include <sys/socket.h>
#include <netinet/in.h>
+/* All struct in_addr anywhere in adns are in NETWORK byte order. */
+
typedef struct adns__state *adns_state;
typedef struct adns__query *adns_query;
}
void adns__isort(void *array, int nobjs, int sz, void *tempbuf,
- int (*needswap)(const void *a, const void *b)) {
+ int (*needswap)(void *context, const void *a, const void *b),
+ void *context) {
byte *data= array;
int i, place;
for (i=0; i<nobjs; i++) {
- for (place= i; place>0 && needswap(data + (place-1)*sz, data + i*sz); place--);
-
+ for (place= i;
+ place>0 && needswap(context, data + (place-1)*sz, data + i*sz);
+ place--);
if (place != i) {
memcpy(tempbuf, data + i*sz, sz);
memmove(data + (place+1)*sz, data + place*sz, (i-place)*sz);
/* Configuration and constants */
#define MAXSERVERS 5
-#define UDPMAXRETRIES /*15*/5
+#define MAXSORTLIST 15
+#define UDPMAXRETRIES /*15 fixme*/5
#define UDPRETRYMS 2000
#define TCPMS 30000
#define LOCALRESOURCEMS 20
* nsstart is the offset of the authority section.
*/
- int (*diff_needswap)(const void *datap_a, const void *datap_b);
+ int (*diff_needswap)(adns_state ads, const void *datap_a, const void *datap_b);
/* Returns !0 if RR a should be strictly after RR b in the sort order,
* 0 otherwise. Must not fail.
*/
struct { adns_query head, tail; } timew, childw, output;
int nextid, udpsocket, tcpsocket;
vbuf tcpsend, tcprecv;
- int nservers, tcpserver;
+ int nservers, nsortlist, tcpserver;
enum adns__tcpstate { server_disconnected, server_connecting, server_ok } tcpstate;
struct timeval tcptimeout;
struct server {
struct in_addr addr;
} servers[MAXSERVERS];
+ struct sortlist {
+ struct in_addr base, mask;
+ } sortlist[MAXSORTLIST];
};
/* From setup.c: */
*/
void adns__isort(void *array, int nobjs, int sz, void *tempbuf,
- int (*needswap)(const void *a, const void *b));
+ int (*needswap)(void *context, const void *a, const void *b),
+ void *context);
/* Does an insertion sort of array which must contain nobjs objects
* each sz bytes long. tempbuf must point to a buffer at least
* sz bytes long. needswap should return !0 if a>b (strictly, ie
return;
}
adns__isort(ans->rrs.bytes, ans->nrrs, ans->rrsz,
- qu->vb.buf, qu->typei->diff_needswap);
+ qu->vb.buf,
+ (int(*)(void*, const void*, const void*))qu->typei->diff_needswap,
+ qu->ads);
}
parent= qu->parent;
adns__diag(ads,-1,0,"warning - `search' ignored fixme");
}
-static void ccf_sortlist(adns_state ads, const char *fn, int lno, const char *buf) {
- adns__diag(ads,-1,0,"warning - `sortlist' ignored fixme");
+static void ccf_sortlist(adns_state ads, const char *fn, int lno, const char *bufp) {
+ const char *p, *q;
+ char tbuf[200], *slash, *ep;
+ struct in_addr base, mask;
+ int l;
+ unsigned long initial, baselocal;
+
+ ads->nsortlist= 0;
+ if (!bufp) return;
+
+ for (;;) {
+ while (ctype_whitespace(*bufp)) bufp++;
+ if (!*bufp) return;
+
+ q= bufp;
+ while (*q && !ctype_whitespace(*q)) q++;
+
+ p= bufp;
+ l= q-p;
+ bufp= q;
+
+ if (ads->nsortlist >= MAXSORTLIST) {
+ adns__diag(ads,-1,0,"too many sortlist entries, ignoring %.*s onwards",l,p);
+ return;
+ }
+
+ if (l >= sizeof(tbuf)) {
+ configparseerr(ads,fn,lno,"sortlist entry `%.*s' too long",l,p);
+ continue;
+ }
+
+ memcpy(tbuf,p,l);
+ slash= strchr(tbuf,'/');
+ if (slash) *slash++= 0;
+
+ if (!inet_aton(tbuf,&base)) {
+ configparseerr(ads,fn,lno,"invalid address `%s' in sortlist",tbuf);
+ continue;
+ }
+
+ if (slash) {
+ if (strchr(slash,'.')) {
+ if (!inet_aton(slash,&mask)) {
+ configparseerr(ads,fn,lno,"invalid mask `%s' in sortlist",slash);
+ continue;
+ }
+ if (base.s_addr & ~mask.s_addr) {
+ configparseerr(ads,fn,lno,
+ "mask `%s' in sortlist overlaps address `%s'",slash,tbuf);
+ continue;
+ }
+ } else {
+ initial= strtoul(slash,&ep,10);
+ if (*ep || initial>32) {
+ configparseerr(ads,fn,lno,"mask length `%s' invalid",slash);
+ continue;
+ }
+ mask.s_addr= htonl((0x0ffffffffUL) << (32-initial));
+ }
+ } else {
+ baselocal= ntohl(base.s_addr);
+ if (!baselocal & 0x080000000UL) /* class A */
+ mask.s_addr= htonl(0x0ff000000UL);
+ else if ((baselocal & 0x0c0000000UL) == 0x080000000UL)
+ mask.s_addr= htonl(0x0ffff0000UL); /* class B */
+ else if ((baselocal & 0x0f0000000UL) == 0x0e0000000UL)
+ mask.s_addr= htonl(0x0ff000000UL); /* class C */
+ else {
+ configparseerr(ads,fn,lno,
+ "network address `%s' in sortlist is not in classed ranges,"
+ " must specify mask explicitly", tbuf);
+ continue;
+ }
+ }
+
+ ads->sortlist[ads->nsortlist].base= base;
+ ads->sortlist[ads->nsortlist].mask= mask;
+ ads->nsortlist++;
+ }
}
static void ccf_options(adns_state ads, const char *fn, int lno, const char *buf) {
static int gl_text(adns_state ads, getline_ctx *src_io, const char *filename,
int lno, char *buf, int buflen) {
- const char *cp= src_io->text, *nn;
+ const char *cp= src_io->text;
int l;
- if (!cp) return -1;
-
- nn= strchr(cp,'\n');
+ if (!cp || !*cp) return -1;
- l= nn ? nn-cp : strlen(cp);
- src_io->text= nn ? nn+1 : 0;
+ if (*cp == ';' || *cp == '\n') cp++;
+ l= strcspn(cp,";\n");
+ src_io->text = cp+l;
if (l >= buflen) {
adns__diag(ads,-1,0,"%s:%d: line too long, ignored",filename,lno);
if (filename) readconfig(ads,filename);
}
+static void readconfigenvtext(adns_state ads, const char *envvar) {
+ const char *textdata;
+
+ if (ads->iflags & adns_if_noenv) {
+ adns__debug(ads,-1,0,"not checking environment variable `%s'",envvar);
+ return;
+ }
+ textdata= instrum_getenv(ads,envvar);
+ if (textdata) readconfigtext(ads,textdata,envvar);
+}
+
int adns__setnonblock(adns_state ads, int fd) {
int r;
ads->udpsocket= ads->tcpsocket= -1;
adns__vbuf_init(&ads->tcpsend);
adns__vbuf_init(&ads->tcprecv);
- ads->nservers= ads->tcpserver= 0;
+ ads->nservers= ads->nsortlist= ads->tcpserver= 0;
ads->tcpstate= server_disconnected;
timerclear(&ads->tcptimeout);
if (!ads->nservers) {
if (ads->diagfile && ads->iflags & adns_if_debug)
fprintf(ads->diagfile,"adns: no nameservers, using localhost\n");
- ia.s_addr= INADDR_LOOPBACK;
+ ia.s_addr= htonl(INADDR_LOOPBACK);
addserver(ads,ia);
}
readconfigenv(ads,"RES_CONF");
readconfigenv(ads,"ADNS_RES_CONF");
+ readconfigenvtext(ads,"RES_CONF_TEXT");
+ readconfigenvtext(ads,"ADNS_RES_CONF_TEXT");
+
ccf_options(ads,"RES_OPTIONS",-1,res_options);
ccf_options(ads,"ADNS_RES_OPTIONS",-1,adns_res_options);
return adns_s_ok;
}
-static int dip_inaddr(struct in_addr a, struct in_addr b) {
- /* fixme implement sortlist */
- return 0;
+static int search_sortlist(adns_state ads, struct in_addr ad) {
+ const struct sortlist *slp;
+ int i;
+
+ for (i=0, slp=ads->sortlist;
+ i<ads->nsortlist && !((ad.s_addr & slp->mask.s_addr) == slp->base.s_addr);
+ i++, slp++);
+ return i;
}
-static int di_inaddr(const void *datap_a, const void *datap_b) {
+static int dip_inaddr(adns_state ads, struct in_addr a, struct in_addr b) {
+ int ai, bi;
+
+ if (!ads->nsortlist) return 0;
+
+ ai= search_sortlist(ads,a);
+ bi= search_sortlist(ads,b);
+ return bi<ai;
+}
+
+static int di_inaddr(adns_state ads, const void *datap_a, const void *datap_b) {
const struct in_addr *ap= datap_a, *bp= datap_b;
- return dip_inaddr(*ap,*bp);
+ return dip_inaddr(ads,*ap,*bp);
}
static adns_status cs_inaddr(vbuf *vb, const void *datap) {
return adns_s_ok;
}
-static int di_addr(const void *datap_a, const void *datap_b) {
+static int di_addr(adns_state ads, const void *datap_a, const void *datap_b) {
const adns_rr_addr *ap= datap_a, *bp= datap_b;
assert(ap->addr.sa.sa_family == AF_INET);
- return dip_inaddr(ap->addr.inet.sin_addr,bp->addr.inet.sin_addr);
+ return dip_inaddr(ads, ap->addr.inet.sin_addr, bp->addr.inet.sin_addr);
}
+static int div_addr(void *context, const void *datap_a, const void *datap_b) {
+ const adns_state ads= context;
+
+ return di_addr(ads, datap_a, datap_b);
+}
+
static adns_status csp_addr(vbuf *vb, const adns_rr_addr *rrp) {
const char *ia;
static char buf[30];
ha->naddrs= naddrs;
ha->astatus= adns_s_ok;
- adns__isort(ha->addrs, naddrs, sizeof(adns_rr_addr), pai->qu->vb.buf, di_addr);
+ adns__isort(ha->addrs, naddrs, sizeof(adns_rr_addr), pai->qu->vb.buf,
+ div_addr, pai->ads);
}
return adns_s_ok;
}
return adns_s_ok;
}
-static int dip_hostaddr(const adns_rr_hostaddr *ap, const adns_rr_hostaddr *bp) {
+static int dip_hostaddr(adns_state ads, const adns_rr_hostaddr *ap, const adns_rr_hostaddr *bp) {
if (ap->astatus != bp->astatus) return ap->astatus;
if (ap->astatus) return 0;
assert(ap->addrs[0].addr.sa.sa_family == AF_INET);
assert(bp->addrs[0].addr.sa.sa_family == AF_INET);
- return dip_inaddr(ap->addrs[0].addr.inet.sin_addr, bp->addrs[0].addr.inet.sin_addr);
+ return dip_inaddr(ads,
+ ap->addrs[0].addr.inet.sin_addr,
+ bp->addrs[0].addr.inet.sin_addr);
}
-static int di_hostaddr(const void *datap_a, const void *datap_b) {
+static int di_hostaddr(adns_state ads, const void *datap_a, const void *datap_b) {
const adns_rr_hostaddr *ap= datap_a, *bp= datap_b;
- return dip_hostaddr(ap,bp);
+ return dip_hostaddr(ads, ap,bp);
}
static void mfp_hostaddr(adns_query qu, adns_rr_hostaddr *rrp) {
return adns_s_ok;
}
-static int di_mx_raw(const void *datap_a, const void *datap_b) {
+static int di_mx_raw(adns_state ads, const void *datap_a, const void *datap_b) {
const adns_rr_intstr *ap= datap_a, *bp= datap_b;
if (ap->i < bp->i) return 0;
return adns_s_ok;
}
-static int di_mx(const void *datap_a, const void *datap_b) {
+static int di_mx(adns_state ads, const void *datap_a, const void *datap_b) {
const adns_rr_inthostaddr *ap= datap_a, *bp= datap_b;
if (ap->i < bp->i) return 0;
if (ap->i > bp->i) return 1;
- return dip_hostaddr(&ap->ha,&bp->ha);
+ return dip_hostaddr(ads, &ap->ha, &bp->ha);
}
/*