nightmaremail

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

dns.c (8577B)


      1 #include "dns.h"
      2 
      3 #include <stdio.h>
      4 #include <netdb.h>
      5 #include <sys/types.h>
      6 #include <netinet/in.h>
      7 #ifndef BIND_8_COMPAT
      8 #define BIND_8_COMPAT /* Mac OS X: if Bind 9, Bind 8 compatibility */
      9 #endif
     10 #include <arpa/nameser.h>
     11 #include <resolv.h>
     12 #include <errno.h>
     13 extern int res_query();
     14 extern int res_search();
     15 #include "ip.h"
     16 #include "ipalloc.h"
     17 #include "fmt.h"
     18 #include "alloc.h"
     19 #include "str.h"
     20 #include "stralloc.h"
     21 #include "case.h"
     22 
     23 #define MAX_EDNS_RESPONSE_SIZE 65536
     24 
     25 static unsigned short getshort(c) unsigned char *c;
     26 { unsigned short u; u = c[0]; return (u << 8) + c[1]; }
     27 
     28 static struct { unsigned char *buf; } response;
     29 static int responsebuflen = 0;
     30 static int responselen;
     31 static unsigned char *responseend;
     32 static unsigned char *responsepos;
     33 static unsigned long saveresoptions;
     34 
     35 static int numanswers;
     36 static char name[MAXDNAME];
     37 static struct ip_address ip;
     38 unsigned short pref;
     39 
     40 static stralloc glue = {0};
     41 
     42 static int (*lookup)() = res_query;
     43 
     44 static int resolve(domain,type)
     45 stralloc *domain;
     46 int type;
     47 {
     48  int n;
     49  int i;
     50 
     51  errno = 0;
     52  if (!stralloc_copy(&glue,domain)) return DNS_MEM;
     53  if (!stralloc_0(&glue)) return DNS_MEM;
     54  if (!responsebuflen) {
     55   if ((response.buf = malloc(PACKETSZ+1)))
     56    responsebuflen = PACKETSZ+1;
     57   else return DNS_MEM;
     58  }
     59 
     60  responselen = lookup(glue.s,C_IN,type,response.buf,responsebuflen);
     61  if ((responselen >= responsebuflen) ||
     62      (responselen > 0 && (((HEADER *)response.buf)->tc)))
     63   {
     64    if (responsebuflen < MAX_EDNS_RESPONSE_SIZE) {
     65     unsigned char *newbuf = realloc(response.buf, MAX_EDNS_RESPONSE_SIZE);
     66     if (newbuf) {
     67      response.buf = newbuf;
     68      responsebuflen = MAX_EDNS_RESPONSE_SIZE;
     69     }
     70     else return DNS_MEM;
     71     saveresoptions = _res.options;
     72     _res.options |= RES_USEVC;
     73     responselen = lookup(glue.s,C_IN,type,response.buf,responsebuflen);
     74     _res.options = saveresoptions;
     75    }
     76   }
     77  if (responselen <= 0)
     78   {
     79    if (errno == ECONNREFUSED) return DNS_SOFT;
     80    if (h_errno == TRY_AGAIN) return DNS_SOFT;
     81    return DNS_HARD;
     82   }
     83  responseend = response.buf + responselen;
     84  responsepos = response.buf + sizeof(HEADER);
     85  n = ntohs(((HEADER *)response.buf)->qdcount);
     86  while (n-- > 0)
     87   {
     88    i = dn_expand(response.buf,responseend,responsepos,name,MAXDNAME);
     89    if (i < 0) return DNS_SOFT;
     90    responsepos += i;
     91    i = responseend - responsepos;
     92    if (i < QFIXEDSZ) return DNS_SOFT;
     93    responsepos += QFIXEDSZ;
     94   }
     95  numanswers = ntohs(((HEADER *)response.buf)->ancount);
     96  return 0;
     97 }
     98 
     99 static int findname(wanttype)
    100 int wanttype;
    101 {
    102  unsigned short rrtype;
    103  unsigned short rrdlen;
    104  int i;
    105 
    106  if (numanswers <= 0) return 2;
    107  --numanswers;
    108  if (responsepos == responseend) return DNS_SOFT;
    109 
    110  i = dn_expand(response.buf,responseend,responsepos,name,MAXDNAME);
    111  if (i < 0) return DNS_SOFT;
    112  responsepos += i;
    113 
    114  i = responseend - responsepos;
    115  if (i < 4 + 3 * 2) return DNS_SOFT;
    116    
    117  rrtype = getshort(responsepos);
    118  rrdlen = getshort(responsepos + 8);
    119  responsepos += 10;
    120 
    121  if (rrtype == wanttype)
    122   {
    123    if (dn_expand(response.buf,responseend,responsepos,name,MAXDNAME) < 0)
    124      return DNS_SOFT;
    125    responsepos += rrdlen;
    126    return 1;
    127   }
    128    
    129  responsepos += rrdlen;
    130  return 0;
    131 }
    132 
    133 // XXX A only
    134 static int findip(int wanttype)
    135 {
    136  unsigned short rrtype;
    137  unsigned short rrdlen;
    138  int i;
    139 
    140  if (numanswers <= 0) return 2;
    141  --numanswers;
    142  if (responsepos == responseend) return DNS_SOFT;
    143 
    144  i = dn_expand(response.buf,responseend,responsepos,name,MAXDNAME);
    145  if (i < 0) return DNS_SOFT;
    146  responsepos += i;
    147 
    148  i = responseend - responsepos;
    149  if (i < 4 + 3 * 2) return DNS_SOFT;
    150 
    151  rrtype = getshort(responsepos);
    152  rrdlen = getshort(responsepos + 8);
    153  responsepos += 10;
    154 
    155  if (rrtype == wanttype)
    156   {
    157    if (rrdlen < 4)
    158      return DNS_SOFT;
    159    ip.is6 = 0;
    160    ip.ip.a.d[0] = responsepos[0];
    161    ip.ip.a.d[1] = responsepos[1];
    162    ip.ip.a.d[2] = responsepos[2];
    163    ip.ip.a.d[3] = responsepos[3];
    164    responsepos += rrdlen;
    165    return 1;
    166   }
    167    
    168  responsepos += rrdlen;
    169  return 0;
    170 }
    171 
    172 static int findmx(wanttype)
    173 int wanttype;
    174 {
    175  unsigned short rrtype;
    176  unsigned short rrdlen;
    177  int i;
    178 
    179  if (numanswers <= 0) return 2;
    180  --numanswers;
    181  if (responsepos == responseend) return DNS_SOFT;
    182 
    183  i = dn_expand(response.buf,responseend,responsepos,name,MAXDNAME);
    184  if (i < 0) return DNS_SOFT;
    185  responsepos += i;
    186 
    187  i = responseend - responsepos;
    188  if (i < 4 + 3 * 2) return DNS_SOFT;
    189    
    190  rrtype = getshort(responsepos);
    191  rrdlen = getshort(responsepos + 8);
    192  responsepos += 10;
    193 
    194  if (rrtype == wanttype)
    195   {
    196    if (rrdlen < 3)
    197      return DNS_SOFT;
    198    pref = (responsepos[0] << 8) + responsepos[1];
    199    if (dn_expand(response.buf,responseend,responsepos + 2,name,MAXDNAME) < 0)
    200      return DNS_SOFT;
    201    responsepos += rrdlen;
    202    return 1;
    203   }
    204    
    205  responsepos += rrdlen;
    206  return 0;
    207 }
    208 
    209 void dns_init(flagsearch)
    210 int flagsearch;
    211 {
    212  res_init();
    213  if (flagsearch) lookup = res_search;
    214 }
    215 
    216 #define FMT_IAA 40
    217 
    218 static int iaafmt(char *s, struct ip_address *ipa)
    219 {
    220  unsigned int i;
    221  unsigned int len;
    222  len = 0;
    223  i = fmt_ulong(s,(unsigned long) ipa->ip.a.d[3]); len += i; if (s) s += i;
    224  i = fmt_str(s,"."); len += i; if (s) s += i;
    225  i = fmt_ulong(s,(unsigned long) ipa->ip.a.d[2]); len += i; if (s) s += i;
    226  i = fmt_str(s,"."); len += i; if (s) s += i;
    227  i = fmt_ulong(s,(unsigned long) ipa->ip.a.d[1]); len += i; if (s) s += i;
    228  i = fmt_str(s,"."); len += i; if (s) s += i;
    229  i = fmt_ulong(s,(unsigned long) ipa->ip.a.d[0]); len += i; if (s) s += i;
    230  i = fmt_str(s,".in-addr.arpa."); len += i; if (s) s += i;
    231  return len;
    232 }
    233 
    234 int dns_ptr(stralloc *sa, struct ip_address *ipa)
    235 {
    236  int r;
    237 
    238  if (!stralloc_ready(sa,iaafmt(NULL,ipa))) return DNS_MEM;
    239  sa->len = iaafmt(sa->s,ipa);
    240  switch(resolve(sa,T_PTR))
    241   {
    242    case DNS_MEM: return DNS_MEM;
    243    case DNS_SOFT: return DNS_SOFT;
    244    case DNS_HARD: return DNS_HARD;
    245   }
    246  while ((r = findname(T_PTR)) != 2)
    247   {
    248    if (r == DNS_SOFT) return DNS_SOFT;
    249    if (r == 1)
    250     {
    251      if (!stralloc_copys(sa,name)) return DNS_MEM;
    252      return 0;
    253     }
    254   }
    255  return DNS_HARD;
    256 }
    257 
    258 static int dns_ipplus(ipalloc *ia, stralloc *sa, int dpref)
    259 {
    260  int r;
    261  struct ip_mx ix;
    262 
    263  if (!stralloc_copy(&glue,sa)) return DNS_MEM;
    264  if (!stralloc_0(&glue)) return DNS_MEM;
    265  if (glue.s[0]) {
    266    ix.pref = 0;
    267    if (!glue.s[ip_scan(glue.s,&ix.ip)] || !glue.s[ip_scanbracket(glue.s,&ix.ip)])
    268     {
    269      if (!ipalloc_append(ia,&ix)) return DNS_MEM;
    270      return 0;
    271     }
    272  }
    273 
    274  switch(resolve(sa,T_A))
    275   {
    276    case DNS_MEM: return DNS_MEM;
    277    case DNS_SOFT: return DNS_SOFT;
    278    case DNS_HARD: return DNS_HARD;
    279   }
    280  while ((r = findip(T_A)) != 2)
    281   {
    282    ix.ip = ip;
    283    ix.pref = dpref;
    284    if (r == DNS_SOFT) return DNS_SOFT;
    285    if (r == 1)
    286      if (!ipalloc_append(ia,&ix)) return DNS_MEM;
    287   }
    288  return 0;
    289 }
    290 
    291 int dns_ip(ia,sa)
    292 ipalloc *ia;
    293 stralloc *sa;
    294 {
    295  if (!ipalloc_readyplus(ia,0)) return DNS_MEM;
    296  ia->len = 0;
    297  return dns_ipplus(ia,sa,0);
    298 }
    299 
    300 int dns_mxip(ia,sa,random)
    301 ipalloc *ia;
    302 stralloc *sa;
    303 unsigned long random;
    304 {
    305  int r;
    306  struct mx { stralloc sa; unsigned short p; } *mx;
    307  struct ip_mx ix;
    308  int nummx;
    309  int i;
    310  int j;
    311  int flagsoft;
    312 
    313  if (!ipalloc_readyplus(ia,0)) return DNS_MEM;
    314  ia->len = 0;
    315 
    316  if (!stralloc_copy(&glue,sa)) return DNS_MEM;
    317  if (!stralloc_0(&glue)) return DNS_MEM;
    318  if (glue.s[0]) {
    319    ix.pref = 0;
    320    if (!glue.s[ip_scan(glue.s,&ix.ip)] || !glue.s[ip_scanbracket(glue.s,&ix.ip)])
    321     {
    322      if (!ipalloc_append(ia,&ix)) return DNS_MEM;
    323      return 0;
    324     }
    325  }
    326 
    327  switch(resolve(sa,T_MX))
    328   {
    329    case DNS_MEM: return DNS_MEM;
    330    case DNS_SOFT: return DNS_SOFT;
    331    case DNS_HARD: return dns_ip(ia,sa);
    332   }
    333 
    334  mx = (struct mx *) alloc(numanswers * sizeof(struct mx));
    335  if (!mx) return DNS_MEM;
    336  nummx = 0;
    337 
    338  while ((r = findmx(T_MX)) != 2)
    339   {
    340    if (r == DNS_SOFT) { alloc_free(mx); return DNS_SOFT; }
    341    if (r == 1)
    342     {
    343      mx[nummx].p = pref;
    344      mx[nummx].sa.s = 0;
    345      if (!stralloc_copys(&mx[nummx].sa,name))
    346       {
    347        while (nummx > 0) alloc_free(mx[--nummx].sa.s);
    348        alloc_free(mx); return DNS_MEM;
    349       }
    350      ++nummx;
    351     }
    352   }
    353 
    354  if (!nummx) return dns_ip(ia,sa); /* e.g., CNAME -> A */
    355 
    356  flagsoft = 0;
    357  while (nummx > 0)
    358   {
    359    unsigned long numsame;
    360 
    361    i = 0;
    362    numsame = 1;
    363    for (j = 1;j < nummx;++j)
    364      if (mx[j].p < mx[i].p)
    365       {
    366        i = j;
    367        numsame = 1;
    368       }
    369      else if (mx[j].p == mx[i].p)
    370       {
    371        ++numsame;
    372        random = random * 69069 + 1;
    373        if ((random / 2) < (2147483647 / numsame))
    374          i = j;
    375       }
    376 
    377    switch(dns_ipplus(ia,&mx[i].sa,mx[i].p))
    378     {
    379      case DNS_MEM: case DNS_SOFT:
    380        flagsoft = 1; break;
    381     }
    382 
    383    alloc_free(mx[i].sa.s);
    384    mx[i] = mx[--nummx];
    385   }
    386 
    387  alloc_free(mx);
    388  return flagsoft;
    389 }