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