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