aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/res_update.h65
-rw-r--r--lib/libc/include/isc/list.h112
-rw-r--r--lib/libc/resolv/res_findzonecut.c719
-rw-r--r--lib/libc/resolv/res_mkupdate.c1158
-rw-r--r--lib/libc/resolv/res_update.c212
5 files changed, 2266 insertions, 0 deletions
diff --git a/include/res_update.h b/include/res_update.h
new file mode 100644
index 000000000000..07a37f34c264
--- /dev/null
+++ b/include/res_update.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1999 by Internet Software Consortium, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * $Id: res_update.h,v 1.1.206.1 2004/03/09 08:33:29 marka Exp $
+ */
+
+#ifndef __RES_UPDATE_H
+#define __RES_UPDATE_H
+
+#include <sys/types.h>
+#include <arpa/nameser.h>
+#include <isc/list.h>
+#include <resolv.h>
+
+/*
+ * This RR-like structure is particular to UPDATE.
+ */
+struct ns_updrec {
+ LINK(struct ns_updrec) r_link, r_glink;
+ ns_sect r_section; /* ZONE/PREREQUISITE/UPDATE */
+ char * r_dname; /* owner of the RR */
+ ns_class r_class; /* class number */
+ ns_type r_type; /* type number */
+ u_int32_t r_ttl; /* time to live */
+ u_char * r_data; /* rdata fields as text string */
+ u_int r_size; /* size of r_data field */
+ int r_opcode; /* type of operation */
+ /* following fields for private use by the resolver/server routines */
+ struct databuf *r_dp; /* databuf to process */
+ struct databuf *r_deldp; /* databuf's deleted/overwritten */
+ u_int r_zone; /* zone number on server */
+};
+typedef struct ns_updrec ns_updrec;
+typedef LIST(ns_updrec) ns_updque;
+
+#define res_mkupdate __res_mkupdate
+#define res_update __res_update
+#define res_mkupdrec __res_mkupdrec
+#define res_freeupdrec __res_freeupdrec
+#define res_nmkupdate __res_nmkupdate
+#define res_nupdate __res_nupdate
+
+int res_mkupdate __P((ns_updrec *, u_char *, int));
+int res_update __P((ns_updrec *));
+ns_updrec * res_mkupdrec __P((int, const char *, u_int, u_int, u_long));
+void res_freeupdrec __P((ns_updrec *));
+int res_nmkupdate __P((res_state, ns_updrec *, u_char *, int));
+int res_nupdate __P((res_state, ns_updrec *, ns_tsig_key *));
+
+#endif /*__RES_UPDATE_H*/
diff --git a/lib/libc/include/isc/list.h b/lib/libc/include/isc/list.h
new file mode 100644
index 000000000000..ad574ac2b587
--- /dev/null
+++ b/lib/libc/include/isc/list.h
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1997,1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef LIST_H
+#define LIST_H 1
+#include <isc/assertions.h>
+
+#define LIST(type) struct { type *head, *tail; }
+#define INIT_LIST(list) \
+ do { (list).head = NULL; (list).tail = NULL; } while (0)
+
+#define LINK(type) struct { type *prev, *next; }
+#define INIT_LINK_TYPE(elt, link, type) \
+ do { \
+ (elt)->link.prev = (type *)(-1); \
+ (elt)->link.next = (type *)(-1); \
+ } while (0)
+#define INIT_LINK(elt, link) \
+ INIT_LINK_TYPE(elt, link, void)
+#define LINKED(elt, link) ((void *)((elt)->link.prev) != (void *)(-1))
+
+#define HEAD(list) ((list).head)
+#define TAIL(list) ((list).tail)
+#define EMPTY(list) ((list).head == NULL)
+
+#define PREPEND(list, elt, link) \
+ do { \
+ INSIST(!LINKED(elt, link));\
+ if ((list).head != NULL) \
+ (list).head->link.prev = (elt); \
+ else \
+ (list).tail = (elt); \
+ (elt)->link.prev = NULL; \
+ (elt)->link.next = (list).head; \
+ (list).head = (elt); \
+ } while (0)
+
+#define APPEND(list, elt, link) \
+ do { \
+ INSIST(!LINKED(elt, link));\
+ if ((list).tail != NULL) \
+ (list).tail->link.next = (elt); \
+ else \
+ (list).head = (elt); \
+ (elt)->link.prev = (list).tail; \
+ (elt)->link.next = NULL; \
+ (list).tail = (elt); \
+ } while (0)
+
+#define UNLINK_TYPE(list, elt, link, type) \
+ do { \
+ INSIST(LINKED(elt, link));\
+ if ((elt)->link.next != NULL) \
+ (elt)->link.next->link.prev = (elt)->link.prev; \
+ else \
+ (list).tail = (elt)->link.prev; \
+ if ((elt)->link.prev != NULL) \
+ (elt)->link.prev->link.next = (elt)->link.next; \
+ else \
+ (list).head = (elt)->link.next; \
+ INIT_LINK_TYPE(elt, link, type); \
+ } while (0)
+#define UNLINK(list, elt, link) \
+ UNLINK_TYPE(list, elt, link, void)
+
+#define PREV(elt, link) ((elt)->link.prev)
+#define NEXT(elt, link) ((elt)->link.next)
+
+#define INSERT_BEFORE(list, before, elt, link) \
+ do { \
+ INSIST(!LINKED(elt, link));\
+ if ((before)->link.prev == NULL) \
+ PREPEND(list, elt, link); \
+ else { \
+ (elt)->link.prev = (before)->link.prev; \
+ (before)->link.prev = (elt); \
+ (elt)->link.prev->link.next = (elt); \
+ (elt)->link.next = (before); \
+ } \
+ } while (0)
+
+#define INSERT_AFTER(list, after, elt, link) \
+ do { \
+ INSIST(!LINKED(elt, link));\
+ if ((after)->link.next == NULL) \
+ APPEND(list, elt, link); \
+ else { \
+ (elt)->link.next = (after)->link.next; \
+ (after)->link.next = (elt); \
+ (elt)->link.next->link.prev = (elt); \
+ (elt)->link.prev = (after); \
+ } \
+ } while (0)
+
+#define ENQUEUE(list, elt, link) APPEND(list, elt, link)
+#define DEQUEUE(list, elt, link) UNLINK(list, elt, link)
+
+#endif /* LIST_H */
diff --git a/lib/libc/resolv/res_findzonecut.c b/lib/libc/resolv/res_findzonecut.c
new file mode 100644
index 000000000000..804beb647464
--- /dev/null
+++ b/lib/libc/resolv/res_findzonecut.c
@@ -0,0 +1,719 @@
+#if !defined(lint) && !defined(SABER)
+static const char rcsid[] = "$Id: res_findzonecut.c,v 1.2.2.3.4.4 2005/10/11 00:48:16 marka Exp $";
+#endif /* not lint */
+
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* Import. */
+
+#include "port_before.h"
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+
+#include <errno.h>
+#include <limits.h>
+#include <netdb.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <isc/list.h>
+
+#include "port_after.h"
+
+#include <resolv.h>
+
+/* Data structures. */
+
+typedef struct rr_a {
+ LINK(struct rr_a) link;
+ union res_sockaddr_union addr;
+} rr_a;
+typedef LIST(rr_a) rrset_a;
+
+typedef struct rr_ns {
+ LINK(struct rr_ns) link;
+ const char * name;
+ unsigned int flags;
+ rrset_a addrs;
+} rr_ns;
+typedef LIST(rr_ns) rrset_ns;
+
+#define RR_NS_HAVE_V4 0x01
+#define RR_NS_HAVE_V6 0x02
+
+/* Forward. */
+
+static int satisfy(res_state, const char *, rrset_ns *,
+ union res_sockaddr_union *, int);
+static int add_addrs(res_state, rr_ns *,
+ union res_sockaddr_union *, int);
+static int get_soa(res_state, const char *, ns_class, int,
+ char *, size_t, char *, size_t,
+ rrset_ns *);
+static int get_ns(res_state, const char *, ns_class, int, rrset_ns *);
+static int get_glue(res_state, ns_class, int, rrset_ns *);
+static int save_ns(res_state, ns_msg *, ns_sect,
+ const char *, ns_class, int, rrset_ns *);
+static int save_a(res_state, ns_msg *, ns_sect,
+ const char *, ns_class, int, rr_ns *);
+static void free_nsrrset(rrset_ns *);
+static void free_nsrr(rrset_ns *, rr_ns *);
+static rr_ns * find_ns(rrset_ns *, const char *);
+static int do_query(res_state, const char *, ns_class, ns_type,
+ u_char *, ns_msg *);
+static void res_dprintf(const char *, ...) ISC_FORMAT_PRINTF(1, 2);
+
+/* Macros. */
+
+#define DPRINTF(x) do {\
+ int save_errno = errno; \
+ if ((statp->options & RES_DEBUG) != 0U) res_dprintf x; \
+ errno = save_errno; \
+ } while (0)
+
+/* Public. */
+
+/*
+ * int
+ * res_findzonecut(res, dname, class, zname, zsize, addrs, naddrs)
+ * find enclosing zone for a <dname,class>, and some server addresses
+ * parameters:
+ * res - resolver context to work within (is modified)
+ * dname - domain name whose enclosing zone is desired
+ * class - class of dname (and its enclosing zone)
+ * zname - found zone name
+ * zsize - allocated size of zname
+ * addrs - found server addresses
+ * naddrs - max number of addrs
+ * return values:
+ * < 0 - an error occurred (check errno)
+ * = 0 - zname is now valid, but addrs[] wasn't changed
+ * > 0 - zname is now valid, and return value is number of addrs[] found
+ * notes:
+ * this function calls res_nsend() which means it depends on correctly
+ * functioning recursive nameservers (usually defined in /etc/resolv.conf
+ * or its local equivilent).
+ *
+ * we start by asking for an SOA<dname,class>. if we get one as an
+ * answer, that just means <dname,class> is a zone top, which is fine.
+ * more than likely we'll be told to go pound sand, in the form of a
+ * negative answer.
+ *
+ * note that we are not prepared to deal with referrals since that would
+ * only come from authority servers and our correctly functioning local
+ * recursive server would have followed the referral and got us something
+ * more definite.
+ *
+ * if the authority section contains an SOA, this SOA should also be the
+ * closest enclosing zone, since any intermediary zone cuts would've been
+ * returned as referrals and dealt with by our correctly functioning local
+ * recursive name server. but an SOA in the authority section should NOT
+ * match our dname (since that would have been returned in the answer
+ * section). an authority section SOA has to be "above" our dname.
+ *
+ * however, since authority section SOA's were once optional, it's
+ * possible that we'll have to go hunting for the enclosing SOA by
+ * ripping labels off the front of our dname -- this is known as "doing
+ * it the hard way."
+ *
+ * ultimately we want some server addresses, which are ideally the ones
+ * pertaining to the SOA.MNAME, but only if there is a matching NS RR.
+ * so the second phase (after we find an SOA) is to go looking for the
+ * NS RRset for that SOA's zone.
+ *
+ * no answer section processed by this code is allowed to contain CNAME
+ * or DNAME RR's. for the SOA query this means we strip a label and
+ * keep going. for the NS and A queries this means we just give up.
+ */
+
+int
+res_findzonecut(res_state statp, const char *dname, ns_class class, int opts,
+ char *zname, size_t zsize, struct in_addr *addrs, int naddrs)
+{
+ int result, i;
+ union res_sockaddr_union *u;
+
+
+ opts |= RES_IPV4ONLY;
+ opts &= ~RES_IPV6ONLY;
+
+ u = calloc(naddrs, sizeof(*u));
+ if (u == NULL)
+ return(-1);
+
+ result = res_findzonecut2(statp, dname, class, opts, zname, zsize,
+ u, naddrs);
+
+ for (i = 0; i < result; i++) {
+ addrs[i] = u[i].sin.sin_addr;
+ }
+ free(u);
+ return (result);
+}
+
+int
+res_findzonecut2(res_state statp, const char *dname, ns_class class, int opts,
+ char *zname, size_t zsize, union res_sockaddr_union *addrs,
+ int naddrs)
+{
+ char mname[NS_MAXDNAME];
+ u_long save_pfcode;
+ rrset_ns nsrrs;
+ int n;
+
+ DPRINTF(("START dname='%s' class=%s, zsize=%ld, naddrs=%d",
+ dname, p_class(class), (long)zsize, naddrs));
+ save_pfcode = statp->pfcode;
+ statp->pfcode |= RES_PRF_HEAD2 | RES_PRF_HEAD1 | RES_PRF_HEADX |
+ RES_PRF_QUES | RES_PRF_ANS |
+ RES_PRF_AUTH | RES_PRF_ADD;
+ INIT_LIST(nsrrs);
+
+ DPRINTF(("get the soa, and see if it has enough glue"));
+ if ((n = get_soa(statp, dname, class, opts, zname, zsize,
+ mname, sizeof mname, &nsrrs)) < 0 ||
+ ((opts & RES_EXHAUSTIVE) == 0 &&
+ (n = satisfy(statp, mname, &nsrrs, addrs, naddrs)) > 0))
+ goto done;
+
+ DPRINTF(("get the ns rrset and see if it has enough glue"));
+ if ((n = get_ns(statp, zname, class, opts, &nsrrs)) < 0 ||
+ ((opts & RES_EXHAUSTIVE) == 0 &&
+ (n = satisfy(statp, mname, &nsrrs, addrs, naddrs)) > 0))
+ goto done;
+
+ DPRINTF(("get the missing glue and see if it's finally enough"));
+ if ((n = get_glue(statp, class, opts, &nsrrs)) >= 0)
+ n = satisfy(statp, mname, &nsrrs, addrs, naddrs);
+
+ done:
+ DPRINTF(("FINISH n=%d (%s)", n, (n < 0) ? strerror(errno) : "OK"));
+ free_nsrrset(&nsrrs);
+ statp->pfcode = save_pfcode;
+ return (n);
+}
+
+/* Private. */
+
+static int
+satisfy(res_state statp, const char *mname, rrset_ns *nsrrsp,
+ union res_sockaddr_union *addrs, int naddrs)
+{
+ rr_ns *nsrr;
+ int n, x;
+
+ n = 0;
+ nsrr = find_ns(nsrrsp, mname);
+ if (nsrr != NULL) {
+ x = add_addrs(statp, nsrr, addrs, naddrs);
+ addrs += x;
+ naddrs -= x;
+ n += x;
+ }
+ for (nsrr = HEAD(*nsrrsp);
+ nsrr != NULL && naddrs > 0;
+ nsrr = NEXT(nsrr, link))
+ if (ns_samename(nsrr->name, mname) != 1) {
+ x = add_addrs(statp, nsrr, addrs, naddrs);
+ addrs += x;
+ naddrs -= x;
+ n += x;
+ }
+ DPRINTF(("satisfy(%s): %d", mname, n));
+ return (n);
+}
+
+static int
+add_addrs(res_state statp, rr_ns *nsrr,
+ union res_sockaddr_union *addrs, int naddrs)
+{
+ rr_a *arr;
+ int n = 0;
+
+ for (arr = HEAD(nsrr->addrs); arr != NULL; arr = NEXT(arr, link)) {
+ if (naddrs <= 0)
+ return (0);
+ *addrs++ = arr->addr;
+ naddrs--;
+ n++;
+ }
+ DPRINTF(("add_addrs: %d", n));
+ return (n);
+}
+
+static int
+get_soa(res_state statp, const char *dname, ns_class class, int opts,
+ char *zname, size_t zsize, char *mname, size_t msize,
+ rrset_ns *nsrrsp)
+{
+ char tname[NS_MAXDNAME];
+ u_char *resp = NULL;
+ int n, i, ancount, nscount;
+ ns_sect sect;
+ ns_msg msg;
+ u_int rcode;
+
+ /*
+ * Find closest enclosing SOA, even if it's for the root zone.
+ */
+
+ /* First canonicalize dname (exactly one unescaped trailing "."). */
+ if (ns_makecanon(dname, tname, sizeof tname) < 0)
+ goto cleanup;
+ dname = tname;
+
+ resp = malloc(NS_MAXMSG);
+ if (resp == NULL)
+ goto cleanup;
+
+ /* Now grovel the subdomains, hunting for an SOA answer or auth. */
+ for (;;) {
+ /* Leading or inter-label '.' are skipped here. */
+ while (*dname == '.')
+ dname++;
+
+ /* Is there an SOA? */
+ n = do_query(statp, dname, class, ns_t_soa, resp, &msg);
+ if (n < 0) {
+ DPRINTF(("get_soa: do_query('%s', %s) failed (%d)",
+ dname, p_class(class), n));
+ goto cleanup;
+ }
+ if (n > 0) {
+ DPRINTF(("get_soa: CNAME or DNAME found"));
+ sect = ns_s_max, n = 0;
+ } else {
+ rcode = ns_msg_getflag(msg, ns_f_rcode);
+ ancount = ns_msg_count(msg, ns_s_an);
+ nscount = ns_msg_count(msg, ns_s_ns);
+ if (ancount > 0 && rcode == ns_r_noerror)
+ sect = ns_s_an, n = ancount;
+ else if (nscount > 0)
+ sect = ns_s_ns, n = nscount;
+ else
+ sect = ns_s_max, n = 0;
+ }
+ for (i = 0; i < n; i++) {
+ const char *t;
+ const u_char *rdata;
+ ns_rr rr;
+
+ if (ns_parserr(&msg, sect, i, &rr) < 0) {
+ DPRINTF(("get_soa: ns_parserr(%s, %d) failed",
+ p_section(sect, ns_o_query), i));
+ goto cleanup;
+ }
+ if (ns_rr_type(rr) == ns_t_cname ||
+ ns_rr_type(rr) == ns_t_dname)
+ break;
+ if (ns_rr_type(rr) != ns_t_soa ||
+ ns_rr_class(rr) != class)
+ continue;
+ t = ns_rr_name(rr);
+ switch (sect) {
+ case ns_s_an:
+ if (ns_samedomain(dname, t) == 0) {
+ DPRINTF(
+ ("get_soa: ns_samedomain('%s', '%s') == 0",
+ dname, t)
+ );
+ errno = EPROTOTYPE;
+ goto cleanup;
+ }
+ break;
+ case ns_s_ns:
+ if (ns_samename(dname, t) == 1 ||
+ ns_samedomain(dname, t) == 0) {
+ DPRINTF(
+ ("get_soa: ns_samename() || !ns_samedomain('%s', '%s')",
+ dname, t)
+ );
+ errno = EPROTOTYPE;
+ goto cleanup;
+ }
+ break;
+ default:
+ abort();
+ }
+ if (strlen(t) + 1 > zsize) {
+ DPRINTF(("get_soa: zname(%lu) too small (%lu)",
+ (unsigned long)zsize,
+ (unsigned long)strlen(t) + 1));
+ errno = EMSGSIZE;
+ goto cleanup;
+ }
+ strcpy(zname, t);
+ rdata = ns_rr_rdata(rr);
+ if (ns_name_uncompress(resp, ns_msg_end(msg), rdata,
+ mname, msize) < 0) {
+ DPRINTF(("get_soa: ns_name_uncompress failed")
+ );
+ goto cleanup;
+ }
+ if (save_ns(statp, &msg, ns_s_ns,
+ zname, class, opts, nsrrsp) < 0) {
+ DPRINTF(("get_soa: save_ns failed"));
+ goto cleanup;
+ }
+ free(resp);
+ return (0);
+ }
+
+ /* If we're out of labels, then not even "." has an SOA! */
+ if (*dname == '\0')
+ break;
+
+ /* Find label-terminating "."; top of loop will skip it. */
+ while (*dname != '.') {
+ if (*dname == '\\')
+ if (*++dname == '\0') {
+ errno = EMSGSIZE;
+ goto cleanup;
+ }
+ dname++;
+ }
+ }
+ DPRINTF(("get_soa: out of labels"));
+ errno = EDESTADDRREQ;
+ cleanup:
+ if (resp != NULL)
+ free(resp);
+ return (-1);
+}
+
+static int
+get_ns(res_state statp, const char *zname, ns_class class, int opts,
+ rrset_ns *nsrrsp)
+{
+ u_char *resp;
+ ns_msg msg;
+ int n;
+
+ resp = malloc(NS_MAXMSG);
+ if (resp == NULL)
+ return (-1);
+
+ /* Go and get the NS RRs for this zone. */
+ n = do_query(statp, zname, class, ns_t_ns, resp, &msg);
+ if (n != 0) {
+ DPRINTF(("get_ns: do_query('%s', %s) failed (%d)",
+ zname, p_class(class), n));
+ free(resp);
+ return (-1);
+ }
+
+ /* Remember the NS RRs and associated A RRs that came back. */
+ if (save_ns(statp, &msg, ns_s_an, zname, class, opts, nsrrsp) < 0) {
+ DPRINTF(("get_ns save_ns('%s', %s) failed",
+ zname, p_class(class)));
+ free(resp);
+ return (-1);
+ }
+
+ free(resp);
+ return (0);
+}
+
+static int
+get_glue(res_state statp, ns_class class, int opts, rrset_ns *nsrrsp) {
+ rr_ns *nsrr, *nsrr_n;
+ u_char *resp;
+
+ resp = malloc(NS_MAXMSG);
+ if (resp == NULL)
+ return(-1);
+
+ /* Go and get the A RRs for each empty NS RR on our list. */
+ for (nsrr = HEAD(*nsrrsp); nsrr != NULL; nsrr = nsrr_n) {
+ ns_msg msg;
+ int n;
+
+ nsrr_n = NEXT(nsrr, link);
+
+ if ((nsrr->flags & RR_NS_HAVE_V4) == 0) {
+ n = do_query(statp, nsrr->name, class, ns_t_a,
+ resp, &msg);
+ if (n < 0) {
+ DPRINTF(
+ ("get_glue: do_query('%s', %s') failed",
+ nsrr->name, p_class(class)));
+ goto cleanup;
+ }
+ if (n > 0) {
+ DPRINTF((
+ "get_glue: do_query('%s', %s') CNAME or DNAME found",
+ nsrr->name, p_class(class)));
+ }
+ if (save_a(statp, &msg, ns_s_an, nsrr->name, class,
+ opts, nsrr) < 0) {
+ DPRINTF(("get_glue: save_r('%s', %s) failed",
+ nsrr->name, p_class(class)));
+ goto cleanup;
+ }
+ }
+
+ if ((nsrr->flags & RR_NS_HAVE_V6) == 0) {
+ n = do_query(statp, nsrr->name, class, ns_t_aaaa,
+ resp, &msg);
+ if (n < 0) {
+ DPRINTF(
+ ("get_glue: do_query('%s', %s') failed",
+ nsrr->name, p_class(class)));
+ goto cleanup;
+ }
+ if (n > 0) {
+ DPRINTF((
+ "get_glue: do_query('%s', %s') CNAME or DNAME found",
+ nsrr->name, p_class(class)));
+ }
+ if (save_a(statp, &msg, ns_s_an, nsrr->name, class,
+ opts, nsrr) < 0) {
+ DPRINTF(("get_glue: save_r('%s', %s) failed",
+ nsrr->name, p_class(class)));
+ goto cleanup;
+ }
+ }
+
+ /* If it's still empty, it's just chaff. */
+ if (EMPTY(nsrr->addrs)) {
+ DPRINTF(("get_glue: removing empty '%s' NS",
+ nsrr->name));
+ free_nsrr(nsrrsp, nsrr);
+ }
+ }
+ free(resp);
+ return (0);
+
+ cleanup:
+ free(resp);
+ return (-1);
+}
+
+static int
+save_ns(res_state statp, ns_msg *msg, ns_sect sect,
+ const char *owner, ns_class class, int opts,
+ rrset_ns *nsrrsp)
+{
+ int i;
+
+ for (i = 0; i < ns_msg_count(*msg, sect); i++) {
+ char tname[MAXDNAME];
+ const u_char *rdata;
+ rr_ns *nsrr;
+ ns_rr rr;
+
+ if (ns_parserr(msg, sect, i, &rr) < 0) {
+ DPRINTF(("save_ns: ns_parserr(%s, %d) failed",
+ p_section(sect, ns_o_query), i));
+ return (-1);
+ }
+ if (ns_rr_type(rr) != ns_t_ns ||
+ ns_rr_class(rr) != class ||
+ ns_samename(ns_rr_name(rr), owner) != 1)
+ continue;
+ nsrr = find_ns(nsrrsp, ns_rr_name(rr));
+ if (nsrr == NULL) {
+ nsrr = malloc(sizeof *nsrr);
+ if (nsrr == NULL) {
+ DPRINTF(("save_ns: malloc failed"));
+ return (-1);
+ }
+ rdata = ns_rr_rdata(rr);
+ if (ns_name_uncompress(ns_msg_base(*msg),
+ ns_msg_end(*msg), rdata,
+ tname, sizeof tname) < 0) {
+ DPRINTF(("save_ns: ns_name_uncompress failed")
+ );
+ free(nsrr);
+ return (-1);
+ }
+ nsrr->name = strdup(tname);
+ if (nsrr->name == NULL) {
+ DPRINTF(("save_ns: strdup failed"));
+ free(nsrr);
+ return (-1);
+ }
+ INIT_LINK(nsrr, link);
+ INIT_LIST(nsrr->addrs);
+ nsrr->flags = 0;
+ APPEND(*nsrrsp, nsrr, link);
+ }
+ if (save_a(statp, msg, ns_s_ar,
+ nsrr->name, class, opts, nsrr) < 0) {
+ DPRINTF(("save_ns: save_r('%s', %s) failed",
+ nsrr->name, p_class(class)));
+ return (-1);
+ }
+ }
+ return (0);
+}
+
+static int
+save_a(res_state statp, ns_msg *msg, ns_sect sect,
+ const char *owner, ns_class class, int opts,
+ rr_ns *nsrr)
+{
+ int i;
+
+ for (i = 0; i < ns_msg_count(*msg, sect); i++) {
+ ns_rr rr;
+ rr_a *arr;
+
+ if (ns_parserr(msg, sect, i, &rr) < 0) {
+ DPRINTF(("save_a: ns_parserr(%s, %d) failed",
+ p_section(sect, ns_o_query), i));
+ return (-1);
+ }
+ if ((ns_rr_type(rr) != ns_t_a &&
+ ns_rr_type(rr) != ns_t_aaaa) ||
+ ns_rr_class(rr) != class ||
+ ns_samename(ns_rr_name(rr), owner) != 1 ||
+ ns_rr_rdlen(rr) != NS_INADDRSZ)
+ continue;
+ if ((opts & RES_IPV6ONLY) != 0 && ns_rr_type(rr) != ns_t_aaaa)
+ continue;
+ if ((opts & RES_IPV4ONLY) != 0 && ns_rr_type(rr) != ns_t_a)
+ continue;
+ arr = malloc(sizeof *arr);
+ if (arr == NULL) {
+ DPRINTF(("save_a: malloc failed"));
+ return (-1);
+ }
+ INIT_LINK(arr, link);
+ memset(&arr->addr, 0, sizeof(arr->addr));
+ switch (ns_rr_type(rr)) {
+ case ns_t_a:
+ arr->addr.sin.sin_family = AF_INET;
+#ifdef HAVE_SA_LEN
+ arr->addr.sin.sin_len = sizeof(arr->addr.sin);
+#endif
+ memcpy(&arr->addr.sin.sin_addr, ns_rr_rdata(rr),
+ NS_INADDRSZ);
+ arr->addr.sin.sin_port = htons(NAMESERVER_PORT);
+ nsrr->flags |= RR_NS_HAVE_V4;
+ break;
+ case ns_t_aaaa:
+ arr->addr.sin6.sin6_family = AF_INET6;
+#ifdef HAVE_SA_LEN
+ arr->addr.sin6.sin6_len = sizeof(arr->addr.sin6);
+#endif
+ memcpy(&arr->addr.sin6.sin6_addr, ns_rr_rdata(rr), 16);
+ arr->addr.sin.sin_port = htons(NAMESERVER_PORT);
+ nsrr->flags |= RR_NS_HAVE_V6;
+ break;
+ default:
+ abort();
+ }
+ APPEND(nsrr->addrs, arr, link);
+ }
+ return (0);
+}
+
+static void
+free_nsrrset(rrset_ns *nsrrsp) {
+ rr_ns *nsrr;
+
+ while ((nsrr = HEAD(*nsrrsp)) != NULL)
+ free_nsrr(nsrrsp, nsrr);
+}
+
+static void
+free_nsrr(rrset_ns *nsrrsp, rr_ns *nsrr) {
+ rr_a *arr;
+ char *tmp;
+
+ while ((arr = HEAD(nsrr->addrs)) != NULL) {
+ UNLINK(nsrr->addrs, arr, link);
+ free(arr);
+ }
+ DE_CONST(nsrr->name, tmp);
+ free(tmp);
+ UNLINK(*nsrrsp, nsrr, link);
+ free(nsrr);
+}
+
+static rr_ns *
+find_ns(rrset_ns *nsrrsp, const char *dname) {
+ rr_ns *nsrr;
+
+ for (nsrr = HEAD(*nsrrsp); nsrr != NULL; nsrr = NEXT(nsrr, link))
+ if (ns_samename(nsrr->name, dname) == 1)
+ return (nsrr);
+ return (NULL);
+}
+
+static int
+do_query(res_state statp, const char *dname, ns_class class, ns_type qtype,
+ u_char *resp, ns_msg *msg)
+{
+ u_char req[NS_PACKETSZ];
+ int i, n;
+
+ n = res_nmkquery(statp, ns_o_query, dname, class, qtype,
+ NULL, 0, NULL, req, NS_PACKETSZ);
+ if (n < 0) {
+ DPRINTF(("do_query: res_nmkquery failed"));
+ return (-1);
+ }
+ n = res_nsend(statp, req, n, resp, NS_MAXMSG);
+ if (n < 0) {
+ DPRINTF(("do_query: res_nsend failed"));
+ return (-1);
+ }
+ if (n == 0) {
+ DPRINTF(("do_query: res_nsend returned 0"));
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ if (ns_initparse(resp, n, msg) < 0) {
+ DPRINTF(("do_query: ns_initparse failed"));
+ return (-1);
+ }
+ n = 0;
+ for (i = 0; i < ns_msg_count(*msg, ns_s_an); i++) {
+ ns_rr rr;
+
+ if (ns_parserr(msg, ns_s_an, i, &rr) < 0) {
+ DPRINTF(("do_query: ns_parserr failed"));
+ return (-1);
+ }
+ n += (ns_rr_class(rr) == class &&
+ (ns_rr_type(rr) == ns_t_cname ||
+ ns_rr_type(rr) == ns_t_dname));
+ }
+ return (n);
+}
+
+static void
+res_dprintf(const char *fmt, ...) {
+ va_list ap;
+
+ va_start(ap, fmt);
+ fputs(";; res_findzonecut: ", stderr);
+ vfprintf(stderr, fmt, ap);
+ fputc('\n', stderr);
+ va_end(ap);
+}
diff --git a/lib/libc/resolv/res_mkupdate.c b/lib/libc/resolv/res_mkupdate.c
new file mode 100644
index 000000000000..01078f1a51a6
--- /dev/null
+++ b/lib/libc/resolv/res_mkupdate.c
@@ -0,0 +1,1158 @@
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1996-1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * Based on the Dynamic DNS reference implementation by Viraj Bais
+ * <viraj_bais@ccm.fm.intel.com>
+ */
+
+#if !defined(lint) && !defined(SABER)
+static const char rcsid[] = "$Id: res_mkupdate.c,v 1.1.2.1.4.5 2005/10/14 05:43:47 marka Exp $";
+#endif /* not lint */
+
+#include "port_before.h"
+
+#include <sys/types.h>
+#include <sys/param.h>
+
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+#include <arpa/inet.h>
+
+#include <errno.h>
+#include <limits.h>
+#include <netdb.h>
+#include <resolv.h>
+#include <res_update.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <ctype.h>
+
+#include "port_after.h"
+
+/* Options. Leave them on. */
+#define DEBUG
+#define MAXPORT 1024
+
+static int getnum_str(u_char **, u_char *);
+static int gethexnum_str(u_char **, u_char *);
+static int getword_str(char *, int, u_char **, u_char *);
+static int getstr_str(char *, int, u_char **, u_char *);
+
+#define ShrinkBuffer(x) if ((buflen -= x) < 0) return (-2);
+
+/* Forward. */
+
+int res_protocolnumber(const char *);
+int res_servicenumber(const char *);
+
+/*
+ * Form update packets.
+ * Returns the size of the resulting packet if no error
+ * On error,
+ * returns -1 if error in reading a word/number in rdata
+ * portion for update packets
+ * -2 if length of buffer passed is insufficient
+ * -3 if zone section is not the first section in
+ * the linked list, or section order has a problem
+ * -4 on a number overflow
+ * -5 unknown operation or no records
+ */
+int
+res_nmkupdate(res_state statp, ns_updrec *rrecp_in, u_char *buf, int buflen) {
+ ns_updrec *rrecp_start = rrecp_in;
+ HEADER *hp;
+ u_char *cp, *sp2, *startp, *endp;
+ int n, i, soanum, multiline;
+ ns_updrec *rrecp;
+ struct in_addr ina;
+ struct in6_addr in6a;
+ char buf2[MAXDNAME];
+ u_char buf3[MAXDNAME];
+ int section, numrrs = 0, counts[ns_s_max];
+ u_int16_t rtype, rclass;
+ u_int32_t n1, rttl;
+ u_char *dnptrs[20], **dpp, **lastdnptr;
+ int siglen, keylen, certlen;
+
+ /*
+ * Initialize header fields.
+ */
+ if ((buf == NULL) || (buflen < HFIXEDSZ))
+ return (-1);
+ memset(buf, 0, HFIXEDSZ);
+ hp = (HEADER *) buf;
+ hp->id = htons(++statp->id);
+ hp->opcode = ns_o_update;
+ hp->rcode = NOERROR;
+ cp = buf + HFIXEDSZ;
+ buflen -= HFIXEDSZ;
+ dpp = dnptrs;
+ *dpp++ = buf;
+ *dpp++ = NULL;
+ lastdnptr = dnptrs + sizeof dnptrs / sizeof dnptrs[0];
+
+ if (rrecp_start == NULL)
+ return (-5);
+ else if (rrecp_start->r_section != S_ZONE)
+ return (-3);
+
+ memset(counts, 0, sizeof counts);
+ for (rrecp = rrecp_start; rrecp; rrecp = NEXT(rrecp, r_glink)) {
+ numrrs++;
+ section = rrecp->r_section;
+ if (section < 0 || section >= ns_s_max)
+ return (-1);
+ counts[section]++;
+ for (i = section + 1; i < ns_s_max; i++)
+ if (counts[i])
+ return (-3);
+ rtype = rrecp->r_type;
+ rclass = rrecp->r_class;
+ rttl = rrecp->r_ttl;
+ /* overload class and type */
+ if (section == S_PREREQ) {
+ rttl = 0;
+ switch (rrecp->r_opcode) {
+ case YXDOMAIN:
+ rclass = C_ANY;
+ rtype = T_ANY;
+ rrecp->r_size = 0;
+ break;
+ case NXDOMAIN:
+ rclass = C_NONE;
+ rtype = T_ANY;
+ rrecp->r_size = 0;
+ break;
+ case NXRRSET:
+ rclass = C_NONE;
+ rrecp->r_size = 0;
+ break;
+ case YXRRSET:
+ if (rrecp->r_size == 0)
+ rclass = C_ANY;
+ break;
+ default:
+ fprintf(stderr,
+ "res_mkupdate: incorrect opcode: %d\n",
+ rrecp->r_opcode);
+ fflush(stderr);
+ return (-1);
+ }
+ } else if (section == S_UPDATE) {
+ switch (rrecp->r_opcode) {
+ case DELETE:
+ rclass = rrecp->r_size == 0 ? C_ANY : C_NONE;
+ break;
+ case ADD:
+ break;
+ default:
+ fprintf(stderr,
+ "res_mkupdate: incorrect opcode: %d\n",
+ rrecp->r_opcode);
+ fflush(stderr);
+ return (-1);
+ }
+ }
+
+ /*
+ * XXX appending default domain to owner name is omitted,
+ * fqdn must be provided
+ */
+ if ((n = dn_comp(rrecp->r_dname, cp, buflen, dnptrs,
+ lastdnptr)) < 0)
+ return (-1);
+ cp += n;
+ ShrinkBuffer(n + 2*INT16SZ);
+ PUTSHORT(rtype, cp);
+ PUTSHORT(rclass, cp);
+ if (section == S_ZONE) {
+ if (numrrs != 1 || rrecp->r_type != T_SOA)
+ return (-3);
+ continue;
+ }
+ ShrinkBuffer(INT32SZ + INT16SZ);
+ PUTLONG(rttl, cp);
+ sp2 = cp; /* save pointer to length byte */
+ cp += INT16SZ;
+ if (rrecp->r_size == 0) {
+ if (section == S_UPDATE && rclass != C_ANY)
+ return (-1);
+ else {
+ PUTSHORT(0, sp2);
+ continue;
+ }
+ }
+ startp = rrecp->r_data;
+ endp = startp + rrecp->r_size - 1;
+ /* XXX this should be done centrally. */
+ switch (rrecp->r_type) {
+ case T_A:
+ if (!getword_str(buf2, sizeof buf2, &startp, endp))
+ return (-1);
+ if (!inet_aton(buf2, &ina))
+ return (-1);
+ n1 = ntohl(ina.s_addr);
+ ShrinkBuffer(INT32SZ);
+ PUTLONG(n1, cp);
+ break;
+ case T_CNAME:
+ case T_MB:
+ case T_MG:
+ case T_MR:
+ case T_NS:
+ case T_PTR:
+ case ns_t_dname:
+ if (!getword_str(buf2, sizeof buf2, &startp, endp))
+ return (-1);
+ n = dn_comp(buf2, cp, buflen, dnptrs, lastdnptr);
+ if (n < 0)
+ return (-1);
+ cp += n;
+ ShrinkBuffer(n);
+ break;
+ case T_MINFO:
+ case T_SOA:
+ case T_RP:
+ for (i = 0; i < 2; i++) {
+ if (!getword_str(buf2, sizeof buf2, &startp,
+ endp))
+ return (-1);
+ n = dn_comp(buf2, cp, buflen,
+ dnptrs, lastdnptr);
+ if (n < 0)
+ return (-1);
+ cp += n;
+ ShrinkBuffer(n);
+ }
+ if (rrecp->r_type == T_SOA) {
+ ShrinkBuffer(5 * INT32SZ);
+ while (isspace(*startp) || !*startp)
+ startp++;
+ if (*startp == '(') {
+ multiline = 1;
+ startp++;
+ } else
+ multiline = 0;
+ /* serial, refresh, retry, expire, minimum */
+ for (i = 0; i < 5; i++) {
+ soanum = getnum_str(&startp, endp);
+ if (soanum < 0)
+ return (-1);
+ PUTLONG(soanum, cp);
+ }
+ if (multiline) {
+ while (isspace(*startp) || !*startp)
+ startp++;
+ if (*startp != ')')
+ return (-1);
+ }
+ }
+ break;
+ case T_MX:
+ case T_AFSDB:
+ case T_RT:
+ n = getnum_str(&startp, endp);
+ if (n < 0)
+ return (-1);
+ ShrinkBuffer(INT16SZ);
+ PUTSHORT(n, cp);
+ if (!getword_str(buf2, sizeof buf2, &startp, endp))
+ return (-1);
+ n = dn_comp(buf2, cp, buflen, dnptrs, lastdnptr);
+ if (n < 0)
+ return (-1);
+ cp += n;
+ ShrinkBuffer(n);
+ break;
+ case T_SRV:
+ n = getnum_str(&startp, endp);
+ if (n < 0)
+ return (-1);
+ ShrinkBuffer(INT16SZ);
+ PUTSHORT(n, cp);
+
+ n = getnum_str(&startp, endp);
+ if (n < 0)
+ return (-1);
+ ShrinkBuffer(INT16SZ);
+ PUTSHORT(n, cp);
+
+ n = getnum_str(&startp, endp);
+ if (n < 0)
+ return (-1);
+ ShrinkBuffer(INT16SZ);
+ PUTSHORT(n, cp);
+
+ if (!getword_str(buf2, sizeof buf2, &startp, endp))
+ return (-1);
+ n = dn_comp(buf2, cp, buflen, NULL, NULL);
+ if (n < 0)
+ return (-1);
+ cp += n;
+ ShrinkBuffer(n);
+ break;
+ case T_PX:
+ n = getnum_str(&startp, endp);
+ if (n < 0)
+ return (-1);
+ PUTSHORT(n, cp);
+ ShrinkBuffer(INT16SZ);
+ for (i = 0; i < 2; i++) {
+ if (!getword_str(buf2, sizeof buf2, &startp,
+ endp))
+ return (-1);
+ n = dn_comp(buf2, cp, buflen, dnptrs,
+ lastdnptr);
+ if (n < 0)
+ return (-1);
+ cp += n;
+ ShrinkBuffer(n);
+ }
+ break;
+ case T_WKS: {
+ char bm[MAXPORT/8];
+ unsigned int maxbm = 0;
+
+ if (!getword_str(buf2, sizeof buf2, &startp, endp))
+ return (-1);
+ if (!inet_aton(buf2, &ina))
+ return (-1);
+ n1 = ntohl(ina.s_addr);
+ ShrinkBuffer(INT32SZ);
+ PUTLONG(n1, cp);
+
+ if (!getword_str(buf2, sizeof buf2, &startp, endp))
+ return (-1);
+ if ((i = res_protocolnumber(buf2)) < 0)
+ return (-1);
+ ShrinkBuffer(1);
+ *cp++ = i & 0xff;
+
+ for (i = 0; i < MAXPORT/8 ; i++)
+ bm[i] = 0;
+
+ while (getword_str(buf2, sizeof buf2, &startp, endp)) {
+ if ((n = res_servicenumber(buf2)) <= 0)
+ return (-1);
+
+ if (n < MAXPORT) {
+ bm[n/8] |= (0x80>>(n%8));
+ if ((unsigned)n > maxbm)
+ maxbm = n;
+ } else
+ return (-1);
+ }
+ maxbm = maxbm/8 + 1;
+ ShrinkBuffer(maxbm);
+ memcpy(cp, bm, maxbm);
+ cp += maxbm;
+ break;
+ }
+ case T_HINFO:
+ for (i = 0; i < 2; i++) {
+ if ((n = getstr_str(buf2, sizeof buf2,
+ &startp, endp)) < 0)
+ return (-1);
+ if (n > 255)
+ return (-1);
+ ShrinkBuffer(n+1);
+ *cp++ = n;
+ memcpy(cp, buf2, n);
+ cp += n;
+ }
+ break;
+ case T_TXT:
+ for (;;) {
+ if ((n = getstr_str(buf2, sizeof buf2,
+ &startp, endp)) < 0) {
+ if (cp != (sp2 + INT16SZ))
+ break;
+ return (-1);
+ }
+ if (n > 255)
+ return (-1);
+ ShrinkBuffer(n+1);
+ *cp++ = n;
+ memcpy(cp, buf2, n);
+ cp += n;
+ }
+ break;
+ case T_X25:
+ /* RFC 1183 */
+ if ((n = getstr_str(buf2, sizeof buf2, &startp,
+ endp)) < 0)
+ return (-1);
+ if (n > 255)
+ return (-1);
+ ShrinkBuffer(n+1);
+ *cp++ = n;
+ memcpy(cp, buf2, n);
+ cp += n;
+ break;
+ case T_ISDN:
+ /* RFC 1183 */
+ if ((n = getstr_str(buf2, sizeof buf2, &startp,
+ endp)) < 0)
+ return (-1);
+ if ((n > 255) || (n == 0))
+ return (-1);
+ ShrinkBuffer(n+1);
+ *cp++ = n;
+ memcpy(cp, buf2, n);
+ cp += n;
+ if ((n = getstr_str(buf2, sizeof buf2, &startp,
+ endp)) < 0)
+ n = 0;
+ if (n > 255)
+ return (-1);
+ ShrinkBuffer(n+1);
+ *cp++ = n;
+ memcpy(cp, buf2, n);
+ cp += n;
+ break;
+ case T_NSAP:
+ if ((n = inet_nsap_addr((char *)startp, (u_char *)buf2, sizeof(buf2))) != 0) {
+ ShrinkBuffer(n);
+ memcpy(cp, buf2, n);
+ cp += n;
+ } else {
+ return (-1);
+ }
+ break;
+ case T_LOC:
+ if ((n = loc_aton((char *)startp, (u_char *)buf2)) != 0) {
+ ShrinkBuffer(n);
+ memcpy(cp, buf2, n);
+ cp += n;
+ } else
+ return (-1);
+ break;
+ case ns_t_sig:
+ {
+ int sig_type, success, dateerror;
+ u_int32_t exptime, timesigned;
+
+ /* type */
+ if ((n = getword_str(buf2, sizeof buf2,
+ &startp, endp)) < 0)
+ return (-1);
+ sig_type = sym_ston(__p_type_syms, buf2, &success);
+ if (!success || sig_type == ns_t_any)
+ return (-1);
+ ShrinkBuffer(INT16SZ);
+ PUTSHORT(sig_type, cp);
+ /* alg */
+ n = getnum_str(&startp, endp);
+ if (n < 0)
+ return (-1);
+ ShrinkBuffer(1);
+ *cp++ = n;
+ /* labels */
+ n = getnum_str(&startp, endp);
+ if (n <= 0 || n > 255)
+ return (-1);
+ ShrinkBuffer(1);
+ *cp++ = n;
+ /* ottl & expire */
+ if (!getword_str(buf2, sizeof buf2, &startp, endp))
+ return (-1);
+ exptime = ns_datetosecs(buf2, &dateerror);
+ if (!dateerror) {
+ ShrinkBuffer(INT32SZ);
+ PUTLONG(rttl, cp);
+ }
+ else {
+ char *ulendp;
+ u_int32_t ottl;
+
+ errno = 0;
+ ottl = strtoul(buf2, &ulendp, 10);
+ if (errno != 0 ||
+ (ulendp != NULL && *ulendp != '\0'))
+ return (-1);
+ ShrinkBuffer(INT32SZ);
+ PUTLONG(ottl, cp);
+ if (!getword_str(buf2, sizeof buf2, &startp,
+ endp))
+ return (-1);
+ exptime = ns_datetosecs(buf2, &dateerror);
+ if (dateerror)
+ return (-1);
+ }
+ /* expire */
+ ShrinkBuffer(INT32SZ);
+ PUTLONG(exptime, cp);
+ /* timesigned */
+ if (!getword_str(buf2, sizeof buf2, &startp, endp))
+ return (-1);
+ timesigned = ns_datetosecs(buf2, &dateerror);
+ if (!dateerror) {
+ ShrinkBuffer(INT32SZ);
+ PUTLONG(timesigned, cp);
+ }
+ else
+ return (-1);
+ /* footprint */
+ n = getnum_str(&startp, endp);
+ if (n < 0)
+ return (-1);
+ ShrinkBuffer(INT16SZ);
+ PUTSHORT(n, cp);
+ /* signer name */
+ if (!getword_str(buf2, sizeof buf2, &startp, endp))
+ return (-1);
+ n = dn_comp(buf2, cp, buflen, dnptrs, lastdnptr);
+ if (n < 0)
+ return (-1);
+ cp += n;
+ ShrinkBuffer(n);
+ /* sig */
+ if ((n = getword_str(buf2, sizeof buf2,
+ &startp, endp)) < 0)
+ return (-1);
+ siglen = b64_pton(buf2, buf3, sizeof(buf3));
+ if (siglen < 0)
+ return (-1);
+ ShrinkBuffer(siglen);
+ memcpy(cp, buf3, siglen);
+ cp += siglen;
+ break;
+ }
+ case ns_t_key:
+ /* flags */
+ n = gethexnum_str(&startp, endp);
+ if (n < 0)
+ return (-1);
+ ShrinkBuffer(INT16SZ);
+ PUTSHORT(n, cp);
+ /* proto */
+ n = getnum_str(&startp, endp);
+ if (n < 0)
+ return (-1);
+ ShrinkBuffer(1);
+ *cp++ = n;
+ /* alg */
+ n = getnum_str(&startp, endp);
+ if (n < 0)
+ return (-1);
+ ShrinkBuffer(1);
+ *cp++ = n;
+ /* key */
+ if ((n = getword_str(buf2, sizeof buf2,
+ &startp, endp)) < 0)
+ return (-1);
+ keylen = b64_pton(buf2, buf3, sizeof(buf3));
+ if (keylen < 0)
+ return (-1);
+ ShrinkBuffer(keylen);
+ memcpy(cp, buf3, keylen);
+ cp += keylen;
+ break;
+ case ns_t_nxt:
+ {
+ int success, nxt_type;
+ u_char data[32];
+ int maxtype;
+
+ /* next name */
+ if (!getword_str(buf2, sizeof buf2, &startp, endp))
+ return (-1);
+ n = dn_comp(buf2, cp, buflen, NULL, NULL);
+ if (n < 0)
+ return (-1);
+ cp += n;
+ ShrinkBuffer(n);
+ maxtype = 0;
+ memset(data, 0, sizeof data);
+ for (;;) {
+ if (!getword_str(buf2, sizeof buf2, &startp,
+ endp))
+ break;
+ nxt_type = sym_ston(__p_type_syms, buf2,
+ &success);
+ if (!success || !ns_t_rr_p(nxt_type))
+ return (-1);
+ NS_NXT_BIT_SET(nxt_type, data);
+ if (nxt_type > maxtype)
+ maxtype = nxt_type;
+ }
+ n = maxtype/NS_NXT_BITS+1;
+ ShrinkBuffer(n);
+ memcpy(cp, data, n);
+ cp += n;
+ break;
+ }
+ case ns_t_cert:
+ /* type */
+ n = getnum_str(&startp, endp);
+ if (n < 0)
+ return (-1);
+ ShrinkBuffer(INT16SZ);
+ PUTSHORT(n, cp);
+ /* key tag */
+ n = getnum_str(&startp, endp);
+ if (n < 0)
+ return (-1);
+ ShrinkBuffer(INT16SZ);
+ PUTSHORT(n, cp);
+ /* alg */
+ n = getnum_str(&startp, endp);
+ if (n < 0)
+ return (-1);
+ ShrinkBuffer(1);
+ *cp++ = n;
+ /* cert */
+ if ((n = getword_str(buf2, sizeof buf2,
+ &startp, endp)) < 0)
+ return (-1);
+ certlen = b64_pton(buf2, buf3, sizeof(buf3));
+ if (certlen < 0)
+ return (-1);
+ ShrinkBuffer(certlen);
+ memcpy(cp, buf3, certlen);
+ cp += certlen;
+ break;
+ case ns_t_aaaa:
+ if (!getword_str(buf2, sizeof buf2, &startp, endp))
+ return (-1);
+ if (inet_pton(AF_INET6, buf2, &in6a) <= 0)
+ return (-1);
+ ShrinkBuffer(NS_IN6ADDRSZ);
+ memcpy(cp, &in6a, NS_IN6ADDRSZ);
+ cp += NS_IN6ADDRSZ;
+ break;
+ case ns_t_naptr:
+ /* Order Preference Flags Service Replacement Regexp */
+ /* Order */
+ n = getnum_str(&startp, endp);
+ if (n < 0 || n > 65535)
+ return (-1);
+ ShrinkBuffer(INT16SZ);
+ PUTSHORT(n, cp);
+ /* Preference */
+ n = getnum_str(&startp, endp);
+ if (n < 0 || n > 65535)
+ return (-1);
+ ShrinkBuffer(INT16SZ);
+ PUTSHORT(n, cp);
+ /* Flags */
+ if ((n = getstr_str(buf2, sizeof buf2,
+ &startp, endp)) < 0) {
+ return (-1);
+ }
+ if (n > 255)
+ return (-1);
+ ShrinkBuffer(n+1);
+ *cp++ = n;
+ memcpy(cp, buf2, n);
+ cp += n;
+ /* Service Classes */
+ if ((n = getstr_str(buf2, sizeof buf2,
+ &startp, endp)) < 0) {
+ return (-1);
+ }
+ if (n > 255)
+ return (-1);
+ ShrinkBuffer(n+1);
+ *cp++ = n;
+ memcpy(cp, buf2, n);
+ cp += n;
+ /* Pattern */
+ if ((n = getstr_str(buf2, sizeof buf2,
+ &startp, endp)) < 0) {
+ return (-1);
+ }
+ if (n > 255)
+ return (-1);
+ ShrinkBuffer(n+1);
+ *cp++ = n;
+ memcpy(cp, buf2, n);
+ cp += n;
+ /* Replacement */
+ if (!getword_str(buf2, sizeof buf2, &startp, endp))
+ return (-1);
+ n = dn_comp(buf2, cp, buflen, NULL, NULL);
+ if (n < 0)
+ return (-1);
+ cp += n;
+ ShrinkBuffer(n);
+ break;
+ default:
+ return (-1);
+ } /*switch*/
+ n = (u_int16_t)((cp - sp2) - INT16SZ);
+ PUTSHORT(n, sp2);
+ } /*for*/
+
+ hp->qdcount = htons(counts[0]);
+ hp->ancount = htons(counts[1]);
+ hp->nscount = htons(counts[2]);
+ hp->arcount = htons(counts[3]);
+ return (cp - buf);
+}
+
+/*
+ * Get a whitespace delimited word from a string (not file)
+ * into buf. modify the start pointer to point after the
+ * word in the string.
+ */
+static int
+getword_str(char *buf, int size, u_char **startpp, u_char *endp) {
+ char *cp;
+ int c;
+
+ for (cp = buf; *startpp <= endp; ) {
+ c = **startpp;
+ if (isspace(c) || c == '\0') {
+ if (cp != buf) /* trailing whitespace */
+ break;
+ else { /* leading whitespace */
+ (*startpp)++;
+ continue;
+ }
+ }
+ (*startpp)++;
+ if (cp >= buf+size-1)
+ break;
+ *cp++ = (u_char)c;
+ }
+ *cp = '\0';
+ return (cp != buf);
+}
+
+/*
+ * get a white spae delimited string from memory. Process quoted strings
+ * and \DDD escapes. Return length or -1 on error. Returned string may
+ * contain nulls.
+ */
+static char digits[] = "0123456789";
+static int
+getstr_str(char *buf, int size, u_char **startpp, u_char *endp) {
+ char *cp;
+ int c, c1 = 0;
+ int inquote = 0;
+ int seen_quote = 0;
+ int escape = 0;
+ int dig = 0;
+
+ for (cp = buf; *startpp <= endp; ) {
+ if ((c = **startpp) == '\0')
+ break;
+ /* leading white space */
+ if ((cp == buf) && !seen_quote && isspace(c)) {
+ (*startpp)++;
+ continue;
+ }
+
+ switch (c) {
+ case '\\':
+ if (!escape) {
+ escape = 1;
+ dig = 0;
+ c1 = 0;
+ (*startpp)++;
+ continue;
+ }
+ goto do_escape;
+ case '"':
+ if (!escape) {
+ inquote = !inquote;
+ seen_quote = 1;
+ (*startpp)++;
+ continue;
+ }
+ /* fall through */
+ default:
+ do_escape:
+ if (escape) {
+ switch (c) {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ c1 = c1 * 10 +
+ (strchr(digits, c) - digits);
+
+ if (++dig == 3) {
+ c = c1 &0xff;
+ break;
+ }
+ (*startpp)++;
+ continue;
+ }
+ escape = 0;
+ } else if (!inquote && isspace(c))
+ goto done;
+ if (cp >= buf+size-1)
+ goto done;
+ *cp++ = (u_char)c;
+ (*startpp)++;
+ }
+ }
+ done:
+ *cp = '\0';
+ return ((cp == buf)? (seen_quote? 0: -1): (cp - buf));
+}
+/*
+ * Get a whitespace delimited base 16 number from a string (not file) into buf
+ * update the start pointer to point after the number in the string.
+ */
+static int
+gethexnum_str(u_char **startpp, u_char *endp) {
+ int c, n;
+ int seendigit = 0;
+ int m = 0;
+
+ if (*startpp + 2 >= endp || strncasecmp((char *)*startpp, "0x", 2) != 0)
+ return getnum_str(startpp, endp);
+ (*startpp)+=2;
+ for (n = 0; *startpp <= endp; ) {
+ c = **startpp;
+ if (isspace(c) || c == '\0') {
+ if (seendigit) /* trailing whitespace */
+ break;
+ else { /* leading whitespace */
+ (*startpp)++;
+ continue;
+ }
+ }
+ if (c == ';') {
+ while ((*startpp <= endp) &&
+ ((c = **startpp) != '\n'))
+ (*startpp)++;
+ if (seendigit)
+ break;
+ continue;
+ }
+ if (!isxdigit(c)) {
+ if (c == ')' && seendigit) {
+ (*startpp)--;
+ break;
+ }
+ return (-1);
+ }
+ (*startpp)++;
+ if (isdigit(c))
+ n = n * 16 + (c - '0');
+ else
+ n = n * 16 + (tolower(c) - 'a' + 10);
+ seendigit = 1;
+ }
+ return (n + m);
+}
+
+/*
+ * Get a whitespace delimited base 10 number from a string (not file) into buf
+ * update the start pointer to point after the number in the string.
+ */
+static int
+getnum_str(u_char **startpp, u_char *endp) {
+ int c, n;
+ int seendigit = 0;
+ int m = 0;
+
+ for (n = 0; *startpp <= endp; ) {
+ c = **startpp;
+ if (isspace(c) || c == '\0') {
+ if (seendigit) /* trailing whitespace */
+ break;
+ else { /* leading whitespace */
+ (*startpp)++;
+ continue;
+ }
+ }
+ if (c == ';') {
+ while ((*startpp <= endp) &&
+ ((c = **startpp) != '\n'))
+ (*startpp)++;
+ if (seendigit)
+ break;
+ continue;
+ }
+ if (!isdigit(c)) {
+ if (c == ')' && seendigit) {
+ (*startpp)--;
+ break;
+ }
+ return (-1);
+ }
+ (*startpp)++;
+ n = n * 10 + (c - '0');
+ seendigit = 1;
+ }
+ return (n + m);
+}
+
+/*
+ * Allocate a resource record buffer & save rr info.
+ */
+ns_updrec *
+res_mkupdrec(int section, const char *dname,
+ u_int class, u_int type, u_long ttl) {
+ ns_updrec *rrecp = (ns_updrec *)calloc(1, sizeof(ns_updrec));
+
+ if (!rrecp || !(rrecp->r_dname = strdup(dname))) {
+ if (rrecp)
+ free((char *)rrecp);
+ return (NULL);
+ }
+ INIT_LINK(rrecp, r_link);
+ INIT_LINK(rrecp, r_glink);
+ rrecp->r_class = (ns_class)class;
+ rrecp->r_type = (ns_type)type;
+ rrecp->r_ttl = ttl;
+ rrecp->r_section = (ns_sect)section;
+ return (rrecp);
+}
+
+/*
+ * Free a resource record buffer created by res_mkupdrec.
+ */
+void
+res_freeupdrec(ns_updrec *rrecp) {
+ /* Note: freeing r_dp is the caller's responsibility. */
+ if (rrecp->r_dname != NULL)
+ free(rrecp->r_dname);
+ free(rrecp);
+}
+
+struct valuelist {
+ struct valuelist * next;
+ struct valuelist * prev;
+ char * name;
+ char * proto;
+ int port;
+};
+static struct valuelist *servicelist, *protolist;
+
+static void
+res_buildservicelist() {
+ struct servent *sp;
+ struct valuelist *slp;
+
+#ifdef MAYBE_HESIOD
+ setservent(0);
+#else
+ setservent(1);
+#endif
+ while ((sp = getservent()) != NULL) {
+ slp = (struct valuelist *)malloc(sizeof(struct valuelist));
+ if (!slp)
+ break;
+ slp->name = strdup(sp->s_name);
+ slp->proto = strdup(sp->s_proto);
+ if ((slp->name == NULL) || (slp->proto == NULL)) {
+ if (slp->name) free(slp->name);
+ if (slp->proto) free(slp->proto);
+ free(slp);
+ break;
+ }
+ slp->port = ntohs((u_int16_t)sp->s_port); /* host byt order */
+ slp->next = servicelist;
+ slp->prev = NULL;
+ if (servicelist)
+ servicelist->prev = slp;
+ servicelist = slp;
+ }
+ endservent();
+}
+
+void
+res_destroyservicelist() {
+ struct valuelist *slp, *slp_next;
+
+ for (slp = servicelist; slp != NULL; slp = slp_next) {
+ slp_next = slp->next;
+ free(slp->name);
+ free(slp->proto);
+ free(slp);
+ }
+ servicelist = (struct valuelist *)0;
+}
+
+void
+res_buildprotolist(void) {
+ struct protoent *pp;
+ struct valuelist *slp;
+
+#ifdef MAYBE_HESIOD
+ setprotoent(0);
+#else
+ setprotoent(1);
+#endif
+ while ((pp = getprotoent()) != NULL) {
+ slp = (struct valuelist *)malloc(sizeof(struct valuelist));
+ if (!slp)
+ break;
+ slp->name = strdup(pp->p_name);
+ if (slp->name == NULL) {
+ free(slp);
+ break;
+ }
+ slp->port = pp->p_proto; /* host byte order */
+ slp->next = protolist;
+ slp->prev = NULL;
+ if (protolist)
+ protolist->prev = slp;
+ protolist = slp;
+ }
+ endprotoent();
+}
+
+void
+res_destroyprotolist(void) {
+ struct valuelist *plp, *plp_next;
+
+ for (plp = protolist; plp != NULL; plp = plp_next) {
+ plp_next = plp->next;
+ free(plp->name);
+ free(plp);
+ }
+ protolist = (struct valuelist *)0;
+}
+
+static int
+findservice(const char *s, struct valuelist **list) {
+ struct valuelist *lp = *list;
+ int n;
+
+ for (; lp != NULL; lp = lp->next)
+ if (strcasecmp(lp->name, s) == 0) {
+ if (lp != *list) {
+ lp->prev->next = lp->next;
+ if (lp->next)
+ lp->next->prev = lp->prev;
+ (*list)->prev = lp;
+ lp->next = *list;
+ *list = lp;
+ }
+ return (lp->port); /* host byte order */
+ }
+ if (sscanf(s, "%d", &n) != 1 || n <= 0)
+ n = -1;
+ return (n);
+}
+
+/*
+ * Convert service name or (ascii) number to int.
+ */
+int
+res_servicenumber(const char *p) {
+ if (servicelist == (struct valuelist *)0)
+ res_buildservicelist();
+ return (findservice(p, &servicelist));
+}
+
+/*
+ * Convert protocol name or (ascii) number to int.
+ */
+int
+res_protocolnumber(const char *p) {
+ if (protolist == (struct valuelist *)0)
+ res_buildprotolist();
+ return (findservice(p, &protolist));
+}
+
+static struct servent *
+cgetservbyport(u_int16_t port, const char *proto) { /* Host byte order. */
+ struct valuelist **list = &servicelist;
+ struct valuelist *lp = *list;
+ static struct servent serv;
+
+ port = ntohs(port);
+ for (; lp != NULL; lp = lp->next) {
+ if (port != (u_int16_t)lp->port) /* Host byte order. */
+ continue;
+ if (strcasecmp(lp->proto, proto) == 0) {
+ if (lp != *list) {
+ lp->prev->next = lp->next;
+ if (lp->next)
+ lp->next->prev = lp->prev;
+ (*list)->prev = lp;
+ lp->next = *list;
+ *list = lp;
+ }
+ serv.s_name = lp->name;
+ serv.s_port = htons((u_int16_t)lp->port);
+ serv.s_proto = lp->proto;
+ return (&serv);
+ }
+ }
+ return (0);
+}
+
+static struct protoent *
+cgetprotobynumber(int proto) { /* Host byte order. */
+ struct valuelist **list = &protolist;
+ struct valuelist *lp = *list;
+ static struct protoent prot;
+
+ for (; lp != NULL; lp = lp->next)
+ if (lp->port == proto) { /* Host byte order. */
+ if (lp != *list) {
+ lp->prev->next = lp->next;
+ if (lp->next)
+ lp->next->prev = lp->prev;
+ (*list)->prev = lp;
+ lp->next = *list;
+ *list = lp;
+ }
+ prot.p_name = lp->name;
+ prot.p_proto = lp->port; /* Host byte order. */
+ return (&prot);
+ }
+ return (0);
+}
+
+const char *
+res_protocolname(int num) {
+ static char number[8];
+ struct protoent *pp;
+
+ if (protolist == (struct valuelist *)0)
+ res_buildprotolist();
+ pp = cgetprotobynumber(num);
+ if (pp == 0) {
+ (void) sprintf(number, "%d", num);
+ return (number);
+ }
+ return (pp->p_name);
+}
+
+const char *
+res_servicename(u_int16_t port, const char *proto) { /* Host byte order. */
+ static char number[8];
+ struct servent *ss;
+
+ if (servicelist == (struct valuelist *)0)
+ res_buildservicelist();
+ ss = cgetservbyport(htons(port), proto);
+ if (ss == 0) {
+ (void) sprintf(number, "%d", port);
+ return (number);
+ }
+ return (ss->s_name);
+}
diff --git a/lib/libc/resolv/res_update.c b/lib/libc/resolv/res_update.c
new file mode 100644
index 000000000000..8783d8a7645b
--- /dev/null
+++ b/lib/libc/resolv/res_update.c
@@ -0,0 +1,212 @@
+#if !defined(lint) && !defined(SABER)
+static const char rcsid[] = "$Id: res_update.c,v 1.6.2.4.4.2 2004/03/16 12:34:20 marka Exp $";
+#endif /* not lint */
+
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1996-1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * Based on the Dynamic DNS reference implementation by Viraj Bais
+ * <viraj_bais@ccm.fm.intel.com>
+ */
+
+#include "port_before.h"
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+
+#include <errno.h>
+#include <limits.h>
+#include <netdb.h>
+#include <res_update.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <isc/list.h>
+#include <resolv.h>
+
+#include "port_after.h"
+#include "res_private.h"
+
+/*
+ * Separate a linked list of records into groups so that all records
+ * in a group will belong to a single zone on the nameserver.
+ * Create a dynamic update packet for each zone and send it to the
+ * nameservers for that zone, and await answer.
+ * Abort if error occurs in updating any zone.
+ * Return the number of zones updated on success, < 0 on error.
+ *
+ * On error, caller must deal with the unsynchronized zones
+ * eg. an A record might have been successfully added to the forward
+ * zone but the corresponding PTR record would be missing if error
+ * was encountered while updating the reverse zone.
+ */
+
+struct zonegrp {
+ char z_origin[MAXDNAME];
+ ns_class z_class;
+ union res_sockaddr_union z_nsaddrs[MAXNS];
+ int z_nscount;
+ int z_flags;
+ LIST(ns_updrec) z_rrlist;
+ LINK(struct zonegrp) z_link;
+};
+
+#define ZG_F_ZONESECTADDED 0x0001
+
+/* Forward. */
+
+static void res_dprintf(const char *, ...) ISC_FORMAT_PRINTF(1, 2);
+
+/* Macros. */
+
+#define DPRINTF(x) do {\
+ int save_errno = errno; \
+ if ((statp->options & RES_DEBUG) != 0U) res_dprintf x; \
+ errno = save_errno; \
+ } while (0)
+
+/* Public. */
+
+int
+res_nupdate(res_state statp, ns_updrec *rrecp_in, ns_tsig_key *key) {
+ ns_updrec *rrecp;
+ u_char answer[PACKETSZ];
+ u_char *packet;
+ struct zonegrp *zptr, tgrp;
+ LIST(struct zonegrp) zgrps;
+ int nzones = 0, nscount = 0, n;
+ union res_sockaddr_union nsaddrs[MAXNS];
+
+ packet = malloc(NS_MAXMSG);
+ if (packet == NULL) {
+ DPRINTF(("malloc failed"));
+ return (0);
+ }
+ /* Thread all of the updates onto a list of groups. */
+ INIT_LIST(zgrps);
+ memset(&tgrp, 0, sizeof (tgrp));
+ for (rrecp = rrecp_in; rrecp;
+ rrecp = LINKED(rrecp, r_link) ? NEXT(rrecp, r_link) : NULL) {
+ int nscnt;
+ /* Find the origin for it if there is one. */
+ tgrp.z_class = rrecp->r_class;
+ nscnt = res_findzonecut2(statp, rrecp->r_dname, tgrp.z_class,
+ RES_EXHAUSTIVE, tgrp.z_origin,
+ sizeof tgrp.z_origin,
+ tgrp.z_nsaddrs, MAXNS);
+ if (nscnt <= 0) {
+ DPRINTF(("res_findzonecut failed (%d)", nscnt));
+ goto done;
+ }
+ tgrp.z_nscount = nscnt;
+ /* Find the group for it if there is one. */
+ for (zptr = HEAD(zgrps); zptr != NULL; zptr = NEXT(zptr, z_link))
+ if (ns_samename(tgrp.z_origin, zptr->z_origin) == 1 &&
+ tgrp.z_class == zptr->z_class)
+ break;
+ /* Make a group for it if there isn't one. */
+ if (zptr == NULL) {
+ zptr = malloc(sizeof *zptr);
+ if (zptr == NULL) {
+ DPRINTF(("malloc failed"));
+ goto done;
+ }
+ *zptr = tgrp;
+ zptr->z_flags = 0;
+ INIT_LINK(zptr, z_link);
+ INIT_LIST(zptr->z_rrlist);
+ APPEND(zgrps, zptr, z_link);
+ }
+ /* Thread this rrecp onto the right group. */
+ APPEND(zptr->z_rrlist, rrecp, r_glink);
+ }
+
+ for (zptr = HEAD(zgrps); zptr != NULL; zptr = NEXT(zptr, z_link)) {
+ /* Construct zone section and prepend it. */
+ rrecp = res_mkupdrec(ns_s_zn, zptr->z_origin,
+ zptr->z_class, ns_t_soa, 0);
+ if (rrecp == NULL) {
+ DPRINTF(("res_mkupdrec failed"));
+ goto done;
+ }
+ PREPEND(zptr->z_rrlist, rrecp, r_glink);
+ zptr->z_flags |= ZG_F_ZONESECTADDED;
+
+ /* Marshall the update message. */
+ n = res_nmkupdate(statp, HEAD(zptr->z_rrlist),
+ packet, NS_MAXMSG);
+ DPRINTF(("res_mkupdate -> %d", n));
+ if (n < 0)
+ goto done;
+
+ /* Temporarily replace the resolver's nameserver set. */
+ nscount = res_getservers(statp, nsaddrs, MAXNS);
+ res_setservers(statp, zptr->z_nsaddrs, zptr->z_nscount);
+
+ /* Send the update and remember the result. */
+ if (key != NULL)
+ n = res_nsendsigned(statp, packet, n, key,
+ answer, sizeof answer);
+ else
+ n = res_nsend(statp, packet, n, answer, sizeof answer);
+ if (n < 0) {
+ DPRINTF(("res_nsend: send error, n=%d (%s)\n",
+ n, strerror(errno)));
+ goto done;
+ }
+ if (((HEADER *)answer)->rcode == NOERROR)
+ nzones++;
+
+ /* Restore resolver's nameserver set. */
+ res_setservers(statp, nsaddrs, nscount);
+ nscount = 0;
+ }
+ done:
+ while (!EMPTY(zgrps)) {
+ zptr = HEAD(zgrps);
+ if ((zptr->z_flags & ZG_F_ZONESECTADDED) != 0)
+ res_freeupdrec(HEAD(zptr->z_rrlist));
+ UNLINK(zgrps, zptr, z_link);
+ free(zptr);
+ }
+ if (nscount != 0)
+ res_setservers(statp, nsaddrs, nscount);
+
+ free(packet);
+ return (nzones);
+}
+
+/* Private. */
+
+static void
+res_dprintf(const char *fmt, ...) {
+ va_list ap;
+
+ va_start(ap, fmt);
+ fputs(";; res_nupdate: ", stderr);
+ vfprintf(stderr, fmt, ap);
+ fputc('\n', stderr);
+ va_end(ap);
+}