xref: /titanic_50/usr/src/lib/smbsrv/libsmbns/common/smbns_dyndns.c (revision 7c2fbfb345896881c631598ee3852ce9ce33fb07)
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 2008 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 static char *
504 dyndns_put_int(char *buf, int val)
505 {
506 	(void) memcpy(buf, &val, sizeof (int));
507 	buf += sizeof (int);
508 	return (buf);
509 }
510 
511 /*
512  * dyndns_stuff_str
513  * Converts a domain string by removing periods and replacing with a byte value
514  * of how many characters following period.  A byte value is placed in front
515  * to indicate how many characters before first period.  A NULL character is
516  * placed at the end. i.e. host.procom.com -> 4host5procom3com0
517  * Buffer space checking is done by caller.
518  * Parameters:
519  *   ptr : address of pointer to buffer to store converted string
520  *   zone: domain name string
521  * Returns:
522  *   ptr: address of pointer to next available buffer space
523  *   -1 : error
524  *    0 : success
525  */
526 static int
527 dyndns_stuff_str(char **ptr, char *zone)
528 {
529 	int len;
530 	char *lenPtr, *zonePtr;
531 
532 	for (zonePtr = zone; *zonePtr; ) {
533 		lenPtr = *ptr;
534 		*ptr = *ptr + 1;
535 		len = 0;
536 		while (*zonePtr != '.' && *zonePtr != 0) {
537 			*ptr = dyndns_put_byte(*ptr, *zonePtr);
538 			zonePtr++;
539 			len++;
540 		}
541 		*lenPtr = len;
542 		if (*zonePtr == '.')
543 			zonePtr++;
544 	}
545 	*ptr = dyndns_put_byte(*ptr, 0);
546 	return (0);
547 }
548 
549 /*
550  * dyndns_build_header
551  * Build the header for DNS query and DNS update request message.
552  * Parameters:
553  *   ptr               : address of pointer to buffer to store header
554  *   buf_len           : buffer length
555  *   msg_id            : message id
556  *   query_req         : use REQ_QUERY for query message or REQ_UPDATE for
557  *                       update message
558  *   quest_zone_cnt    : number of question record for query message or
559  *                       number of zone record for update message
560  *   ans_prereq_cnt    : number of answer record for query message or
561  *                       number of prerequisite record for update message
562  *   nameser_update_cnt: number of name server for query message or
563  *                       number of update record for update message
564  *   addit_cnt         : number of additional record
565  *   flags             : query flags word
566  * Returns:
567  *   ptr: address of pointer to next available buffer space
568  *   -1 : error
569  *    0 : success
570  */
571 static int
572 dyndns_build_header(char **ptr, int buf_len, uint16_t msg_id, int query_req,
573     uint16_t quest_zone_cnt, uint16_t ans_prereq_cnt,
574     uint16_t nameser_update_cnt, uint16_t addit_cnt, int flags)
575 {
576 	uint16_t opcode;
577 
578 	if (buf_len < 12) {
579 		syslog(LOG_ERR, "dyndns header section: buffer too small");
580 		return (-1);
581 	}
582 
583 	*ptr = dyndns_put_nshort(*ptr, msg_id);	/* mesg ID */
584 	if (query_req == REQ_QUERY)
585 		opcode = ns_o_query;	/* query msg */
586 	else
587 		opcode = ns_o_update << 11;	/* update msg */
588 	opcode |= flags;
589 	/* mesg opcode */
590 	*ptr = dyndns_put_nshort(*ptr, opcode);
591 	/* zone record count */
592 	*ptr = dyndns_put_nshort(*ptr, quest_zone_cnt);
593 	/* prerequiste record count */
594 	*ptr = dyndns_put_nshort(*ptr, ans_prereq_cnt);
595 	/* update record count */
596 	*ptr = dyndns_put_nshort(*ptr, nameser_update_cnt);
597 	/* additional record count */
598 	*ptr = dyndns_put_nshort(*ptr, addit_cnt);
599 
600 	return (0);
601 }
602 
603 /*
604  * dyndns_build_quest_zone
605  * Build the question section for query message or zone section for
606  * update message.
607  * Parameters:
608  *   ptr    : address of pointer to buffer to store question or zone section
609  *   buf_len: buffer length
610  *   name   : question or zone name
611  *   type   : type of question or zone
612  *   class  : class of question or zone
613  * Returns:
614  *   ptr: address of pointer to next available buffer space
615  *   -1 : error
616  *    0 : success
617  */
618 static int
619 dyndns_build_quest_zone(char **ptr, int buf_len, char *name, int type,
620 	int class)
621 {
622 	char *zonePtr;
623 
624 	if ((strlen(name) + 6) > buf_len) {
625 		syslog(LOG_ERR, "dyndns question section: buffer too small");
626 		return (-1);
627 	}
628 
629 	zonePtr = *ptr;
630 	(void) dyndns_stuff_str(&zonePtr, name);
631 	*ptr = zonePtr;
632 	*ptr = dyndns_put_nshort(*ptr, type);
633 	*ptr = dyndns_put_nshort(*ptr, class);
634 	return (0);
635 }
636 
637 /*
638  * dyndns_build_update
639  * Build update section of update message for adding and removing a record.
640  * If the ttl value is 0 then this message is for record deletion.
641  *
642  * Parameters:
643  *   ptr     : address of pointer to buffer to store update section
644  *   buf_len : buffer length
645  *   name    : resource name of this record
646  *   type    : type of this record
647  *   class   : class of this record
648  *   ttl     : time-to-live, cached time of this entry by others and not
649  *             within DNS database, a zero value for record(s) deletion
650  *   data    : data of this resource record
651  *   forw_rev: UPDATE_FORW for forward zone, UPDATE_REV for reverse zone
652  *   add_del : UPDATE_ADD for adding entry, UPDATE_DEL for removing zone
653  *   del_type: DEL_ONE for deleting one entry, DEL_ALL for deleting all
654  *             entries of the same resource name.  Only valid for UPDATE_DEL.
655  * Returns:
656  *   ptr: address of pointer to next available buffer space
657  *   -1 : error
658  *    0 : success
659  */
660 static int
661 dyndns_build_update(char **ptr, int buf_len, char *name, int type, int class,
662 	uint32_t ttl, char *data, int forw_rev, int add_del, int del_type)
663 {
664 	char *namePtr;
665 	int rec_len, data_len;
666 
667 	rec_len = strlen(name) + 10;
668 	if (add_del == UPDATE_ADD) {
669 		if (forw_rev == UPDATE_FORW)
670 			data_len = 4;
671 		else
672 			data_len = strlen(data) + 2;
673 	} else {
674 		if (del_type == DEL_ALL)
675 			data_len = 0;
676 		else if (forw_rev == UPDATE_FORW)
677 			data_len = 4;
678 		else
679 			data_len = strlen(data) + 2;
680 	}
681 
682 	if (rec_len + data_len > buf_len) {
683 		syslog(LOG_ERR, "dyndns update section: buffer too small");
684 		return (-1);
685 	}
686 
687 	namePtr = *ptr;
688 	(void) dyndns_stuff_str(&namePtr, name);
689 	*ptr = namePtr;
690 	*ptr = dyndns_put_nshort(*ptr, type);
691 	*ptr = dyndns_put_nshort(*ptr, class);
692 	*ptr = dyndns_put_nlong(*ptr, ttl);
693 
694 	if (add_del == UPDATE_DEL && del_type == DEL_ALL) {
695 		*ptr = dyndns_put_nshort(*ptr, 0);
696 		return (0);
697 	}
698 
699 	if (forw_rev == UPDATE_FORW) {
700 		*ptr = dyndns_put_nshort(*ptr, 4);
701 		*ptr = dyndns_put_int(*ptr, inet_addr(data));	/* ip address */
702 	} else {
703 		*ptr = dyndns_put_nshort(*ptr, strlen(data)+2);
704 		namePtr = *ptr;
705 		(void) dyndns_stuff_str(&namePtr, data);	/* hostname */
706 		*ptr = namePtr;
707 	}
708 	return (0);
709 }
710 
711 /*
712  * dyndns_build_tkey
713  * Build TKEY section to establish security context for secure dynamic DNS
714  * update.  DNS header and question sections need to be build before this
715  * section.  The TKEY data are the tokens generated during security context
716  * establishment and the TKEY message is used to transmit those tokens, one
717  * at a time, to the DNS server.
718  * Parameters:
719  *   ptr       : address of pointer to buffer to store TKEY
720  *   buf_len   : buffer length
721  *   name      : key name, must be unique and same as for TSIG record
722  *   key_expire: expiration time of this key in second
723  *   data      : TKEY data
724  *   data_size : data size
725  * Returns:
726  *   ptr: address of the pointer to the next available buffer space
727  *   -1 : error
728  *    0 : success
729  */
730 static int
731 dyndns_build_tkey(char **ptr, int buf_len, char *name, int key_expire,
732 	char *data, int data_size)
733 {
734 	char *namePtr;
735 	struct timeval tp;
736 
737 	if (strlen(name)+2 + 45 + data_size > buf_len) {
738 		syslog(LOG_ERR, "dyndns TKEY: buffer too small");
739 		return (-1);
740 	}
741 
742 	namePtr = *ptr;
743 	(void) dyndns_stuff_str(&namePtr, name);	/* unique global name */
744 	*ptr = namePtr;
745 	*ptr = dyndns_put_nshort(*ptr, ns_t_tkey);
746 	*ptr = dyndns_put_nshort(*ptr, ns_c_any);
747 	*ptr = dyndns_put_nlong(*ptr, 0);
748 	/* 19 + 14 + data_size + 2 */
749 	*ptr = dyndns_put_nshort(*ptr, 35 + data_size);
750 	namePtr = *ptr;
751 	(void) dyndns_stuff_str(&namePtr, "gss.microsoft.com");
752 	*ptr = namePtr;
753 	(void) gettimeofday(&tp, 0);
754 	*ptr = dyndns_put_nlong(*ptr, tp.tv_sec);	/* inception */
755 	/* expiration, 86400 */
756 	*ptr = dyndns_put_nlong(*ptr, tp.tv_sec + key_expire);
757 	*ptr = dyndns_put_nshort(*ptr, MODE_GSS_API);	/* mode: gss-api */
758 	*ptr = dyndns_put_nshort(*ptr, 0);		/* error */
759 	*ptr = dyndns_put_nshort(*ptr, data_size);	/* key size */
760 	(void) memcpy(*ptr, data, data_size);	/* key data */
761 	*ptr += data_size;
762 	*ptr = dyndns_put_nshort(*ptr, 0);	/* other */
763 	return (0);
764 }
765 
766 /*
767  * dyndns_build_tsig
768  * Build TSIG section for secure dynamic DNS update.  This routine will be
769  * called twice.  First called with TSIG_UNSIGNED, and second with TSIG_SIGNED.
770  * The TSIG data is NULL and ignored for TSIG_UNSIGNED and is the update request
771  * message encrypted for TSIG_SIGNED.  The message id must be the same id as the
772  * one in the update request before it is encrypted.
773  * Parameters:
774  *   ptr        : address of pointer to buffer to store TSIG
775  *   buf_len    : buffer length
776  *   msg_id     : message id
777  *   name       : key name, must be the same as in TKEY record
778  *   fudge_time : amount of error time allow in seconds
779  *   data       : TSIG data if TSIG_SIGNED, otherwise NULL
780  *   data_size  : size of data, otherwise 0 if data is NULL
781  *   data_signed: TSIG_SIGNED to indicate data is signed and encrypted,
782  *                otherwise TSIG_UNSIGNED
783  * Returns:
784  *   ptr: address of pointer to next available buffer space
785  *   -1 : error
786  *    0 : success
787  */
788 static int
789 dyndns_build_tsig(char **ptr, int buf_len, int msg_id, char *name,
790 	int fudge_time, char *data, int data_size, int data_signed)
791 {
792 	char *namePtr;
793 	struct timeval tp;
794 	int signtime, fudge, rec_len;
795 
796 	if (data_signed == TSIG_UNSIGNED)
797 		rec_len = strlen(name)+2 + 37;
798 	else
799 		rec_len = strlen(name)+2 + 45 + data_size;
800 
801 	if (rec_len > buf_len) {
802 		syslog(LOG_ERR, "dyndns TSIG: buffer too small");
803 		return (-1);
804 	}
805 
806 	namePtr = *ptr;
807 	(void) dyndns_stuff_str(&namePtr, name);	/* unique global name */
808 	*ptr = namePtr;
809 	if (data_signed == TSIG_SIGNED)
810 		*ptr = dyndns_put_nshort(*ptr, ns_t_tsig);
811 	*ptr = dyndns_put_nshort(*ptr, ns_c_any);
812 	*ptr = dyndns_put_nlong(*ptr, 0);
813 	if (data_signed == TSIG_SIGNED) {
814 		/* 19 + 10 + data_size + 6 */
815 		*ptr = dyndns_put_nshort(*ptr, 35 + data_size);
816 	}
817 	namePtr = *ptr;
818 	(void) dyndns_stuff_str(&namePtr, "gss.microsoft.com");
819 	*ptr = namePtr;
820 	(void) gettimeofday(&tp, 0);
821 	signtime = tp.tv_sec >> 16;
822 	*ptr = dyndns_put_nlong(*ptr, signtime);	/* sign time */
823 	fudge = tp.tv_sec << 16;
824 	fudge |= fudge_time;
825 	*ptr = dyndns_put_nlong(*ptr, fudge);	/* fudge time */
826 	if (data_signed == TSIG_SIGNED) {
827 		/* signed data size */
828 		*ptr = dyndns_put_nshort(*ptr, data_size);
829 		(void) memcpy(*ptr, data, data_size);	/* signed data */
830 		*ptr += data_size;
831 		*ptr = dyndns_put_nshort(*ptr, msg_id);	/* original id */
832 	}
833 	*ptr = dyndns_put_nshort(*ptr, 0);	/* error */
834 	*ptr = dyndns_put_nshort(*ptr, 0);	/* other */
835 	return (0);
836 }
837 
838 /*
839  * dyndns_open_init_socket
840  * This routine creates a SOCK_STREAM or SOCK_DGRAM socket and initializes it
841  * by doing bind() and setting linger option to off.
842  *
843  * Parameters:
844  *   sock_type: SOCK_STREAM for TCP or SOCK_DGRAM for UDP
845  *   dest_addr: destination address in network byte order
846  *   port     : destination port number
847  * Returns:
848  *   descriptor: descriptor referencing the created socket
849  *   -1        : error
850  */
851 static int
852 dyndns_open_init_socket(int sock_type, unsigned long dest_addr, int port)
853 {
854 	int s;
855 	struct sockaddr_in my_addr;
856 	struct linger l;
857 	struct sockaddr_in serv_addr;
858 
859 	if ((s = socket(AF_INET, sock_type, 0)) == -1) {
860 		syslog(LOG_ERR, "dyndns: socket error");
861 		return (-1);
862 	}
863 
864 	l.l_onoff = 0;
865 	if (setsockopt(s, SOL_SOCKET, SO_LINGER,
866 	    (char *)&l, sizeof (l)) == -1) {
867 		syslog(LOG_ERR, "dyndns: setsockopt error");
868 		(void) close(s);
869 		return (-1);
870 	}
871 
872 	bzero(&my_addr, sizeof (my_addr));
873 	my_addr.sin_family = AF_INET;
874 	my_addr.sin_port = htons(0);
875 	my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
876 
877 	if (bind(s, (struct sockaddr *)&my_addr, sizeof (my_addr)) < 0) {
878 		syslog(LOG_ERR, "dyndns: client bind error");
879 		(void) close(s);
880 		return (-1);
881 	}
882 
883 	serv_addr.sin_family = AF_INET;
884 	serv_addr.sin_port = htons(port);
885 	serv_addr.sin_addr.s_addr = dest_addr;
886 
887 	if (connect(s, (struct sockaddr *)&serv_addr,
888 	    sizeof (struct sockaddr_in)) < 0) {
889 		syslog(LOG_ERR, "dyndns: client connect error (%s)",
890 		    strerror(errno));
891 		(void) close(s);
892 		return (-1);
893 	}
894 
895 	return (s);
896 }
897 
898 /*
899  * dyndns_build_tkey_msg
900  * This routine is used to build the TKEY message to transmit GSS tokens
901  * during GSS security context establishment for secure DNS update.  The
902  * TKEY message format uses the DNS query message format.  The TKEY section
903  * is the answer section of the query message format.
904  * Microsoft uses a value of 86400 seconds (24 hours) for key expiration time.
905  * Parameters:
906  *   buf     : buffer to build and store TKEY message
907  *   key_name: a unique key name, this same key name must be also be used in
908  *             the TSIG message
909  *   out_tok : TKEY message data (GSS tokens)
910  * Returns:
911  *   id          : message id of this TKEY message
912  *   message size: the size of the TKEY message
913  *   -1          : error
914  */
915 static int
916 dyndns_build_tkey_msg(char *buf, char *key_name, uint16_t *id,
917 	gss_buffer_desc *out_tok)
918 {
919 	int queryReq, zoneCount, preqCount, updateCount, additionalCount;
920 	int zoneType, zoneClass;
921 	char *bufptr;
922 
923 	queryReq = REQ_QUERY;
924 	/* query section of query request */
925 	zoneCount = 1;
926 	/* answer section of query request */
927 	preqCount = 1;
928 	updateCount = 0;
929 	additionalCount = 0;
930 
931 	(void) memset(buf, 0, MAX_TCP_SIZE);
932 	bufptr = buf;
933 	*id = dyndns_get_msgid();
934 
935 	/* add TCP length info that follows this field */
936 	bufptr = dyndns_put_nshort(bufptr,
937 	    26 + (strlen(key_name)+2)*2 + 35 + out_tok->length);
938 
939 	if (dyndns_build_header(&bufptr, BUFLEN_TCP(bufptr, buf), *id, queryReq,
940 	    zoneCount, preqCount, updateCount, additionalCount, 0) == -1) {
941 		return (-1);
942 	}
943 
944 	zoneType = ns_t_tkey;
945 	zoneClass = ns_c_in;
946 	if (dyndns_build_quest_zone(&bufptr, BUFLEN_TCP(bufptr, buf), key_name,
947 	    zoneType, zoneClass) == -1) {
948 		return (-1);
949 	}
950 
951 	if (dyndns_build_tkey(&bufptr, BUFLEN_TCP(bufptr, buf), key_name,
952 	    86400, out_tok->value, out_tok->length) == -1) {
953 		return (-1);
954 	}
955 
956 	return (bufptr - buf);
957 }
958 
959 /*
960  * dyndns_establish_sec_ctx
961  * This routine is used to establish a security context with the DNS server
962  * by building TKEY messages and sending them to the DNS server.  TKEY messages
963  * are also received from the DNS server for processing.   The security context
964  * establishment is done with the GSS client on the system producing a token
965  * and sending the token within the TKEY message to the GSS server on the DNS
966  * server.  The GSS server then processes the token and then send a TKEY reply
967  * message with a new token to be processed by the GSS client.  The GSS client
968  * processes the new token and then generates a new token to be sent to the
969  * GSS server.  This cycle is continued until the security establishment is
970  * done.  TCP is used to send and receive TKEY messages.
971  * Parameters:
972  *   cred_handle  : handle to credential
973  *   s           : socket descriptor to DNS server
974  *   key_name    : TKEY key name
975  *   dns_hostname: fully qualified DNS hostname
976  *   oid         : contains Kerberos 5 object identifier
977  * Returns:
978  *   gss_context    : handle to security context
979  */
980 static int
981 dyndns_establish_sec_ctx(gss_ctx_id_t *gss_context, gss_cred_id_t cred_handle,
982     int s, char *key_name, char *dns_hostname, gss_OID oid)
983 {
984 	uint16_t id, rid, rsz;
985 	char buf[MAX_TCP_SIZE], buf2[MAX_TCP_SIZE];
986 	int ret;
987 	char *service_name, *tmpptr;
988 	int service_sz;
989 	OM_uint32 min, maj, time_rec;
990 	gss_buffer_desc service_buf, in_tok, out_tok;
991 	gss_name_t target_name;
992 	gss_buffer_desc *inputptr;
993 	int gss_flags;
994 	OM_uint32 ret_flags;
995 	int buf_sz;
996 
997 	service_sz = strlen(dns_hostname) + 5;
998 	service_name = (char *)malloc(sizeof (char) * service_sz);
999 	if (service_name == NULL)
1000 		return (-1);
1001 
1002 	(void) snprintf(service_name, service_sz, "DNS@%s", dns_hostname);
1003 	service_buf.value = service_name;
1004 	service_buf.length = strlen(service_name)+1;
1005 	if ((maj = gss_import_name(&min, &service_buf,
1006 	    GSS_C_NT_HOSTBASED_SERVICE, &target_name)) != GSS_S_COMPLETE) {
1007 		display_stat(maj, min);
1008 		(void) free(service_name);
1009 		return (-1);
1010 	}
1011 	(void) free(service_name);
1012 
1013 	inputptr = GSS_C_NO_BUFFER;
1014 	*gss_context = GSS_C_NO_CONTEXT;
1015 	gss_flags = GSS_C_MUTUAL_FLAG | GSS_C_DELEG_FLAG | GSS_C_REPLAY_FLAG |
1016 	    GSS_C_SEQUENCE_FLAG | GSS_C_CONF_FLAG | GSS_C_INTEG_FLAG;
1017 	do {
1018 		maj = gss_init_sec_context(&min, cred_handle, gss_context,
1019 		    target_name, oid, gss_flags, 0, NULL, inputptr, NULL,
1020 		    &out_tok, &ret_flags, &time_rec);
1021 
1022 		if (maj != GSS_S_COMPLETE && maj != GSS_S_CONTINUE_NEEDED) {
1023 			assert(gss_context);
1024 			if (*gss_context != GSS_C_NO_CONTEXT)
1025 				(void) gss_delete_sec_context(&min,
1026 				    gss_context, NULL);
1027 
1028 			display_stat(maj, min);
1029 			(void) gss_release_name(&min, &target_name);
1030 			return (-1);
1031 		}
1032 
1033 		if ((maj == GSS_S_COMPLETE) &&
1034 		    !(ret_flags & GSS_C_REPLAY_FLAG)) {
1035 			syslog(LOG_ERR, "dyndns: No GSS_C_REPLAY_FLAG");
1036 			if (out_tok.length > 0)
1037 				(void) gss_release_buffer(&min, &out_tok);
1038 			(void) gss_release_name(&min, &target_name);
1039 			return (-1);
1040 		}
1041 
1042 		if ((maj == GSS_S_COMPLETE) &&
1043 		    !(ret_flags & GSS_C_MUTUAL_FLAG)) {
1044 			syslog(LOG_ERR, "dyndns: No GSS_C_MUTUAL_FLAG");
1045 			if (out_tok.length > 0)
1046 				(void) gss_release_buffer(&min, &out_tok);
1047 			(void) gss_release_name(&min, &target_name);
1048 			return (-1);
1049 		}
1050 
1051 		if (out_tok.length > 0) {
1052 			if ((buf_sz = dyndns_build_tkey_msg(buf, key_name,
1053 			    &id, &out_tok)) <= 0) {
1054 				(void) gss_release_buffer(&min, &out_tok);
1055 				(void) gss_release_name(&min, &target_name);
1056 				return (-1);
1057 			}
1058 
1059 			(void) gss_release_buffer(&min, &out_tok);
1060 
1061 			if (send(s, buf, buf_sz, 0) == -1) {
1062 				syslog(LOG_ERR, "dyndns: TKEY send error");
1063 				(void) gss_release_name(&min, &target_name);
1064 				return (-1);
1065 			}
1066 
1067 			bzero(buf2, MAX_TCP_SIZE);
1068 			if (recv(s, buf2, MAX_TCP_SIZE, 0) == -1) {
1069 				syslog(LOG_ERR, "dyndns: TKEY recv error");
1070 				(void) gss_release_name(&min, &target_name);
1071 				return (-1);
1072 			}
1073 
1074 			ret = buf2[5] & 0xf;	/* error field in TCP */
1075 			if (ret != NOERROR) {
1076 				dyndns_syslog(LOG_ERR, ret, "TKEY reply");
1077 				(void) gss_release_name(&min, &target_name);
1078 				return (-1);
1079 			}
1080 
1081 			tmpptr = &buf2[2];
1082 			(void) dyndns_get_nshort(tmpptr, &rid);
1083 			if (id != rid) {
1084 				(void) gss_release_name(&min, &target_name);
1085 				return (-1);
1086 			}
1087 
1088 			tmpptr = &buf2[59+(strlen(key_name)+2)*2];
1089 			(void) dyndns_get_nshort(tmpptr, &rsz);
1090 			in_tok.length = rsz;
1091 
1092 			/* bsd38 -> 2*7=14 */
1093 			in_tok.value = &buf2[61+(strlen(key_name)+2)*2];
1094 			inputptr = &in_tok;
1095 		}
1096 
1097 	} while (maj != GSS_S_COMPLETE);
1098 
1099 	(void) gss_release_name(&min, &target_name);
1100 
1101 	return (0);
1102 }
1103 
1104 /*
1105  * dyndns_get_sec_context
1106  * Get security context for secure dynamic DNS update.  This routine opens
1107  * a TCP socket to the DNS server and establishes a security context with
1108  * the DNS server using host principal to perform secure dynamic DNS update.
1109  * Parameters:
1110  *   hostname: fully qualified hostname
1111  *   dns_ip  : ip address of hostname in network byte order
1112  * Returns:
1113  *   gss_handle: gss credential handle
1114  *   gss_context: gss security context
1115  *   -1: error
1116  *    0: success
1117  */
1118 static gss_ctx_id_t
1119 dyndns_get_sec_context(const char *hostname, int dns_ip)
1120 {
1121 	int s;
1122 	gss_cred_id_t cred_handle;
1123 	gss_ctx_id_t gss_context;
1124 	gss_OID oid;
1125 	struct hostent *hentry;
1126 	char *key_name, dns_hostname[MAXHOSTNAMELEN];
1127 
1128 	cred_handle = GSS_C_NO_CREDENTIAL;
1129 	oid = GSS_C_NO_OID;
1130 	key_name = (char *)hostname;
1131 
1132 	hentry = gethostbyaddr((char *)&dns_ip, 4, AF_INET);
1133 	if (hentry == NULL) {
1134 		syslog(LOG_ERR, "dyndns gethostbyaddr failed");
1135 		return (NULL);
1136 	}
1137 	(void) strcpy(dns_hostname, hentry->h_name);
1138 
1139 	if ((s = dyndns_open_init_socket(SOCK_STREAM, dns_ip, 53)) < 0) {
1140 		return (NULL);
1141 	}
1142 
1143 	if (dyndns_establish_sec_ctx(&gss_context, cred_handle, s, key_name,
1144 	    dns_hostname, oid))
1145 		gss_context = NULL;
1146 
1147 	(void) close(s);
1148 	return (gss_context);
1149 }
1150 
1151 /*
1152  * dyndns_build_add_remove_msg
1153  * This routine builds the update request message for adding and removing DNS
1154  * entries which is used for non-secure and secure DNS update.
1155  * This routine builds an UDP message.
1156  * Parameters:
1157  *   buf        : buffer to build message
1158  *   update_zone: the type of zone to update, use UPDATE_FORW for forward
1159  *                lookup zone, use UPDATE_REV for reverse lookup zone
1160  *   hostname   : fully qualified hostname to update DNS with
1161  *   ip_addr    : IP address of hostname
1162  *   life_time  : cached time of this entry by others and not within DNS
1163  *                database
1164  *   update_type: UPDATE_ADD to add entry, UPDATE_DEL to remove entry
1165  *   del_type   : DEL_ONE for deleting one entry, DEL_ALL for deleting all
1166  *                entries of the same resource name.  Only valid for UPDATE_DEL.
1167  *   addit_cnt  : Indicate how many record is in the additional section of
1168  *                the DNS message.  A value of zero is always used with
1169  *                non-secure update message. For secure update message,
1170  *                the value will be one because the signed TSIG message
1171  *                is added as the additional record of the DNS update message.
1172  *   id         : DNS message ID.  If a positive value then this ID value is
1173  *                used, otherwise the next incremented value is used
1174  *   level      : This is the domain level which we send the request to, level
1175  *                zero is the default level, it can go upto 2 in reverse zone
1176  *                and virtually to any level in forward zone.
1177  * Returns:
1178  *   buf      : buffer containing update message
1179  *   id       : DNS message ID
1180  *   int      : size of update message
1181  *   -1       : error
1182  *
1183  * This function is changed to handle dynamic DNS update retires to higher
1184  * authoritative domains.
1185  */
1186 static int
1187 dyndns_build_add_remove_msg(char *buf, int update_zone, const char *hostname,
1188 	const char *ip_addr, int life_time, int update_type, int del_type,
1189 	int addit_cnt, uint16_t *id, int level)
1190 {
1191 	int a, b, c, d;
1192 	char *bufptr;
1193 	int queryReq, zoneCount, preqCount, updateCount, additionalCount;
1194 	char *zone, *resource, *data, zone_buf[100], resrc_buf[100];
1195 	int zoneType, zoneClass, type, class, ttl;
1196 	char *p;
1197 
1198 	queryReq = REQ_UPDATE;
1199 	zoneCount = 1;
1200 	preqCount = 0;
1201 	updateCount = 1;
1202 	additionalCount = addit_cnt;
1203 
1204 	(void) memset(buf, 0, NS_PACKETSZ);
1205 	bufptr = buf;
1206 
1207 	if (*id == 0)
1208 		*id = dyndns_get_msgid();
1209 
1210 	if (dyndns_build_header(&bufptr, BUFLEN_UDP(bufptr, buf), *id, queryReq,
1211 	    zoneCount, preqCount, updateCount, additionalCount, 0) == -1) {
1212 		return (-1);
1213 	}
1214 
1215 	zoneType = ns_t_soa;
1216 	zoneClass = ns_c_in;
1217 
1218 	if (update_zone == UPDATE_FORW) {
1219 		p = (char *)hostname;
1220 
1221 		/* Try higher domains according to the level requested */
1222 		do {
1223 			/* domain */
1224 			if ((zone = (char *)strchr(p, '.')) == NULL)
1225 				return (-1);
1226 			zone += 1;
1227 			p = zone;
1228 		} while (--level >= 0);
1229 		resource = (char *)hostname;
1230 		data = (char *)ip_addr;
1231 	} else {
1232 		(void) sscanf(ip_addr, "%d.%d.%d.%d", &a, &b, &c, &d);
1233 		(void) sprintf(zone_buf, "%d.%d.%d.in-addr.arpa", c, b, a);
1234 		zone = p = zone_buf;
1235 
1236 		/* Try higher domains according to the level requested */
1237 		while (--level >= 0) {
1238 			/* domain */
1239 			if ((zone = (char *)strchr(p, '.')) == NULL) {
1240 				return (-1);
1241 			}
1242 			zone += 1;
1243 			p = zone;
1244 		}
1245 
1246 		(void) sprintf(resrc_buf, "%d.%d.%d.%d.in-addr.arpa",
1247 		    d, c, b, a);
1248 		resource = resrc_buf;	/* ip info */
1249 		data = (char *)hostname;
1250 	}
1251 
1252 	if (dyndns_build_quest_zone(&bufptr, BUFLEN_UDP(bufptr, buf), zone,
1253 	    zoneType, zoneClass) == -1) {
1254 		return (-1);
1255 	}
1256 
1257 	if (update_zone == UPDATE_FORW)
1258 		type = ns_t_a;
1259 	else
1260 		type = ns_t_ptr;
1261 
1262 	if (update_type == UPDATE_ADD) {
1263 		class = ns_c_in;
1264 		ttl = life_time;
1265 	} else {
1266 		if (del_type == DEL_ONE)
1267 			class = ns_c_none;	/* remove one */
1268 		else
1269 			class = ns_c_any;	/* remove all */
1270 		ttl = 0;
1271 	}
1272 	if (dyndns_build_update(&bufptr, BUFLEN_UDP(bufptr, buf),
1273 	    resource, type, class, ttl, data, update_zone,
1274 	    update_type, del_type) == -1) {
1275 		return (-1);
1276 	}
1277 
1278 	return (bufptr - buf);
1279 }
1280 
1281 /*
1282  * dyndns_build_unsigned_tsig_msg
1283  * This routine is used to build the unsigned TSIG message for signing.  The
1284  * unsigned TSIG message contains the update request message with certain TSIG
1285  * fields included.  An error time of 300 seconds is used for fudge time.  This
1286  * is the number used by Microsoft clients.
1287  * This routine builds a UDP message.
1288  * Parameters:
1289  *   buf        : buffer to build message
1290  *   update_zone: the type of zone to update, use UPDATE_FORW for forward
1291  *                lookup zone, use UPDATE_REV for reverse lookup zone
1292  *   hostname   : fully qualified hostname to update DNS with
1293  *   ip_addr    : IP address of hostname
1294  *   life_time  : cached time of this entry by others and not within DNS
1295  *                database
1296  *   update_type: UPDATE_ADD to add entry, UPDATE_DEL to remove entry
1297  *   del_type   : DEL_ONE for deleting one entry, DEL_ALL for deleting all
1298  *                entries of the same resource name.  Only valid for UPDATE_DEL.
1299  *   key_name   : same key name used in TKEY message
1300  *   id         : DNS message ID.  If a positive value then this ID value is
1301  *                used, otherwise the next incremented value is used
1302  *   level      : This is the domain level which we send the request to, level
1303  *                zero is the default level, it can go upto 2 in reverse zone
1304  *                and virtually to any level in forward zone.
1305  * Returns:
1306  *   buf      : buffer containing update message
1307  *   id       : DNS message ID
1308  *   int      : size of update message
1309  *   -1       : error
1310  */
1311 static int
1312 dyndns_build_unsigned_tsig_msg(char *buf, int update_zone, const char *hostname,
1313 	const char *ip_addr, int life_time, int update_type, int del_type,
1314 	char *key_name, uint16_t *id, int level)
1315 {
1316 	char *bufptr;
1317 	int buf_sz;
1318 
1319 	if ((buf_sz = dyndns_build_add_remove_msg(buf, update_zone, hostname,
1320 	    ip_addr, life_time, update_type, del_type, 0, id, level)) <= 0) {
1321 		return (-1);
1322 	}
1323 
1324 	bufptr = buf + buf_sz;
1325 
1326 	if (dyndns_build_tsig(&bufptr, BUFLEN_UDP(bufptr, buf), 0,
1327 	    key_name, 300, NULL, 0, TSIG_UNSIGNED) == -1) {
1328 		return (-1);
1329 	}
1330 
1331 	return (bufptr - buf);
1332 }
1333 
1334 /*
1335  * dyndns_build_signed_tsig_msg
1336  * This routine build the signed TSIG message which contains the update
1337  * request message encrypted.  An error time of 300 seconds is used for fudge
1338  * time.  This is the number used by Microsoft clients.
1339  * This routine builds a UDP message.
1340  * Parameters:
1341  *   buf        : buffer to build message
1342  *   update_zone: the type of zone to update, use UPDATE_FORW for forward
1343  *                lookup zone, use UPDATE_REV for reverse lookup zone
1344  *   hostname   : fully qualified hostname to update DNS with
1345  *   ip_addr    : IP address of hostname
1346  *   life_time  : cached time of this entry by others and not within DNS
1347  *                database
1348  *   update_type: UPDATE_ADD to add entry, UPDATE_DEL to remove entry
1349  *   del_type   : DEL_ONE for deleting one entry, DEL_ALL for deleting all
1350  *                entries of the same resource name.  Only valid for UPDATE_DEL.
1351  *   key_name   : same key name used in TKEY message
1352  *   id         : DNS message ID.  If a positive value then this ID value is
1353  *                used, otherwise the next incremented value is used
1354  *   in_mic     : the update request message encrypted
1355  *   level      : This is the domain level which we send the request to, level
1356  *                zero is the default level, it can go upto 2 in reverse zone
1357  *                and virtually to any level in forward zone.
1358  *
1359  * Returns:
1360  *   buf      : buffer containing update message
1361  *   id       : DNS message ID
1362  *   int      : size of update message
1363  *   -1       : error
1364  */
1365 static int
1366 dyndns_build_signed_tsig_msg(char *buf, int update_zone, const char *hostname,
1367 	const char *ip_addr, int life_time, int update_type, int del_type,
1368 	char *key_name, uint16_t *id, gss_buffer_desc *in_mic, int level)
1369 {
1370 	char *bufptr;
1371 	int buf_sz;
1372 
1373 	if ((buf_sz = dyndns_build_add_remove_msg(buf, update_zone, hostname,
1374 	    ip_addr, life_time, update_type, del_type, 1, id, level)) <= 0) {
1375 		return (-1);
1376 	}
1377 
1378 	bufptr = buf + buf_sz;
1379 
1380 	if (dyndns_build_tsig(&bufptr, BUFLEN_UDP(bufptr, buf),
1381 	    *id, key_name, 300, in_mic->value,
1382 	    in_mic->length, TSIG_SIGNED) == -1) {
1383 		return (-1);
1384 	}
1385 
1386 	return (bufptr - buf);
1387 }
1388 
1389 /*
1390  * dyndns_udp_send_recv
1391  * This routine sends and receives UDP DNS request and reply messages.
1392  *
1393  * Pre-condition: Caller must call dyndns_open_init_socket() before calling
1394  * this function.
1395  *
1396  * Parameters:
1397  *   s        : socket descriptor
1398  *   buf      : buffer containing data to send
1399  *   buf_sz   : size of data to send
1400  * Returns:
1401  *   -1     : error
1402  *   rec_buf: reply dat
1403  *    0     : success
1404  */
1405 static int
1406 dyndns_udp_send_recv(int s, char *buf, int buf_sz, char *rec_buf)
1407 {
1408 	int i, retval, addr_len;
1409 	struct timeval tv, timeout;
1410 	fd_set rfds;
1411 	struct sockaddr_in from_addr;
1412 
1413 	timeout.tv_usec = 0;
1414 	timeout.tv_sec = DYNDNS_QUERY_TIMEOUT;
1415 
1416 	for (i = 0; i <= DYNDNS_MAX_QUERY_RETRIES; i++) {
1417 		if (send(s, buf, buf_sz, 0) == -1) {
1418 			syslog(LOG_ERR, "dyndns: UDP send error (%s)",
1419 			    strerror(errno));
1420 			return (-1);
1421 		}
1422 
1423 		FD_ZERO(&rfds);
1424 		FD_SET(s, &rfds);
1425 
1426 		tv = timeout;
1427 
1428 		retval = select(s+1, &rfds, NULL, NULL, &tv);
1429 
1430 		if (retval == -1) {
1431 			return (-1);
1432 		} else if (retval > 0) {
1433 			bzero(rec_buf, NS_PACKETSZ);
1434 			/* required by recvfrom */
1435 			addr_len = sizeof (struct sockaddr_in);
1436 			if (recvfrom(s, rec_buf, NS_PACKETSZ, 0,
1437 			    (struct sockaddr *)&from_addr, &addr_len) == -1) {
1438 				syslog(LOG_ERR, "dyndns: UDP recv error");
1439 				return (-1);
1440 			}
1441 			break;
1442 		}
1443 	}
1444 
1445 	/* did not receive anything */
1446 	if (i == (DYNDNS_MAX_QUERY_RETRIES + 1)) {
1447 		syslog(LOG_ERR, "dyndns: max retries for UDP recv reached");
1448 		return (-1);
1449 	}
1450 
1451 	return (0);
1452 }
1453 
1454 /*
1455  * dyndns_sec_add_remove_entry
1456  * Perform secure dynamic DNS update after getting security context.
1457  * This routine opens a UDP socket to the DNS sever, gets the security context,
1458  * builds the unsigned TSIG message and signed TSIG message.  The signed TSIG
1459  * message containing the encrypted update request message is sent to the DNS
1460  * server.  The response is received and check for error.  If there is no
1461  * error then credential handle and security context are released and the local
1462  * NSS cached is purged.
1463  * Parameters:
1464  *   update_zone : UPDATE_FORW for forward zone, UPDATE_REV for reverse zone
1465  *   hostname    : fully qualified hostname
1466  *   ip_addr     : ip address of hostname in string format
1467  *   life_time   : cached time of this entry by others and not within DNS
1468  *                 database
1469  *   max_retries : maximum retries for sending DNS update request
1470  *   recv_timeout: receive timeout
1471  *   update_type : UPDATE_ADD for adding entry, UPDATE_DEL for removing entry
1472  *   del_type    : DEL_ONE for deleting one entry, DEL_ALL for deleting all
1473  *                 entries of the same resource name.  Only valid for UPDATE_DEL
1474  *   dns_str     : DNS IP address in string format
1475  * Returns:
1476  *   -1: error
1477  *    0: success
1478  *
1479  * This function is enhanced to handle the case of NOTAUTH error when DNS server
1480  * is not authoritative for specified zone. In this case we need to resend the
1481  * same request to the higher authoritative domains.
1482  * This is true for both secure and unsecure dynamic DNS updates.
1483  */
1484 static int
1485 dyndns_sec_add_remove_entry(int update_zone, const char *hostname,
1486     const char *ip_addr, int life_time, int update_type, int del_type,
1487     char *dns_str)
1488 {
1489 	int s2;
1490 	uint16_t id, rid;
1491 	char buf[NS_PACKETSZ], buf2[NS_PACKETSZ];
1492 	int ret;
1493 	OM_uint32 min, maj;
1494 	gss_buffer_desc in_mic, out_mic;
1495 	gss_ctx_id_t gss_context;
1496 	int dns_ip;
1497 	char *key_name;
1498 	int buf_sz;
1499 	int level = 0;
1500 
1501 	assert(dns_str);
1502 	assert(*dns_str);
1503 
1504 	dns_ip = inet_addr(dns_str);
1505 
1506 sec_retry_higher:
1507 
1508 	if ((gss_context = dyndns_get_sec_context(hostname,
1509 	    dns_ip)) == NULL) {
1510 		return (-1);
1511 	}
1512 
1513 	key_name = (char *)hostname;
1514 
1515 	if ((s2 = dyndns_open_init_socket(SOCK_DGRAM, dns_ip, 53)) < 0) {
1516 		if (gss_context != GSS_C_NO_CONTEXT)
1517 			(void) gss_delete_sec_context(&min, &gss_context, NULL);
1518 		return (-1);
1519 	}
1520 
1521 	id = 0;
1522 	if ((buf_sz = dyndns_build_unsigned_tsig_msg(buf, update_zone, hostname,
1523 	    ip_addr, life_time, update_type, del_type,
1524 	    key_name, &id, level)) <= 0) {
1525 		(void) close(s2);
1526 		if (gss_context != GSS_C_NO_CONTEXT)
1527 			(void) gss_delete_sec_context(&min, &gss_context, NULL);
1528 		return (-1);
1529 	}
1530 
1531 	in_mic.length = buf_sz;
1532 	in_mic.value = buf;
1533 
1534 	/* sign update message */
1535 	if ((maj = gss_get_mic(&min, gss_context, 0, &in_mic, &out_mic)) !=
1536 	    GSS_S_COMPLETE) {
1537 		display_stat(maj, min);
1538 		(void) close(s2);
1539 		if (gss_context != GSS_C_NO_CONTEXT)
1540 			(void) gss_delete_sec_context(&min, &gss_context, NULL);
1541 		return (-1);
1542 	}
1543 
1544 	if ((buf_sz = dyndns_build_signed_tsig_msg(buf, update_zone, hostname,
1545 	    ip_addr, life_time, update_type, del_type, key_name, &id,
1546 	    &out_mic, level)) <= 0) {
1547 		(void) close(s2);
1548 		(void) gss_release_buffer(&min, &out_mic);
1549 		if (gss_context != GSS_C_NO_CONTEXT)
1550 			(void) gss_delete_sec_context(&min, &gss_context, NULL);
1551 		return (-1);
1552 	}
1553 
1554 	(void) gss_release_buffer(&min, &out_mic);
1555 
1556 	if (dyndns_udp_send_recv(s2, buf, buf_sz, buf2)) {
1557 		(void) close(s2);
1558 		if (gss_context != GSS_C_NO_CONTEXT)
1559 			(void) gss_delete_sec_context(&min, &gss_context, NULL);
1560 		return (-1);
1561 	}
1562 
1563 	(void) close(s2);
1564 
1565 	if (gss_context != GSS_C_NO_CONTEXT)
1566 		(void) gss_delete_sec_context(&min, &gss_context, NULL);
1567 
1568 	ret = buf2[3] & 0xf;	/* error field in UDP */
1569 
1570 	/*
1571 	 * If it is a NOTAUTH error we should retry with higher domains
1572 	 * until we get a successful reply or the maximum retries is met.
1573 	 */
1574 	if (ret == NOTAUTH && level++ < MAX_AUTH_RETRIES)
1575 		goto sec_retry_higher;
1576 
1577 	/* check here for update request is successful */
1578 	if (ret != NOERROR) {
1579 		dyndns_syslog(LOG_ERR, ret, "TSIG reply");
1580 		return (-1);
1581 	}
1582 
1583 	(void) dyndns_get_nshort(buf2, &rid);
1584 	if (id != rid)
1585 		return (-1);
1586 
1587 	return (0);
1588 }
1589 
1590 /*
1591  * dyndns_seach_entry
1592  * Query DNS server for entry.  This routine can indicate if an entry exist
1593  * or not during forward or reverse lookup.  Also can indicate if the data
1594  * of the entry matched.  For example, for forward lookup, the entry is
1595  * searched using the hostname and the data is the IP address.  For reverse
1596  * lookup, the entry is searched using the IP address and the data is the
1597  * hostname.
1598  * Parameters:
1599  *   update_zone: UPDATE_FORW for forward zone, UPDATE_REV for reverse zone
1600  *   hostname   : fully qualified hostname
1601  *   ip_addr    : ip address of hostname in string format
1602  *   update_type: UPDATE_ADD for adding entry, UPDATE_DEL for removing entry
1603  * Returns:
1604  *   time_out: no use
1605  *   is_match: is 1 for found matching entry, otherwise 0
1606  *   1       : an entry exist but not necessarily match
1607  *   0       : an entry does not exist
1608  */
1609 /*ARGSUSED*/
1610 static int
1611 dyndns_search_entry(int update_zone, const char *hostname, const char *ip_addr,
1612     int update_type, struct timeval *time_out, int *is_match)
1613 {
1614 	struct hostent *hentry;
1615 	struct in_addr in;
1616 	in_addr_t ip;
1617 	int i;
1618 
1619 	*is_match = 0;
1620 	if (update_zone == UPDATE_FORW) {
1621 		hentry = gethostbyname(hostname);
1622 		if (hentry) {
1623 			ip = inet_addr(ip_addr);
1624 			for (i = 0; hentry->h_addr_list[i]; i++) {
1625 				(void) memcpy(&in.s_addr,
1626 				    hentry->h_addr_list[i], sizeof (in.s_addr));
1627 				if (ip == in.s_addr) {
1628 					*is_match = 1;
1629 					break;
1630 				}
1631 			}
1632 			return (1);
1633 		}
1634 	} else {
1635 		int dns_ip = inet_addr(ip_addr);
1636 		hentry = gethostbyaddr((char *)&dns_ip, 4, AF_INET);
1637 		if (hentry) {
1638 			if (strncasecmp(hentry->h_name, hostname,
1639 			    strlen(hostname)) == 0) {
1640 				*is_match = 1;
1641 			}
1642 			return (1);
1643 		}
1644 	}
1645 
1646 	/* entry does not exist */
1647 	return (0);
1648 }
1649 
1650 /*
1651  * dyndns_add_remove_entry
1652  * Perform non-secure dynamic DNS update.  If it fails and host principal
1653  * keys can be found in the local keytab file, secure update will be performed.
1654  *
1655  * This routine opens a UDP socket to the DNS sever, build the update request
1656  * message, and sends the message to the DNS server.  The response is received
1657  * and check for error.  If there is no error then the local NSS cached is
1658  * purged.  DNS may be used to check to see if an entry already exist before
1659  * adding or to see if an entry does exist before removing it.  Adding
1660  * duplicate entries or removing non-existing entries does not cause any
1661  * problems.  DNS is not check when doing a delete all.
1662  * Parameters:
1663  *   update_zone: UPDATE_FORW for forward zone, UPDATE_REV for reverse zone
1664  *   hostname   : fully qualified hostname
1665  *   ip_addr    : ip address of hostname in string format
1666  *   life_time  : cached time of this entry by others and not within DNS
1667  *                database
1668  *   update_type: UPDATE_ADD to add entry, UPDATE_DEL to remove entry
1669  *   do_check   : DNS_CHECK to check first in DNS, DNS_NOCHECK for no DNS
1670  *                checking before update
1671  *   del_type   : DEL_ONE for deleting one entry, DEL_ALL for deleting all
1672  *                entries of the same resource name.  Only valid for UPDATE_DEL.
1673  *   dns_str    : DNS IP address in string format
1674  * Returns:
1675  *   -1: error
1676  *    0: success
1677  *
1678  * This function is enhanced to handle the case of NOTAUTH error when DNS server
1679  * is not authoritative for specified zone. In this case we need to resend the
1680  * same request to the higher authoritative domains.
1681  * This is true for both secure and unsecure dynamic DNS updates.
1682  */
1683 static int
1684 dyndns_add_remove_entry(int update_zone, const char *hostname,
1685     const char *ip_addr, int life_time, int update_type,
1686     int do_check, int del_type, char *dns_str)
1687 {
1688 	int s;
1689 	uint16_t id, rid;
1690 	char buf[NS_PACKETSZ], buf2[NS_PACKETSZ];
1691 	int ret, dns_ip;
1692 	int is_exist, is_match;
1693 	struct timeval timeout;
1694 	int buf_sz;
1695 	int level = 0;
1696 
1697 	assert(dns_str);
1698 	assert(*dns_str);
1699 
1700 	dns_ip = inet_addr(dns_str);
1701 
1702 	if (do_check == DNS_CHECK && del_type != DEL_ALL) {
1703 		is_exist = dyndns_search_entry(update_zone, hostname, ip_addr,
1704 		    update_type, &timeout, &is_match);
1705 
1706 		if (update_type == UPDATE_ADD && is_exist && is_match) {
1707 			return (0);
1708 		} else if (update_type == UPDATE_DEL && !is_exist) {
1709 			return (0);
1710 		}
1711 	}
1712 
1713 retry_higher:
1714 	if ((s = dyndns_open_init_socket(SOCK_DGRAM, dns_ip, 53)) < 0) {
1715 		return (-1);
1716 	}
1717 
1718 	id = 0;
1719 	if ((buf_sz = dyndns_build_add_remove_msg(buf, update_zone, hostname,
1720 	    ip_addr, life_time, update_type, del_type, 0, &id, level)) <= 0) {
1721 		(void) close(s);
1722 		return (-1);
1723 	}
1724 
1725 	if (dyndns_udp_send_recv(s, buf, buf_sz, buf2)) {
1726 		(void) close(s);
1727 		return (-1);
1728 	}
1729 
1730 	(void) close(s);
1731 
1732 	ret = buf2[3] & 0xf;	/* error field in UDP */
1733 
1734 	/*
1735 	 * If it is a NOTAUTH error we should retry with higher domains
1736 	 * until we get a successful reply
1737 	 */
1738 	if (ret == NOTAUTH && level++ < MAX_AUTH_RETRIES)
1739 		goto retry_higher;
1740 
1741 	/* check here for update request is successful */
1742 	if (ret == NOERROR) {
1743 		(void) dyndns_get_nshort(buf2, &rid);
1744 		if (id != rid)
1745 			return (-1);
1746 		return (0);
1747 	}
1748 
1749 	if (ret == NOTIMP) {
1750 		dyndns_syslog(LOG_NOTICE, NOTIMP, "dynamic updates");
1751 		return (-1);
1752 	} else if (ret == NOTAUTH) {
1753 		dyndns_syslog(LOG_NOTICE, NOTAUTH, "DNS");
1754 		return (-1);
1755 	}
1756 
1757 	if (smb_krb5_find_keytab_entries(hostname, SMBNS_KRB5_KEYTAB))
1758 		ret = dyndns_sec_add_remove_entry(update_zone, hostname,
1759 		    ip_addr, life_time, update_type, del_type, dns_str);
1760 
1761 	return (ret);
1762 }
1763 
1764 /*
1765  * dyndns_add_entry
1766  * Main routine to add an entry into DNS.  The attempt will be made on the
1767  * the servers returned by smb_get_nameserver().  Upon a successful
1768  * attempt on any one of the server, the function will exit with 0.
1769  * Otherwise, -1 is retuned to indicate the update attempt on all the
1770  * nameservers has failed.
1771  *
1772  * Parameters:
1773  *   update_zone: the type of zone to update, use UPDATE_FORW for forward
1774  *                lookup zone, use UPDATE_REV for reverse lookup zone
1775  *   hostname   : fully qualified hostname
1776  *   ip_addr    : ip address of hostname in string format
1777  *   life_time  : cached time of this entry by others and not within DNS
1778  *                database
1779  * Returns:
1780  *   -1: error
1781  *    0: success
1782  */
1783 static int
1784 dyndns_add_entry(int update_zone, const char *hostname, const char *ip_addr,
1785     int life_time)
1786 {
1787 	char *dns_str;
1788 	char *which_zone;
1789 	struct in_addr ns_list[MAXNS];
1790 	int i, cnt;
1791 	int addr, rc = 0;
1792 
1793 	if (hostname == NULL || ip_addr == NULL)
1794 		return (-1);
1795 
1796 	addr = (int)inet_addr(ip_addr);
1797 	if ((addr == -1) || (addr == 0))
1798 		return (-1);
1799 
1800 	cnt = smb_get_nameservers(ns_list, MAXNS);
1801 
1802 	for (i = 0; i < cnt; i++) {
1803 		dns_str = inet_ntoa(ns_list[i]);
1804 		if ((dns_str == NULL) ||
1805 		    (strcmp(dns_str, "0.0.0.0") == 0)) {
1806 			continue;
1807 		}
1808 
1809 		which_zone = (update_zone == UPDATE_FORW) ?
1810 		    "forward" : "reverse";
1811 
1812 		syslog(LOG_DEBUG, "dyndns %s lookup zone update %s (%s)",
1813 		    which_zone, hostname, ip_addr);
1814 
1815 		if (dyndns_add_remove_entry(update_zone, hostname,
1816 		    ip_addr, life_time,
1817 		    UPDATE_ADD, DNS_NOCHECK, DEL_NONE, dns_str) != -1) {
1818 			rc = 1;
1819 			break;
1820 		}
1821 	}
1822 
1823 	return (rc ? 0 : -1);
1824 }
1825 
1826 /*
1827  * dyndns_remove_entry
1828  * Main routine to remove an entry or all entries of the same resource name
1829  * from DNS.  The update attempt will be made on the primary DNS server.  If
1830  * there is a failure then another attempt will be made on the secondary DNS
1831  * server.
1832  * Parameters:
1833  *   update_zone: the type of zone to update, use UPDATE_FORW for forward
1834  *                lookup zone, use UPDATE_REV for reverse lookup zone
1835  *   hostname   : fully qualified hostname
1836  *   ip_addr    : ip address of hostname in string format
1837  *   del_type   : DEL_ONE for deleting one entry, DEL_ALL for deleting all
1838  *                entries of the same resource name.  Only valid for UPDATE_DEL
1839  * Returns:
1840  *   -1: error
1841  *    0: success
1842  */
1843 static int
1844 dyndns_remove_entry(int update_zone, const char *hostname, const char *ip_addr,
1845 	int del_type)
1846 {
1847 	char *dns_str;
1848 	char *which_zone;
1849 	struct in_addr ns_list[MAXNS];
1850 	int i, cnt, scnt;
1851 	int addr;
1852 
1853 	if ((hostname == NULL || ip_addr == NULL)) {
1854 		return (-1);
1855 	}
1856 
1857 	addr = (int)inet_addr(ip_addr);
1858 	if ((addr == -1) || (addr == 0)) {
1859 		return (-1);
1860 	}
1861 
1862 	cnt = smb_get_nameservers(ns_list, MAXNS);
1863 	scnt = 0;
1864 
1865 	for (i = 0; i < cnt; i++) {
1866 		dns_str = inet_ntoa(ns_list[i]);
1867 		if ((dns_str == NULL) ||
1868 		    (strcmp(dns_str, "0.0.0.0") == 0)) {
1869 			continue;
1870 		}
1871 
1872 		which_zone = (update_zone == UPDATE_FORW) ?
1873 		    "forward" : "reverse";
1874 
1875 		if (del_type == DEL_ONE) {
1876 			syslog(LOG_DEBUG,
1877 			    "dyndns %s lookup zone remove %s (%s)",
1878 			    which_zone, hostname, ip_addr);
1879 		} else {
1880 			syslog(LOG_DEBUG,
1881 			    "dyndns %s lookup zone remove all %s",
1882 			    which_zone, hostname);
1883 		}
1884 
1885 		if (dyndns_add_remove_entry(update_zone, hostname, ip_addr, 0,
1886 		    UPDATE_DEL, DNS_NOCHECK, del_type, dns_str) != -1) {
1887 			scnt++;
1888 			break;
1889 		}
1890 	}
1891 	if (scnt)
1892 		return (0);
1893 	return (-1);
1894 }
1895 
1896 /*
1897  * dyndns_update_core
1898  * Perform dynamic update on both forward and reverse lookup zone using
1899  * the specified hostname and IP addresses.  Before updating DNS, existing
1900  * host entries with the same hostname in the forward lookup zone are removed
1901  * and existing pointer entries with the same IP addresses in the reverse
1902  * lookup zone are removed.  After DNS update, host entries for current
1903  * hostname will show current IP addresses and pointer entries for current
1904  * IP addresses will show current hostname.
1905  * Parameters:
1906  *  fqhn - fully-qualified hostname
1907  *
1908  * Returns:
1909  *   -1: some dynamic DNS updates errors
1910  *    0: successful or DDNS disabled.
1911  */
1912 static int
1913 dyndns_update_core(char *fqdn)
1914 {
1915 	int forw_update_ok, error;
1916 	char *my_ip;
1917 	struct in_addr addr;
1918 	smb_niciter_t ni;
1919 	int rc;
1920 	char fqhn[MAXHOSTNAMELEN];
1921 
1922 	if (fqdn == NULL || *fqdn == '\0')
1923 		return (0);
1924 
1925 	if (!smb_config_getbool(SMB_CI_DYNDNS_ENABLE))
1926 		return (0);
1927 
1928 	if (smb_gethostname(fqhn, MAXHOSTNAMELEN, 0) != 0)
1929 		return (-1);
1930 
1931 	(void) snprintf(fqhn, MAXHOSTNAMELEN, "%s.%s", fqhn, fqdn);
1932 	error = 0;
1933 	forw_update_ok = 0;
1934 
1935 	/*
1936 	 * Dummy IP is okay since we are removing all using the hostname.
1937 	 */
1938 	if (dyndns_remove_entry(UPDATE_FORW, fqhn, "1.1.1.1", DEL_ALL) == 0) {
1939 		forw_update_ok = 1;
1940 	} else {
1941 		error++;
1942 	}
1943 
1944 	if (smb_nic_getfirst(&ni) != 0)
1945 		return (-1);
1946 
1947 	do {
1948 		if (ni.ni_nic.nic_sysflags & (IFF_STANDBY | IFF_PRIVATE))
1949 			continue;
1950 
1951 		addr.s_addr = ni.ni_nic.nic_ip;
1952 		my_ip = (char *)strdup(inet_ntoa(addr));
1953 		if (my_ip == NULL) {
1954 			error++;
1955 			continue;
1956 		}
1957 
1958 		if (forw_update_ok) {
1959 			rc = dyndns_add_entry(UPDATE_FORW, fqhn, my_ip,
1960 			    DDNS_TTL);
1961 
1962 			if (rc == -1)
1963 				error++;
1964 		}
1965 
1966 		rc = dyndns_remove_entry(UPDATE_REV, fqhn, my_ip, DEL_ALL);
1967 		if (rc == 0) {
1968 			rc = dyndns_add_entry(UPDATE_REV, fqhn, my_ip,
1969 			    DDNS_TTL);
1970 		}
1971 
1972 		if (rc == -1)
1973 			error++;
1974 
1975 		(void) free(my_ip);
1976 	} while (smb_nic_getnext(&ni) == 0);
1977 
1978 	return ((error == 0) ? 0 : -1);
1979 }
1980 
1981 /*
1982  * dyndns_clear_rev_zone
1983  * Clear the rev zone records. Must be called to clear the OLD if list
1984  * of down records prior to updating the list with new information.
1985  *
1986  * Parameters:
1987  *   fqhn - fully-qualified hostname
1988  * Returns:
1989  *   -1: some dynamic DNS updates errors
1990  *    0: successful or DDNS disabled.
1991  */
1992 static int
1993 dyndns_clear_rev_zone(char *fqdn)
1994 {
1995 	int error;
1996 	char *my_ip;
1997 	struct in_addr addr;
1998 	smb_niciter_t ni;
1999 	int rc;
2000 	char fqhn[MAXHOSTNAMELEN];
2001 
2002 	if (!smb_config_getbool(SMB_CI_DYNDNS_ENABLE))
2003 		return (0);
2004 
2005 	if (smb_gethostname(fqhn, MAXHOSTNAMELEN, 0) != 0)
2006 		return (-1);
2007 
2008 	(void) snprintf(fqhn, MAXHOSTNAMELEN, "%s.%s", fqhn, fqdn);
2009 	error = 0;
2010 
2011 	if (smb_nic_getfirst(&ni) != 0)
2012 		return (-1);
2013 
2014 	do {
2015 		if (ni.ni_nic.nic_sysflags & (IFF_STANDBY | IFF_PRIVATE))
2016 			continue;
2017 
2018 		addr.s_addr = ni.ni_nic.nic_ip;
2019 		my_ip = (char *)strdup(inet_ntoa(addr));
2020 		if (my_ip == NULL) {
2021 			error++;
2022 			continue;
2023 		}
2024 
2025 		rc = dyndns_remove_entry(UPDATE_REV, fqhn, my_ip, DEL_ALL);
2026 		if (rc != 0)
2027 			error++;
2028 
2029 		(void) free(my_ip);
2030 	} while (smb_nic_getnext(&ni) == 0);
2031 
2032 	return ((error == 0) ? 0 : -1);
2033 }
2034