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