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