chiark / gitweb /
Merge changes from chiark: cvs rdiff -u -r tochiark-1998-11-09-b -r
authorian <ian>
Sat, 14 Nov 1998 13:18:00 +0000 (13:18 +0000)
committerian <ian>
Sat, 14 Nov 1998 13:18:00 +0000 (13:18 +0000)
fromchiark-1998-11-14 dnslib

client/adnstest.c
src/adns.h
src/event.c
src/internal.h
src/query.c
src/reply.c
src/transmit.c
src/types.c

index 7c7d185..a7a247a 100644 (file)
@@ -39,26 +39,54 @@ static const adns_rrtype defaulttypes[]= {
   adns_r_ns_raw,
   adns_r_cname,
   adns_r_ptr_raw,
+  adns_r_mx_raw,
+  adns_r_rp_raw,
   adns_r_txt,
   adns_r_none
 };
 
-int main(int argc, const char *const *argv) {
+static void dumptype(adns_status ri, const char *rrtn, const char *fmtn) {
+  fprintf(stdout, "%s(%s)%s%s",
+         ri ? "?" : rrtn, ri ? "?" : fmtn ? fmtn : "-",
+         ri ? " " : "", ri ? adns_strerror(ri) : "");
+}
+
+int main(int argc, char *const *argv) {
   adns_state ads;
   adns_query *qus, qu;
   adns_answer *ans;
-  const char *rrtn, *fmtn;
-  char *show;
-  int len, i, qc, qi, tc, ti;
+  const char *rrtn, *fmtn, *const *domlist;
+  char *show, *cp;
+  int len, i, qc, qi, tc, ti, ch;
   adns_status r, ri;
   const adns_rrtype *types;
+  adns_rrtype *types_a;
 
-  if (argv[0] && argv[1]) argv++;
-  else argv= defaultargv;
-
-  types= defaulttypes;
+  if (argv[0] && argv[1] && argv[1][0] == ':') {
+    for (cp= argv[1]+1, tc=1; (ch= *cp); cp++)
+      if (ch==',') tc++;
+    types_a= malloc(sizeof(*types_a)*tc);
+    if (!types_a) { perror("malloc types"); exit(3); }
+    for (cp= argv[1]+1, ti=0; ti<tc; ti++) {
+      types_a[ti]= strtoul(cp,&cp,10);
+      if ((ch= *cp)) {
+       if (ch != ':') {
+         fputs("usage: dtest [:<typenum>,...] [<domain> ...]",stderr);
+         exit(4);
+       }
+       cp++;
+      }
+    }
+    types= types_a;
+    argv++;
+  } else {
+    types= defaulttypes;
+  }
+  
+  if (argv[0] && argv[1]) domlist= (const char *const*)argv+1;
+  else domlist= defaultargv;
 
-  for (qc=0; qc[argv]; qc++);
+  for (qc=0; qc[domlist]; qc++);
   for (tc=0; types[tc] != adns_r_none; tc++);
   qus= malloc(sizeof(qus)*qc*tc);
   if (!qus) { perror("malloc qus"); exit(3); }
@@ -68,14 +96,17 @@ int main(int argc, const char *const *argv) {
 
   for (qi=0; qi<qc; qi++) {
     for (ti=0; ti<tc; ti++) {
-      fprintf(stdout,"%s type %d",argv[qi],types[ti]);
-      r= adns_submit(ads,argv[qi],types[ti],0,0,&qus[qi*tc+ti]);
+      fprintf(stdout,"%s type %d",domlist[qi],types[ti]);
+      r= adns_submit(ads,domlist[qi],types[ti],0,0,&qus[qi*tc+ti]);
       if (r == adns_s_notimplemented) {
        fprintf(stdout," not implemented\n");
        qus[qi*tc+ti]= 0;
       } else if (r) {
        failure("submit",r);
       } else {
+       ri= adns_rr_info(types[ti], &rrtn,&fmtn,0, 0,0);
+       putchar(' ');
+       dumptype(ri,rrtn,fmtn);
        fprintf(stdout," submitted\n");
       }
     }
@@ -89,11 +120,9 @@ int main(int argc, const char *const *argv) {
       if (r) failure("wait",r);
 
       ri= adns_rr_info(ans->type, &rrtn,&fmtn,&len, 0,0);
-      fprintf(stdout, "%s type %s(%s)%s%s: ",
-             argv[qi],
-             ri ? "?" : rrtn, ri ? "?" : fmtn ? fmtn : "-",
-             ri ? " " : "", ri ? adns_strerror(ri) : "");
-      fprintf(stdout, "%s; nrrs=%d; cname=%s\n",
+      fprintf(stdout, "%s type ", domlist[qi]);
+      dumptype(ri,rrtn,fmtn);
+      fprintf(stdout, ": %s; nrrs=%d; cname=%s\n",
              adns_strerror(ans->status),
              ans->nrrs, ans->cname ? ans->cname : "$");
       if (ans->nrrs) {
index d265506..d5cddec 100644 (file)
@@ -96,6 +96,7 @@ typedef enum {
  */
 
 typedef enum {
+  /* fixme: think about error codes */
   adns_s_ok,
   adns_s_timeout,
   adns_s_nolocalmem,
@@ -137,6 +138,11 @@ typedef struct {
 } adns_rr_intdmaddr;
 
 typedef struct {
+  /* Used both for mx_raw, in which case i is the preference and str the domain,
+   * and for txt, in which case each entry has i for the `text' length,
+   * and str for the data (which will have had an extra nul appended
+   * so that if it was plain text it is now a null-terminated string).
+   */
   int i;
   char *str;
 } adns_rr_intstr;
@@ -155,10 +161,10 @@ typedef struct {
     void *untyped;
     unsigned char *bytes;
     char *(*str);                  /* ns_raw, cname, ptr, ptr_raw */
-    char *(**manystr);             /* txt (list is null-terminated) */
+    adns_rr_intstr *(*manyistr);   /* txt (list of strings ends with i=-1, str=0) */
     struct in_addr *inaddr;        /* a */
     adns_rr_dmaddr *dmaddr;        /* ns */
-    adns_rr_strpair *strpair;      /* hinfo, rp, rp_raw */
+    adns_rr_strpair *strpair;      /* hinfo ??fixme, rp, rp_raw */
     adns_rr_intdmaddr *intdmaddr;  /* mx */
     adns_rr_intstr *intstr;        /* mx_raw */
     adns_rr_soa *soa;              /* soa, soa_raw */
index 778c776..a61b8dd 100644 (file)
@@ -201,6 +201,7 @@ void adns_interest(adns_state ads, int *maxfd,
     inter_addfd(maxfd,readfds,ads->tcpsocket);
     inter_addfd(maxfd,exceptfds,ads->tcpsocket);
     if (ads->tcpsend.used) inter_addfd(maxfd,writefds,ads->tcpsocket);
+    break;
   default:
     abort();
   }
@@ -230,7 +231,7 @@ static int internal_callback(adns_state ads, int maxfd,
     if (callb_checkfd(maxfd,writefds,ads->tcpsocket)) {
       count++;
       assert(ads->tcprecv.used==0);
-      adns__vbuf_ensure(&ads->tcprecv,1);
+      if (!adns__vbuf_ensure(&ads->tcprecv,1)) return -1;
       if (ads->tcprecv.buf) {
        r= read(ads->tcpsocket,&ads->tcprecv.buf,1);
        if (r==0 || (r<0 && (errno==EAGAIN || errno==EWOULDBLOCK))) {
@@ -263,8 +264,10 @@ static int internal_callback(adns_state ads, int maxfd,
        }
        ads->tcprecv.used -= skip;
        memmove(ads->tcprecv.buf,ads->tcprecv.buf+skip,ads->tcprecv.used);
-       adns__vbuf_ensure(&ads->tcprecv,want);
-       if (ads->tcprecv.used >= ads->tcprecv.avail) break;
+       skip= 0;
+       if (!adns__vbuf_ensure(&ads->tcprecv,want)) return -1;
+       assert(ads->tcprecv.used <= ads->tcprecv.avail);
+       if (ads->tcprecv.used == ads->tcprecv.avail) continue;
        r= read(ads->tcpsocket,
                ads->tcprecv.buf+ads->tcprecv.used,
                ads->tcprecv.avail-ads->tcprecv.used);
@@ -292,6 +295,7 @@ static int internal_callback(adns_state ads, int maxfd,
        memmove(ads->tcpsend.buf,ads->tcpsend.buf+r,ads->tcpsend.used);
       }
     }
+    break;
   default:
     abort();
   }
index 852c05c..69eb827 100644 (file)
@@ -108,6 +108,11 @@ typedef struct {
    * If there is an overrun which might indicate truncation, it should set
    * *rdstart to -1; otherwise it may set it to anything else positive.
    */
+
+  int (*diff_needswap)(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.
+   */
 } typeinfo;
 
 typedef struct allocnode {
@@ -134,7 +139,7 @@ struct adns__query {
   void *final_allocspace;
   
   const typeinfo *typei;
-  char *query_dgram;
+  byte *query_dgram;
   int query_dglen;
   
   vbuf vb;
@@ -273,6 +278,13 @@ adns_status adns__mkquery(adns_state ads, vbuf *vb, int *id_r,
                          const typeinfo *typei, adns_queryflags flags);
 /* Assembles a query packet in vb, and returns id at *id_r. */
 
+adns_status adns__mkquery_frdgram(adns_state ads, vbuf *vb, int *id_r,
+                                 const byte *qd_dgram, int qd_dglen, int qd_begin,
+                                 adns_rrtype type, adns_queryflags flags);
+/* Same as adns__mkquery, but takes the owner domain from an existing datagram.
+ * That domain must be correct and untruncated.
+ */
+
 void adns__query_tcp(adns_query qu, struct timeval now);
 /* 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.
@@ -322,11 +334,11 @@ void *adns__alloc_interim(adns_query qu, size_t sz);
  * big enough for all these allocations, and then adns__alloc_final
  * will get memory from this buffer.
  *
- * _alloc_interim can fail, in which case it will fail the query too,
- * so nothing more need be done with it.
+ * _alloc_interim can fail (and return 0).
+ * The caller must ensure that the query is failed.
  *
- * adns__alloc_interim(qu,0) will not return 0, but it will not
- * necessarily return a distinct pointer each time.
+ * adns__alloc_interim_{only,fail}(qu,0) will not return 0,
+ * but it will not necessarily return a distinct pointer each time.
  */
 
 void *adns__alloc_mine(adns_query qu, size_t sz);
index 27eb221..41aaf5f 100644 (file)
@@ -164,10 +164,7 @@ static void *alloc_common(adns_query qu, size_t sz) {
   if (!sz) return qu; /* Any old pointer will do */
   assert(!qu->final_allocspace);
   an= malloc(MEM_ROUND(MEM_ROUND(sizeof(*an)) + sz));
-  if (!an) {
-    adns__query_fail(qu,adns_s_nolocalmem);
-    return 0;
-  }
+  if (!an) return 0;
   an->next= qu->allocations;
   qu->allocations= an;
   return (byte*)an + MEM_ROUND(sizeof(*an));
@@ -211,10 +208,19 @@ void adns__query_done(adns_query qu) {
   allocnode *an, *ann;
   int i;
 
-  qu->answer= ans= realloc(qu->answer,
-                          MEM_ROUND(MEM_ROUND(sizeof(*ans)) +
-                                    qu->interim_allocd));
-  qu->final_allocspace= (byte*)qu->answer + MEM_ROUND(sizeof(*ans));
+  if (qu->answer->status == adns_s_nolocalmem && !qu->interim_allocd) {
+    ans= qu->answer;
+  } else {
+    ans= realloc(qu->answer,
+                MEM_ROUND(MEM_ROUND(sizeof(*ans)) + qu->interim_allocd));
+    if (!ans) {
+      qu->answer->cname= 0;
+      adns__query_fail(qu, adns_s_nolocalmem);
+      return;
+    }
+    qu->answer= ans;
+  }
+  qu->final_allocspace= (byte*)ans + MEM_ROUND(sizeof(*ans));
 
   adns__makefinal_str(qu,&ans->cname);
   if (ans->nrrs) {
index 673196e..87ee361 100644 (file)
  *  Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 
  */
 
-#include "internal.h"
+#include <stdlib.h>
+#include <string.h>
 
-static void cname_recurse(adns_query qu, adns_queryflags xflags) {
-  adns__diag(qu->ads,-1,qu,"cname following not implemented fixme");
-  adns__query_fail(qu,adns_s_notimplemented);
-}
+#include "internal.h"
     
 void adns__procdgram(adns_state ads, const byte *dgram, int dglen,
                     int serv, struct timeval now) {
   int cbyte, rrstart, wantedrrs, rri, foundsoa, foundns, cname_here;
   int id, f1, f2, qdcount, ancount, nscount, arcount;
   int flg_ra, flg_rd, flg_tc, flg_qr, opcode;
-  int rrtype, rrclass, rdlength, rdstart, ownermatched, l;
+  int rrtype, rrclass, rdlength, rdstart;
   int anstart, nsstart, arstart;
+  int ownermatched, l, nrrs, place;
+  const typeinfo *typei;
   adns_query qu, nqu;
   dns_rcode rcode;
   adns_status st;
   vbuf tempvb;
+  byte *newquery, *rrsdata;
   
   if (dglen<DNS_HDRSIZE) {
     adns__diag(ads,serv,0,"received datagram too short for message header (%d)",dglen);
@@ -55,7 +56,7 @@ void adns__procdgram(adns_state ads, const byte *dgram, int dglen,
 
   flg_qr= f1&0x80;
   opcode= (f1&0x78)>>3;
-  flg_tc= f1&0x20;
+  flg_tc= f1&0x02;
   flg_rd= f1&0x01;
   flg_ra= f2&0x80;
   rcode= (f2&0x0f);
@@ -154,11 +155,9 @@ void adns__procdgram(adns_state ads, const byte *dgram, int dglen,
       }
       continue;
     }
-    if (rrtype == adns_r_cname &&
+    if (rrtype == adns_r_cname && /* fixme - implement adns_qf_nocname */
        (qu->typei->type & adns__rrt_typemask) != adns_r_cname) {
       if (!qu->cname_dgram) { /* Ignore second and subsequent CNAMEs */
-       qu->cname_dgram= adns__alloc_mine(qu,dglen);
-       if (!qu->cname_dgram) return;
        qu->cname_begin= rdstart;
        qu->cname_dglen= dglen;
        st= adns__parse_domain(ads,serv,qu, &qu->vb,qu->flags,
@@ -167,7 +166,11 @@ void adns__procdgram(adns_state ads, const byte *dgram, int dglen,
        if (st) { adns__query_fail(qu,st); return; }
        l= strlen(qu->vb.buf)+1;
        qu->answer->cname= adns__alloc_interim(qu,l);
-       if (!qu->answer->cname) return;
+       if (!qu->answer->cname) { adns__query_fail(qu,adns_s_nolocalmem); return; }
+
+       qu->cname_dgram= adns__alloc_mine(qu,dglen);
+       memcpy(qu->cname_dgram,dgram,dglen);
+
        memcpy(qu->answer->cname,qu->vb.buf,l);
        cname_here= 1;
        /* If we find the answer section truncated after this point we restart
@@ -188,7 +191,11 @@ void adns__procdgram(adns_state ads, const byte *dgram, int dglen,
     }
   }
 
-  /* If we got here then the answer section is intact. */
+  /* We defer handling truncated responses here, in case there was a CNAME
+   * which we could use.
+   */
+  if (flg_tc) goto x_truncated;
+  
   nsstart= cbyte;
 
   if (!wantedrrs) {
@@ -228,7 +235,7 @@ void adns__procdgram(adns_state ads, const byte *dgram, int dglen,
      * a CNAME in this datagram then we should probably do our own CNAME
      * lookup now in the hope that we won't get a referral again.
      */
-    if (cname_here) { cname_recurse(qu,0); return; }
+    if (cname_here) goto x_restartquery;
 
     /* Bloody hell, I thought we asked for recursion ? */
     if (flg_rd) {
@@ -247,10 +254,21 @@ void adns__procdgram(adns_state ads, const byte *dgram, int dglen,
   /* Now, we have some RRs which we wanted. */
 
   qu->answer->rrs.untyped= adns__alloc_interim(qu,qu->typei->rrsz*wantedrrs);
-  if (!qu->answer->rrs.untyped) return;
+  if (!qu->answer->rrs.untyped) { adns__query_fail(qu,adns_s_nolocalmem); return; }
 
+  typei= qu->typei;
   cbyte= anstart;
   arstart= -1;
+  rrsdata= qu->answer->rrs.bytes;
+  
+  if (typei->diff_needswap) {
+    if (!adns__vbuf_ensure(&qu->vb,typei->rrsz)) {
+      adns__query_fail(qu,adns_s_nolocalmem);
+      return;
+    }
+  }
+  nrrs= 0;
+  
   for (rri=0; rri<ancount; rri++) {
     st= adns__findrr(qu,serv, dgram,dglen,&cbyte,
                     &rrtype,&rrclass,&rdlength,&rdstart,
@@ -260,14 +278,28 @@ void adns__procdgram(adns_state ads, const byte *dgram, int dglen,
        rrtype != (qu->typei->type & adns__rrt_typemask) ||
        !ownermatched)
       continue;
-    assert(qu->answer->nrrs<wantedrrs);
-    st= qu->typei->parse(qu,serv,
-                        dgram,dglen, rdstart,rdstart+rdlength,
-                        qu->answer->rrs.bytes+qu->answer->nrrs*qu->typei->rrsz);
+    st= typei->parse(qu,serv, dgram,dglen, rdstart,rdstart+rdlength,
+                    rrsdata+nrrs*typei->rrsz);
     if (st) { adns__query_fail(qu,st); return; }
     if (rdstart==-1) goto x_truncated;
-    qu->answer->nrrs++;
+
+    if (typei->diff_needswap) {
+      for (place= nrrs;
+          place>0 && typei->diff_needswap(rrsdata+(place-1)*typei->rrsz,
+                                          rrsdata+nrrs*typei->rrsz);
+          place--);
+      if (place != nrrs) {
+       memcpy(qu->vb.buf,rrsdata+nrrs*typei->rrsz,typei->rrsz);
+       memmove(rrsdata+(place+1)*typei->rrsz,
+               rrsdata+place*typei->rrsz,
+               (nrrs-place)*typei->rrsz);
+       memcpy(rrsdata+place*typei->rrsz,qu->vb.buf,typei->rrsz);
+      }
+    }
+    nrrs++;
   }
+  assert(nrrs==wantedrrs);
+  qu->answer->nrrs= nrrs;
 
   /* This may have generated some child queries ... */
   if (qu->children.head) {
@@ -280,13 +312,30 @@ void adns__procdgram(adns_state ads, const byte *dgram, int dglen,
   return;
 
  x_truncated:
+  
   if (!flg_tc) {
     adns__diag(ads,serv,qu,"server sent datagram which points outside itself");
     adns__query_fail(qu,adns_s_serverfaulty);
     return;
   }
-  if (qu->cname_dgram) { cname_recurse(qu,adns_qf_usevc); return; }
-  adns__reset_cnameonly(qu);
   qu->flags |= adns_qf_usevc;
+  
+ x_restartquery:
+  
+  if (qu->cname_dgram) {
+    st= adns__mkquery_frdgram(qu->ads,&qu->vb,&qu->id,
+                             qu->cname_dgram, qu->cname_dglen, qu->cname_begin,
+                             qu->typei->type, qu->flags);
+    if (st) { adns__query_fail(qu,st); return; }
+    
+    newquery= realloc(qu->query_dgram,qu->vb.used);
+    if (!newquery) { adns__query_fail(qu,adns_s_nolocalmem); return; }
+    
+    qu->query_dgram= newquery;
+    qu->query_dglen= qu->vb.used;
+    memcpy(newquery,qu->vb.buf,qu->vb.used);
+  }
+  
+  adns__reset_cnameonly(qu);
   adns__query_udp(qu,now);
 }
index c3ae94d..46d2cbf 100644 (file)
 
 #include "internal.h"
 
-adns_status adns__mkquery(adns_state ads, vbuf *vb, int *id_r,
-                         const char *owner, int ol,
-                         const typeinfo *typei, adns_queryflags flags) {
-  int ll, c, nlabs, id;
-  byte label[255], *rqp;
-  const char *p, *pe;
-
+#define MKQUERY_START(vb) (rqp= (vb)->buf+(vb)->used)
 #define MKQUERY_ADDB(b) *rqp++= (b)
 #define MKQUERY_ADDW(w) (MKQUERY_ADDB(((w)>>8)&0x0ff), MKQUERY_ADDB((w)&0x0ff))
+#define MKQUERY_STOP(vb) ((vb)->used= rqp-(vb)->buf)
 
-  vb->used= 0;
-  if (!adns__vbuf_ensure(vb,DNS_HDRSIZE+strlen(owner)+1+5))
-    return adns_s_nolocalmem;
-  rqp= vb->buf;
+static adns_status mkquery_header(adns_state ads, vbuf *vb, int *id_r, int qdlen) {
+  int id;
+  byte *rqp;
+  
+  if (!adns__vbuf_ensure(vb,DNS_HDRSIZE+qdlen+4)) return adns_s_nolocalmem;
 
   *id_r= id= (ads->nextid++) & 0x0ffff;
-
+  
+  vb->used= 0;
+  MKQUERY_START(vb);
+  
   MKQUERY_ADDW(id);
   MKQUERY_ADDB(0x01); /* QR=Q(0), OPCODE=QUERY(0000), !AA, !TC, RD */
   MKQUERY_ADDB(0x00); /* !RA, Z=000, RCODE=NOERROR(0000) */
@@ -52,6 +51,36 @@ adns_status adns__mkquery(adns_state ads, vbuf *vb, int *id_r,
   MKQUERY_ADDW(0); /* ANCOUNT=0 */
   MKQUERY_ADDW(0); /* NSCOUNT=0 */
   MKQUERY_ADDW(0); /* ARCOUNT=0 */
+
+  MKQUERY_STOP(vb);
+  
+  return adns_s_ok;
+}
+
+static adns_status mkquery_footer(vbuf *vb, adns_rrtype type) {
+  byte *rqp;
+
+  MKQUERY_START(vb);
+  MKQUERY_ADDW(type & adns__rrt_typemask); /* QTYPE */
+  MKQUERY_ADDW(DNS_CLASS_IN); /* QCLASS=IN */
+  MKQUERY_STOP(vb);
+  assert(vb->used <= vb->avail);
+  
+  return adns_s_ok;
+}
+
+adns_status adns__mkquery(adns_state ads, vbuf *vb, int *id_r,
+                         const char *owner, int ol,
+                         const typeinfo *typei, adns_queryflags flags) {
+  int ll, c, nlabs;
+  byte label[255], *rqp;
+  const char *p, *pe;
+  adns_status st;
+
+  st= mkquery_header(ads,vb,id_r,strlen(owner)+2); if (st) return st;
+  
+  MKQUERY_START(vb);
+
   p= owner; pe= owner+ol;
   nlabs= 0;
   if (!*p) return adns_s_invalidquerydomain;
@@ -86,13 +115,41 @@ adns_status adns__mkquery(adns_state ads, vbuf *vb, int *id_r,
     MKQUERY_ADDB(ll);
     memcpy(rqp,label,ll); rqp+= ll;
   } while (p!=pe);
+  MKQUERY_ADDB(0);
 
+  MKQUERY_STOP(vb);
+  
+  st= mkquery_footer(vb,typei->type);
+  
+  return adns_s_ok;
+}
+
+adns_status adns__mkquery_frdgram(adns_state ads, vbuf *vb, int *id_r,
+                                 const byte *qd_dgram, int qd_dglen, int qd_begin,
+                                 adns_rrtype type, adns_queryflags flags) {
+  byte *rqp;
+  findlabel_state fls;
+  int lablen, labstart;
+  adns_status st;
+
+  st= mkquery_header(ads,vb,id_r,qd_dglen); if (st) return st;
+
+  MKQUERY_START(vb);
+
+  adns__findlabel_start(&fls,ads,-1,0,qd_dgram,qd_dglen,qd_dglen,qd_begin,0);
+  for (;;) {
+    st= adns__findlabel_next(&fls,&lablen,&labstart); assert(!st);
+    if (!lablen) break;
+    assert(lablen<255);
+    MKQUERY_ADDB(lablen);
+    memcpy(rqp,qd_dgram+labstart,lablen);
+    rqp+= lablen;
+  }
   MKQUERY_ADDB(0);
-  MKQUERY_ADDW(typei->type & adns__rrt_typemask); /* QTYPE */
-  MKQUERY_ADDW(DNS_CLASS_IN); /* QCLASS=IN */
 
-  vb->used= rqp - vb->buf;
-  assert(vb->used <= vb->avail);
+  MKQUERY_STOP(vb);
+  
+  st= mkquery_footer(vb,type);
   
   return adns_s_ok;
 }
@@ -114,7 +171,6 @@ void adns__query_tcp(adns_query qu, struct timeval now) {
   timevaladd(&now,TCPMS);
   qu->timeout= now;
   qu->state= query_tcpsent;
-  LIST_LINK_TAIL(ads->timew,qu);
 
   if (ads->tcpsend.used) {
     wr= 0;
index 2a96609..4b5e752 100644 (file)
 
 static adns_status pa_inaddr(adns_query qu, int serv,
                             const byte *dgram, int dglen, int cbyte, int max,
-                            void *store_r) {
-  struct in_addr *dr= store_r;
+                            void *datap) {
+  struct in_addr *storeto= datap;
   
   if (max-cbyte != 4) return adns_s_invaliddata;
-  memcpy(dr,dgram+cbyte,4);
+  memcpy(storeto,dgram+cbyte,4);
   return adns_s_ok;
 }
 
-static adns_status cs_inaddr(vbuf *vb, const void *data) {
-  const struct in_addr *dp= data;
+static int dip_inaddr(struct in_addr a, struct in_addr b) {
+  /* fixme implement sortlist */
+  return 0;
+}
+
+static int di_inaddr(const void *datap_a, const void *datap_b) {
+  const struct in_addr *ap= datap_a, *bp= datap_b;
+
+  return dip_inaddr(*ap,*bp);
+}
+
+static adns_status cs_inaddr(vbuf *vb, const void *datap) {
+  const struct in_addr *rrp= datap, rr= *rrp;
   const char *ia;
 
-  ia= inet_ntoa(*dp); assert(ia);
+  ia= inet_ntoa(rr); assert(ia);
   return adns__vbuf_appendstr(vb,ia) ? adns_s_ok : adns_s_nolocalmem;
 }
 
+static adns_status pap_domain(adns_query qu, int serv,
+                              const byte *dgram, int dglen, int *cbyte_io, int max,
+                              char **domain_r) {
+  adns_status st;
+  char *dm;
+  
+  st= adns__parse_domain(qu->ads,serv,qu,&qu->vb,qu->flags,
+                        dgram,dglen, cbyte_io,max);
+  if (st) return st;
+  if (!qu->vb.used) return adns_s_invaliddata;
+
+  dm= adns__alloc_interim(qu,qu->vb.used+1);
+  if (!dm) return adns_s_nolocalmem;
+
+  dm[qu->vb.used]= 0;
+  memcpy(dm,qu->vb.buf,qu->vb.used);
+  
+  *domain_r= dm;
+  return adns_s_ok;
+}
+
 static adns_status pa_domain_raw(adns_query qu, int serv,
                                 const byte *dgram, int dglen, int cbyte, int max,
-                                void *store_r) {
-  char **dpp= store_r;
+                                void *datap) {
+  char **rrp= datap;
   adns_status st;
-  vbuf vb;
-  char *dp;
 
-  adns__vbuf_init(&vb);
-  st= adns__parse_domain(qu->ads,serv,qu,&vb,qu->flags,
-                        dgram,dglen, &cbyte,max);
-  if (st) goto x_error;
-
-  dp= adns__alloc_interim(qu,vb.used+1);
-  if (!dp) { st= adns_s_nolocalmem; goto x_error; }
+  st= pap_domain(qu,serv,dgram,dglen,&cbyte,max,rrp);
+  if (st) return st;
+  
+  if (cbyte != max) return adns_s_invaliddata;
+  return adns_s_ok;
+}
 
-  dp[vb.used]= 0;
-  memcpy(dp,vb.buf,vb.used);
+static adns_status pa_mx_raw(adns_query qu, int serv,
+                            const byte *dgram, int dglen, int cbyte, int max,
+                            void *datap) {
+  adns_rr_intstr *rrp= datap;
+  adns_status st;
+  int pref;
 
-  if (cbyte != max) { st= adns_s_invaliddata; goto x_error; }
+  if (cbyte+2 > max) return adns_s_invaliddata;
+  GET_W(cbyte,pref);
+  rrp->i= pref;
+  st= pap_domain(qu,serv,dgram,dglen,&cbyte,max,&rrp->str);
+  if (st) return st;
+  
+  if (cbyte != max) return adns_s_invaliddata;
+  return adns_s_ok;
+}
 
-  st= adns_s_ok;
-  *dpp= dp;
+static int di_mx_raw(const void *datap_a, const void *datap_b) {
+  const adns_rr_intstr *ap= datap_a, *bp= datap_b;
 
- x_error:
-  adns__vbuf_free(&vb);
-  return st;
+  if (ap->i < bp->i) return 0;
+  if (ap->i > bp->i) return 1;
+  return 0;
 }
 
-static void mf_str(adns_query qu, void *data) {
-  char **ddp= data;
+static adns_status pa_txt(adns_query qu, int serv,
+                         const byte *dgram, int dglen, int startbyte, int max,
+                         void *datap) {
+  adns_rr_intstr **rrp= datap, *table, *te;
+  int ti, tc, cbyte, l;
 
-  adns__makefinal_str(qu,ddp);
+  cbyte= startbyte;
+  if (cbyte >= max) return adns_s_invaliddata;
+  tc= 0;
+  while (cbyte < max) {
+    GET_B(cbyte,l);
+    cbyte+= l;
+  }
+  if (cbyte != max) return adns_s_invaliddata;
+
+  table= adns__alloc_interim(qu,sizeof(*table)*(tc+1));
+  if (!table) return adns_s_nolocalmem;
+
+  for (cbyte=startbyte, ti=0, te=table; ti<tc; ti++, te++) {
+    GET_B(cbyte,l);
+    te->str= adns__alloc_interim(qu,l+1);
+    if (!te->str) return adns_s_nolocalmem;
+    te->str[l]= 0;
+    memcpy(te->str,dgram+cbyte,l);
+    te->i= l;
+  }
+  assert(cbyte == max);
+
+  te->i= -1;
+  te->str= 0;
+  
+  *rrp= table;
+  return adns_s_ok;
 }
 
-static int csp_qstring(vbuf *vb, const char *dp) {
+static int csp_textdata(vbuf *vb, const char *dp, int len) {
   unsigned char ch;
   char buf[10];
+  int cn;
 
   if (!adns__vbuf_append(vb,"\"",1)) return 0;
 
-  while ((ch= *dp++)) {
+  for (cn=0; cn<len; cn++) {
+    ch= *dp++;
     if (ch >= 32 && ch <= 126 && ch != '"' && ch != '\\') {
       if (!adns__vbuf_append(vb,&ch,1)) return 0;
     } else {
@@ -98,17 +170,72 @@ static int csp_qstring(vbuf *vb, const char *dp) {
   return 1;
 }
 
-static adns_status cs_str(vbuf *vb, const void *data) {
-  const char *const *dpp= data;
+static int csp_qstring(vbuf *vb, const char *dp) {
+  return csp_textdata(vb, dp, strlen(dp));
+}
+
+static adns_status cs_str(vbuf *vb, const void *datap) {
+  const char *const *rrp= datap;
+
+  return csp_qstring(vb,*rrp) ? adns_s_ok : adns_s_nolocalmem;
+}
+
+static adns_status cs_intstr(vbuf *vb, const void *datap) {
+  const adns_rr_intstr *rrp= datap;
+  char buf[10];
+
+  sprintf(buf,"%u ",rrp->i);
+  return (adns__vbuf_appendstr(vb,buf) &&
+         csp_qstring(vb,rrp->str)) ? adns_s_ok : adns_s_nolocalmem;
+}
+
+static adns_status cs_manyistr(vbuf *vb, const void *datap) {
+  const adns_rr_intstr *const *rrp= datap;
+  const adns_rr_intstr *current;
+  int spc;
+
+  for (spc=0, current= *rrp; current->i >= 0; current++) {
+    if (spc)
+      if (!adns__vbuf_append(vb," ",1)) goto x_nomem;
+    if (!csp_textdata(vb,current->str,current->i)) goto x_nomem;
+  }
+  return adns_s_ok;
 
-  return csp_qstring(vb,*dpp) ? adns_s_ok : adns_s_nolocalmem;
+ x_nomem:
+  return adns_s_nolocalmem;
+}
+
+static void mf_str(adns_query qu, void *datap) {
+  char **rrp= datap;
+
+  adns__makefinal_str(qu,rrp);
+}
+
+static void mf_intstr(adns_query qu, void *datap) {
+  adns_rr_intstr *rrp= datap;
+
+  adns__makefinal_str(qu,&rrp->str);
+}
+
+static void mf_manyistr(adns_query qu, void *datap) {
+  adns_rr_intstr **rrp= datap;
+  adns_rr_intstr *te, *table;
+  void *tablev;
+  int tc;
+
+  for (tc=0, te= *rrp; te->i >= 0; te++, tc++);
+  tablev= *rrp;
+  adns__makefinal_block(qu,&tablev,sizeof(*te)*(tc+1));
+  *rrp= table= tablev;
+  for (te= *rrp; te->i >= 0; te++)
+    adns__makefinal_str(qu,&te->str);
 }
 
 static void mf_flat(adns_query qu, void *data) { }
 
 #define TYPE_SF(size,func,cp,free) size, pa_##func, mf_##free, cs_##cp
 #define TYPE_SN(size,func,cp)      size, pa_##func, mf_flat, cs_##cp
-#define TYPESZ_M(member)           (sizeof(((adns_answer*)0)->rrs.member))
+#define TYPESZ_M(member)           (sizeof(*((adns_answer*)0)->rrs.member))
 #define TYPE_MF(memb,parse)        TYPE_SF(TYPESZ_M(memb),parse,memb,memb)
 #define TYPE_MN(memb,parse)        TYPE_SN(TYPESZ_M(memb),parse,memb)
 
@@ -124,27 +251,33 @@ static void mf_flat(adns_query qu, void *data) { }
 
 static const typeinfo typeinfos[] = {
   /* Must be in ascending order of rrtype ! */
-  /* rr type code   rrt     fmt        mem.mgmt  member      parser        */
+  /* rr type code   rrt     fmt      mem.mgmt  member      parser         comparer */
   
-  { adns_r_a,       "A",     0,        FLAT_MEMB(inaddr),    pa_inaddr      },
-  { adns_r_ns_raw,  "NS",   "raw",     DEEP_MEMB(str),       pa_domain_raw  },
-  { adns_r_cname,   "CNAME", 0,        DEEP_MEMB(str),       pa_domain_raw  },
+  { adns_r_a,       "A",     0,      FLAT_MEMB(inaddr),    pa_inaddr,     di_inaddr  },
+  { adns_r_ns_raw,  "NS",   "raw",   DEEP_MEMB(str),       pa_domain_raw, 0          },
+  { adns_r_cname,   "CNAME", 0,      DEEP_MEMB(str),       pa_domain_raw, 0          },
 #if 0 /*fixme*/                                                                   
-  { adns_r_soa_raw, "SOA",  "raw",     DEEP_MEMB(soa),       pa_soa         },
+  { adns_r_soa_raw, "SOA",  "raw",   DEEP_MEMB(soa),       pa_soa,        0          },
 #endif
-  { adns_r_ptr_raw, "PTR",  "raw",     DEEP_MEMB(str),       pa_domain_raw  },
-#if 0 /*fixme*/
-  { adns_r_hinfo,   "HINFO", 0,        DEEP_MEMB(strpair),   pa_hinfo       },
-  { adns_r_mx_raw,  "MX",   "raw",     DEEP_MEMB(intstr),    pa_mx_raw      },
-  { adns_r_txt,     "TXT",   0,        DEEP_MEMB(manystr),   pa_txt         },
-  { adns_r_rp_raw,  "RP",   "raw",     DEEP_MEMB(strpair),   pa_rp          },
+  { adns_r_ptr_raw, "PTR",  "raw",   DEEP_MEMB(str),       pa_domain_raw, 0          },
+#if 0 /*fixme*/                                                                   
+  { adns_r_hinfo,   "HINFO", 0,      DEEP_MEMB(strpair),   pa_hinfo,      0          },
+#endif
+  { adns_r_mx_raw,  "MX",   "raw",   DEEP_MEMB(intstr),    pa_mx_raw,     di_mx_raw  },
+  { adns_r_txt,     "TXT",   0,      DEEP_MEMB(manyistr),  pa_txt,        0          },
+#if 0 /*fixme*/                                                                   
+  { adns_r_rp_raw,  "RP",   "raw",   DEEP_MEMB(strpair),   pa_rp,         0          },
+#endif
+#if 0 /*fixme*/                                                                   
                                                                           
-  { adns_r_ns,      "NS",   "+addr",   DEEP_MEMB(dmaddr),    pa_dmaddr      },
-  { adns_r_ptr,     "PTR",  "checked", DEEP_MEMB(str),       pa_ptr         },
-  { adns_r_mx,      "MX",   "+addr",   DEEP_MEMB(intdmaddr), pa_mx          },
+  { adns_r_ns,      "NS", "+addr",   DEEP_MEMB(dmaddr),    pa_dmaddr,     di_dmaddr  },
+  { adns_r_ptr,     "PTR","checked", DEEP_MEMB(str),       pa_ptr,        0          },
+  { adns_r_mx,      "MX", "+addr",   DEEP_MEMB(intdmaddr), pa_mx,         di_mx      },
                                                                           
-  { adns_r_soa,     "SOA",  "822",     DEEP_MEMB(soa),       pa_soa         },
-  { adns_r_rp,      "RP",   "822",     DEEP_MEMB(strpair),   pa_rp          },
+#endif
+#if 0 /*fixme*/
+  { adns_r_soa,     "SOA",  "822",   DEEP_MEMB(soa),       pa_soa,        0          },
+  { adns_r_rp,      "RP",   "822",   DEEP_MEMB(strpair),   pa_rp,         0          },
 #endif
 };