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