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