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