*/
#include <stdio.h>
+#include <sys/time.h>
#include <unistd.h>
#include <assert.h>
#include <stdlib.h>
int len, i, qc, qi, tc, ti, ch;
adns_status r, ri;
const adns_rrtype *types;
+ struct timeval now;
adns_rrtype *types_a;
if (argv[0] && argv[1] && argv[1][0] == '/') {
r= adns_wait(ads,&qu,&ans,0);
if (r) failure("wait",r);
+ if (gettimeofday(&now,0)) { perror("gettimeofday"); exit(3); }
+
ri= adns_rr_info(ans->type, &rrtn,&fmtn,&len, 0,0);
fprintf(stdout, "%s type ", domlist[qi]);
dumptype(ri,rrtn,fmtn);
- fprintf(stdout, ": %s; nrrs=%d; cname=%s\n",
+ fprintf(stdout, ": %s; nrrs=%d; cname=%s; ttl=%ld\n",
adns_strerror(ans->status),
- ans->nrrs, ans->cname ? ans->cname : "$");
+ ans->nrrs, ans->cname ? ans->cname : "$",
+ (long)ans->expires - (long)now.tv_sec);
if (ans->nrrs) {
assert(!ri);
for (i=0; i<ans->nrrs; i++) {
check: $(TARGETS)
./checkall
-hrecord: $(srcdir)/../client/adnstest.o hrecord.o hcommon.o $(HARNLOBJS)
-hplayback: $(srcdir)/../client/adnstest.o hplayback.o hcommon.o $(HARNLOBJS)
+hrecord: adnstest_c.o hrecord.o hcommon.o $(HARNLOBJS)
+hplayback: adnstest_c.o hplayback.o hcommon.o $(HARNLOBJS)
%_d.o: $(srcdir)/../src/%.c
$(CC) $(HCPPFLAGS) -c -g -o $@ $<
+%_c.o: $(srcdir)/../client/%.c
+ $(CC) $(HCPPFLAGS) -I $(srcdir)/../src -c -g -o $@ $<
+
$(ALLOBJS): $(srcdir)/../src/adns.h $(srcdir)/../src/internal.h harness.h
%:: %.m4 hmacros.i4 hsyscalls.i4
adns debug: using nameserver 172.18.45.6
manyaddrs.test.iwj.relativity.greenend.org.uk type 1 A(-) submitted
-manyaddrs.test.iwj.relativity.greenend.org.uk type A(-): OK; nrrs=4; cname=$
+manyaddrs.test.iwj.relativity.greenend.org.uk type A(-): OK; nrrs=4; cname=$; ttl=60
127.0.0.1
172.18.45.6
172.18.45.1
adns debug: using nameserver 172.18.45.6
chiark.greenend.org.uk type 1 A(-) submitted
-chiark.greenend.org.uk type A(-): OK; nrrs=1; cname=$
+chiark.greenend.org.uk type A(-): OK; nrrs=1; cname=$; ttl=86400
195.224.76.132
adns debug: using nameserver 172.18.45.36
chiark.greenend.org.uk type 1 A(-) submitted
-chiark.greenend.org.uk type A(-): DNS query timed out; nrrs=0; cname=$
+chiark.greenend.org.uk type A(-): DNS query timed out; nrrs=0; cname=$; ttl=604770
adns debug: using nameserver 172.18.45.6
trunc.test.iwj.relativity.greenend.org.uk type 1 A(-) submitted
-trunc.test.iwj.relativity.greenend.org.uk type A(-): No such data; nrrs=0; cname=$
+trunc.test.iwj.relativity.greenend.org.uk type A(-): No such data; nrrs=0; cname=$; ttl=60
adns_status status;
char *cname; /* always NULL if query was for CNAME records */
adns_rrtype type; /* guaranteed to be same as in query */
+ time_t expires; /* expiry time, defined only if _s_ok, nxdomain or nodata. NOT TTL! */
int nrrs, rrsz;
union {
void *untyped;
adns_query *query_io,
adns_answer **answer_r,
void **context_r);
-/* fixme: include TTL in answer somehow */
/* fixme: easy way to get lists of fd's */
+/* fixme: minor cache */
void adns_cancel(adns_query query);
#define UDPRETRYMS 2000
#define TCPMS 30000
#define LOCALRESOURCEMS 20
+#define MAXTTLBELIEVE (7*86400) /* any TTL > 7 days is capped */
#define DNS_PORT 53
#define DNS_MAXUDP 512
int udpnextserver;
unsigned long udpsent, tcpfailed; /* bitmap indexed by server */
struct timeval timeout;
+ time_t expires; /* Earliest expiry time of any record we used. */
qcontext ctx;
*
* It is legal to call adns__transfer_interim with a null pointer; this
* has no effect.
+ *
+ * _transfer_interim also ensures that the expiry time of the `to' query
+ * is no later than that of the `from' query, so that child queries'
+ * TTLs get inherited by their parents.
*/
void *adns__alloc_mine(adns_query qu, size_t sz);
adns_status adns__findrr(adns_query qu, int serv,
const byte *dgram, int dglen, int *cbyte_io,
- int *type_r, int *class_r, int *rdlen_r, int *rdstart_r,
+ int *type_r, int *class_r, unsigned long *ttl_r,
+ int *rdlen_r, int *rdstart_r,
int *ownermatchedquery_r);
/* Finds the extent and some of the contents of an RR in a datagram
* and does some checks. The datagram is *dgram, length dglen, and
* the RR starts at *cbyte_io (which is updated afterwards to point
* to the end of the RR).
*
- * The type, class and RRdata length and start are returned iff
- * the corresponding pointer variables are not null. type_r and
- * class_r may not be null.
+ * The type, class, TTL and RRdata length and start are returned iff
+ * the corresponding pointer variables are not null. type_r, class_r
+ * and ttl_r may not be null. The TTL will be capped.
*
* If ownermatchedquery_r != 0 then the owner domain of this
* RR will be compared with that in the query (or, if the query
adns_status adns__findrr_anychk(adns_query qu, int serv,
const byte *dgram, int dglen, int *cbyte_io,
- int *type_r, int *class_r, int *rdlen_r, int *rdstart_r,
+ int *type_r, int *class_r, unsigned long *ttl_r,
+ int *rdlen_r, int *rdstart_r,
const byte *eo_dgram, int eo_dglen, int eo_cbyte,
int *eo_matched_r);
/* Like adns__findrr_checked, except that the datagram and
* untruncated.
*/
+void adns__update_expires(adns_query qu, unsigned long ttl, struct timeval now);
+/* Updates the `expires' field in the query, so that it doesn't exceed
+ * now + ttl.
+ */
+
int vbuf__append_quoted1035(vbuf *vb, const byte *buf, int len);
/* From event.c: */
#define GETIL_B(cb) (((dgram)[(cb)++]) & 0x0ff)
#define GET_B(cb,tv) ((tv)= GETIL_B((cb)))
#define GET_W(cb,tv) ((tv)=0, (tv)|=(GETIL_B((cb))<<8), (tv)|=GETIL_B(cb), (tv))
+#define GET_L(cb,tv) ( (tv)=0, \
+ (tv)|=(GETIL_B((cb))<<24), \
+ (tv)|=(GETIL_B((cb))<<16), \
+ (tv)|=(GETIL_B((cb))<<8), \
+ (tv)|=GETIL_B(cb), \
+ (tv) )
#endif
adns_status adns__findrr_anychk(adns_query qu, int serv,
const byte *dgram, int dglen, int *cbyte_io,
- int *type_r, int *class_r, int *rdlen_r, int *rdstart_r,
+ int *type_r, int *class_r, unsigned long *ttl_r,
+ int *rdlen_r, int *rdstart_r,
const byte *eo_dgram, int eo_dglen, int eo_cbyte,
int *eo_matched_r) {
findlabel_state fls, eo_fls;
int cbyte;
int tmp, rdlen, mismatch;
+ unsigned long ttl;
int lablen, labstart, ch;
int eo_lablen, eo_labstart, eo_ch;
adns_status st;
if (cbyte+10>dglen) goto x_truncated;
GET_W(cbyte,tmp); *type_r= tmp;
GET_W(cbyte,tmp); *class_r= tmp;
- cbyte+= 4; /* we skip the TTL */
+
+ GET_L(cbyte,ttl);
+ if (ttl > MAXTTLBELIEVE) ttl= MAXTTLBELIEVE;
+ *ttl_r= ttl;
+
GET_W(cbyte,rdlen); if (rdlen_r) *rdlen_r= rdlen;
if (rdstart_r) *rdstart_r= cbyte;
cbyte+= rdlen;
adns_status adns__findrr(adns_query qu, int serv,
const byte *dgram, int dglen, int *cbyte_io,
- int *type_r, int *class_r, int *rdlen_r, int *rdstart_r,
+ int *type_r, int *class_r, unsigned long *ttl_r,
+ int *rdlen_r, int *rdstart_r,
int *ownermatchedquery_r) {
if (!ownermatchedquery_r) {
return adns__findrr_anychk(qu,serv,
dgram,dglen,cbyte_io,
- type_r,class_r,rdlen_r,rdstart_r,
+ type_r,class_r,ttl_r,rdlen_r,rdstart_r,
0,0,0, 0);
} else if (!qu->cname_dgram) {
return adns__findrr_anychk(qu,serv,
dgram,dglen,cbyte_io,
- type_r,class_r,rdlen_r,rdstart_r,
+ type_r,class_r,ttl_r,rdlen_r,rdstart_r,
qu->query_dgram,qu->query_dglen,DNS_HDRSIZE,
ownermatchedquery_r);
} else {
return adns__findrr_anychk(qu,serv,
dgram,dglen,cbyte_io,
- type_r,class_r,rdlen_r,rdstart_r,
+ type_r,class_r,ttl_r,rdlen_r,rdstart_r,
qu->cname_dgram,qu->cname_dglen,qu->cname_begin,
ownermatchedquery_r);
}
qu->udpsent= qu->tcpfailed= 0;
timerclear(&qu->timeout);
memcpy(&qu->ctx,ctx,sizeof(qu->ctx));
+ qu->expires= now.tv_sec + MAXTTLBELIEVE;
qu->answer->status= adns_s_ok;
qu->answer->cname= 0;
from->interim_allocd -= sz;
to->interim_allocd += sz;
+
+ if (to->expires > from->expires) to->expires= from->expires;
}
void *adns__alloc_final(adns_query qu, size_t sz) {
free(qu);
}
+void adns__update_expires(adns_query qu, unsigned long ttl, struct timeval now) {
+ time_t max;
+
+ assert(ttl <= MAXTTLBELIEVE);
+ max= now.tv_sec + ttl;
+ if (qu->expires < max) return;
+ qu->expires= max;
+}
+
static void makefinal_query(adns_query qu) {
adns_answer *ans;
int rrn;
for (rrn=0; rrn<ans->nrrs; rrn++)
qu->typei->makefinal(qu, ans->rrs.bytes + rrn*ans->rrsz);
}
-
+
free_query_allocs(qu);
return;
qu->ads);
}
+ ans->expires= qu->expires;
parent= qu->parent;
if (parent) {
LIST_UNLINK_PART(parent->children,qu,siblings.);
int rrtype, rrclass, rdlength, rdstart;
int anstart, nsstart, arstart;
int ownermatched, l, nrrs;
+ unsigned long ttl, soattl;
const typeinfo *typei;
adns_query qu, nqu;
dns_rcode rcode;
for (rri= 0; rri<ancount; rri++) {
rrstart= cbyte;
st= adns__findrr(qu,serv, dgram,dglen,&cbyte,
- &rrtype,&rrclass,&rdlength,&rdstart,
+ &rrtype,&rrclass,&ttl, &rdlength,&rdstart,
&ownermatched);
if (st) { adns__query_fail(qu,st); return; }
if (rrtype == -1) goto x_truncated;
memcpy(qu->answer->cname,qu->vb.buf,l);
cname_here= 1;
+ adns__update_expires(qu,ttl,now);
/* If we find the answer section truncated after this point we restart
* the query at the CNAME; if beforehand then we obviously have to use
* TCP. If there is no truncation we can use the whole answer if
if (!wantedrrs) {
/* Oops, NODATA or NXDOMAIN or perhaps a referral (which would be a problem) */
-
- if (rcode == rcode_nxdomain) {
- adns__query_fail(qu,adns_s_nxdomain);
- return;
- }
/* RFC2308: NODATA has _either_ a SOA _or_ _no_ NS records in authority section */
- foundsoa= 0; foundns= 0;
+ foundsoa= 0; soattl= 0; foundns= 0;
for (rri= 0; rri<nscount; rri++) {
rrstart= cbyte;
st= adns__findrr(qu,serv, dgram,dglen,&cbyte,
- &rrtype,&rrclass,&rdlength,&rdstart, 0);
+ &rrtype,&rrclass,&ttl, &rdlength,&rdstart, 0);
if (st) { adns__query_fail(qu,st); return; }
if (rrtype==-1) goto x_truncated;
if (rrclass != DNS_CLASS_IN) {
rrclass,DNS_CLASS_IN);
continue;
}
- if (rrtype == adns_r_soa_raw) { foundsoa= 1; break; }
+ if (rrtype == adns_r_soa_raw) { foundsoa= 1; soattl= ttl; break; }
else if (rrtype == adns_r_ns_raw) { foundns= 1; }
}
+
+ if (rcode == rcode_nxdomain) {
+ /* We still wanted to look for the SOA so we could find the TTL. */
+ adns__update_expires(qu,soattl,now);
+ adns__query_fail(qu,adns_s_nxdomain);
+ return;
+ }
if (foundsoa || !foundns) {
/* Aha ! A NODATA response, good. */
+ adns__update_expires(qu,soattl,now);
adns__query_fail(qu,adns_s_nodata);
return;
}
for (rri=0, nrrs=0; rri<ancount; rri++) {
st= adns__findrr(qu,serv, dgram,dglen,&cbyte,
- &rrtype,&rrclass,&rdlength,&rdstart,
+ &rrtype,&rrclass,&ttl, &rdlength,&rdstart,
&ownermatched);
assert(!st); assert(rrtype != -1);
if (rrclass != DNS_CLASS_IN ||
rrtype != (qu->typei->type & adns__rrt_typemask) ||
!ownermatched)
continue;
+ adns__update_expires(qu,ttl,now);
st= typei->parse(&pai, rdstart,rdstart+rdlength, rrsdata+nrrs*typei->rrsz);
if (st) { adns__query_fail(qu,st); return; }
if (rdstart==-1) goto x_truncated;
int *cbyte_io, int count, int dmstart) {
int rri, naddrs;
int type, class, rdlen, rdstart, ownermatched;
+ unsigned long ttl;
adns_status st;
for (rri=0, naddrs=-1; rri<count; rri++) {
st= adns__findrr_anychk(pai->qu, pai->serv, pai->dgram, pai->dglen, cbyte_io,
- &type, &class, &rdlen, &rdstart,
+ &type, &class, &ttl, &rdlen, &rdstart,
pai->dgram, pai->dglen, dmstart, &ownermatched);
if (st) return st;
if (!ownermatched || class != DNS_CLASS_IN || type != adns_r_a) {
naddrs= 0;
}
if (!adns__vbuf_ensure(&pai->qu->vb, (naddrs+1)*sizeof(adns_rr_addr))) R_NOMEM;
+ adns__update_expires(pai->qu,ttl,pai->now);
st= pa_addr(pai, rdstart,rdstart+rdlen,
pai->qu->vb.buf + naddrs*sizeof(adns_rr_addr));
if (st) return st;