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