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