Commit | Line | Data |
---|---|---|
2117e02e MW |
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 | ||
ddd426ff | 24 | static union { HEADER hdr; unsigned char buf[65536]; } response; |
2117e02e MW |
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 | ||
212b6f5d MW |
274 | if (!stralloc_copy(&glue,sa)) return DNS_MEM; |
275 | if (!stralloc_0(&glue)) return DNS_MEM; | |
276 | if (glue.s[0]) { | |
2117e02e | 277 | ix.pref = 0; |
212b6f5d | 278 | if (!glue.s[ip_scan(glue.s,&ix.ip)] || !glue.s[ip_scanbracket(glue.s,&ix.ip)]) |
2117e02e MW |
279 | { |
280 | if (!ipalloc_append(ia,&ix)) return DNS_MEM; | |
281 | return 0; | |
282 | } | |
212b6f5d | 283 | } |
2117e02e MW |
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; | |
212b6f5d | 318 | struct ip_mx ix; |
2117e02e MW |
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 | ||
212b6f5d MW |
327 | if (!stralloc_copy(&glue,sa)) return DNS_MEM; |
328 | if (!stralloc_0(&glue)) return DNS_MEM; | |
329 | if (glue.s[0]) { | |
2117e02e | 330 | ix.pref = 0; |
212b6f5d | 331 | if (!glue.s[ip_scan(glue.s,&ix.ip)] || !glue.s[ip_scanbracket(glue.s,&ix.ip)]) |
2117e02e MW |
332 | { |
333 | if (!ipalloc_append(ia,&ix)) return DNS_MEM; | |
334 | return 0; | |
335 | } | |
212b6f5d | 336 | } |
2117e02e MW |
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 | } |