Home | History | Annotate | Download | only in common
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 /*
     22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  */
     25 
     26 #include <assert.h>
     27 #include <errno.h>
     28 #include <stdio.h>
     29 #include <stdlib.h>
     30 #include <strings.h>
     31 #include <sys/types.h>
     32 #include <sys/socket.h>
     33 #include <netinet/in.h>
     34 #include <arpa/inet.h>
     35 #include <arpa/nameser.h>
     36 #include <net/if.h>
     37 #include <resolv.h>
     38 #include <sys/time.h>
     39 #include <unistd.h>
     40 #include <string.h>
     41 #include <pthread.h>
     42 #include <netdb.h>
     43 #include <rpc/rpc.h>
     44 #include <syslog.h>
     45 #include <gssapi/gssapi.h>
     46 #include <kerberosv5/krb5.h>
     47 
     48 #include <smbns_dyndns.h>
     49 #include <smbns_krb.h>
     50 
     51 /*
     52  * The following can be removed once head/arpa/nameser_compat.h
     53  * defines BADSIG, BADKEY and BADTIME.
     54  */
     55 #ifndef	BADSIG
     56 #define	BADSIG ns_r_badsig
     57 #endif /* BADSIG */
     58 
     59 #ifndef	BADKEY
     60 #define	BADKEY ns_r_badkey
     61 #endif /* BADKEY */
     62 
     63 #ifndef	BADTIME
     64 #define	BADTIME ns_r_badtime
     65 #endif /* BADTIME */
     66 
     67 /* internal use, in dyndns_add_entry */
     68 #define	DEL_NONE			2
     69 
     70 /* Maximum retires if not authoritative */
     71 #define	MAX_AUTH_RETRIES		3
     72 
     73 /* Number of times to retry a DNS query */
     74 #define	DYNDNS_MAX_QUERY_RETRIES	3
     75 
     76 /* Timeout value, in seconds, for DNS query responses */
     77 #define	DYNDNS_QUERY_TIMEOUT		2
     78 
     79 static uint16_t dns_msgid;
     80 mutex_t dns_msgid_mtx;
     81 
     82 #define	DYNDNS_OP_CLEAR			1
     83 #define	DYNDNS_OP_UPDATE		2
     84 
     85 #define	DYNDNS_STATE_INIT		0
     86 #define	DYNDNS_STATE_READY		1
     87 #define	DYNDNS_STATE_PUBLISHING		2
     88 #define	DYNDNS_STATE_STOPPING		3
     89 
     90 typedef struct dyndns_qentry {
     91 	list_node_t	dqe_lnd;
     92 	int		dqe_op;
     93 	char		dqe_fqdn[MAXHOSTNAMELEN];
     94 } dyndns_qentry_t;
     95 
     96 typedef struct dyndns_queue {
     97 	list_t		ddq_list;
     98 	mutex_t		ddq_mtx;
     99 	cond_t		ddq_cv;
    100 	uint32_t	ddq_state;
    101 } dyndns_queue_t;
    102 
    103 static dyndns_queue_t dyndns_queue;
    104 
    105 static void dyndns_queue_request(int, const char *);
    106 static void dyndns_queue_flush(list_t *);
    107 static void *dyndns_publisher(void *);
    108 static void dyndns_process(list_t *);
    109 static int dyndns_update_core(char *);
    110 static int dyndns_clear_rev_zone(char *);
    111 static void dyndns_msgid_init(void);
    112 static int dyndns_get_msgid(void);
    113 static void dyndns_syslog(int, int, const char *);
    114 
    115 int
    116 dyndns_start(void)
    117 {
    118 	pthread_t publisher;
    119 	pthread_attr_t tattr;
    120 	int rc;
    121 
    122 	if (!smb_config_getbool(SMB_CI_DYNDNS_ENABLE))
    123 		return (0);
    124 
    125 	(void) mutex_lock(&dyndns_queue.ddq_mtx);
    126 	if (dyndns_queue.ddq_state != DYNDNS_STATE_INIT) {
    127 		(void) mutex_unlock(&dyndns_queue.ddq_mtx);
    128 		return (0);
    129 	}
    130 
    131 	dyndns_msgid_init();
    132 
    133 	list_create(&dyndns_queue.ddq_list, sizeof (dyndns_qentry_t),
    134 	    offsetof(dyndns_qentry_t, dqe_lnd));
    135 	dyndns_queue.ddq_state = DYNDNS_STATE_READY;
    136 	(void) mutex_unlock(&dyndns_queue.ddq_mtx);
    137 
    138 	(void) pthread_attr_init(&tattr);
    139 	(void) pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED);
    140 	rc = pthread_create(&publisher, &tattr, dyndns_publisher, 0);
    141 	(void) pthread_attr_destroy(&tattr);
    142 	return (rc);
    143 }
    144 
    145 void
    146 dyndns_stop(void)
    147 {
    148 	(void) mutex_lock(&dyndns_queue.ddq_mtx);
    149 
    150 	switch (dyndns_queue.ddq_state) {
    151 	case DYNDNS_STATE_READY:
    152 	case DYNDNS_STATE_PUBLISHING:
    153 		dyndns_queue.ddq_state = DYNDNS_STATE_STOPPING;
    154 		(void) cond_signal(&dyndns_queue.ddq_cv);
    155 		break;
    156 	default:
    157 		break;
    158 	}
    159 
    160 	(void) mutex_unlock(&dyndns_queue.ddq_mtx);
    161 }
    162 
    163 /*
    164  * Clear all records in both zones.
    165  */
    166 void
    167 dyndns_clear_zones(void)
    168 {
    169 	char fqdn[MAXHOSTNAMELEN];
    170 
    171 	if (smb_getfqdomainname(fqdn, MAXHOSTNAMELEN) != 0) {
    172 		syslog(LOG_ERR, "dyndns: failed to get domainname");
    173 		return;
    174 	}
    175 
    176 	dyndns_queue_request(DYNDNS_OP_CLEAR, fqdn);
    177 }
    178 
    179 /*
    180  * Update all records in both zones.
    181  */
    182 void
    183 dyndns_update_zones(void)
    184 {
    185 	char fqdn[MAXHOSTNAMELEN];
    186 
    187 	if (smb_getfqdomainname(fqdn, MAXHOSTNAMELEN) != 0) {
    188 		syslog(LOG_ERR, "dyndns: failed to get domainname");
    189 		return;
    190 	}
    191 
    192 	dyndns_queue_request(DYNDNS_OP_UPDATE, fqdn);
    193 }
    194 
    195 /*
    196  * Add a request to the queue.
    197  */
    198 static void
    199 dyndns_queue_request(int op, const char *fqdn)
    200 {
    201 	dyndns_qentry_t *entry;
    202 
    203 	if (!smb_config_getbool(SMB_CI_DYNDNS_ENABLE))
    204 		return;
    205 
    206 	(void) mutex_lock(&dyndns_queue.ddq_mtx);
    207 
    208 	switch (dyndns_queue.ddq_state) {
    209 	case DYNDNS_STATE_READY:
    210 	case DYNDNS_STATE_PUBLISHING:
    211 		break;
    212 	default:
    213 		(void) mutex_unlock(&dyndns_queue.ddq_mtx);
    214 		return;
    215 	}
    216 
    217 	if ((entry = malloc(sizeof (dyndns_qentry_t))) == NULL) {
    218 		(void) mutex_unlock(&dyndns_queue.ddq_mtx);
    219 		return;
    220 	}
    221 
    222 	bzero(entry, sizeof (dyndns_qentry_t));
    223 	entry->dqe_op = op;
    224 	(void) strlcpy(entry->dqe_fqdn, fqdn, MAXNAMELEN);
    225 
    226 	list_insert_tail(&dyndns_queue.ddq_list, entry);
    227 	(void) cond_signal(&dyndns_queue.ddq_cv);
    228 	(void) mutex_unlock(&dyndns_queue.ddq_mtx);
    229 }
    230 
    231 /*
    232  * Flush all remaining items from the specified list/queue.
    233  */
    234 static void
    235 dyndns_queue_flush(list_t *lst)
    236 {
    237 	dyndns_qentry_t *entry;
    238 
    239 	while ((entry = list_head(lst)) != NULL) {
    240 		list_remove(lst, entry);
    241 		free(entry);
    242 	}
    243 }
    244 
    245 /*
    246  * Dyndns update thread.  While running, the thread waits on a condition
    247  * variable until notified that an entry needs to be updated.
    248  *
    249  * If the outgoing queue is not empty, the thread wakes up every 60 seconds
    250  * to retry.
    251  */
    252 /*ARGSUSED*/
    253 static void *
    254 dyndns_publisher(void *arg)
    255 {
    256 	dyndns_qentry_t *entry;
    257 	list_t publist;
    258 
    259 	(void) mutex_lock(&dyndns_queue.ddq_mtx);
    260 	if (dyndns_queue.ddq_state != DYNDNS_STATE_READY) {
    261 		(void) mutex_unlock(&dyndns_queue.ddq_mtx);
    262 		return (NULL);
    263 	}
    264 	dyndns_queue.ddq_state = DYNDNS_STATE_PUBLISHING;
    265 	(void) mutex_unlock(&dyndns_queue.ddq_mtx);
    266 
    267 	list_create(&publist, sizeof (dyndns_qentry_t),
    268 	    offsetof(dyndns_qentry_t, dqe_lnd));
    269 
    270 	for (;;) {
    271 		(void) mutex_lock(&dyndns_queue.ddq_mtx);
    272 
    273 		while (list_is_empty(&dyndns_queue.ddq_list) &&
    274 		    (dyndns_queue.ddq_state == DYNDNS_STATE_PUBLISHING)) {
    275 			(void) cond_wait(&dyndns_queue.ddq_cv,
    276 			    &dyndns_queue.ddq_mtx);
    277 		}
    278 
    279 		if (dyndns_queue.ddq_state != DYNDNS_STATE_PUBLISHING) {
    280 			(void) mutex_unlock(&dyndns_queue.ddq_mtx);
    281 			break;
    282 		}
    283 
    284 		/*
    285 		 * Transfer queued items to the local list so that
    286 		 * the mutex can be released.
    287 		 */
    288 		while ((entry = list_head(&dyndns_queue.ddq_list)) != NULL) {
    289 			list_remove(&dyndns_queue.ddq_list, entry);
    290 			list_insert_tail(&publist, entry);
    291 		}
    292 
    293 		(void) mutex_unlock(&dyndns_queue.ddq_mtx);
    294 
    295 		dyndns_process(&publist);
    296 	}
    297 
    298 	(void) mutex_lock(&dyndns_queue.ddq_mtx);
    299 	dyndns_queue_flush(&dyndns_queue.ddq_list);
    300 	list_destroy(&dyndns_queue.ddq_list);
    301 	dyndns_queue.ddq_state = DYNDNS_STATE_INIT;
    302 	(void) mutex_unlock(&dyndns_queue.ddq_mtx);
    303 
    304 	dyndns_queue_flush(&publist);
    305 	list_destroy(&publist);
    306 	return (NULL);
    307 }
    308 
    309 /*
    310  * Remove items from the queue and process them.
    311  */
    312 static void
    313 dyndns_process(list_t *publist)
    314 {
    315 	dyndns_qentry_t *entry;
    316 
    317 	while ((entry = list_head(publist)) != NULL) {
    318 		(void) mutex_lock(&dyndns_queue.ddq_mtx);
    319 		if (dyndns_queue.ddq_state != DYNDNS_STATE_PUBLISHING) {
    320 			(void) mutex_unlock(&dyndns_queue.ddq_mtx);
    321 			dyndns_queue_flush(publist);
    322 			return;
    323 		}
    324 		(void) mutex_unlock(&dyndns_queue.ddq_mtx);
    325 
    326 		list_remove(publist, entry);
    327 
    328 		switch (entry->dqe_op) {
    329 		case DYNDNS_OP_CLEAR:
    330 			(void) dyndns_clear_rev_zone(entry->dqe_fqdn);
    331 			break;
    332 		case DYNDNS_OP_UPDATE:
    333 			(void) dyndns_update_core(entry->dqe_fqdn);
    334 			break;
    335 		default:
    336 			break;
    337 		}
    338 
    339 		free(entry);
    340 	}
    341 }
    342 
    343 /*
    344  * Dynamic DNS update API for kclient.
    345  *
    346  * Returns 0 upon success.  Otherwise, returns -1.
    347  */
    348 int
    349 dyndns_update(char *fqdn)
    350 {
    351 	int rc;
    352 
    353 	if (smb_nic_init() != 0)
    354 		return (-1);
    355 
    356 	dyndns_msgid_init();
    357 	rc = dyndns_update_core(fqdn);
    358 	smb_nic_fini();
    359 	return (rc);
    360 }
    361 
    362 /*
    363  * Initializes the DNS message ID counter using the algorithm
    364  * that resolver library uses to initialize the ID field of any res
    365  * structure.
    366  */
    367 static void
    368 dyndns_msgid_init(void)
    369 {
    370 	struct timeval now;
    371 
    372 	(void) gettimeofday(&now, NULL);
    373 	(void) mutex_lock(&dns_msgid_mtx);
    374 	dns_msgid = (0xffff & (now.tv_sec ^ now.tv_usec ^ getpid()));
    375 	(void) mutex_unlock(&dns_msgid_mtx);
    376 }
    377 
    378 static int
    379 dyndns_get_msgid(void)
    380 {
    381 	uint16_t id;
    382 
    383 	(void) mutex_lock(&dns_msgid_mtx);
    384 	id = ++dns_msgid;
    385 	(void) mutex_unlock(&dns_msgid_mtx);
    386 	return (id);
    387 }
    388 
    389 /*
    390  * Log a DNS error message
    391  */
    392 static void
    393 dyndns_syslog(int severity, int errnum, const char *text)
    394 {
    395 	struct {
    396 		int errnum;
    397 		char *errmsg;
    398 	} errtab[] = {
    399 		{ FORMERR,  "message format error" },
    400 		{ SERVFAIL, "server internal error" },
    401 		{ NXDOMAIN, "entry should exist but does not exist" },
    402 		{ NOTIMP,   "not supported" },
    403 		{ REFUSED,  "operation refused" },
    404 		{ YXDOMAIN, "entry should not exist but does exist" },
    405 		{ YXRRSET,  "RRSet should not exist but does exist" },
    406 		{ NXRRSET,  "RRSet should exist but does not exist" },
    407 		{ NOTAUTH,  "server is not authoritative for specified zone" },
    408 		{ NOTZONE,  "name not within specified zone" },
    409 		{ BADSIG,   "bad transaction signature (TSIG)" },
    410 		{ BADKEY,   "bad transaction key (TKEY)" },
    411 		{ BADTIME,  "time not synchronized" },
    412 	};
    413 
    414 	char *errmsg = "unknown error";
    415 	int i;
    416 
    417 	if (errnum == NOERROR)
    418 		return;
    419 
    420 	for (i = 0; i < (sizeof (errtab) / sizeof (errtab[0])); ++i) {
    421 		if (errtab[i].errnum == errnum) {
    422 			errmsg = errtab[i].errmsg;
    423 			break;
    424 		}
    425 	}
    426 
    427 	syslog(severity, "dyndns: %s: %s: %d", text, errmsg, errnum);
    428 }
    429 
    430 /*
    431  * display_stat
    432  * Display GSS error message from error code.  This routine is used to display
    433  * the mechanism independent and mechanism specific error messages for GSS
    434  * routines.  The major status error code is the mechanism independent error
    435  * code and the minor status error code is the mechanism specific error code.
    436  * Parameters:
    437  *   maj: GSS major status
    438  *   min: GSS minor status
    439  * Returns:
    440  *   None
    441  */
    442 static void
    443 display_stat(OM_uint32 maj, OM_uint32 min)
    444 {
    445 	gss_buffer_desc msg;
    446 	OM_uint32 msg_ctx = 0;
    447 	OM_uint32 min2;
    448 
    449 	(void) gss_display_status(&min2, maj, GSS_C_GSS_CODE, GSS_C_NULL_OID,
    450 	    &msg_ctx, &msg);
    451 	syslog(LOG_ERR, "dyndns: GSS major status error: %s",
    452 	    (char *)msg.value);
    453 	(void) gss_release_buffer(&min2, &msg);
    454 
    455 	(void) gss_display_status(&min2, min, GSS_C_MECH_CODE, GSS_C_NULL_OID,
    456 	    &msg_ctx, &msg);
    457 	syslog(LOG_ERR, "dyndns: GSS minor status error: %s",
    458 	    (char *)msg.value);
    459 	(void) gss_release_buffer(&min2, &msg);
    460 }
    461 
    462 static char *
    463 dyndns_put_nshort(char *buf, uint16_t val)
    464 {
    465 	uint16_t nval;
    466 
    467 	nval = htons(val);
    468 	(void) memcpy(buf, &nval, sizeof (uint16_t));
    469 	buf += sizeof (uint16_t);
    470 	return (buf);
    471 }
    472 
    473 static char *
    474 dyndns_get_nshort(char *buf, uint16_t *val)
    475 {
    476 	uint16_t nval;
    477 
    478 	(void) memcpy(&nval, buf, sizeof (uint16_t));
    479 	*val = ntohs(nval);
    480 	buf += sizeof (uint16_t);
    481 	return (buf);
    482 }
    483 
    484 static char *
    485 dyndns_put_nlong(char *buf, uint32_t val)
    486 {
    487 	uint32_t lval;
    488 
    489 	lval = htonl(val);
    490 	(void) memcpy(buf, &lval, sizeof (uint32_t));
    491 	buf += sizeof (uint32_t);
    492 	return (buf);
    493 }
    494 
    495 static char *
    496 dyndns_put_byte(char *buf, char val)
    497 {
    498 	*buf = val;
    499 	buf++;
    500 	return (buf);
    501 }
    502 
    503 
    504 
    505 
    506 static char *
    507 dyndns_put_int(char *buf, int val)
    508 {
    509 	(void) memcpy(buf, &val, sizeof (int));
    510 	buf += sizeof (int);
    511 	return (buf);
    512 }
    513 
    514 static char *
    515 dyndns_put_v6addr(char *buf, smb_inaddr_t *val)
    516 {
    517 
    518 	val->a_family = AF_INET6;
    519 	(void) memcpy(buf, &val->a_ipv6, IN6ADDRSZ);
    520 	buf += IN6ADDRSZ;
    521 	return (buf);
    522 }
    523 /*
    524  * dyndns_stuff_str
    525  * Converts a domain string by removing periods and replacing with a byte value
    526  * of how many characters following period.  A byte value is placed in front
    527  * to indicate how many characters before first period.  A NULL character is
    528  * placed at the end. i.e. host.procom.com -> 4host5procom3com0
    529  * Buffer space checking is done by caller.
    530  * Parameters:
    531  *   ptr : address of pointer to buffer to store converted string
    532  *   zone: domain name string
    533  * Returns:
    534  *   ptr: address of pointer to next available buffer space
    535  *   -1 : error
    536  *    0 : success
    537  */
    538 static int
    539 dyndns_stuff_str(char **ptr, char *zone)
    540 {
    541 	int len;
    542 	char *lenPtr, *zonePtr;
    543 
    544 	for (zonePtr = zone; *zonePtr; ) {
    545 		lenPtr = *ptr;
    546 		*ptr = *ptr + 1;
    547 		len = 0;
    548 		while (*zonePtr != '.' && *zonePtr != 0) {
    549 			*ptr = dyndns_put_byte(*ptr, *zonePtr);
    550 			zonePtr++;
    551 			len++;
    552 		}
    553 		*lenPtr = len;
    554 		if (*zonePtr == '.')
    555 			zonePtr++;
    556 	}
    557 	*ptr = dyndns_put_byte(*ptr, 0);
    558 	return (0);
    559 }
    560 
    561 /*
    562  * dyndns_build_header
    563  * Build the header for DNS query and DNS update request message.
    564  * Parameters:
    565  *   ptr               : address of pointer to buffer to store header
    566  *   buf_len           : buffer length
    567  *   msg_id            : message id
    568  *   query_req         : use REQ_QUERY for query message or REQ_UPDATE for
    569  *                       update message
    570  *   quest_zone_cnt    : number of question record for query message or
    571  *                       number of zone record for update message
    572  *   ans_prereq_cnt    : number of answer record for query message or
    573  *                       number of prerequisite record for update message
    574  *   nameser_update_cnt: number of name server for query message or
    575  *                       number of update record for update message
    576  *   addit_cnt         : number of additional record
    577  *   flags             : query flags word
    578  * Returns:
    579  *   ptr: address of pointer to next available buffer space
    580  *   -1 : error
    581  *    0 : success
    582  */
    583 static int
    584 dyndns_build_header(char **ptr, int buf_len, uint16_t msg_id, int query_req,
    585     uint16_t quest_zone_cnt, uint16_t ans_prereq_cnt,
    586     uint16_t nameser_update_cnt, uint16_t addit_cnt, int flags)
    587 {
    588 	uint16_t opcode;
    589 
    590 	if (buf_len < 12) {
    591 		syslog(LOG_ERR, "dyndns header section: buffer too small");
    592 		return (-1);
    593 	}
    594 
    595 	*ptr = dyndns_put_nshort(*ptr, msg_id);	/* mesg ID */
    596 	if (query_req == REQ_QUERY)
    597 		opcode = ns_o_query;	/* query msg */
    598 	else
    599 		opcode = ns_o_update << 11;	/* update msg */
    600 	opcode |= flags;
    601 	/* mesg opcode */
    602 	*ptr = dyndns_put_nshort(*ptr, opcode);
    603 	/* zone record count */
    604 	*ptr = dyndns_put_nshort(*ptr, quest_zone_cnt);
    605 	/* prerequiste record count */
    606 	*ptr = dyndns_put_nshort(*ptr, ans_prereq_cnt);
    607 	/* update record count */
    608 	*ptr = dyndns_put_nshort(*ptr, nameser_update_cnt);
    609 	/* additional record count */
    610 	*ptr = dyndns_put_nshort(*ptr, addit_cnt);
    611 
    612 	return (0);
    613 }
    614 
    615 /*
    616  * dyndns_build_quest_zone
    617  * Build the question section for query message or zone section for
    618  * update message.
    619  * Parameters:
    620  *   ptr    : address of pointer to buffer to store question or zone section
    621  *   buf_len: buffer length
    622  *   name   : question or zone name
    623  *   type   : type of question or zone
    624  *   class  : class of question or zone
    625  * Returns:
    626  *   ptr: address of pointer to next available buffer space
    627  *   -1 : error
    628  *    0 : success
    629  */
    630 static int
    631 dyndns_build_quest_zone(char **ptr, int buf_len, char *name, int type,
    632 	int class)
    633 {
    634 	char *zonePtr;
    635 
    636 	if ((strlen(name) + 6) > buf_len) {
    637 		syslog(LOG_ERR, "dyndns question section: buffer too small");
    638 		return (-1);
    639 	}
    640 
    641 	zonePtr = *ptr;
    642 	(void) dyndns_stuff_str(&zonePtr, name);
    643 	*ptr = zonePtr;
    644 	*ptr = dyndns_put_nshort(*ptr, type);
    645 	*ptr = dyndns_put_nshort(*ptr, class);
    646 	return (0);
    647 }
    648 
    649 /*
    650  * dyndns_build_update
    651  * Build update section of update message for adding and removing a record.
    652  * If the ttl value is 0 then this message is for record deletion.
    653  *
    654  * Parameters:
    655  *   ptr     : address of pointer to buffer to store update section
    656  *   buf_len : buffer length
    657  *   name    : resource name of this record
    658  *   type    : type of this record
    659  *   class   : class of this record
    660  *   ttl     : time-to-live, cached time of this entry by others and not
    661  *             within DNS database, a zero value for record(s) deletion
    662  *   data    : data of this resource record
    663  *   forw_rev: UPDATE_FORW for forward zone, UPDATE_REV for reverse zone
    664  *   add_del : UPDATE_ADD for adding entry, UPDATE_DEL for removing zone
    665  *   del_type: DEL_ONE for deleting one entry, DEL_ALL for deleting all
    666  *             entries of the same resource name.  Only valid for UPDATE_DEL.
    667  * Returns:
    668  *   ptr: address of pointer to next available buffer space
    669  *   -1 : error
    670  *    0 : success
    671  */
    672 static int
    673 dyndns_build_update(char **ptr, int buf_len, char *name, int type, int class,
    674 	uint32_t ttl, char *data, int forw_rev, int add_del, int del_type)
    675 {
    676 	char *namePtr;
    677 	int rec_len, data_len;
    678 	smb_inaddr_t ipaddr;
    679 	int isv4 = 1;
    680 
    681 	rec_len = strlen(name) + 10;
    682 	if (inet_pton(AF_INET, data, &ipaddr) == 1)
    683 		isv4 = 1;
    684 	else if (inet_pton(AF_INET6, data, &ipaddr) == 1)
    685 		isv4 = 0;
    686 
    687 	if (add_del == UPDATE_ADD) {
    688 		if (forw_rev == UPDATE_FORW)
    689 			data_len = isv4 ? 4 : 16;
    690 		else
    691 			data_len = strlen(data) + 2;
    692 	} else {
    693 		if (del_type == DEL_ALL)
    694 			data_len = 0;
    695 		else if (forw_rev == UPDATE_FORW)
    696 			data_len = isv4 ? 4 : 16;
    697 		else
    698 			data_len = strlen(data) + 2;
    699 	}
    700 	if (rec_len + data_len > buf_len) {
    701 		syslog(LOG_ERR, "dyndns update section: buffer too small");
    702 		return (-1);
    703 	}
    704 
    705 	namePtr = *ptr;
    706 	(void) dyndns_stuff_str(&namePtr, name);
    707 	*ptr = namePtr;
    708 	if (isv4)
    709 		*ptr = dyndns_put_nshort(*ptr, type);
    710 	else
    711 		*ptr = dyndns_put_nshort(*ptr, ns_t_aaaa);
    712 	*ptr = dyndns_put_nshort(*ptr, class);
    713 	*ptr = dyndns_put_nlong(*ptr, ttl);
    714 
    715 	if (add_del == UPDATE_DEL && del_type == DEL_ALL) {
    716 		*ptr = dyndns_put_nshort(*ptr, 0);
    717 		return (0);
    718 	}
    719 
    720 	if (forw_rev == UPDATE_FORW) {
    721 		if (isv4) {
    722 			*ptr = dyndns_put_nshort(*ptr, 4);
    723 			*ptr = dyndns_put_int(*ptr, ipaddr.a_ipv4);
    724 		} else {
    725 			*ptr = dyndns_put_nshort(*ptr, 16);
    726 			*ptr = dyndns_put_v6addr(*ptr, &ipaddr);
    727 		}
    728 	} else {
    729 		*ptr = dyndns_put_nshort(*ptr, strlen(data)+2);
    730 		namePtr = *ptr;
    731 		(void) dyndns_stuff_str(&namePtr, data);	/* hostname */
    732 		*ptr = namePtr;
    733 	}
    734 	return (0);
    735 }
    736 
    737 /*
    738  * dyndns_build_tkey
    739  * Build TKEY section to establish security context for secure dynamic DNS
    740  * update.  DNS header and question sections need to be build before this
    741  * section.  The TKEY data are the tokens generated during security context
    742  * establishment and the TKEY message is used to transmit those tokens, one
    743  * at a time, to the DNS server.
    744  * Parameters:
    745  *   ptr       : address of pointer to buffer to store TKEY
    746  *   buf_len   : buffer length
    747  *   name      : key name, must be unique and same as for TSIG record
    748  *   key_expire: expiration time of this key in second
    749  *   data      : TKEY data
    750  *   data_size : data size
    751  * Returns:
    752  *   ptr: address of the pointer to the next available buffer space
    753  *   -1 : error
    754  *    0 : success
    755  */
    756 static int
    757 dyndns_build_tkey(char **ptr, int buf_len, char *name, int key_expire,
    758 	char *data, int data_size)
    759 {
    760 	char *namePtr;
    761 	struct timeval tp;
    762 
    763 	if (strlen(name)+2 + 45 + data_size > buf_len) {
    764 		syslog(LOG_ERR, "dyndns TKEY: buffer too small");
    765 		return (-1);
    766 	}
    767 
    768 	namePtr = *ptr;
    769 	(void) dyndns_stuff_str(&namePtr, name);	/* unique global name */
    770 	*ptr = namePtr;
    771 	*ptr = dyndns_put_nshort(*ptr, ns_t_tkey);
    772 	*ptr = dyndns_put_nshort(*ptr, ns_c_any);
    773 	*ptr = dyndns_put_nlong(*ptr, 0);
    774 	/* 19 + 14 + data_size + 2 */
    775 	*ptr = dyndns_put_nshort(*ptr, 35 + data_size);
    776 	namePtr = *ptr;
    777 	(void) dyndns_stuff_str(&namePtr, "gss.microsoft.com");
    778 	*ptr = namePtr;
    779 	(void) gettimeofday(&tp, 0);
    780 	*ptr = dyndns_put_nlong(*ptr, tp.tv_sec);	/* inception */
    781 	/* expiration, 86400 */
    782 	*ptr = dyndns_put_nlong(*ptr, tp.tv_sec + key_expire);
    783 	*ptr = dyndns_put_nshort(*ptr, MODE_GSS_API);	/* mode: gss-api */
    784 	*ptr = dyndns_put_nshort(*ptr, 0);		/* error */
    785 	*ptr = dyndns_put_nshort(*ptr, data_size);	/* key size */
    786 	(void) memcpy(*ptr, data, data_size);	/* key data */
    787 	*ptr += data_size;
    788 	*ptr = dyndns_put_nshort(*ptr, 0);	/* other */
    789 	return (0);
    790 }
    791 
    792 /*
    793  * dyndns_build_tsig
    794  * Build TSIG section for secure dynamic DNS update.  This routine will be
    795  * called twice.  First called with TSIG_UNSIGNED, and second with TSIG_SIGNED.
    796  * The TSIG data is NULL and ignored for TSIG_UNSIGNED and is the update request
    797  * message encrypted for TSIG_SIGNED.  The message id must be the same id as the
    798  * one in the update request before it is encrypted.
    799  * Parameters:
    800  *   ptr        : address of pointer to buffer to store TSIG
    801  *   buf_len    : buffer length
    802  *   msg_id     : message id
    803  *   name       : key name, must be the same as in TKEY record
    804  *   fudge_time : amount of error time allow in seconds
    805  *   data       : TSIG data if TSIG_SIGNED, otherwise NULL
    806  *   data_size  : size of data, otherwise 0 if data is NULL
    807  *   data_signed: TSIG_SIGNED to indicate data is signed and encrypted,
    808  *                otherwise TSIG_UNSIGNED
    809  * Returns:
    810  *   ptr: address of pointer to next available buffer space
    811  *   -1 : error
    812  *    0 : success
    813  */
    814 static int
    815 dyndns_build_tsig(char **ptr, int buf_len, int msg_id, char *name,
    816 	int fudge_time, char *data, int data_size, int data_signed)
    817 {
    818 	char *namePtr;
    819 	struct timeval tp;
    820 	int signtime, fudge, rec_len;
    821 
    822 	if (data_signed == TSIG_UNSIGNED)
    823 		rec_len = strlen(name)+2 + 37;
    824 	else
    825 		rec_len = strlen(name)+2 + 45 + data_size;
    826 
    827 	if (rec_len > buf_len) {
    828 		syslog(LOG_ERR, "dyndns TSIG: buffer too small");
    829 		return (-1);
    830 	}
    831 
    832 	namePtr = *ptr;
    833 	(void) dyndns_stuff_str(&namePtr, name);	/* unique global name */
    834 	*ptr = namePtr;
    835 	if (data_signed == TSIG_SIGNED)
    836 		*ptr = dyndns_put_nshort(*ptr, ns_t_tsig);
    837 	*ptr = dyndns_put_nshort(*ptr, ns_c_any);
    838 	*ptr = dyndns_put_nlong(*ptr, 0);
    839 	if (data_signed == TSIG_SIGNED) {
    840 		/* 19 + 10 + data_size + 6 */
    841 		*ptr = dyndns_put_nshort(*ptr, 35 + data_size);
    842 	}
    843 	namePtr = *ptr;
    844 	(void) dyndns_stuff_str(&namePtr, "gss.microsoft.com");
    845 	*ptr = namePtr;
    846 	(void) gettimeofday(&tp, 0);
    847 	signtime = tp.tv_sec >> 16;
    848 	*ptr = dyndns_put_nlong(*ptr, signtime);	/* sign time */
    849 	fudge = tp.tv_sec << 16;
    850 	fudge |= fudge_time;
    851 	*ptr = dyndns_put_nlong(*ptr, fudge);	/* fudge time */
    852 	if (data_signed == TSIG_SIGNED) {
    853 		/* signed data size */
    854 		*ptr = dyndns_put_nshort(*ptr, data_size);
    855 		(void) memcpy(*ptr, data, data_size);	/* signed data */
    856 		*ptr += data_size;
    857 		*ptr = dyndns_put_nshort(*ptr, msg_id);	/* original id */
    858 	}
    859 	*ptr = dyndns_put_nshort(*ptr, 0);	/* error */
    860 	*ptr = dyndns_put_nshort(*ptr, 0);	/* other */
    861 	return (0);
    862 }
    863 
    864 /*
    865  * dyndns_open_init_socket
    866  * This routine creates a SOCK_STREAM or SOCK_DGRAM socket and initializes it
    867  * by doing bind() and setting linger option to off.
    868  *
    869  * Parameters:
    870  *   sock_type: SOCK_STREAM for TCP or SOCK_DGRAM for UDP
    871  *   dest_addr: destination address in network byte order
    872  *   port     : destination port number
    873  * Returns:
    874  *   descriptor: descriptor referencing the created socket
    875  *   -1        : error
    876  */
    877 
    878 static int
    879 dyndns_open_init_socket(int sock_type, smb_inaddr_t *dest_addr, int port)
    880 {
    881 	int s;
    882 	struct sockaddr_in my_addr;
    883 	struct sockaddr_in6 my6_addr;
    884 	struct sockaddr_in serv_addr;
    885 	struct sockaddr_in6 serv6_addr;
    886 	int family;
    887 
    888 	family = dest_addr->a_family;
    889 
    890 	if ((s = socket(family, sock_type, 0)) == -1) {
    891 		syslog(LOG_ERR, "dyndns: socket error\n");
    892 		return (-1);
    893 	}
    894 	if (family == AF_INET) {
    895 		bzero(&my_addr, sizeof (my_addr));
    896 		my_addr.sin_family = family;
    897 		my_addr.sin_port = htons(0);
    898 		my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    899 		if (bind(s, (struct sockaddr *)&my_addr,
    900 		    sizeof (my_addr)) < 0) {
    901 			syslog(LOG_ERR, "dyndns: client bind err\n");
    902 			(void) close(s);
    903 			return (-1);
    904 		}
    905 		serv_addr.sin_family = family;
    906 		serv_addr.sin_port = htons(port);
    907 		serv_addr.sin_addr.s_addr = dest_addr->a_ipv4;
    908 		if (connect(s, (struct sockaddr *)&serv_addr,
    909 		    sizeof (struct sockaddr_in)) < 0) {
    910 			syslog(LOG_ERR, "dyndns: client connect (%s)\n",
    911 			    strerror(errno));
    912 			(void) close(s);
    913 			return (-1);
    914 		}
    915 	} else {
    916 		bzero(&my6_addr, sizeof (my6_addr));
    917 		my6_addr.sin6_family = family;
    918 		my6_addr.sin6_port = htons(0);
    919 		bzero(&my6_addr.sin6_addr.s6_addr, IN6ADDRSZ);
    920 		if (bind(s, (struct sockaddr *)&my6_addr,
    921 		    sizeof (my6_addr)) < 0) {
    922 			syslog(LOG_ERR, "dyndns: client bind err\n");
    923 			(void) close(s);
    924 			return (-1);
    925 		}
    926 		serv6_addr.sin6_family = family;
    927 		serv6_addr.sin6_port = htons(port);
    928 		bcopy(&serv6_addr.sin6_addr.s6_addr, &dest_addr->a_ipv6,
    929 		    IN6ADDRSZ);
    930 		if (connect(s, (struct sockaddr *)&serv6_addr,
    931 		    sizeof (struct sockaddr_in6)) < 0) {
    932 			syslog(LOG_ERR, "dyndns: client connect err (%s)\n",
    933 			    strerror(errno));
    934 			(void) close(s);
    935 			return (-1);
    936 		}
    937 	}
    938 	return (s);
    939 }
    940 /*
    941  * dyndns_build_tkey_msg
    942  * This routine is used to build the TKEY message to transmit GSS tokens
    943  * during GSS security context establishment for secure DNS update.  The
    944  * TKEY message format uses the DNS query message format.  The TKEY section
    945  * is the answer section of the query message format.
    946  * Microsoft uses a value of 86400 seconds (24 hours) for key expiration time.
    947  * Parameters:
    948  *   buf     : buffer to build and store TKEY message
    949  *   key_name: a unique key name, this same key name must be also be used in
    950  *             the TSIG message
    951  *   out_tok : TKEY message data (GSS tokens)
    952  * Returns:
    953  *   id          : message id of this TKEY message
    954  *   message size: the size of the TKEY message
    955  *   -1          : error
    956  */
    957 static int
    958 dyndns_build_tkey_msg(char *buf, char *key_name, uint16_t *id,
    959 	gss_buffer_desc *out_tok)
    960 {
    961 	int queryReq, zoneCount, preqCount, updateCount, additionalCount;
    962 	int zoneType, zoneClass;
    963 	char *bufptr;
    964 
    965 	queryReq = REQ_QUERY;
    966 	/* query section of query request */
    967 	zoneCount = 1;
    968 	/* answer section of query request */
    969 	preqCount = 1;
    970 	updateCount = 0;
    971 	additionalCount = 0;
    972 
    973 	(void) memset(buf, 0, MAX_TCP_SIZE);
    974 	bufptr = buf;
    975 	*id = dyndns_get_msgid();
    976 
    977 	/* add TCP length info that follows this field */
    978 	bufptr = dyndns_put_nshort(bufptr,
    979 	    26 + (strlen(key_name)+2)*2 + 35 + out_tok->length);
    980 
    981 	if (dyndns_build_header(&bufptr, BUFLEN_TCP(bufptr, buf), *id, queryReq,
    982 	    zoneCount, preqCount, updateCount, additionalCount, 0) == -1) {
    983 		return (-1);
    984 	}
    985 
    986 	zoneType = ns_t_tkey;
    987 	zoneClass = ns_c_in;
    988 	if (dyndns_build_quest_zone(&bufptr, BUFLEN_TCP(bufptr, buf), key_name,
    989 	    zoneType, zoneClass) == -1) {
    990 		return (-1);
    991 	}
    992 
    993 	if (dyndns_build_tkey(&bufptr, BUFLEN_TCP(bufptr, buf), key_name,
    994 	    86400, out_tok->value, out_tok->length) == -1) {
    995 		return (-1);
    996 	}
    997 
    998 	return (bufptr - buf);
    999 }
   1000 
   1001 /*
   1002  * dyndns_establish_sec_ctx
   1003  * This routine is used to establish a security context with the DNS server
   1004  * by building TKEY messages and sending them to the DNS server.  TKEY messages
   1005  * are also received from the DNS server for processing.   The security context
   1006  * establishment is done with the GSS client on the system producing a token
   1007  * and sending the token within the TKEY message to the GSS server on the DNS
   1008  * server.  The GSS server then processes the token and then send a TKEY reply
   1009  * message with a new token to be processed by the GSS client.  The GSS client
   1010  * processes the new token and then generates a new token to be sent to the
   1011  * GSS server.  This cycle is continued until the security establishment is
   1012  * done.  TCP is used to send and receive TKEY messages.
   1013  * Parameters:
   1014  *   cred_handle  : handle to credential
   1015  *   s           : socket descriptor to DNS server
   1016  *   key_name    : TKEY key name
   1017  *   dns_hostname: fully qualified DNS hostname
   1018  *   oid         : contains Kerberos 5 object identifier
   1019  * Returns:
   1020  *   gss_context    : handle to security context
   1021  */
   1022 static int
   1023 dyndns_establish_sec_ctx(gss_ctx_id_t *gss_context, gss_cred_id_t cred_handle,
   1024     int s, char *key_name, char *dns_hostname, gss_OID oid)
   1025 {
   1026 	uint16_t id, rid, rsz;
   1027 	char buf[MAX_TCP_SIZE], buf2[MAX_TCP_SIZE];
   1028 	int ret;
   1029 	char *service_name, *tmpptr;
   1030 	int service_sz;
   1031 	OM_uint32 min, maj, time_rec;
   1032 	gss_buffer_desc service_buf, in_tok, out_tok;
   1033 	gss_name_t target_name;
   1034 	gss_buffer_desc *inputptr;
   1035 	int gss_flags;
   1036 	OM_uint32 ret_flags;
   1037 	int buf_sz;
   1038 
   1039 	service_sz = strlen(dns_hostname) + 5;
   1040 	service_name = (char *)malloc(sizeof (char) * service_sz);
   1041 	if (service_name == NULL)
   1042 		return (-1);
   1043 
   1044 	(void) snprintf(service_name, service_sz, "DNS@%s", dns_hostname);
   1045 	service_buf.value = service_name;
   1046 	service_buf.length = strlen(service_name)+1;
   1047 	if ((maj = gss_import_name(&min, &service_buf,
   1048 	    GSS_C_NT_HOSTBASED_SERVICE, &target_name)) != GSS_S_COMPLETE) {
   1049 		display_stat(maj, min);
   1050 		(void) free(service_name);
   1051 		return (-1);
   1052 	}
   1053 	(void) free(service_name);
   1054 
   1055 	inputptr = GSS_C_NO_BUFFER;
   1056 	*gss_context = GSS_C_NO_CONTEXT;
   1057 	gss_flags = GSS_C_MUTUAL_FLAG | GSS_C_DELEG_FLAG | GSS_C_REPLAY_FLAG |
   1058 	    GSS_C_SEQUENCE_FLAG | GSS_C_CONF_FLAG | GSS_C_INTEG_FLAG;
   1059 	do {
   1060 		maj = gss_init_sec_context(&min, cred_handle, gss_context,
   1061 		    target_name, oid, gss_flags, 0, NULL, inputptr, NULL,
   1062 		    &out_tok, &ret_flags, &time_rec);
   1063 
   1064 		if (maj != GSS_S_COMPLETE && maj != GSS_S_CONTINUE_NEEDED) {
   1065 			assert(gss_context);
   1066 			if (*gss_context != GSS_C_NO_CONTEXT)
   1067 				(void) gss_delete_sec_context(&min,
   1068 				    gss_context, NULL);
   1069 
   1070 			display_stat(maj, min);
   1071 			(void) gss_release_name(&min, &target_name);
   1072 			return (-1);
   1073 		}
   1074 
   1075 		if ((maj == GSS_S_COMPLETE) &&
   1076 		    !(ret_flags & GSS_C_REPLAY_FLAG)) {
   1077 			syslog(LOG_ERR, "dyndns: No GSS_C_REPLAY_FLAG");
   1078 			if (out_tok.length > 0)
   1079 				(void) gss_release_buffer(&min, &out_tok);
   1080 			(void) gss_release_name(&min, &target_name);
   1081 			return (-1);
   1082 		}
   1083 
   1084 		if ((maj == GSS_S_COMPLETE) &&
   1085 		    !(ret_flags & GSS_C_MUTUAL_FLAG)) {
   1086 			syslog(LOG_ERR, "dyndns: No GSS_C_MUTUAL_FLAG");
   1087 			if (out_tok.length > 0)
   1088 				(void) gss_release_buffer(&min, &out_tok);
   1089 			(void) gss_release_name(&min, &target_name);
   1090 			return (-1);
   1091 		}
   1092 
   1093 		if (out_tok.length > 0) {
   1094 			if ((buf_sz = dyndns_build_tkey_msg(buf, key_name,
   1095 			    &id, &out_tok)) <= 0) {
   1096 				(void) gss_release_buffer(&min, &out_tok);
   1097 				(void) gss_release_name(&min, &target_name);
   1098 				return (-1);
   1099 			}
   1100 
   1101 			(void) gss_release_buffer(&min, &out_tok);
   1102 
   1103 			if (send(s, buf, buf_sz, 0) == -1) {
   1104 				syslog(LOG_ERR, "dyndns: TKEY send error");
   1105 				(void) gss_release_name(&min, &target_name);
   1106 				return (-1);
   1107 			}
   1108 
   1109 			bzero(buf2, MAX_TCP_SIZE);
   1110 			if (recv(s, buf2, MAX_TCP_SIZE, 0) == -1) {
   1111 				syslog(LOG_ERR, "dyndns: TKEY recv error");
   1112 				(void) gss_release_name(&min, &target_name);
   1113 				return (-1);
   1114 			}
   1115 
   1116 			ret = buf2[5] & 0xf;	/* error field in TCP */
   1117 			if (ret != NOERROR) {
   1118 				dyndns_syslog(LOG_ERR, ret, "TKEY reply");
   1119 				(void) gss_release_name(&min, &target_name);
   1120 				return (-1);
   1121 			}
   1122 
   1123 			tmpptr = &buf2[2];
   1124 			(void) dyndns_get_nshort(tmpptr, &rid);
   1125 			if (id != rid) {
   1126 				(void) gss_release_name(&min, &target_name);
   1127 				return (-1);
   1128 			}
   1129 
   1130 			tmpptr = &buf2[59+(strlen(key_name)+2)*2];
   1131 			(void) dyndns_get_nshort(tmpptr, &rsz);
   1132 			in_tok.length = rsz;
   1133 
   1134 			/* bsd38 -> 2*7=14 */
   1135 			in_tok.value = &buf2[61+(strlen(key_name)+2)*2];
   1136 			inputptr = &in_tok;
   1137 		}
   1138 
   1139 	} while (maj != GSS_S_COMPLETE);
   1140 
   1141 	(void) gss_release_name(&min, &target_name);
   1142 
   1143 	return (0);
   1144 }
   1145 
   1146 /*
   1147  * dyndns_get_sec_context
   1148  * Get security context for secure dynamic DNS update.  This routine opens
   1149  * a TCP socket to the DNS server and establishes a security context with
   1150  * the DNS server using host principal to perform secure dynamic DNS update.
   1151  * Parameters:
   1152  *   hostname: fully qualified hostname
   1153  *   dns_ip  : ip address of hostname in network byte order
   1154  * Returns:
   1155  *   gss_handle: gss credential handle
   1156  *   gss_context: gss security context
   1157  *   -1: error
   1158  *    0: success
   1159  */
   1160 
   1161 static gss_ctx_id_t
   1162 dyndns_get_sec_context(const char *hostname, smb_inaddr_t *dns_ip)
   1163 {
   1164 	int s;
   1165 	gss_cred_id_t cred_handle;
   1166 	gss_ctx_id_t gss_context;
   1167 	gss_OID oid;
   1168 	char *key_name, dns_hostname[MAXHOSTNAMELEN];
   1169 
   1170 	cred_handle = GSS_C_NO_CREDENTIAL;
   1171 	oid = GSS_C_NO_OID;
   1172 	key_name = (char *)hostname;
   1173 
   1174 	if (smb_getnameinfo(dns_ip, dns_hostname,
   1175 	    sizeof (dns_hostname), 0)) {
   1176 		return (NULL);
   1177 	}
   1178 	if ((s = dyndns_open_init_socket(SOCK_STREAM, dns_ip, 53)) < 0) {
   1179 		return (NULL);
   1180 	}
   1181 
   1182 	if (dyndns_establish_sec_ctx(&gss_context, cred_handle, s, key_name,
   1183 	    dns_hostname, oid))
   1184 		gss_context = NULL;
   1185 
   1186 	(void) close(s);
   1187 	return (gss_context);
   1188 }
   1189 
   1190 /*
   1191  * dyndns_build_add_remove_msg
   1192  * This routine builds the update request message for adding and removing DNS
   1193  * entries which is used for non-secure and secure DNS update.
   1194  * This routine builds an UDP message.
   1195  * Parameters:
   1196  *   buf        : buffer to build message
   1197  *   update_zone: the type of zone to update, use UPDATE_FORW for forward
   1198  *                lookup zone, use UPDATE_REV for reverse lookup zone
   1199  *   hostname   : fully qualified hostname to update DNS with
   1200  *   ip_addr    : IP address of hostname
   1201  *   life_time  : cached time of this entry by others and not within DNS
   1202  *                database
   1203  *   update_type: UPDATE_ADD to add entry, UPDATE_DEL to remove entry
   1204  *   del_type   : DEL_ONE for deleting one entry, DEL_ALL for deleting all
   1205  *                entries of the same resource name.  Only valid for UPDATE_DEL.
   1206  *   addit_cnt  : Indicate how many record is in the additional section of
   1207  *                the DNS message.  A value of zero is always used with
   1208  *                non-secure update message. For secure update message,
   1209  *                the value will be one because the signed TSIG message
   1210  *                is added as the additional record of the DNS update message.
   1211  *   id         : DNS message ID.  If a positive value then this ID value is
   1212  *                used, otherwise the next incremented value is used
   1213  *   level      : This is the domain level which we send the request to, level
   1214  *                zero is the default level, it can go upto 2 in reverse zone
   1215  *                and virtually to any level in forward zone.
   1216  * Returns:
   1217  *   buf      : buffer containing update message
   1218  *   id       : DNS message ID
   1219  *   int      : size of update message
   1220  *   -1       : error
   1221  *
   1222  * This function is changed to handle dynamic DNS update retires to higher
   1223  * authoritative domains.
   1224  */
   1225 static int
   1226 dyndns_build_add_remove_msg(char *buf, int update_zone, const char *hostname,
   1227 	const char *ip_addr, int life_time, int update_type, int del_type,
   1228 	int addit_cnt, uint16_t *id, int level)
   1229 {
   1230 	int a, b, c, d;
   1231 	char *bufptr;
   1232 	int queryReq, zoneCount, preqCount, updateCount, additionalCount;
   1233 	char *zone, *resource, *data, zone_buf[100], resrc_buf[100];
   1234 	int zoneType, zoneClass, type, class, ttl;
   1235 	char *p;
   1236 	smb_inaddr_t tmp_addr;
   1237 	int i, j, k;
   1238 	int fourcnt;
   1239 
   1240 	queryReq = REQ_UPDATE;
   1241 	zoneCount = 1;
   1242 	preqCount = 0;
   1243 	updateCount = 1;
   1244 	additionalCount = addit_cnt;
   1245 
   1246 	(void) memset(buf, 0, NS_PACKETSZ);
   1247 	bufptr = buf;
   1248 
   1249 	if (*id == 0)
   1250 		*id = dyndns_get_msgid();
   1251 
   1252 	if (dyndns_build_header(&bufptr, BUFLEN_UDP(bufptr, buf), *id, queryReq,
   1253 	    zoneCount, preqCount, updateCount, additionalCount, 0) == -1) {
   1254 		return (-1);
   1255 	}
   1256 
   1257 	zoneType = ns_t_soa;
   1258 	zoneClass = ns_c_in;
   1259 
   1260 	if (update_zone == UPDATE_FORW) {
   1261 		p = (char *)hostname;
   1262 
   1263 		/* Try higher domains according to the level requested */
   1264 		do {
   1265 			/* domain */
   1266 			if ((zone = (char *)strchr(p, '.')) == NULL)
   1267 				return (-1);
   1268 			zone += 1;
   1269 			p = zone;
   1270 		} while (--level >= 0);
   1271 		resource = (char *)hostname;
   1272 		data = (char *)ip_addr;
   1273 	} else {
   1274 		if (inet_pton(AF_INET, ip_addr, &tmp_addr) == 1) {
   1275 			(void) sscanf(ip_addr, "%d.%d.%d.%d", &a, &b, &c, &d);
   1276 			(void) sprintf(zone_buf, "%d.%d.%d.in-addr.arpa",
   1277 			    c, b, a);
   1278 			zone = p = zone_buf;
   1279 
   1280 			/* Try higher domains based on level requested */
   1281 			while (--level >= 0) {
   1282 				/* domain */
   1283 				if ((zone = (char *)strchr(p, '.')) == NULL) {
   1284 					return (-1);
   1285 				}
   1286 				zone += 1;
   1287 				p = zone;
   1288 			}
   1289 			(void) sprintf(resrc_buf, "%d.%d.%d.%d.in-addr.arpa",
   1290 			    d, c, b, a);
   1291 		} else {
   1292 			/*
   1293 			 * create reverse nibble ipv6 format
   1294 			 */
   1295 			bzero(resrc_buf, 100);
   1296 			i = 0;
   1297 			j = 0;
   1298 			while (ip_addr[i] != 0)
   1299 				i++;
   1300 			i--;
   1301 			while (i >= 0) {
   1302 				fourcnt = 3;
   1303 				while ((i >= 0) && (ip_addr[i] != ':')) {
   1304 					resrc_buf[j++] = ip_addr[i];
   1305 					(void) strcat(&resrc_buf[j++], ".");
   1306 					fourcnt --;
   1307 					i--;
   1308 				}
   1309 				for (k = 0; k <= fourcnt; k++) {
   1310 					resrc_buf[j++] = '0';
   1311 					(void) strcat(&resrc_buf[j++], ".");
   1312 				}
   1313 				i--;
   1314 			}
   1315 			(void) strcat(resrc_buf, "ip6.arpa");
   1316 			(void) strcpy(zone_buf, &resrc_buf[32]);
   1317 			zone = zone_buf;
   1318 		}
   1319 		resource = resrc_buf;	/* ip info */
   1320 		data = (char *)hostname;
   1321 	}
   1322 	if (dyndns_build_quest_zone(&bufptr, BUFLEN_UDP(bufptr, buf), zone,
   1323 	    zoneType, zoneClass) == -1) {
   1324 		return (-1);
   1325 	}
   1326 
   1327 	if (update_zone == UPDATE_FORW)
   1328 		type = ns_t_a;
   1329 	else
   1330 		type = ns_t_ptr;
   1331 
   1332 	if (update_type == UPDATE_ADD) {
   1333 		class = ns_c_in;
   1334 		ttl = life_time;
   1335 	} else {
   1336 		if (del_type == DEL_ONE)
   1337 			class = ns_c_none;	/* remove one */
   1338 		else
   1339 			class = ns_c_any;	/* remove all */
   1340 		ttl = 0;
   1341 	}
   1342 	if (dyndns_build_update(&bufptr, BUFLEN_UDP(bufptr, buf),
   1343 	    resource, type, class, ttl, data, update_zone,
   1344 	    update_type, del_type) == -1) {
   1345 		return (-1);
   1346 	}
   1347 
   1348 	return (bufptr - buf);
   1349 }
   1350 
   1351 /*
   1352  * dyndns_build_unsigned_tsig_msg
   1353  * This routine is used to build the unsigned TSIG message for signing.  The
   1354  * unsigned TSIG message contains the update request message with certain TSIG
   1355  * fields included.  An error time of 300 seconds is used for fudge time.  This
   1356  * is the number used by Microsoft clients.
   1357  * This routine builds a UDP message.
   1358  * Parameters:
   1359  *   buf        : buffer to build message
   1360  *   update_zone: the type of zone to update, use UPDATE_FORW for forward
   1361  *                lookup zone, use UPDATE_REV for reverse lookup zone
   1362  *   hostname   : fully qualified hostname to update DNS with
   1363  *   ip_addr    : IP address of hostname
   1364  *   life_time  : cached time of this entry by others and not within DNS
   1365  *                database
   1366  *   update_type: UPDATE_ADD to add entry, UPDATE_DEL to remove entry
   1367  *   del_type   : DEL_ONE for deleting one entry, DEL_ALL for deleting all
   1368  *                entries of the same resource name.  Only valid for UPDATE_DEL.
   1369  *   key_name   : same key name used in TKEY message
   1370  *   id         : DNS message ID.  If a positive value then this ID value is
   1371  *                used, otherwise the next incremented value is used
   1372  *   level      : This is the domain level which we send the request to, level
   1373  *                zero is the default level, it can go upto 2 in reverse zone
   1374  *                and virtually to any level in forward zone.
   1375  * Returns:
   1376  *   buf      : buffer containing update message
   1377  *   id       : DNS message ID
   1378  *   int      : size of update message
   1379  *   -1       : error
   1380  */
   1381 static int
   1382 dyndns_build_unsigned_tsig_msg(char *buf, int update_zone, const char *hostname,
   1383 	const char *ip_addr, int life_time, int update_type, int del_type,
   1384 	char *key_name, uint16_t *id, int level)
   1385 {
   1386 	char *bufptr;
   1387 	int buf_sz;
   1388 
   1389 	if ((buf_sz = dyndns_build_add_remove_msg(buf, update_zone, hostname,
   1390 	    ip_addr, life_time, update_type, del_type, 0, id, level)) <= 0) {
   1391 		return (-1);
   1392 	}
   1393 
   1394 	bufptr = buf + buf_sz;
   1395 
   1396 	if (dyndns_build_tsig(&bufptr, BUFLEN_UDP(bufptr, buf), 0,
   1397 	    key_name, 300, NULL, 0, TSIG_UNSIGNED) == -1) {
   1398 		return (-1);
   1399 	}
   1400 
   1401 	return (bufptr - buf);
   1402 }
   1403 
   1404 /*
   1405  * dyndns_build_signed_tsig_msg
   1406  * This routine build the signed TSIG message which contains the update
   1407  * request message encrypted.  An error time of 300 seconds is used for fudge
   1408  * time.  This is the number used by Microsoft clients.
   1409  * This routine builds a UDP message.
   1410  * Parameters:
   1411  *   buf        : buffer to build message
   1412  *   update_zone: the type of zone to update, use UPDATE_FORW for forward
   1413  *                lookup zone, use UPDATE_REV for reverse lookup zone
   1414  *   hostname   : fully qualified hostname to update DNS with
   1415  *   ip_addr    : IP address of hostname
   1416  *   life_time  : cached time of this entry by others and not within DNS
   1417  *                database
   1418  *   update_type: UPDATE_ADD to add entry, UPDATE_DEL to remove entry
   1419  *   del_type   : DEL_ONE for deleting one entry, DEL_ALL for deleting all
   1420  *                entries of the same resource name.  Only valid for UPDATE_DEL.
   1421  *   key_name   : same key name used in TKEY message
   1422  *   id         : DNS message ID.  If a positive value then this ID value is
   1423  *                used, otherwise the next incremented value is used
   1424  *   in_mic     : the update request message encrypted
   1425  *   level      : This is the domain level which we send the request to, level
   1426  *                zero is the default level, it can go upto 2 in reverse zone
   1427  *                and virtually to any level in forward zone.
   1428  *
   1429  * Returns:
   1430  *   buf      : buffer containing update message
   1431  *   id       : DNS message ID
   1432  *   int      : size of update message
   1433  *   -1       : error
   1434  */
   1435 static int
   1436 dyndns_build_signed_tsig_msg(char *buf, int update_zone, const char *hostname,
   1437 	const char *ip_addr, int life_time, int update_type, int del_type,
   1438 	char *key_name, uint16_t *id, gss_buffer_desc *in_mic, int level)
   1439 {
   1440 	char *bufptr;
   1441 	int buf_sz;
   1442 
   1443 	if ((buf_sz = dyndns_build_add_remove_msg(buf, update_zone, hostname,
   1444 	    ip_addr, life_time, update_type, del_type, 1, id, level)) <= 0) {
   1445 		return (-1);
   1446 	}
   1447 
   1448 	bufptr = buf + buf_sz;
   1449 
   1450 	if (dyndns_build_tsig(&bufptr, BUFLEN_UDP(bufptr, buf),
   1451 	    *id, key_name, 300, in_mic->value,
   1452 	    in_mic->length, TSIG_SIGNED) == -1) {
   1453 		return (-1);
   1454 	}
   1455 
   1456 	return (bufptr - buf);
   1457 }
   1458 
   1459 /*
   1460  * dyndns_udp_send_recv
   1461  * This routine sends and receives UDP DNS request and reply messages.
   1462  *
   1463  * Pre-condition: Caller must call dyndns_open_init_socket() before calling
   1464  * this function.
   1465  *
   1466  * Parameters:
   1467  *   s        : socket descriptor
   1468  *   buf      : buffer containing data to send
   1469  *   buf_sz   : size of data to send
   1470  * Returns:
   1471  *   -1     : error
   1472  *   rec_buf: reply dat
   1473  *    0     : success
   1474  */
   1475 
   1476 static int
   1477 dyndns_udp_send_recv(int s, char *buf, int buf_sz, char *rec_buf)
   1478 {
   1479 	int i, retval, addr_len;
   1480 	struct timeval tv, timeout;
   1481 	fd_set rfds;
   1482 	struct sockaddr_in6 from_addr;
   1483 
   1484 	timeout.tv_usec = 0;
   1485 	timeout.tv_sec = DYNDNS_QUERY_TIMEOUT;
   1486 
   1487 	for (i = 0; i <= DYNDNS_MAX_QUERY_RETRIES; i++) {
   1488 		if (send(s, buf, buf_sz, 0) == -1) {
   1489 			syslog(LOG_ERR, "dyndns: UDP send error (%s)",
   1490 			    strerror(errno));
   1491 			return (-1);
   1492 		}
   1493 
   1494 		FD_ZERO(&rfds);
   1495 		FD_SET(s, &rfds);
   1496 
   1497 		tv = timeout;
   1498 
   1499 		retval = select(s+1, &rfds, NULL, NULL, &tv);
   1500 
   1501 		if (retval == -1) {
   1502 			return (-1);
   1503 		} else if (retval > 0) {
   1504 			bzero(rec_buf, NS_PACKETSZ);
   1505 			addr_len = sizeof (struct sockaddr_in6);
   1506 			if (recvfrom(s, rec_buf, NS_PACKETSZ, 0,
   1507 			    (struct sockaddr *)&from_addr, &addr_len) == -1) {
   1508 				syslog(LOG_ERR, "dyndns: UDP recv error ");
   1509 				return (-1);
   1510 			}
   1511 			break;
   1512 		}
   1513 	}
   1514 
   1515 	/* did not receive anything */
   1516 	if (i == (DYNDNS_MAX_QUERY_RETRIES + 1)) {
   1517 		syslog(LOG_ERR, "dyndns: max retries for UDP recv reached");
   1518 		return (-1);
   1519 	}
   1520 
   1521 	return (0);
   1522 }
   1523 /*
   1524  * dyndns_sec_add_remove_entry
   1525  * Perform secure dynamic DNS update after getting security context.
   1526  * This routine opens a UDP socket to the DNS sever, gets the security context,
   1527  * builds the unsigned TSIG message and signed TSIG message.  The signed TSIG
   1528  * message containing the encrypted update request message is sent to the DNS
   1529  * server.  The response is received and check for error.  If there is no
   1530  * error then credential handle and security context are released and the local
   1531  * NSS cached is purged.
   1532  * Parameters:
   1533  *   update_zone : UPDATE_FORW for forward zone, UPDATE_REV for reverse zone
   1534  *   hostname    : fully qualified hostname
   1535  *   ip_addr     : ip address of hostname in string format
   1536  *   life_time   : cached time of this entry by others and not within DNS
   1537  *                 database
   1538  *   max_retries : maximum retries for sending DNS update request
   1539  *   recv_timeout: receive timeout
   1540  *   update_type : UPDATE_ADD for adding entry, UPDATE_DEL for removing entry
   1541  *   del_type    : DEL_ONE for deleting one entry, DEL_ALL for deleting all
   1542  *                 entries of the same resource name.  Only valid for UPDATE_DEL
   1543  *   dns_str     : DNS IP address in string format
   1544  * Returns:
   1545  *   -1: error
   1546  *    0: success
   1547  *
   1548  * This function is enhanced to handle the case of NOTAUTH error when DNS server
   1549  * is not authoritative for specified zone. In this case we need to resend the
   1550  * same request to the higher authoritative domains.
   1551  * This is true for both secure and unsecure dynamic DNS updates.
   1552  */
   1553 static int
   1554 dyndns_sec_add_remove_entry(int update_zone, const char *hostname,
   1555     const char *ip_addr, int life_time, int update_type, int del_type,
   1556     char *dns_str)
   1557 {
   1558 	int s2;
   1559 	uint16_t id, rid;
   1560 	char buf[NS_PACKETSZ], buf2[NS_PACKETSZ];
   1561 	int ret;
   1562 	OM_uint32 min, maj;
   1563 	gss_buffer_desc in_mic, out_mic;
   1564 	gss_ctx_id_t gss_context;
   1565 	smb_inaddr_t dns_ip;
   1566 	char *key_name;
   1567 	int buf_sz;
   1568 	int level = 0;
   1569 
   1570 	assert(dns_str);
   1571 	assert(*dns_str);
   1572 
   1573 	if (inet_pton(AF_INET, dns_str, &dns_ip) == 1)
   1574 		dns_ip.a_family = AF_INET;
   1575 	else if (inet_pton(AF_INET6, dns_str, &dns_ip) == 1)
   1576 		dns_ip.a_family = AF_INET6;
   1577 
   1578 sec_retry_higher:
   1579 
   1580 	if ((gss_context = dyndns_get_sec_context(hostname,
   1581 	    &dns_ip)) == NULL) {
   1582 		return (-1);
   1583 	}
   1584 
   1585 	key_name = (char *)hostname;
   1586 
   1587 	if ((s2 = dyndns_open_init_socket(SOCK_DGRAM, &dns_ip, 53)) < 0) {
   1588 		if (gss_context != GSS_C_NO_CONTEXT)
   1589 			(void) gss_delete_sec_context(&min, &gss_context, NULL);
   1590 		return (-1);
   1591 	}
   1592 
   1593 	id = 0;
   1594 	if ((buf_sz = dyndns_build_unsigned_tsig_msg(buf, update_zone, hostname,
   1595 	    ip_addr, life_time, update_type, del_type,
   1596 	    key_name, &id, level)) <= 0) {
   1597 		(void) close(s2);
   1598 		if (gss_context != GSS_C_NO_CONTEXT)
   1599 			(void) gss_delete_sec_context(&min, &gss_context, NULL);
   1600 		return (-1);
   1601 	}
   1602 
   1603 	in_mic.length = buf_sz;
   1604 	in_mic.value = buf;
   1605 
   1606 	/* sign update message */
   1607 	if ((maj = gss_get_mic(&min, gss_context, 0, &in_mic, &out_mic)) !=
   1608 	    GSS_S_COMPLETE) {
   1609 		display_stat(maj, min);
   1610 		(void) close(s2);
   1611 		if (gss_context != GSS_C_NO_CONTEXT)
   1612 			(void) gss_delete_sec_context(&min, &gss_context, NULL);
   1613 		return (-1);
   1614 	}
   1615 
   1616 	if ((buf_sz = dyndns_build_signed_tsig_msg(buf, update_zone, hostname,
   1617 	    ip_addr, life_time, update_type, del_type, key_name, &id,
   1618 	    &out_mic, level)) <= 0) {
   1619 		(void) close(s2);
   1620 		(void) gss_release_buffer(&min, &out_mic);
   1621 		if (gss_context != GSS_C_NO_CONTEXT)
   1622 			(void) gss_delete_sec_context(&min, &gss_context, NULL);
   1623 		return (-1);
   1624 	}
   1625 
   1626 	(void) gss_release_buffer(&min, &out_mic);
   1627 
   1628 	if (dyndns_udp_send_recv(s2, buf, buf_sz, buf2)) {
   1629 		(void) close(s2);
   1630 		if (gss_context != GSS_C_NO_CONTEXT)
   1631 			(void) gss_delete_sec_context(&min, &gss_context, NULL);
   1632 		return (-1);
   1633 	}
   1634 
   1635 	(void) close(s2);
   1636 
   1637 	if (gss_context != GSS_C_NO_CONTEXT)
   1638 		(void) gss_delete_sec_context(&min, &gss_context, NULL);
   1639 
   1640 	ret = buf2[3] & 0xf;	/* error field in UDP */
   1641 
   1642 	/*
   1643 	 * If it is a NOTAUTH error we should retry with higher domains
   1644 	 * until we get a successful reply or the maximum retries is met.
   1645 	 */
   1646 	if (ret == NOTAUTH && level++ < MAX_AUTH_RETRIES)
   1647 		goto sec_retry_higher;
   1648 
   1649 	/* check here for update request is successful */
   1650 	if (ret != NOERROR) {
   1651 		dyndns_syslog(LOG_ERR, ret, "TSIG reply");
   1652 		return (-1);
   1653 	}
   1654 
   1655 	(void) dyndns_get_nshort(buf2, &rid);
   1656 	if (id != rid)
   1657 		return (-1);
   1658 
   1659 	return (0);
   1660 }
   1661 
   1662 /*
   1663  * dyndns_seach_entry
   1664  * Query DNS server for entry.  This routine can indicate if an entry exist
   1665  * or not during forward or reverse lookup.  Also can indicate if the data
   1666  * of the entry matched.  For example, for forward lookup, the entry is
   1667  * searched using the hostname and the data is the IP address.  For reverse
   1668  * lookup, the entry is searched using the IP address and the data is the
   1669  * hostname.
   1670  * Parameters:
   1671  *   update_zone: UPDATE_FORW for forward zone, UPDATE_REV for reverse zone
   1672  *   hostname   : fully qualified hostname
   1673  *   ip_addr    : ip address of hostname in string format
   1674  *   update_type: UPDATE_ADD for adding entry, UPDATE_DEL for removing entry
   1675  * Returns:
   1676  *   time_out: no use
   1677  *   is_match: is 1 for found matching entry, otherwise 0
   1678  *   1       : an entry exist but not necessarily match
   1679  *   0       : an entry does not exist
   1680  */
   1681 /*ARGSUSED*/
   1682 
   1683 static int
   1684 dyndns_search_entry(int update_zone, const char *hostname, const char *ip_addr,
   1685     int update_type, struct timeval *time_out, int *is_match)
   1686 {
   1687 	smb_inaddr_t ipaddr, dnsip;
   1688 	char dns_hostname[NI_MAXHOST];
   1689 	struct addrinfo hints, *res = NULL;
   1690 	int salen;
   1691 	int family;
   1692 
   1693 	*is_match = 0;
   1694 	if (inet_pton(AF_INET, ip_addr, &ipaddr) == 1) {
   1695 		salen = sizeof (ipaddr.a_ipv4);
   1696 		family = AF_INET;
   1697 	} else if (inet_pton(AF_INET6, ip_addr, &ipaddr) == 1) {
   1698 		salen = sizeof (ipaddr.a_ipv6);
   1699 		family = AF_INET6;
   1700 	}
   1701 	if (update_zone == UPDATE_FORW) {
   1702 		bzero((char *)&hints, sizeof (hints));
   1703 		hints.ai_family = family;
   1704 		hints.ai_flags = AI_NUMERICHOST;
   1705 		if (getaddrinfo(hostname, NULL, &hints, &res)) {
   1706 			return (NULL);
   1707 		}
   1708 		if (res) {
   1709 			/*
   1710 			 * if both ips aren't the same family skip to
   1711 			 * the next record
   1712 			 */
   1713 			do {
   1714 				if ((res->ai_family == AF_INET) &&
   1715 				    (family == AF_INET)) {
   1716 					(void) memcpy(&dnsip, &res->ai_addr[0],
   1717 					    salen);
   1718 					if (ipaddr.a_ipv4 ==
   1719 					    dnsip.a_ipv4) {
   1720 						*is_match = 1;
   1721 						break;
   1722 					}
   1723 				} else if ((res->ai_family == AF_INET6) &&
   1724 				    (family == AF_INET6)) {
   1725 					(void) memcpy(&dnsip, &res->ai_addr[0],
   1726 					    salen);
   1727 					/* need compare macro here */
   1728 					if (!memcmp(&ipaddr, &dnsip,
   1729 					    IN6ADDRSZ)) {
   1730 						*is_match = 1;
   1731 						break;
   1732 					}
   1733 				}
   1734 			} while (res->ai_next);
   1735 			freeaddrinfo(res);
   1736 			return (1);
   1737 		}
   1738 	} else {
   1739 		if (smb_getnameinfo(&ipaddr, dns_hostname, NI_MAXHOST, 0))
   1740 			return (NULL);
   1741 
   1742 		if (strncasecmp(dns_hostname, hostname,
   1743 		    strlen(hostname)) == 0) {
   1744 			*is_match = 1;
   1745 		}
   1746 		return (1);
   1747 	}
   1748 
   1749 	/* entry does not exist */
   1750 	return (0);
   1751 }
   1752 
   1753 /*
   1754  * dyndns_add_remove_entry
   1755  * Perform non-secure dynamic DNS update.  If it fails and host principal
   1756  * keys can be found in the local keytab file, secure update will be performed.
   1757  *
   1758  * This routine opens a UDP socket to the DNS sever, build the update request
   1759  * message, and sends the message to the DNS server.  The response is received
   1760  * and check for error.  If there is no error then the local NSS cached is
   1761  * purged.  DNS may be used to check to see if an entry already exist before
   1762  * adding or to see if an entry does exist before removing it.  Adding
   1763  * duplicate entries or removing non-existing entries does not cause any
   1764  * problems.  DNS is not check when doing a delete all.
   1765  * Parameters:
   1766  *   update_zone: UPDATE_FORW for forward zone, UPDATE_REV for reverse zone
   1767  *   hostname   : fully qualified hostname
   1768  *   ip_addr    : ip address of hostname in string format
   1769  *   life_time  : cached time of this entry by others and not within DNS
   1770  *                database
   1771  *   update_type: UPDATE_ADD to add entry, UPDATE_DEL to remove entry
   1772  *   do_check   : DNS_CHECK to check first in DNS, DNS_NOCHECK for no DNS
   1773  *                checking before update
   1774  *   del_type   : DEL_ONE for deleting one entry, DEL_ALL for deleting all
   1775  *                entries of the same resource name.  Only valid for UPDATE_DEL.
   1776  *   dns_str    : DNS IP address in string format
   1777  * Returns:
   1778  *   -1: error
   1779  *    0: success
   1780  *
   1781  * This function is enhanced to handle the case of NOTAUTH error when DNS server
   1782  * is not authoritative for specified zone. In this case we need to resend the
   1783  * same request to the higher authoritative domains.
   1784  * This is true for both secure and unsecure dynamic DNS updates.
   1785  */
   1786 static int
   1787 dyndns_add_remove_entry(int update_zone, const char *hostname,
   1788     const char *ip_addr, int life_time, int update_type,
   1789     int do_check, int del_type, char *dns_str)
   1790 {
   1791 	int s;
   1792 	uint16_t id, rid;
   1793 	char buf[NS_PACKETSZ], buf2[NS_PACKETSZ];
   1794 	int ret;
   1795 	int is_exist, is_match;
   1796 	struct timeval timeout;
   1797 	int buf_sz;
   1798 	int level = 0;
   1799 	smb_inaddr_t dns_ip;
   1800 
   1801 	assert(dns_str);
   1802 	assert(*dns_str);
   1803 
   1804 	if (do_check == DNS_CHECK && del_type != DEL_ALL) {
   1805 		is_exist = dyndns_search_entry(update_zone, hostname, ip_addr,
   1806 		    update_type, &timeout, &is_match);
   1807 
   1808 		if (update_type == UPDATE_ADD && is_exist && is_match) {
   1809 			return (0);
   1810 		} else if (update_type == UPDATE_DEL && !is_exist) {
   1811 			return (0);
   1812 		}
   1813 	}
   1814 
   1815 	if (inet_pton(AF_INET, dns_str, &dns_ip) == 1)
   1816 		dns_ip.a_family = AF_INET;
   1817 	else if (inet_pton(AF_INET6, dns_str, &dns_ip) == 1)
   1818 		dns_ip.a_family = AF_INET6;
   1819 
   1820 retry_higher:
   1821 	if ((s = dyndns_open_init_socket(SOCK_DGRAM, &dns_ip, 53)) < 0)
   1822 		return (-1);
   1823 
   1824 	id = 0;
   1825 	if ((buf_sz = dyndns_build_add_remove_msg(buf, update_zone, hostname,
   1826 	    ip_addr, life_time, update_type, del_type, 0, &id, level)) <= 0) {
   1827 		(void) close(s);
   1828 		return (-1);
   1829 	}
   1830 
   1831 	if (dyndns_udp_send_recv(s, buf, buf_sz, buf2)) {
   1832 		(void) close(s);
   1833 		return (-1);
   1834 	}
   1835 
   1836 	(void) close(s);
   1837 
   1838 	ret = buf2[3] & 0xf;	/* error field in UDP */
   1839 
   1840 	/*
   1841 	 * If it is a NOTAUTH error we should retry with higher domains
   1842 	 * until we get a successful reply
   1843 	 */
   1844 	if (ret == NOTAUTH && level++ < MAX_AUTH_RETRIES)
   1845 		goto retry_higher;
   1846 
   1847 	/* check here for update request is successful */
   1848 	if (ret == NOERROR) {
   1849 		(void) dyndns_get_nshort(buf2, &rid);
   1850 		if (id != rid)
   1851 			return (-1);
   1852 		return (0);
   1853 	}
   1854 
   1855 	if (ret == NOTIMP) {
   1856 		dyndns_syslog(LOG_NOTICE, NOTIMP, "dynamic updates");
   1857 		return (-1);
   1858 	} else if (ret == NOTAUTH) {
   1859 		dyndns_syslog(LOG_NOTICE, NOTAUTH, "DNS");
   1860 		return (-1);
   1861 	}
   1862 
   1863 	if (smb_krb5_find_keytab_entries(hostname, SMBNS_KRB5_KEYTAB))
   1864 		ret = dyndns_sec_add_remove_entry(update_zone, hostname,
   1865 		    ip_addr, life_time, update_type, del_type, dns_str);
   1866 
   1867 	return (ret);
   1868 }
   1869 
   1870 /*
   1871  * dyndns_add_entry
   1872  * Main routine to add an entry into DNS.  The attempt will be made on the
   1873  * the servers returned by smb_get_nameserver().  Upon a successful
   1874  * attempt on any one of the server, the function will exit with 0.
   1875  * Otherwise, -1 is retuned to indicate the update attempt on all the
   1876  * nameservers has failed.
   1877  *
   1878  * Parameters:
   1879  *   update_zone: the type of zone to update, use UPDATE_FORW for forward
   1880  *                lookup zone, use UPDATE_REV for reverse lookup zone
   1881  *   hostname   : fully qualified hostname
   1882  *   ip_addr    : ip address of hostname in string format
   1883  *   life_time  : cached time of this entry by others and not within DNS
   1884  *                database
   1885  * Returns:
   1886  *   -1: error
   1887  *    0: success
   1888  */
   1889 static int
   1890 dyndns_add_entry(int update_zone, const char *hostname, const char *ip_addr,
   1891     int life_time)
   1892 {
   1893 	const char *dns_str;
   1894 	char *which_zone;
   1895 	smb_inaddr_t ns_list[MAXNS];
   1896 	char dns_buf[INET6_ADDRSTRLEN];
   1897 	int i, cnt;
   1898 	int rc = 0;
   1899 
   1900 	if (hostname == NULL || ip_addr == NULL) {
   1901 		return (-1);
   1902 	}
   1903 	cnt = smb_get_nameservers(&ns_list[0], MAXNS);
   1904 
   1905 	for (i = 0; i < cnt; i++) {
   1906 		dns_str = smb_inet_ntop(&ns_list[i], dns_buf,
   1907 		    SMB_IPSTRLEN(ns_list[i].a_family));
   1908 		if (dns_str == NULL)
   1909 			continue;
   1910 
   1911 		which_zone = (update_zone == UPDATE_FORW) ?
   1912 		    "forward" : "reverse";
   1913 		syslog(LOG_DEBUG, "dyndns %s lookup zone update %s (%s)",
   1914 		    which_zone, hostname, ip_addr);
   1915 
   1916 		if (dyndns_add_remove_entry(update_zone, hostname,
   1917 		    ip_addr, life_time,
   1918 		    UPDATE_ADD, DNS_NOCHECK, DEL_NONE, dns_buf) != -1) {
   1919 			rc = 1;
   1920 			break;
   1921 		}
   1922 	}
   1923 
   1924 	return (rc ? 0 : -1);
   1925 }
   1926 
   1927 /*
   1928  * dyndns_remove_entry
   1929  * Main routine to remove an entry or all entries of the same resource name
   1930  * from DNS.  The update attempt will be made on the primary DNS server.  If
   1931  * there is a failure then another attempt will be made on the secondary DNS
   1932  * server.
   1933  * Parameters:
   1934  *   update_zone: the type of zone to update, use UPDATE_FORW for forward
   1935  *                lookup zone, use UPDATE_REV for reverse lookup zone
   1936  *   hostname   : fully qualified hostname
   1937  *   ip_addr    : ip address of hostname in string format
   1938  *   del_type   : DEL_ONE for deleting one entry, DEL_ALL for deleting all
   1939  *                entries of the same resource name.  Only valid for UPDATE_DEL
   1940  * Returns:
   1941  *   -1: error
   1942  *    0: success
   1943  */
   1944 static int
   1945 dyndns_remove_entry(int update_zone, const char *hostname, const char *ip_addr,
   1946 	int del_type)
   1947 {
   1948 	const char *dns_str;
   1949 	smb_inaddr_t ns_list[MAXNS];
   1950 	char dns_buf[INET6_ADDRSTRLEN];
   1951 	int i, cnt, scnt;
   1952 
   1953 	if ((hostname == NULL || ip_addr == NULL)) {
   1954 		return (-1);
   1955 	}
   1956 	cnt = smb_get_nameservers(ns_list, MAXNS);
   1957 	scnt = 0;
   1958 	for (i = 0; i < cnt; i++) {
   1959 		dns_str = smb_inet_ntop(&ns_list[i], dns_buf,
   1960 		    SMB_IPSTRLEN(ns_list[i].a_family));
   1961 		if (dns_str == NULL)
   1962 			continue;
   1963 		if (update_zone == UPDATE_FORW) {
   1964 			if (del_type == DEL_ONE) {
   1965 				syslog(LOG_DEBUG, "Dynamic update "
   1966 				    "on forward lookup "
   1967 				    "zone for %s (%s)...\n", hostname, ip_addr);
   1968 			} else {
   1969 				syslog(LOG_DEBUG, "Removing all "
   1970 				    "entries of %s "
   1971 				    "in forward lookup zone...\n", hostname);
   1972 			}
   1973 		} else {
   1974 			if (del_type == DEL_ONE) {
   1975 				syslog(LOG_DEBUG, "Dynamic update "
   1976 				    "on reverse lookup "
   1977 				    "zone for %s (%s)...\n", hostname, ip_addr);
   1978 			} else {
   1979 				syslog(LOG_DEBUG, "Removing all "
   1980 				    "entries of %s "
   1981 				    "in reverse lookup zone...\n", ip_addr);
   1982 			}
   1983 		}
   1984 		if (dyndns_add_remove_entry(update_zone, hostname, ip_addr, 0,
   1985 		    UPDATE_DEL, DNS_NOCHECK, del_type, dns_buf) != -1) {
   1986 			scnt++;
   1987 			break;
   1988 		}
   1989 	}
   1990 	if (scnt)
   1991 		return (0);
   1992 	return (-1);
   1993 }
   1994 
   1995 /*
   1996  * dyndns_update_core
   1997  * Perform dynamic update on both forward and reverse lookup zone using
   1998  * the specified hostname and IP addresses.  Before updating DNS, existing
   1999  * host entries with the same hostname in the forward lookup zone are removed
   2000  * and existing pointer entries with the same IP addresses in the reverse
   2001  * lookup zone are removed.  After DNS update, host entries for current
   2002  * hostname will show current IP addresses and pointer entries for current
   2003  * IP addresses will show current hostname.
   2004  * Parameters:
   2005  *  fqhn - fully-qualified hostname
   2006  *
   2007  * Returns:
   2008  *   -1: some dynamic DNS updates errors
   2009  *    0: successful or DDNS disabled.
   2010  */
   2011 int
   2012 dyndns_update_core(char *fqdn)
   2013 {
   2014 	int forw_update_ok, error;
   2015 	char my_ip[INET6_ADDRSTRLEN];
   2016 	const char *my_str;
   2017 	smb_niciter_t ni;
   2018 	int rc;
   2019 	char fqhn[MAXHOSTNAMELEN];
   2020 
   2021 	if (fqdn == NULL || *fqdn == '\0')
   2022 		return (0);
   2023 
   2024 	if (!smb_config_getbool(SMB_CI_DYNDNS_ENABLE))
   2025 		return (0);
   2026 
   2027 	if (smb_gethostname(fqhn, MAXHOSTNAMELEN, 0) != 0)
   2028 		return (-1);
   2029 
   2030 	(void) snprintf(fqhn, MAXHOSTNAMELEN, "%s.%s", fqhn, fqdn);
   2031 	error = 0;
   2032 	forw_update_ok = 0;
   2033 
   2034 	/*
   2035 	 * Dummy IP is okay since we are removing all using the hostname.
   2036 	 */
   2037 	if (dyndns_remove_entry(UPDATE_FORW, fqhn, "1.1.1.1", DEL_ALL) == 0) {
   2038 		forw_update_ok = 1;
   2039 	} else {
   2040 		error++;
   2041 	}
   2042 
   2043 	if (smb_nic_getfirst(&ni) != 0)
   2044 		return (-1);
   2045 
   2046 	do {
   2047 		if (ni.ni_nic.nic_sysflags & IFF_PRIVATE)
   2048 			continue;
   2049 		/* first try ipv4, then ipv6 */
   2050 		my_str = smb_inet_ntop(&ni.ni_nic.nic_ip, my_ip,
   2051 		    SMB_IPSTRLEN(ni.ni_nic.nic_ip.a_family));
   2052 		if (my_str == NULL) {
   2053 			error++;
   2054 			continue;
   2055 		}
   2056 
   2057 		if (forw_update_ok) {
   2058 			rc = dyndns_add_entry(UPDATE_FORW, fqhn, my_str,
   2059 			    DDNS_TTL);
   2060 
   2061 			if (rc == -1)
   2062 				error++;
   2063 		}
   2064 
   2065 		rc = dyndns_remove_entry(UPDATE_REV, fqhn, my_str, DEL_ALL);
   2066 		if (rc == 0) {
   2067 			rc = dyndns_add_entry(UPDATE_REV, fqhn, my_str,
   2068 			    DDNS_TTL);
   2069 		}
   2070 
   2071 		if (rc == -1)
   2072 			error++;
   2073 
   2074 	} while (smb_nic_getnext(&ni) == 0);
   2075 
   2076 	return ((error == 0) ? 0 : -1);
   2077 }
   2078 
   2079 /*
   2080  * dyndns_clear_rev_zone
   2081  * Clear the rev zone records. Must be called to clear the OLD if list
   2082  * of down records prior to updating the list with new information.
   2083  *
   2084  * Parameters:
   2085  *   fqhn - fully-qualified hostname
   2086  * Returns:
   2087  *   -1: some dynamic DNS updates errors
   2088  *    0: successful or DDNS disabled.
   2089  */
   2090 int
   2091 dyndns_clear_rev_zone(char *fqdn)
   2092 {
   2093 	int error;
   2094 	char my_ip[INET6_ADDRSTRLEN];
   2095 	smb_niciter_t ni;
   2096 	int rc;
   2097 	char fqhn[MAXHOSTNAMELEN];
   2098 	const char *my_str;
   2099 
   2100 	if (!smb_config_getbool(SMB_CI_DYNDNS_ENABLE))
   2101 		return (0);
   2102 
   2103 	if (smb_gethostname(fqhn, MAXHOSTNAMELEN, 0) != 0)
   2104 		return (-1);
   2105 
   2106 	(void) snprintf(fqhn, MAXHOSTNAMELEN, "%s.%s", fqhn, fqdn);
   2107 	error = 0;
   2108 
   2109 	if (smb_nic_getfirst(&ni) != 0)
   2110 		return (-1);
   2111 
   2112 	do {
   2113 		if (ni.ni_nic.nic_sysflags & IFF_PRIVATE)
   2114 			continue;
   2115 		my_str = smb_inet_ntop(&ni.ni_nic.nic_ip, my_ip,
   2116 		    SMB_IPSTRLEN(ni.ni_nic.nic_ip.a_family));
   2117 		if (my_str == NULL) {
   2118 			error++;
   2119 			continue;
   2120 		}
   2121 
   2122 		rc = dyndns_remove_entry(UPDATE_REV, fqhn, my_ip, DEL_ALL);
   2123 		if (rc != 0)
   2124 			error++;
   2125 
   2126 	} while (smb_nic_getnext(&ni) == 0);
   2127 
   2128 	return ((error == 0) ? 0 : -1);
   2129 }
   2130