chiark / gitweb /
src/: PTR queries now submit A subqueries, not `addr'.
[adns.git] / src / general.c
index 2ced4c0..4404b31 100644 (file)
@@ -4,7 +4,11 @@
  * - vbuf handling
  */
 /*
- *  This file is part of adns, which is Copyright (C) 1997, 1998 Ian Jackson
+ *  This file is part of adns, which is
+ *    Copyright (C) 1997-2000,2003,2006  Ian Jackson
+ *    Copyright (C) 1999-2000,2003,2006  Tony Finch
+ *    Copyright (C) 1991 Massachusetts Institute of Technology
+ *  (See the file INSTALL for full details.)
  *  
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
  */
 
 #include <stdlib.h>
+#include <unistd.h>
 
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
 #include <arpa/inet.h>
 
 #include "internal.h"
 
 /* Core diagnostic functions */
 
+void adns__vlprintf(adns_state ads, const char *fmt, va_list al) {
+  ads->logfn(ads,ads->logfndata,fmt,al);
+}
+
+void adns__lprintf(adns_state ads, const char *fmt, ...) {
+  va_list al;
+  va_start(al,fmt);
+  adns__vlprintf(ads,fmt,al);
+  va_end(al);
+}
+
 void adns__vdiag(adns_state ads, const char *pfx, adns_initflags prevent,
                 int serv, adns_query qu, const char *fmt, va_list al) {
   const char *bef, *aft;
   vbuf vb;
-  if (!(ads->iflags & adns_if_debug) && (!prevent || (ads->iflags & prevent))) return;
+  
+  if (!ads->logfn ||
+      (!(ads->iflags & adns_if_debug)
+       && (!prevent || (ads->iflags & prevent))))
+    return;
 
-  fprintf(stderr,"adns%s: ",pfx);
+  if (ads->iflags & adns_if_logpid) {
+    adns__lprintf(ads,"adns%s [%ld]: ",pfx,(long)getpid());
+  } else {
+    adns__lprintf(ads,"adns%s: ",pfx);
+  }
 
-  vfprintf(stderr,fmt,al);
+  adns__vlprintf(ads,fmt,al);
 
   bef= " (";
   aft= "\n";
 
   if (qu && qu->query_dgram) {
     adns__vbuf_init(&vb);
-    fprintf(stderr,"%sQNAME=%s, QTYPE=%s",
+    adns__lprintf(ads,"%sQNAME=%s, QTYPE=%s",
            bef,
-           adns__diag_domain(qu->ads,-1,0, &vb,qu->flags,
+           adns__diag_domain(qu->ads,-1,0, &vb,
                              qu->query_dgram,qu->query_dglen,DNS_HDRSIZE),
-           qu->typei ? qu->typei->name : "<unknown>");
+           qu->typei ? qu->typei->rrtname : "<unknown>");
+    if (qu->typei && qu->typei->fmtname)
+      adns__lprintf(ads,"(%s)",qu->typei->fmtname);
     bef=", "; aft=")\n";
+    adns__vbuf_free(&vb);
   }
   
   if (serv>=0) {
-    fprintf(stderr,"%sNS=%s",bef,inet_ntoa(ads->servers[serv].addr));
+    adns__lprintf(ads,"%sNS=%s",bef,inet_ntoa(ads->servers[serv].addr));
     bef=", "; aft=")\n";
   }
 
-  fputs(aft,stderr);
+  adns__lprintf(ads,"%s",aft);
 }
 
-void adns__debug(adns_state ads, int serv, adns_query qu, const char *fmt, ...) {
+void adns__debug(adns_state ads, int serv, adns_query qu,
+                const char *fmt, ...) {
   va_list al;
 
   va_start(al,fmt);
@@ -68,15 +99,18 @@ void adns__debug(adns_state ads, int serv, adns_query qu, const char *fmt, ...)
   va_end(al);
 }
 
-void adns__warn(adns_state ads, int serv, adns_query qu, const char *fmt, ...) {
+void adns__warn(adns_state ads, int serv, adns_query qu,
+               const char *fmt, ...) {
   va_list al;
 
   va_start(al,fmt);
-  adns__vdiag(ads," warning",adns_if_noerrprint|adns_if_noserverwarn,serv,qu,fmt,al);
+  adns__vdiag(ads," warning",
+             adns_if_noerrprint|adns_if_noserverwarn, serv,qu,fmt,al);
   va_end(al);
 }
 
-void adns__diag(adns_state ads, int serv, adns_query qu, const char *fmt, ...) {
+void adns__diag(adns_state ads, int serv, adns_query qu,
+               const char *fmt, ...) {
   va_list al;
 
   va_start(al,fmt);
@@ -111,9 +145,10 @@ int adns__vbuf_append(vbuf *vb, const byte *data, int len) {
 
   newlen= vb->used+len;
   if (vb->avail < newlen) {
+    if (newlen<20) newlen= 20;
     newlen <<= 1;
     nb= realloc(vb->buf,newlen);
-    if (!nb) { newlen >>= 1; nb= realloc(vb->buf,newlen); }
+    if (!nb) { newlen= vb->used+len; nb= realloc(vb->buf,newlen); }
     if (!nb) return 0;
     vb->buf= nb;
     vb->avail= newlen;
@@ -122,14 +157,27 @@ int adns__vbuf_append(vbuf *vb, const byte *data, int len) {
   return 1;
 }
 
+int adns__vbuf_appendstr(vbuf *vb, const char *data) {
+  int l;
+  l= strlen(data);
+  return adns__vbuf_append(vb,data,l);
+}
+
+void adns__vbuf_free(vbuf *vb) {
+  free(vb->buf);
+  adns__vbuf_init(vb);
+}
+
 /* Additional diagnostic functions */
 
-const char *adns__diag_domain(adns_state ads, int serv, adns_query qu, vbuf *vb,
-                             int flags, const byte *dgram, int dglen, int cbyte) {
+const char *adns__diag_domain(adns_state ads, int serv, adns_query qu,
+                             vbuf *vb, const byte *dgram,
+                             int dglen, int cbyte) {
   adns_status st;
 
-  st= adns__parse_domain(ads,serv,qu,vb, flags,dgram,dglen,&cbyte,dglen);
-  if (st == adns_s_nolocalmem) {
+  st= adns__parse_domain(ads,serv,qu,vb, pdf_quoteok,
+                        dgram,dglen,&cbyte,dglen);
+  if (st == adns_s_nomemory) {
     return "<cannot report domain... out of memory>";
   }
   if (st) {
@@ -147,9 +195,185 @@ const char *adns__diag_domain(adns_state ads, int serv, adns_query qu, vbuf *vb,
   }
   return vb->buf;
 }
-    
+
+adns_status adns_rr_info(adns_rrtype type,
+                        const char **rrtname_r, const char **fmtname_r,
+                        int *len_r,
+                        const void *datap, char **data_r) {
+  const typeinfo *typei;
+  vbuf vb;
+  adns_status st;
+
+  typei= adns__findtype(type);
+  if (!typei) return adns_s_unknownrrtype;
+
+  if (rrtname_r) *rrtname_r= typei->rrtname;
+  if (fmtname_r) *fmtname_r= typei->fmtname;
+  if (len_r) *len_r= typei->rrsz;
+
+  if (!datap) return adns_s_ok;
+  
+  adns__vbuf_init(&vb);
+  st= typei->convstring(&vb,datap);
+  if (st) goto x_freevb;
+  if (!adns__vbuf_append(&vb,"",1)) { st= adns_s_nomemory; goto x_freevb; }
+  assert(strlen(vb.buf) == vb.used-1);
+  *data_r= realloc(vb.buf,vb.used);
+  if (!*data_r) *data_r= vb.buf;
+  return adns_s_ok;
+
+ x_freevb:
+  adns__vbuf_free(&vb);
+  return st;
+}
+
+
+#define SINFO(n,s) { adns_s_##n, #n, s }
+
+static const struct sinfo {
+  adns_status st;
+  const char *abbrev;
+  const char *string;
+} sinfos[]= {
+  SINFO( ok,                  "OK"                                           ),
+                                                                             
+  SINFO( nomemory,            "Out of memory"                                ),
+  SINFO( unknownrrtype,       "Query not implemented in DNS library"         ),
+  SINFO( systemfail,          "General resolver or system failure"           ),
+                                                                             
+  SINFO( timeout,             "DNS query timed out"                          ),
+  SINFO( allservfail,         "All nameservers failed"                       ),
+  SINFO( norecurse,           "Recursion denied by nameserver"               ),
+  SINFO( invalidresponse,     "Nameserver sent bad response"                 ),
+  SINFO( unknownformat,       "Nameserver used unknown format"               ),
+                                                                             
+  SINFO( rcodeservfail,       "Nameserver reports failure"                   ),
+  SINFO( rcodeformaterror,    "Query not understood by nameserver"           ),
+  SINFO( rcodenotimplemented, "Query not implemented by nameserver"          ),
+  SINFO( rcoderefused,        "Query refused by nameserver"                  ),
+  SINFO( rcodeunknown,        "Nameserver sent unknown response code"        ),
+                                                                             
+  SINFO( inconsistent,        "Inconsistent resource records in DNS"         ),
+  SINFO( prohibitedcname,     "DNS alias found where canonical name wanted"  ),
+  SINFO( answerdomaininvalid, "Found syntactically invalid domain name"      ),
+  SINFO( answerdomaintoolong, "Found overly-long domain name"                ),
+  SINFO( invaliddata,         "Found invalid DNS data"                       ),
+                                                                             
+  SINFO( querydomainwrong,    "Domain invalid for particular DNS query type" ),
+  SINFO( querydomaininvalid,  "Domain name is syntactically invalid"         ),
+  SINFO( querydomaintoolong,  "Domain name or component is too long"         ),
+                                                                             
+  SINFO( nxdomain,            "No such domain"                               ),
+  SINFO( nodata,              "No such data"                                 )
+};
+
+static int si_compar(const void *key, const void *elem) {
+  const adns_status *st= key;
+  const struct sinfo *si= elem;
+
+  return *st < si->st ? -1 : *st > si->st ? 1 : 0;
+}
+
+static const struct sinfo *findsinfo(adns_status st) {
+  return bsearch(&st,sinfos, sizeof(sinfos)/sizeof(*sinfos),
+                sizeof(*sinfos), si_compar);
+}
+
 const char *adns_strerror(adns_status st) {
-  static char buf[100];
-  snprintf(buf,sizeof(buf),"code %d",st);
-  return buf;
+  const struct sinfo *si;
+
+  si= findsinfo(st);
+  return si->string;
+}
+
+const char *adns_errabbrev(adns_status st) {
+  const struct sinfo *si;
+
+  si= findsinfo(st);
+  return si->abbrev;
+}
+
+
+#define STINFO(max) { adns_s_max_##max, #max }
+
+static const struct stinfo {
+  adns_status stmax;
+  const char *abbrev;
+} stinfos[]= {
+  { adns_s_ok, "ok" },
+  STINFO(  localfail   ),
+  STINFO(  remotefail  ),
+  STINFO(  tempfail    ),
+  STINFO(  misconfig   ),
+  STINFO(  misquery    ),
+  STINFO(  permfail    )
+};
+
+static int sti_compar(const void *key, const void *elem) {
+  const adns_status *st= key;
+  const struct stinfo *sti= elem;
+
+  adns_status here, min, max;
+
+  here= *st;
+  min= (sti==stinfos) ? 0 : sti[-1].stmax+1;
+  max= sti->stmax;
+  
+  return here < min  ? -1 : here > max ? 1 : 0;
+}
+
+const char *adns_errtypeabbrev(adns_status st) {
+  const struct stinfo *sti;
+
+  sti= bsearch(&st,stinfos, sizeof(stinfos)/sizeof(*stinfos),
+              sizeof(*stinfos), sti_compar);
+  return sti->abbrev;
+}
+
+
+void adns__isort(void *array, int nobjs, int sz, void *tempbuf,
+                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(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);
+      memcpy(data + place*sz, tempbuf, sz);
+    }
+  }
+}
+
+/* SIGPIPE protection. */
+
+void adns__sigpipe_protect(adns_state ads) {
+  sigset_t toblock;
+  struct sigaction sa;
+  int r;
+
+  if (ads->iflags & adns_if_nosigpipe) return;
+
+  sigfillset(&toblock);
+  sigdelset(&toblock,SIGPIPE);
+
+  sa.sa_handler= SIG_IGN;
+  sigfillset(&sa.sa_mask);
+  sa.sa_flags= 0;
+  
+  r= sigprocmask(SIG_SETMASK,&toblock,&ads->stdsigmask); assert(!r);
+  r= sigaction(SIGPIPE,&sa,&ads->stdsigpipe); assert(!r);
+}
+
+void adns__sigpipe_unprotect(adns_state ads) {
+  int r;
+
+  if (ads->iflags & adns_if_nosigpipe) return;
+
+  r= sigaction(SIGPIPE,&ads->stdsigpipe,0); assert(!r);
+  r= sigprocmask(SIG_SETMASK,&ads->stdsigmask,0); assert(!r);
 }