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