17c478bdstevel@tonic-gate/*
29525b14Rao Shoaib * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
37c478bdstevel@tonic-gate * Copyright (c) 1996-1999 by Internet Software Consortium.
47c478bdstevel@tonic-gate *
57c478bdstevel@tonic-gate * Permission to use, copy, modify, and distribute this software for any
67c478bdstevel@tonic-gate * purpose with or without fee is hereby granted, provided that the above
77c478bdstevel@tonic-gate * copyright notice and this permission notice appear in all copies.
87c478bdstevel@tonic-gate *
99525b14Rao Shoaib * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
109525b14Rao Shoaib * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
119525b14Rao Shoaib * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
129525b14Rao Shoaib * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
139525b14Rao Shoaib * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
149525b14Rao Shoaib * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
159525b14Rao Shoaib * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
167c478bdstevel@tonic-gate */
177c478bdstevel@tonic-gate
189525b14Rao Shoaib/*! \file
199525b14Rao Shoaib * \brief
207c478bdstevel@tonic-gate * Based on the Dynamic DNS reference implementation by Viraj Bais
219525b14Rao Shoaib * <viraj_bais@ccm.fm.intel.com>
227c478bdstevel@tonic-gate */
237c478bdstevel@tonic-gate
247c478bdstevel@tonic-gate#include "port_before.h"
257c478bdstevel@tonic-gate
267c478bdstevel@tonic-gate#include <sys/param.h>
277c478bdstevel@tonic-gate#include <sys/socket.h>
287c478bdstevel@tonic-gate#include <sys/time.h>
297c478bdstevel@tonic-gate
307c478bdstevel@tonic-gate#include <netinet/in.h>
317c478bdstevel@tonic-gate#include <arpa/inet.h>
327c478bdstevel@tonic-gate#include <arpa/nameser.h>
337c478bdstevel@tonic-gate
347c478bdstevel@tonic-gate#include <errno.h>
357c478bdstevel@tonic-gate#include <limits.h>
367c478bdstevel@tonic-gate#include <netdb.h>
377c478bdstevel@tonic-gate#include <res_update.h>
387c478bdstevel@tonic-gate#include <stdarg.h>
397c478bdstevel@tonic-gate#include <stdio.h>
407c478bdstevel@tonic-gate#include <stdlib.h>
417c478bdstevel@tonic-gate#include <string.h>
427c478bdstevel@tonic-gate
437c478bdstevel@tonic-gate#include <isc/list.h>
447c478bdstevel@tonic-gate#include <resolv.h>
457c478bdstevel@tonic-gate
467c478bdstevel@tonic-gate#include "port_after.h"
477c478bdstevel@tonic-gate#include "res_private.h"
487c478bdstevel@tonic-gate
499525b14Rao Shoaib/*%
507c478bdstevel@tonic-gate * Separate a linked list of records into groups so that all records
517c478bdstevel@tonic-gate * in a group will belong to a single zone on the nameserver.
527c478bdstevel@tonic-gate * Create a dynamic update packet for each zone and send it to the
537c478bdstevel@tonic-gate * nameservers for that zone, and await answer.
547c478bdstevel@tonic-gate * Abort if error occurs in updating any zone.
557c478bdstevel@tonic-gate * Return the number of zones updated on success, < 0 on error.
567c478bdstevel@tonic-gate *
577c478bdstevel@tonic-gate * On error, caller must deal with the unsynchronized zones
587c478bdstevel@tonic-gate * eg. an A record might have been successfully added to the forward
597c478bdstevel@tonic-gate * zone but the corresponding PTR record would be missing if error
607c478bdstevel@tonic-gate * was encountered while updating the reverse zone.
617c478bdstevel@tonic-gate */
627c478bdstevel@tonic-gate
637c478bdstevel@tonic-gatestruct zonegrp {
647c478bdstevel@tonic-gate	char			z_origin[MAXDNAME];
657c478bdstevel@tonic-gate	ns_class		z_class;
667c478bdstevel@tonic-gate	union res_sockaddr_union z_nsaddrs[MAXNS];
677c478bdstevel@tonic-gate	int			z_nscount;
687c478bdstevel@tonic-gate	int			z_flags;
697c478bdstevel@tonic-gate	LIST(ns_updrec)		z_rrlist;
707c478bdstevel@tonic-gate	LINK(struct zonegrp)	z_link;
717c478bdstevel@tonic-gate};
727c478bdstevel@tonic-gate
737c478bdstevel@tonic-gate#define ZG_F_ZONESECTADDED	0x0001
747c478bdstevel@tonic-gate
757c478bdstevel@tonic-gate/* Forward. */
767c478bdstevel@tonic-gate
777c478bdstevel@tonic-gatestatic void	res_dprintf(const char *, ...) ISC_FORMAT_PRINTF(1, 2);
787c478bdstevel@tonic-gate
797c478bdstevel@tonic-gate/* Macros. */
807c478bdstevel@tonic-gate
817c478bdstevel@tonic-gate#define DPRINTF(x) do {\
827c478bdstevel@tonic-gate		int save_errno = errno; \
839525b14Rao Shoaib		if ((statp->options & RES_DEBUG) != 0U) res_dprintf x; \
847c478bdstevel@tonic-gate		errno = save_errno; \
857c478bdstevel@tonic-gate	} while (0)
867c478bdstevel@tonic-gate
877c478bdstevel@tonic-gate/* Public. */
887c478bdstevel@tonic-gate
897c478bdstevel@tonic-gateint
907c478bdstevel@tonic-gateres_nupdate(res_state statp, ns_updrec *rrecp_in, ns_tsig_key *key) {
917c478bdstevel@tonic-gate	ns_updrec *rrecp;
927c478bdstevel@tonic-gate	u_char answer[PACKETSZ];
937c478bdstevel@tonic-gate	u_char *packet;
947c478bdstevel@tonic-gate	struct zonegrp *zptr, tgrp;
957c478bdstevel@tonic-gate	LIST(struct zonegrp) zgrps;
967c478bdstevel@tonic-gate	int nzones = 0, nscount = 0, n;
977c478bdstevel@tonic-gate	union res_sockaddr_union nsaddrs[MAXNS];
987c478bdstevel@tonic-gate
997c478bdstevel@tonic-gate	packet = malloc(NS_MAXMSG);
1007c478bdstevel@tonic-gate	if (packet == NULL) {
1017c478bdstevel@tonic-gate		DPRINTF(("malloc failed"));
1027c478bdstevel@tonic-gate		return (0);
1037c478bdstevel@tonic-gate	}
1047c478bdstevel@tonic-gate	/* Thread all of the updates onto a list of groups. */
1057c478bdstevel@tonic-gate	INIT_LIST(zgrps);
1067c478bdstevel@tonic-gate	memset(&tgrp, 0, sizeof (tgrp));
1077c478bdstevel@tonic-gate	for (rrecp = rrecp_in; rrecp;
1087c478bdstevel@tonic-gate	     rrecp = LINKED(rrecp, r_link) ? NEXT(rrecp, r_link) : NULL) {
1097c478bdstevel@tonic-gate		int nscnt;
1107c478bdstevel@tonic-gate		/* Find the origin for it if there is one. */
1117c478bdstevel@tonic-gate		tgrp.z_class = rrecp->r_class;
1127c478bdstevel@tonic-gate		nscnt = res_findzonecut2(statp, rrecp->r_dname, tgrp.z_class,
1137c478bdstevel@tonic-gate					 RES_EXHAUSTIVE, tgrp.z_origin,
1147c478bdstevel@tonic-gate					 sizeof tgrp.z_origin,
1157c478bdstevel@tonic-gate					 tgrp.z_nsaddrs, MAXNS);
1167c478bdstevel@tonic-gate		if (nscnt <= 0) {
1177c478bdstevel@tonic-gate			DPRINTF(("res_findzonecut failed (%d)", nscnt));
1187c478bdstevel@tonic-gate			goto done;
1197c478bdstevel@tonic-gate		}
1207c478bdstevel@tonic-gate		tgrp.z_nscount = nscnt;
1217c478bdstevel@tonic-gate		/* Find the group for it if there is one. */
1227c478bdstevel@tonic-gate		for (zptr = HEAD(zgrps); zptr != NULL; zptr = NEXT(zptr, z_link))
1237c478bdstevel@tonic-gate			if (ns_samename(tgrp.z_origin, zptr->z_origin) == 1 &&
1247c478bdstevel@tonic-gate			    tgrp.z_class == zptr->z_class)
1257c478bdstevel@tonic-gate				break;
1267c478bdstevel@tonic-gate		/* Make a group for it if there isn't one. */
1277c478bdstevel@tonic-gate		if (zptr == NULL) {
1287c478bdstevel@tonic-gate			zptr = malloc(sizeof *zptr);
1297c478bdstevel@tonic-gate			if (zptr == NULL) {
1307c478bdstevel@tonic-gate				DPRINTF(("malloc failed"));
1317c478bdstevel@tonic-gate				goto done;
1327c478bdstevel@tonic-gate			}
1337c478bdstevel@tonic-gate			*zptr = tgrp;
1347c478bdstevel@tonic-gate			zptr->z_flags = 0;
1357c478bdstevel@tonic-gate			INIT_LINK(zptr, z_link);
1367c478bdstevel@tonic-gate			INIT_LIST(zptr->z_rrlist);
1377c478bdstevel@tonic-gate			APPEND(zgrps, zptr, z_link);
1387c478bdstevel@tonic-gate		}
1397c478bdstevel@tonic-gate		/* Thread this rrecp onto the right group. */
1407c478bdstevel@tonic-gate		APPEND(zptr->z_rrlist, rrecp, r_glink);
1417c478bdstevel@tonic-gate	}
1427c478bdstevel@tonic-gate
1437c478bdstevel@tonic-gate	for (zptr = HEAD(zgrps); zptr != NULL; zptr = NEXT(zptr, z_link)) {
1447c478bdstevel@tonic-gate		/* Construct zone section and prepend it. */
1457c478bdstevel@tonic-gate		rrecp = res_mkupdrec(ns_s_zn, zptr->z_origin,
1467c478bdstevel@tonic-gate				     zptr->z_class, ns_t_soa, 0);
1477c478bdstevel@tonic-gate		if (rrecp == NULL) {
1487c478bdstevel@tonic-gate			DPRINTF(("res_mkupdrec failed"));
1497c478bdstevel@tonic-gate			goto done;
1507c478bdstevel@tonic-gate		}
1517c478bdstevel@tonic-gate		PREPEND(zptr->z_rrlist, rrecp, r_glink);
1527c478bdstevel@tonic-gate		zptr->z_flags |= ZG_F_ZONESECTADDED;
1537c478bdstevel@tonic-gate
1547c478bdstevel@tonic-gate		/* Marshall the update message. */
1557c478bdstevel@tonic-gate		n = res_nmkupdate(statp, HEAD(zptr->z_rrlist),
1567c478bdstevel@tonic-gate				  packet, NS_MAXMSG);
1577c478bdstevel@tonic-gate		DPRINTF(("res_mkupdate -> %d", n));
1587c478bdstevel@tonic-gate		if (n < 0)
1597c478bdstevel@tonic-gate			goto done;
1607c478bdstevel@tonic-gate
1617c478bdstevel@tonic-gate		/* Temporarily replace the resolver's nameserver set. */
1627c478bdstevel@tonic-gate		nscount = res_getservers(statp, nsaddrs, MAXNS);
1637c478bdstevel@tonic-gate		res_setservers(statp, zptr->z_nsaddrs, zptr->z_nscount);
1647c478bdstevel@tonic-gate
1657c478bdstevel@tonic-gate		/* Send the update and remember the result. */
1667c478bdstevel@tonic-gate		if (key != NULL)
1677c478bdstevel@tonic-gate			n = res_nsendsigned(statp, packet, n, key,
1687c478bdstevel@tonic-gate					    answer, sizeof answer);
1697c478bdstevel@tonic-gate		else
1707c478bdstevel@tonic-gate			n = res_nsend(statp, packet, n, answer, sizeof answer);
1717c478bdstevel@tonic-gate		if (n < 0) {
1727c478bdstevel@tonic-gate			DPRINTF(("res_nsend: send error, n=%d (%s)\n",
1737c478bdstevel@tonic-gate				 n, strerror(errno)));
1747c478bdstevel@tonic-gate			goto done;
1757c478bdstevel@tonic-gate		}
1767c478bdstevel@tonic-gate		if (((HEADER *)answer)->rcode == NOERROR)
1777c478bdstevel@tonic-gate			nzones++;
1787c478bdstevel@tonic-gate
1797c478bdstevel@tonic-gate		/* Restore resolver's nameserver set. */
1807c478bdstevel@tonic-gate		res_setservers(statp, nsaddrs, nscount);
1817c478bdstevel@tonic-gate		nscount = 0;
1827c478bdstevel@tonic-gate	}
1837c478bdstevel@tonic-gate done:
1847c478bdstevel@tonic-gate	while (!EMPTY(zgrps)) {
1857c478bdstevel@tonic-gate		zptr = HEAD(zgrps);
1867c478bdstevel@tonic-gate		if ((zptr->z_flags & ZG_F_ZONESECTADDED) != 0)
1877c478bdstevel@tonic-gate			res_freeupdrec(HEAD(zptr->z_rrlist));
1887c478bdstevel@tonic-gate		UNLINK(zgrps, zptr, z_link);
1897c478bdstevel@tonic-gate		free(zptr);
1907c478bdstevel@tonic-gate	}
1917c478bdstevel@tonic-gate	if (nscount != 0)
1927c478bdstevel@tonic-gate		res_setservers(statp, nsaddrs, nscount);
1937c478bdstevel@tonic-gate
1947c478bdstevel@tonic-gate	free(packet);
1957c478bdstevel@tonic-gate	return (nzones);
1967c478bdstevel@tonic-gate}
1977c478bdstevel@tonic-gate
1987c478bdstevel@tonic-gate/* Private. */
1997c478bdstevel@tonic-gate
2007c478bdstevel@tonic-gatestatic void
2017c478bdstevel@tonic-gateres_dprintf(const char *fmt, ...) {
2027c478bdstevel@tonic-gate	va_list ap;
2037c478bdstevel@tonic-gate
2047c478bdstevel@tonic-gate	va_start(ap, fmt);
2057c478bdstevel@tonic-gate	fputs(";; res_nupdate: ", stderr);
2067c478bdstevel@tonic-gate	vfprintf(stderr, fmt, ap);
2077c478bdstevel@tonic-gate	fputc('\n', stderr);
2087c478bdstevel@tonic-gate	va_end(ap);
2097c478bdstevel@tonic-gate}
210