1 | #if !defined(lint) && !defined(SABER) |
---|
2 | static const char rcsid[] = "$Id: res_findzonecut.c,v 1.7.18.3 2005/10/11 00:25:11 marka Exp $"; |
---|
3 | #endif /* not lint */ |
---|
4 | |
---|
5 | /* |
---|
6 | * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") |
---|
7 | * Copyright (c) 1999 by Internet Software Consortium. |
---|
8 | * |
---|
9 | * Permission to use, copy, modify, and distribute this software for any |
---|
10 | * purpose with or without fee is hereby granted, provided that the above |
---|
11 | * copyright notice and this permission notice appear in all copies. |
---|
12 | * |
---|
13 | * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES |
---|
14 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
---|
15 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR |
---|
16 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
---|
17 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
---|
18 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT |
---|
19 | * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
---|
20 | */ |
---|
21 | |
---|
22 | #include <sys/cdefs.h> |
---|
23 | __FBSDID("$FreeBSD$"); |
---|
24 | |
---|
25 | /* Import. */ |
---|
26 | |
---|
27 | #include "port_before.h" |
---|
28 | |
---|
29 | #include <sys/param.h> |
---|
30 | #include <sys/socket.h> |
---|
31 | #include <sys/time.h> |
---|
32 | |
---|
33 | #include <netinet/in.h> |
---|
34 | #include <arpa/inet.h> |
---|
35 | #include <arpa/nameser.h> |
---|
36 | |
---|
37 | #include <errno.h> |
---|
38 | #include <limits.h> |
---|
39 | #include <netdb.h> |
---|
40 | #include <stdarg.h> |
---|
41 | #include <stdio.h> |
---|
42 | #include <stdlib.h> |
---|
43 | #include <string.h> |
---|
44 | |
---|
45 | #include <isc/list.h> |
---|
46 | |
---|
47 | #include "port_after.h" |
---|
48 | |
---|
49 | #include <resolv.h> |
---|
50 | |
---|
51 | /* Data structures. */ |
---|
52 | |
---|
53 | typedef struct rr_a { |
---|
54 | LINK(struct rr_a) link; |
---|
55 | union res_sockaddr_union addr; |
---|
56 | } rr_a; |
---|
57 | typedef LIST(rr_a) rrset_a; |
---|
58 | |
---|
59 | typedef struct rr_ns { |
---|
60 | LINK(struct rr_ns) link; |
---|
61 | const char * name; |
---|
62 | unsigned int flags; |
---|
63 | rrset_a addrs; |
---|
64 | } rr_ns; |
---|
65 | typedef LIST(rr_ns) rrset_ns; |
---|
66 | |
---|
67 | #define RR_NS_HAVE_V4 0x01 |
---|
68 | #define RR_NS_HAVE_V6 0x02 |
---|
69 | |
---|
70 | /* Forward. */ |
---|
71 | |
---|
72 | static int satisfy(res_state, const char *, rrset_ns *, |
---|
73 | union res_sockaddr_union *, int); |
---|
74 | static int add_addrs(res_state, rr_ns *, |
---|
75 | union res_sockaddr_union *, int); |
---|
76 | static int get_soa(res_state, const char *, ns_class, int, |
---|
77 | char *, size_t, char *, size_t, |
---|
78 | rrset_ns *); |
---|
79 | static int get_ns(res_state, const char *, ns_class, int, rrset_ns *); |
---|
80 | static int get_glue(res_state, ns_class, int, rrset_ns *); |
---|
81 | static int save_ns(res_state, ns_msg *, ns_sect, |
---|
82 | const char *, ns_class, int, rrset_ns *); |
---|
83 | static int save_a(res_state, ns_msg *, ns_sect, |
---|
84 | const char *, ns_class, int, rr_ns *); |
---|
85 | static void free_nsrrset(rrset_ns *); |
---|
86 | static void free_nsrr(rrset_ns *, rr_ns *); |
---|
87 | static rr_ns * find_ns(rrset_ns *, const char *); |
---|
88 | static int do_query(res_state, const char *, ns_class, ns_type, |
---|
89 | u_char *, ns_msg *); |
---|
90 | static void res_dprintf(const char *, ...) ISC_FORMAT_PRINTF(1, 2); |
---|
91 | |
---|
92 | /* Macros. */ |
---|
93 | |
---|
94 | #define DPRINTF(x) do {\ |
---|
95 | int save_errno = errno; \ |
---|
96 | if ((statp->options & RES_DEBUG) != 0U) res_dprintf x; \ |
---|
97 | errno = save_errno; \ |
---|
98 | } while (0) |
---|
99 | |
---|
100 | /* Public. */ |
---|
101 | |
---|
102 | /*% |
---|
103 | * find enclosing zone for a <dname,class>, and some server addresses |
---|
104 | * |
---|
105 | * parameters: |
---|
106 | *\li res - resolver context to work within (is modified) |
---|
107 | *\li dname - domain name whose enclosing zone is desired |
---|
108 | *\li class - class of dname (and its enclosing zone) |
---|
109 | *\li zname - found zone name |
---|
110 | *\li zsize - allocated size of zname |
---|
111 | *\li addrs - found server addresses |
---|
112 | *\li naddrs - max number of addrs |
---|
113 | * |
---|
114 | * return values: |
---|
115 | *\li < 0 - an error occurred (check errno) |
---|
116 | *\li = 0 - zname is now valid, but addrs[] wasn't changed |
---|
117 | *\li > 0 - zname is now valid, and return value is number of addrs[] found |
---|
118 | * |
---|
119 | * notes: |
---|
120 | *\li this function calls res_nsend() which means it depends on correctly |
---|
121 | * functioning recursive nameservers (usually defined in /etc/resolv.conf |
---|
122 | * or its local equivilent). |
---|
123 | * |
---|
124 | *\li we start by asking for an SOA<dname,class>. if we get one as an |
---|
125 | * answer, that just means <dname,class> is a zone top, which is fine. |
---|
126 | * more than likely we'll be told to go pound sand, in the form of a |
---|
127 | * negative answer. |
---|
128 | * |
---|
129 | *\li note that we are not prepared to deal with referrals since that would |
---|
130 | * only come from authority servers and our correctly functioning local |
---|
131 | * recursive server would have followed the referral and got us something |
---|
132 | * more definite. |
---|
133 | * |
---|
134 | *\li if the authority section contains an SOA, this SOA should also be the |
---|
135 | * closest enclosing zone, since any intermediary zone cuts would've been |
---|
136 | * returned as referrals and dealt with by our correctly functioning local |
---|
137 | * recursive name server. but an SOA in the authority section should NOT |
---|
138 | * match our dname (since that would have been returned in the answer |
---|
139 | * section). an authority section SOA has to be "above" our dname. |
---|
140 | * |
---|
141 | *\li however, since authority section SOA's were once optional, it's |
---|
142 | * possible that we'll have to go hunting for the enclosing SOA by |
---|
143 | * ripping labels off the front of our dname -- this is known as "doing |
---|
144 | * it the hard way." |
---|
145 | * |
---|
146 | *\li ultimately we want some server addresses, which are ideally the ones |
---|
147 | * pertaining to the SOA.MNAME, but only if there is a matching NS RR. |
---|
148 | * so the second phase (after we find an SOA) is to go looking for the |
---|
149 | * NS RRset for that SOA's zone. |
---|
150 | * |
---|
151 | *\li no answer section processed by this code is allowed to contain CNAME |
---|
152 | * or DNAME RR's. for the SOA query this means we strip a label and |
---|
153 | * keep going. for the NS and A queries this means we just give up. |
---|
154 | */ |
---|
155 | |
---|
156 | #ifndef _LIBC |
---|
157 | int |
---|
158 | res_findzonecut(res_state statp, const char *dname, ns_class class, int opts, |
---|
159 | char *zname, size_t zsize, struct in_addr *addrs, int naddrs) |
---|
160 | { |
---|
161 | int result, i; |
---|
162 | union res_sockaddr_union *u; |
---|
163 | |
---|
164 | |
---|
165 | opts |= RES_IPV4ONLY; |
---|
166 | opts &= ~RES_IPV6ONLY; |
---|
167 | |
---|
168 | u = calloc(naddrs, sizeof(*u)); |
---|
169 | if (u == NULL) |
---|
170 | return(-1); |
---|
171 | |
---|
172 | result = res_findzonecut2(statp, dname, class, opts, zname, zsize, |
---|
173 | u, naddrs); |
---|
174 | |
---|
175 | for (i = 0; i < result; i++) { |
---|
176 | addrs[i] = u[i].sin.sin_addr; |
---|
177 | } |
---|
178 | free(u); |
---|
179 | return (result); |
---|
180 | } |
---|
181 | #endif |
---|
182 | |
---|
183 | int |
---|
184 | res_findzonecut2(res_state statp, const char *dname, ns_class class, int opts, |
---|
185 | char *zname, size_t zsize, union res_sockaddr_union *addrs, |
---|
186 | int naddrs) |
---|
187 | { |
---|
188 | char mname[NS_MAXDNAME]; |
---|
189 | u_long save_pfcode; |
---|
190 | rrset_ns nsrrs; |
---|
191 | int n; |
---|
192 | |
---|
193 | DPRINTF(("START dname='%s' class=%s, zsize=%ld, naddrs=%d", |
---|
194 | dname, p_class(class), (long)zsize, naddrs)); |
---|
195 | save_pfcode = statp->pfcode; |
---|
196 | statp->pfcode |= RES_PRF_HEAD2 | RES_PRF_HEAD1 | RES_PRF_HEADX | |
---|
197 | RES_PRF_QUES | RES_PRF_ANS | |
---|
198 | RES_PRF_AUTH | RES_PRF_ADD; |
---|
199 | INIT_LIST(nsrrs); |
---|
200 | |
---|
201 | DPRINTF(("get the soa, and see if it has enough glue")); |
---|
202 | if ((n = get_soa(statp, dname, class, opts, zname, zsize, |
---|
203 | mname, sizeof mname, &nsrrs)) < 0 || |
---|
204 | ((opts & RES_EXHAUSTIVE) == 0 && |
---|
205 | (n = satisfy(statp, mname, &nsrrs, addrs, naddrs)) > 0)) |
---|
206 | goto done; |
---|
207 | |
---|
208 | DPRINTF(("get the ns rrset and see if it has enough glue")); |
---|
209 | if ((n = get_ns(statp, zname, class, opts, &nsrrs)) < 0 || |
---|
210 | ((opts & RES_EXHAUSTIVE) == 0 && |
---|
211 | (n = satisfy(statp, mname, &nsrrs, addrs, naddrs)) > 0)) |
---|
212 | goto done; |
---|
213 | |
---|
214 | DPRINTF(("get the missing glue and see if it's finally enough")); |
---|
215 | if ((n = get_glue(statp, class, opts, &nsrrs)) >= 0) |
---|
216 | n = satisfy(statp, mname, &nsrrs, addrs, naddrs); |
---|
217 | |
---|
218 | done: |
---|
219 | DPRINTF(("FINISH n=%d (%s)", n, (n < 0) ? strerror(errno) : "OK")); |
---|
220 | free_nsrrset(&nsrrs); |
---|
221 | statp->pfcode = save_pfcode; |
---|
222 | return (n); |
---|
223 | } |
---|
224 | |
---|
225 | /* Private. */ |
---|
226 | |
---|
227 | static int |
---|
228 | satisfy(res_state statp, const char *mname, rrset_ns *nsrrsp, |
---|
229 | union res_sockaddr_union *addrs, int naddrs) |
---|
230 | { |
---|
231 | rr_ns *nsrr; |
---|
232 | int n, x; |
---|
233 | |
---|
234 | n = 0; |
---|
235 | nsrr = find_ns(nsrrsp, mname); |
---|
236 | if (nsrr != NULL) { |
---|
237 | x = add_addrs(statp, nsrr, addrs, naddrs); |
---|
238 | addrs += x; |
---|
239 | naddrs -= x; |
---|
240 | n += x; |
---|
241 | } |
---|
242 | for (nsrr = HEAD(*nsrrsp); |
---|
243 | nsrr != NULL && naddrs > 0; |
---|
244 | nsrr = NEXT(nsrr, link)) |
---|
245 | if (ns_samename(nsrr->name, mname) != 1) { |
---|
246 | x = add_addrs(statp, nsrr, addrs, naddrs); |
---|
247 | addrs += x; |
---|
248 | naddrs -= x; |
---|
249 | n += x; |
---|
250 | } |
---|
251 | DPRINTF(("satisfy(%s): %d", mname, n)); |
---|
252 | return (n); |
---|
253 | } |
---|
254 | |
---|
255 | static int |
---|
256 | add_addrs(res_state statp, rr_ns *nsrr, |
---|
257 | union res_sockaddr_union *addrs, int naddrs) |
---|
258 | { |
---|
259 | rr_a *arr; |
---|
260 | int n = 0; |
---|
261 | |
---|
262 | for (arr = HEAD(nsrr->addrs); arr != NULL; arr = NEXT(arr, link)) { |
---|
263 | if (naddrs <= 0) |
---|
264 | return (0); |
---|
265 | *addrs++ = arr->addr; |
---|
266 | naddrs--; |
---|
267 | n++; |
---|
268 | } |
---|
269 | DPRINTF(("add_addrs: %d", n)); |
---|
270 | return (n); |
---|
271 | } |
---|
272 | |
---|
273 | static int |
---|
274 | get_soa(res_state statp, const char *dname, ns_class class, int opts, |
---|
275 | char *zname, size_t zsize, char *mname, size_t msize, |
---|
276 | rrset_ns *nsrrsp) |
---|
277 | { |
---|
278 | char tname[NS_MAXDNAME]; |
---|
279 | u_char *resp = NULL; |
---|
280 | int n, i, ancount, nscount; |
---|
281 | ns_sect sect; |
---|
282 | ns_msg msg; |
---|
283 | u_int rcode; |
---|
284 | |
---|
285 | /* |
---|
286 | * Find closest enclosing SOA, even if it's for the root zone. |
---|
287 | */ |
---|
288 | |
---|
289 | /* First canonicalize dname (exactly one unescaped trailing "."). */ |
---|
290 | if (ns_makecanon(dname, tname, sizeof tname) < 0) |
---|
291 | goto cleanup; |
---|
292 | dname = tname; |
---|
293 | |
---|
294 | resp = malloc(NS_MAXMSG); |
---|
295 | if (resp == NULL) |
---|
296 | goto cleanup; |
---|
297 | |
---|
298 | /* Now grovel the subdomains, hunting for an SOA answer or auth. */ |
---|
299 | for (;;) { |
---|
300 | /* Leading or inter-label '.' are skipped here. */ |
---|
301 | while (*dname == '.') |
---|
302 | dname++; |
---|
303 | |
---|
304 | /* Is there an SOA? */ |
---|
305 | n = do_query(statp, dname, class, ns_t_soa, resp, &msg); |
---|
306 | if (n < 0) { |
---|
307 | DPRINTF(("get_soa: do_query('%s', %s) failed (%d)", |
---|
308 | dname, p_class(class), n)); |
---|
309 | goto cleanup; |
---|
310 | } |
---|
311 | if (n > 0) { |
---|
312 | DPRINTF(("get_soa: CNAME or DNAME found")); |
---|
313 | sect = ns_s_max, n = 0; |
---|
314 | } else { |
---|
315 | rcode = ns_msg_getflag(msg, ns_f_rcode); |
---|
316 | ancount = ns_msg_count(msg, ns_s_an); |
---|
317 | nscount = ns_msg_count(msg, ns_s_ns); |
---|
318 | if (ancount > 0 && rcode == ns_r_noerror) |
---|
319 | sect = ns_s_an, n = ancount; |
---|
320 | else if (nscount > 0) |
---|
321 | sect = ns_s_ns, n = nscount; |
---|
322 | else |
---|
323 | sect = ns_s_max, n = 0; |
---|
324 | } |
---|
325 | for (i = 0; i < n; i++) { |
---|
326 | const char *t; |
---|
327 | const u_char *rdata; |
---|
328 | ns_rr rr; |
---|
329 | |
---|
330 | if (ns_parserr(&msg, sect, i, &rr) < 0) { |
---|
331 | DPRINTF(("get_soa: ns_parserr(%s, %d) failed", |
---|
332 | p_section(sect, ns_o_query), i)); |
---|
333 | goto cleanup; |
---|
334 | } |
---|
335 | if (ns_rr_type(rr) == ns_t_cname || |
---|
336 | ns_rr_type(rr) == ns_t_dname) |
---|
337 | break; |
---|
338 | if (ns_rr_type(rr) != ns_t_soa || |
---|
339 | ns_rr_class(rr) != class) |
---|
340 | continue; |
---|
341 | t = ns_rr_name(rr); |
---|
342 | switch (sect) { |
---|
343 | case ns_s_an: |
---|
344 | if (ns_samedomain(dname, t) == 0) { |
---|
345 | DPRINTF( |
---|
346 | ("get_soa: ns_samedomain('%s', '%s') == 0", |
---|
347 | dname, t) |
---|
348 | ); |
---|
349 | errno = EPROTOTYPE; |
---|
350 | goto cleanup; |
---|
351 | } |
---|
352 | break; |
---|
353 | case ns_s_ns: |
---|
354 | if (ns_samename(dname, t) == 1 || |
---|
355 | ns_samedomain(dname, t) == 0) { |
---|
356 | DPRINTF( |
---|
357 | ("get_soa: ns_samename() || !ns_samedomain('%s', '%s')", |
---|
358 | dname, t) |
---|
359 | ); |
---|
360 | errno = EPROTOTYPE; |
---|
361 | goto cleanup; |
---|
362 | } |
---|
363 | break; |
---|
364 | default: |
---|
365 | abort(); |
---|
366 | } |
---|
367 | if (strlen(t) + 1 > zsize) { |
---|
368 | DPRINTF(("get_soa: zname(%lu) too small (%lu)", |
---|
369 | (unsigned long)zsize, |
---|
370 | (unsigned long)strlen(t) + 1)); |
---|
371 | errno = EMSGSIZE; |
---|
372 | goto cleanup; |
---|
373 | } |
---|
374 | strcpy(zname, t); |
---|
375 | rdata = ns_rr_rdata(rr); |
---|
376 | if (ns_name_uncompress(resp, ns_msg_end(msg), rdata, |
---|
377 | mname, msize) < 0) { |
---|
378 | DPRINTF(("get_soa: ns_name_uncompress failed") |
---|
379 | ); |
---|
380 | goto cleanup; |
---|
381 | } |
---|
382 | if (save_ns(statp, &msg, ns_s_ns, |
---|
383 | zname, class, opts, nsrrsp) < 0) { |
---|
384 | DPRINTF(("get_soa: save_ns failed")); |
---|
385 | goto cleanup; |
---|
386 | } |
---|
387 | free(resp); |
---|
388 | return (0); |
---|
389 | } |
---|
390 | |
---|
391 | /* If we're out of labels, then not even "." has an SOA! */ |
---|
392 | if (*dname == '\0') |
---|
393 | break; |
---|
394 | |
---|
395 | /* Find label-terminating "."; top of loop will skip it. */ |
---|
396 | while (*dname != '.') { |
---|
397 | if (*dname == '\\') |
---|
398 | if (*++dname == '\0') { |
---|
399 | errno = EMSGSIZE; |
---|
400 | goto cleanup; |
---|
401 | } |
---|
402 | dname++; |
---|
403 | } |
---|
404 | } |
---|
405 | DPRINTF(("get_soa: out of labels")); |
---|
406 | errno = EDESTADDRREQ; |
---|
407 | cleanup: |
---|
408 | if (resp != NULL) |
---|
409 | free(resp); |
---|
410 | return (-1); |
---|
411 | } |
---|
412 | |
---|
413 | static int |
---|
414 | get_ns(res_state statp, const char *zname, ns_class class, int opts, |
---|
415 | rrset_ns *nsrrsp) |
---|
416 | { |
---|
417 | u_char *resp; |
---|
418 | ns_msg msg; |
---|
419 | int n; |
---|
420 | |
---|
421 | resp = malloc(NS_MAXMSG); |
---|
422 | if (resp == NULL) |
---|
423 | return (-1); |
---|
424 | |
---|
425 | /* Go and get the NS RRs for this zone. */ |
---|
426 | n = do_query(statp, zname, class, ns_t_ns, resp, &msg); |
---|
427 | if (n != 0) { |
---|
428 | DPRINTF(("get_ns: do_query('%s', %s) failed (%d)", |
---|
429 | zname, p_class(class), n)); |
---|
430 | free(resp); |
---|
431 | return (-1); |
---|
432 | } |
---|
433 | |
---|
434 | /* Remember the NS RRs and associated A RRs that came back. */ |
---|
435 | if (save_ns(statp, &msg, ns_s_an, zname, class, opts, nsrrsp) < 0) { |
---|
436 | DPRINTF(("get_ns save_ns('%s', %s) failed", |
---|
437 | zname, p_class(class))); |
---|
438 | free(resp); |
---|
439 | return (-1); |
---|
440 | } |
---|
441 | |
---|
442 | free(resp); |
---|
443 | return (0); |
---|
444 | } |
---|
445 | |
---|
446 | static int |
---|
447 | get_glue(res_state statp, ns_class class, int opts, rrset_ns *nsrrsp) { |
---|
448 | rr_ns *nsrr, *nsrr_n; |
---|
449 | u_char *resp; |
---|
450 | |
---|
451 | resp = malloc(NS_MAXMSG); |
---|
452 | if (resp == NULL) |
---|
453 | return(-1); |
---|
454 | |
---|
455 | /* Go and get the A RRs for each empty NS RR on our list. */ |
---|
456 | for (nsrr = HEAD(*nsrrsp); nsrr != NULL; nsrr = nsrr_n) { |
---|
457 | ns_msg msg; |
---|
458 | int n; |
---|
459 | |
---|
460 | nsrr_n = NEXT(nsrr, link); |
---|
461 | |
---|
462 | if ((nsrr->flags & RR_NS_HAVE_V4) == 0) { |
---|
463 | n = do_query(statp, nsrr->name, class, ns_t_a, |
---|
464 | resp, &msg); |
---|
465 | if (n < 0) { |
---|
466 | DPRINTF( |
---|
467 | ("get_glue: do_query('%s', %s') failed", |
---|
468 | nsrr->name, p_class(class))); |
---|
469 | goto cleanup; |
---|
470 | } |
---|
471 | if (n > 0) { |
---|
472 | DPRINTF(( |
---|
473 | "get_glue: do_query('%s', %s') CNAME or DNAME found", |
---|
474 | nsrr->name, p_class(class))); |
---|
475 | } |
---|
476 | if (save_a(statp, &msg, ns_s_an, nsrr->name, class, |
---|
477 | opts, nsrr) < 0) { |
---|
478 | DPRINTF(("get_glue: save_r('%s', %s) failed", |
---|
479 | nsrr->name, p_class(class))); |
---|
480 | goto cleanup; |
---|
481 | } |
---|
482 | } |
---|
483 | |
---|
484 | if ((nsrr->flags & RR_NS_HAVE_V6) == 0) { |
---|
485 | n = do_query(statp, nsrr->name, class, ns_t_aaaa, |
---|
486 | resp, &msg); |
---|
487 | if (n < 0) { |
---|
488 | DPRINTF( |
---|
489 | ("get_glue: do_query('%s', %s') failed", |
---|
490 | nsrr->name, p_class(class))); |
---|
491 | goto cleanup; |
---|
492 | } |
---|
493 | if (n > 0) { |
---|
494 | DPRINTF(( |
---|
495 | "get_glue: do_query('%s', %s') CNAME or DNAME found", |
---|
496 | nsrr->name, p_class(class))); |
---|
497 | } |
---|
498 | if (save_a(statp, &msg, ns_s_an, nsrr->name, class, |
---|
499 | opts, nsrr) < 0) { |
---|
500 | DPRINTF(("get_glue: save_r('%s', %s) failed", |
---|
501 | nsrr->name, p_class(class))); |
---|
502 | goto cleanup; |
---|
503 | } |
---|
504 | } |
---|
505 | |
---|
506 | /* If it's still empty, it's just chaff. */ |
---|
507 | if (EMPTY(nsrr->addrs)) { |
---|
508 | DPRINTF(("get_glue: removing empty '%s' NS", |
---|
509 | nsrr->name)); |
---|
510 | free_nsrr(nsrrsp, nsrr); |
---|
511 | } |
---|
512 | } |
---|
513 | free(resp); |
---|
514 | return (0); |
---|
515 | |
---|
516 | cleanup: |
---|
517 | free(resp); |
---|
518 | return (-1); |
---|
519 | } |
---|
520 | |
---|
521 | static int |
---|
522 | save_ns(res_state statp, ns_msg *msg, ns_sect sect, |
---|
523 | const char *owner, ns_class class, int opts, |
---|
524 | rrset_ns *nsrrsp) |
---|
525 | { |
---|
526 | int i; |
---|
527 | |
---|
528 | for (i = 0; i < ns_msg_count(*msg, sect); i++) { |
---|
529 | char tname[MAXDNAME]; |
---|
530 | const u_char *rdata; |
---|
531 | rr_ns *nsrr; |
---|
532 | ns_rr rr; |
---|
533 | |
---|
534 | if (ns_parserr(msg, sect, i, &rr) < 0) { |
---|
535 | DPRINTF(("save_ns: ns_parserr(%s, %d) failed", |
---|
536 | p_section(sect, ns_o_query), i)); |
---|
537 | return (-1); |
---|
538 | } |
---|
539 | if (ns_rr_type(rr) != ns_t_ns || |
---|
540 | ns_rr_class(rr) != class || |
---|
541 | ns_samename(ns_rr_name(rr), owner) != 1) |
---|
542 | continue; |
---|
543 | nsrr = find_ns(nsrrsp, ns_rr_name(rr)); |
---|
544 | if (nsrr == NULL) { |
---|
545 | nsrr = malloc(sizeof *nsrr); |
---|
546 | if (nsrr == NULL) { |
---|
547 | DPRINTF(("save_ns: malloc failed")); |
---|
548 | return (-1); |
---|
549 | } |
---|
550 | rdata = ns_rr_rdata(rr); |
---|
551 | if (ns_name_uncompress(ns_msg_base(*msg), |
---|
552 | ns_msg_end(*msg), rdata, |
---|
553 | tname, sizeof tname) < 0) { |
---|
554 | DPRINTF(("save_ns: ns_name_uncompress failed") |
---|
555 | ); |
---|
556 | free(nsrr); |
---|
557 | return (-1); |
---|
558 | } |
---|
559 | nsrr->name = strdup(tname); |
---|
560 | if (nsrr->name == NULL) { |
---|
561 | DPRINTF(("save_ns: strdup failed")); |
---|
562 | free(nsrr); |
---|
563 | return (-1); |
---|
564 | } |
---|
565 | INIT_LINK(nsrr, link); |
---|
566 | INIT_LIST(nsrr->addrs); |
---|
567 | nsrr->flags = 0; |
---|
568 | APPEND(*nsrrsp, nsrr, link); |
---|
569 | } |
---|
570 | if (save_a(statp, msg, ns_s_ar, |
---|
571 | nsrr->name, class, opts, nsrr) < 0) { |
---|
572 | DPRINTF(("save_ns: save_r('%s', %s) failed", |
---|
573 | nsrr->name, p_class(class))); |
---|
574 | return (-1); |
---|
575 | } |
---|
576 | } |
---|
577 | return (0); |
---|
578 | } |
---|
579 | |
---|
580 | static int |
---|
581 | save_a(res_state statp, ns_msg *msg, ns_sect sect, |
---|
582 | const char *owner, ns_class class, int opts, |
---|
583 | rr_ns *nsrr) |
---|
584 | { |
---|
585 | int i; |
---|
586 | |
---|
587 | for (i = 0; i < ns_msg_count(*msg, sect); i++) { |
---|
588 | ns_rr rr; |
---|
589 | rr_a *arr; |
---|
590 | |
---|
591 | if (ns_parserr(msg, sect, i, &rr) < 0) { |
---|
592 | DPRINTF(("save_a: ns_parserr(%s, %d) failed", |
---|
593 | p_section(sect, ns_o_query), i)); |
---|
594 | return (-1); |
---|
595 | } |
---|
596 | if ((ns_rr_type(rr) != ns_t_a && |
---|
597 | ns_rr_type(rr) != ns_t_aaaa) || |
---|
598 | ns_rr_class(rr) != class || |
---|
599 | ns_samename(ns_rr_name(rr), owner) != 1 || |
---|
600 | ns_rr_rdlen(rr) != NS_INADDRSZ) |
---|
601 | continue; |
---|
602 | if ((opts & RES_IPV6ONLY) != 0 && ns_rr_type(rr) != ns_t_aaaa) |
---|
603 | continue; |
---|
604 | if ((opts & RES_IPV4ONLY) != 0 && ns_rr_type(rr) != ns_t_a) |
---|
605 | continue; |
---|
606 | arr = malloc(sizeof *arr); |
---|
607 | if (arr == NULL) { |
---|
608 | DPRINTF(("save_a: malloc failed")); |
---|
609 | return (-1); |
---|
610 | } |
---|
611 | INIT_LINK(arr, link); |
---|
612 | memset(&arr->addr, 0, sizeof(arr->addr)); |
---|
613 | switch (ns_rr_type(rr)) { |
---|
614 | case ns_t_a: |
---|
615 | arr->addr.sin.sin_family = AF_INET; |
---|
616 | #ifdef HAVE_SA_LEN |
---|
617 | arr->addr.sin.sin_len = sizeof(arr->addr.sin); |
---|
618 | #endif |
---|
619 | memcpy(&arr->addr.sin.sin_addr, ns_rr_rdata(rr), |
---|
620 | NS_INADDRSZ); |
---|
621 | arr->addr.sin.sin_port = htons(NAMESERVER_PORT); |
---|
622 | nsrr->flags |= RR_NS_HAVE_V4; |
---|
623 | break; |
---|
624 | case ns_t_aaaa: |
---|
625 | arr->addr.sin6.sin6_family = AF_INET6; |
---|
626 | #ifdef HAVE_SA_LEN |
---|
627 | arr->addr.sin6.sin6_len = sizeof(arr->addr.sin6); |
---|
628 | #endif |
---|
629 | memcpy(&arr->addr.sin6.sin6_addr, ns_rr_rdata(rr), 16); |
---|
630 | arr->addr.sin.sin_port = htons(NAMESERVER_PORT); |
---|
631 | nsrr->flags |= RR_NS_HAVE_V6; |
---|
632 | break; |
---|
633 | default: |
---|
634 | abort(); |
---|
635 | } |
---|
636 | APPEND(nsrr->addrs, arr, link); |
---|
637 | } |
---|
638 | return (0); |
---|
639 | } |
---|
640 | |
---|
641 | static void |
---|
642 | free_nsrrset(rrset_ns *nsrrsp) { |
---|
643 | rr_ns *nsrr; |
---|
644 | |
---|
645 | while ((nsrr = HEAD(*nsrrsp)) != NULL) |
---|
646 | free_nsrr(nsrrsp, nsrr); |
---|
647 | } |
---|
648 | |
---|
649 | static void |
---|
650 | free_nsrr(rrset_ns *nsrrsp, rr_ns *nsrr) { |
---|
651 | rr_a *arr; |
---|
652 | char *tmp; |
---|
653 | |
---|
654 | while ((arr = HEAD(nsrr->addrs)) != NULL) { |
---|
655 | UNLINK(nsrr->addrs, arr, link); |
---|
656 | free(arr); |
---|
657 | } |
---|
658 | DE_CONST(nsrr->name, tmp); |
---|
659 | free(tmp); |
---|
660 | UNLINK(*nsrrsp, nsrr, link); |
---|
661 | free(nsrr); |
---|
662 | } |
---|
663 | |
---|
664 | static rr_ns * |
---|
665 | find_ns(rrset_ns *nsrrsp, const char *dname) { |
---|
666 | rr_ns *nsrr; |
---|
667 | |
---|
668 | for (nsrr = HEAD(*nsrrsp); nsrr != NULL; nsrr = NEXT(nsrr, link)) |
---|
669 | if (ns_samename(nsrr->name, dname) == 1) |
---|
670 | return (nsrr); |
---|
671 | return (NULL); |
---|
672 | } |
---|
673 | |
---|
674 | static int |
---|
675 | do_query(res_state statp, const char *dname, ns_class class, ns_type qtype, |
---|
676 | u_char *resp, ns_msg *msg) |
---|
677 | { |
---|
678 | u_char req[NS_PACKETSZ]; |
---|
679 | int i, n; |
---|
680 | |
---|
681 | n = res_nmkquery(statp, ns_o_query, dname, class, qtype, |
---|
682 | NULL, 0, NULL, req, NS_PACKETSZ); |
---|
683 | if (n < 0) { |
---|
684 | DPRINTF(("do_query: res_nmkquery failed")); |
---|
685 | return (-1); |
---|
686 | } |
---|
687 | n = res_nsend(statp, req, n, resp, NS_MAXMSG); |
---|
688 | if (n < 0) { |
---|
689 | DPRINTF(("do_query: res_nsend failed")); |
---|
690 | return (-1); |
---|
691 | } |
---|
692 | if (n == 0) { |
---|
693 | DPRINTF(("do_query: res_nsend returned 0")); |
---|
694 | errno = EMSGSIZE; |
---|
695 | return (-1); |
---|
696 | } |
---|
697 | if (ns_initparse(resp, n, msg) < 0) { |
---|
698 | DPRINTF(("do_query: ns_initparse failed")); |
---|
699 | return (-1); |
---|
700 | } |
---|
701 | n = 0; |
---|
702 | for (i = 0; i < ns_msg_count(*msg, ns_s_an); i++) { |
---|
703 | ns_rr rr; |
---|
704 | |
---|
705 | if (ns_parserr(msg, ns_s_an, i, &rr) < 0) { |
---|
706 | DPRINTF(("do_query: ns_parserr failed")); |
---|
707 | return (-1); |
---|
708 | } |
---|
709 | n += (ns_rr_class(rr) == class && |
---|
710 | (ns_rr_type(rr) == ns_t_cname || |
---|
711 | ns_rr_type(rr) == ns_t_dname)); |
---|
712 | } |
---|
713 | return (n); |
---|
714 | } |
---|
715 | |
---|
716 | static void |
---|
717 | res_dprintf(const char *fmt, ...) { |
---|
718 | va_list ap; |
---|
719 | |
---|
720 | va_start(ap, fmt); |
---|
721 | fputs(";; res_findzonecut: ", stderr); |
---|
722 | vfprintf(stderr, fmt, ap); |
---|
723 | fputc('\n', stderr); |
---|
724 | va_end(ap); |
---|
725 | } |
---|
726 | |
---|
727 | /*! \file */ |
---|