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