xref: /titanic_51/usr/src/lib/smbsrv/libsmbns/common/smbns_dyndns.c (revision 26a04d6751d8dbdcf709884101e1cb3d48723af2)
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 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 sockaddr_in serv_addr;
857 
858 	if ((s = socket(AF_INET, sock_type, 0)) == -1) {
859 		syslog(LOG_ERR, "dyndns: socket error");
860 		return (-1);
861 	}
862 
863 	bzero(&my_addr, sizeof (my_addr));
864 	my_addr.sin_family = AF_INET;
865 	my_addr.sin_port = htons(0);
866 	my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
867 
868 	if (bind(s, (struct sockaddr *)&my_addr, sizeof (my_addr)) < 0) {
869 		syslog(LOG_ERR, "dyndns: client bind error");
870 		(void) close(s);
871 		return (-1);
872 	}
873 
874 	serv_addr.sin_family = AF_INET;
875 	serv_addr.sin_port = htons(port);
876 	serv_addr.sin_addr.s_addr = dest_addr;
877 
878 	if (connect(s, (struct sockaddr *)&serv_addr,
879 	    sizeof (struct sockaddr_in)) < 0) {
880 		syslog(LOG_ERR, "dyndns: client connect error (%s)",
881 		    strerror(errno));
882 		(void) close(s);
883 		return (-1);
884 	}
885 
886 	return (s);
887 }
888 
889 /*
890  * dyndns_build_tkey_msg
891  * This routine is used to build the TKEY message to transmit GSS tokens
892  * during GSS security context establishment for secure DNS update.  The
893  * TKEY message format uses the DNS query message format.  The TKEY section
894  * is the answer section of the query message format.
895  * Microsoft uses a value of 86400 seconds (24 hours) for key expiration time.
896  * Parameters:
897  *   buf     : buffer to build and store TKEY message
898  *   key_name: a unique key name, this same key name must be also be used in
899  *             the TSIG message
900  *   out_tok : TKEY message data (GSS tokens)
901  * Returns:
902  *   id          : message id of this TKEY message
903  *   message size: the size of the TKEY message
904  *   -1          : error
905  */
906 static int
907 dyndns_build_tkey_msg(char *buf, char *key_name, uint16_t *id,
908 	gss_buffer_desc *out_tok)
909 {
910 	int queryReq, zoneCount, preqCount, updateCount, additionalCount;
911 	int zoneType, zoneClass;
912 	char *bufptr;
913 
914 	queryReq = REQ_QUERY;
915 	/* query section of query request */
916 	zoneCount = 1;
917 	/* answer section of query request */
918 	preqCount = 1;
919 	updateCount = 0;
920 	additionalCount = 0;
921 
922 	(void) memset(buf, 0, MAX_TCP_SIZE);
923 	bufptr = buf;
924 	*id = dyndns_get_msgid();
925 
926 	/* add TCP length info that follows this field */
927 	bufptr = dyndns_put_nshort(bufptr,
928 	    26 + (strlen(key_name)+2)*2 + 35 + out_tok->length);
929 
930 	if (dyndns_build_header(&bufptr, BUFLEN_TCP(bufptr, buf), *id, queryReq,
931 	    zoneCount, preqCount, updateCount, additionalCount, 0) == -1) {
932 		return (-1);
933 	}
934 
935 	zoneType = ns_t_tkey;
936 	zoneClass = ns_c_in;
937 	if (dyndns_build_quest_zone(&bufptr, BUFLEN_TCP(bufptr, buf), key_name,
938 	    zoneType, zoneClass) == -1) {
939 		return (-1);
940 	}
941 
942 	if (dyndns_build_tkey(&bufptr, BUFLEN_TCP(bufptr, buf), key_name,
943 	    86400, out_tok->value, out_tok->length) == -1) {
944 		return (-1);
945 	}
946 
947 	return (bufptr - buf);
948 }
949 
950 /*
951  * dyndns_establish_sec_ctx
952  * This routine is used to establish a security context with the DNS server
953  * by building TKEY messages and sending them to the DNS server.  TKEY messages
954  * are also received from the DNS server for processing.   The security context
955  * establishment is done with the GSS client on the system producing a token
956  * and sending the token within the TKEY message to the GSS server on the DNS
957  * server.  The GSS server then processes the token and then send a TKEY reply
958  * message with a new token to be processed by the GSS client.  The GSS client
959  * processes the new token and then generates a new token to be sent to the
960  * GSS server.  This cycle is continued until the security establishment is
961  * done.  TCP is used to send and receive TKEY messages.
962  * Parameters:
963  *   cred_handle  : handle to credential
964  *   s           : socket descriptor to DNS server
965  *   key_name    : TKEY key name
966  *   dns_hostname: fully qualified DNS hostname
967  *   oid         : contains Kerberos 5 object identifier
968  * Returns:
969  *   gss_context    : handle to security context
970  */
971 static int
972 dyndns_establish_sec_ctx(gss_ctx_id_t *gss_context, gss_cred_id_t cred_handle,
973     int s, char *key_name, char *dns_hostname, gss_OID oid)
974 {
975 	uint16_t id, rid, rsz;
976 	char buf[MAX_TCP_SIZE], buf2[MAX_TCP_SIZE];
977 	int ret;
978 	char *service_name, *tmpptr;
979 	int service_sz;
980 	OM_uint32 min, maj, time_rec;
981 	gss_buffer_desc service_buf, in_tok, out_tok;
982 	gss_name_t target_name;
983 	gss_buffer_desc *inputptr;
984 	int gss_flags;
985 	OM_uint32 ret_flags;
986 	int buf_sz;
987 
988 	service_sz = strlen(dns_hostname) + 5;
989 	service_name = (char *)malloc(sizeof (char) * service_sz);
990 	if (service_name == NULL)
991 		return (-1);
992 
993 	(void) snprintf(service_name, service_sz, "DNS@%s", dns_hostname);
994 	service_buf.value = service_name;
995 	service_buf.length = strlen(service_name)+1;
996 	if ((maj = gss_import_name(&min, &service_buf,
997 	    GSS_C_NT_HOSTBASED_SERVICE, &target_name)) != GSS_S_COMPLETE) {
998 		display_stat(maj, min);
999 		(void) free(service_name);
1000 		return (-1);
1001 	}
1002 	(void) free(service_name);
1003 
1004 	inputptr = GSS_C_NO_BUFFER;
1005 	*gss_context = GSS_C_NO_CONTEXT;
1006 	gss_flags = GSS_C_MUTUAL_FLAG | GSS_C_DELEG_FLAG | GSS_C_REPLAY_FLAG |
1007 	    GSS_C_SEQUENCE_FLAG | GSS_C_CONF_FLAG | GSS_C_INTEG_FLAG;
1008 	do {
1009 		maj = gss_init_sec_context(&min, cred_handle, gss_context,
1010 		    target_name, oid, gss_flags, 0, NULL, inputptr, NULL,
1011 		    &out_tok, &ret_flags, &time_rec);
1012 
1013 		if (maj != GSS_S_COMPLETE && maj != GSS_S_CONTINUE_NEEDED) {
1014 			assert(gss_context);
1015 			if (*gss_context != GSS_C_NO_CONTEXT)
1016 				(void) gss_delete_sec_context(&min,
1017 				    gss_context, NULL);
1018 
1019 			display_stat(maj, min);
1020 			(void) gss_release_name(&min, &target_name);
1021 			return (-1);
1022 		}
1023 
1024 		if ((maj == GSS_S_COMPLETE) &&
1025 		    !(ret_flags & GSS_C_REPLAY_FLAG)) {
1026 			syslog(LOG_ERR, "dyndns: No GSS_C_REPLAY_FLAG");
1027 			if (out_tok.length > 0)
1028 				(void) gss_release_buffer(&min, &out_tok);
1029 			(void) gss_release_name(&min, &target_name);
1030 			return (-1);
1031 		}
1032 
1033 		if ((maj == GSS_S_COMPLETE) &&
1034 		    !(ret_flags & GSS_C_MUTUAL_FLAG)) {
1035 			syslog(LOG_ERR, "dyndns: No GSS_C_MUTUAL_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 (out_tok.length > 0) {
1043 			if ((buf_sz = dyndns_build_tkey_msg(buf, key_name,
1044 			    &id, &out_tok)) <= 0) {
1045 				(void) gss_release_buffer(&min, &out_tok);
1046 				(void) gss_release_name(&min, &target_name);
1047 				return (-1);
1048 			}
1049 
1050 			(void) gss_release_buffer(&min, &out_tok);
1051 
1052 			if (send(s, buf, buf_sz, 0) == -1) {
1053 				syslog(LOG_ERR, "dyndns: TKEY send error");
1054 				(void) gss_release_name(&min, &target_name);
1055 				return (-1);
1056 			}
1057 
1058 			bzero(buf2, MAX_TCP_SIZE);
1059 			if (recv(s, buf2, MAX_TCP_SIZE, 0) == -1) {
1060 				syslog(LOG_ERR, "dyndns: TKEY recv error");
1061 				(void) gss_release_name(&min, &target_name);
1062 				return (-1);
1063 			}
1064 
1065 			ret = buf2[5] & 0xf;	/* error field in TCP */
1066 			if (ret != NOERROR) {
1067 				dyndns_syslog(LOG_ERR, ret, "TKEY reply");
1068 				(void) gss_release_name(&min, &target_name);
1069 				return (-1);
1070 			}
1071 
1072 			tmpptr = &buf2[2];
1073 			(void) dyndns_get_nshort(tmpptr, &rid);
1074 			if (id != rid) {
1075 				(void) gss_release_name(&min, &target_name);
1076 				return (-1);
1077 			}
1078 
1079 			tmpptr = &buf2[59+(strlen(key_name)+2)*2];
1080 			(void) dyndns_get_nshort(tmpptr, &rsz);
1081 			in_tok.length = rsz;
1082 
1083 			/* bsd38 -> 2*7=14 */
1084 			in_tok.value = &buf2[61+(strlen(key_name)+2)*2];
1085 			inputptr = &in_tok;
1086 		}
1087 
1088 	} while (maj != GSS_S_COMPLETE);
1089 
1090 	(void) gss_release_name(&min, &target_name);
1091 
1092 	return (0);
1093 }
1094 
1095 /*
1096  * dyndns_get_sec_context
1097  * Get security context for secure dynamic DNS update.  This routine opens
1098  * a TCP socket to the DNS server and establishes a security context with
1099  * the DNS server using host principal to perform secure dynamic DNS update.
1100  * Parameters:
1101  *   hostname: fully qualified hostname
1102  *   dns_ip  : ip address of hostname in network byte order
1103  * Returns:
1104  *   gss_handle: gss credential handle
1105  *   gss_context: gss security context
1106  *   -1: error
1107  *    0: success
1108  */
1109 static gss_ctx_id_t
1110 dyndns_get_sec_context(const char *hostname, int dns_ip)
1111 {
1112 	int s;
1113 	gss_cred_id_t cred_handle;
1114 	gss_ctx_id_t gss_context;
1115 	gss_OID oid;
1116 	struct hostent *hentry;
1117 	char *key_name, dns_hostname[MAXHOSTNAMELEN];
1118 
1119 	cred_handle = GSS_C_NO_CREDENTIAL;
1120 	oid = GSS_C_NO_OID;
1121 	key_name = (char *)hostname;
1122 
1123 	hentry = gethostbyaddr((char *)&dns_ip, 4, AF_INET);
1124 	if (hentry == NULL) {
1125 		syslog(LOG_ERR, "dyndns gethostbyaddr failed");
1126 		return (NULL);
1127 	}
1128 	(void) strcpy(dns_hostname, hentry->h_name);
1129 
1130 	if ((s = dyndns_open_init_socket(SOCK_STREAM, dns_ip, 53)) < 0) {
1131 		return (NULL);
1132 	}
1133 
1134 	if (dyndns_establish_sec_ctx(&gss_context, cred_handle, s, key_name,
1135 	    dns_hostname, oid))
1136 		gss_context = NULL;
1137 
1138 	(void) close(s);
1139 	return (gss_context);
1140 }
1141 
1142 /*
1143  * dyndns_build_add_remove_msg
1144  * This routine builds the update request message for adding and removing DNS
1145  * entries which is used for non-secure and secure DNS update.
1146  * This routine builds an UDP message.
1147  * Parameters:
1148  *   buf        : buffer to build message
1149  *   update_zone: the type of zone to update, use UPDATE_FORW for forward
1150  *                lookup zone, use UPDATE_REV for reverse lookup zone
1151  *   hostname   : fully qualified hostname to update DNS with
1152  *   ip_addr    : IP address of hostname
1153  *   life_time  : cached time of this entry by others and not within DNS
1154  *                database
1155  *   update_type: UPDATE_ADD to add entry, UPDATE_DEL to remove entry
1156  *   del_type   : DEL_ONE for deleting one entry, DEL_ALL for deleting all
1157  *                entries of the same resource name.  Only valid for UPDATE_DEL.
1158  *   addit_cnt  : Indicate how many record is in the additional section of
1159  *                the DNS message.  A value of zero is always used with
1160  *                non-secure update message. For secure update message,
1161  *                the value will be one because the signed TSIG message
1162  *                is added as the additional record of the DNS update message.
1163  *   id         : DNS message ID.  If a positive value then this ID value is
1164  *                used, otherwise the next incremented value is used
1165  *   level      : This is the domain level which we send the request to, level
1166  *                zero is the default level, it can go upto 2 in reverse zone
1167  *                and virtually to any level in forward zone.
1168  * Returns:
1169  *   buf      : buffer containing update message
1170  *   id       : DNS message ID
1171  *   int      : size of update message
1172  *   -1       : error
1173  *
1174  * This function is changed to handle dynamic DNS update retires to higher
1175  * authoritative domains.
1176  */
1177 static int
1178 dyndns_build_add_remove_msg(char *buf, int update_zone, const char *hostname,
1179 	const char *ip_addr, int life_time, int update_type, int del_type,
1180 	int addit_cnt, uint16_t *id, int level)
1181 {
1182 	int a, b, c, d;
1183 	char *bufptr;
1184 	int queryReq, zoneCount, preqCount, updateCount, additionalCount;
1185 	char *zone, *resource, *data, zone_buf[100], resrc_buf[100];
1186 	int zoneType, zoneClass, type, class, ttl;
1187 	char *p;
1188 
1189 	queryReq = REQ_UPDATE;
1190 	zoneCount = 1;
1191 	preqCount = 0;
1192 	updateCount = 1;
1193 	additionalCount = addit_cnt;
1194 
1195 	(void) memset(buf, 0, NS_PACKETSZ);
1196 	bufptr = buf;
1197 
1198 	if (*id == 0)
1199 		*id = dyndns_get_msgid();
1200 
1201 	if (dyndns_build_header(&bufptr, BUFLEN_UDP(bufptr, buf), *id, queryReq,
1202 	    zoneCount, preqCount, updateCount, additionalCount, 0) == -1) {
1203 		return (-1);
1204 	}
1205 
1206 	zoneType = ns_t_soa;
1207 	zoneClass = ns_c_in;
1208 
1209 	if (update_zone == UPDATE_FORW) {
1210 		p = (char *)hostname;
1211 
1212 		/* Try higher domains according to the level requested */
1213 		do {
1214 			/* domain */
1215 			if ((zone = (char *)strchr(p, '.')) == NULL)
1216 				return (-1);
1217 			zone += 1;
1218 			p = zone;
1219 		} while (--level >= 0);
1220 		resource = (char *)hostname;
1221 		data = (char *)ip_addr;
1222 	} else {
1223 		(void) sscanf(ip_addr, "%d.%d.%d.%d", &a, &b, &c, &d);
1224 		(void) sprintf(zone_buf, "%d.%d.%d.in-addr.arpa", c, b, a);
1225 		zone = p = zone_buf;
1226 
1227 		/* Try higher domains according to the level requested */
1228 		while (--level >= 0) {
1229 			/* domain */
1230 			if ((zone = (char *)strchr(p, '.')) == NULL) {
1231 				return (-1);
1232 			}
1233 			zone += 1;
1234 			p = zone;
1235 		}
1236 
1237 		(void) sprintf(resrc_buf, "%d.%d.%d.%d.in-addr.arpa",
1238 		    d, c, b, a);
1239 		resource = resrc_buf;	/* ip info */
1240 		data = (char *)hostname;
1241 	}
1242 
1243 	if (dyndns_build_quest_zone(&bufptr, BUFLEN_UDP(bufptr, buf), zone,
1244 	    zoneType, zoneClass) == -1) {
1245 		return (-1);
1246 	}
1247 
1248 	if (update_zone == UPDATE_FORW)
1249 		type = ns_t_a;
1250 	else
1251 		type = ns_t_ptr;
1252 
1253 	if (update_type == UPDATE_ADD) {
1254 		class = ns_c_in;
1255 		ttl = life_time;
1256 	} else {
1257 		if (del_type == DEL_ONE)
1258 			class = ns_c_none;	/* remove one */
1259 		else
1260 			class = ns_c_any;	/* remove all */
1261 		ttl = 0;
1262 	}
1263 	if (dyndns_build_update(&bufptr, BUFLEN_UDP(bufptr, buf),
1264 	    resource, type, class, ttl, data, update_zone,
1265 	    update_type, del_type) == -1) {
1266 		return (-1);
1267 	}
1268 
1269 	return (bufptr - buf);
1270 }
1271 
1272 /*
1273  * dyndns_build_unsigned_tsig_msg
1274  * This routine is used to build the unsigned TSIG message for signing.  The
1275  * unsigned TSIG message contains the update request message with certain TSIG
1276  * fields included.  An error time of 300 seconds is used for fudge time.  This
1277  * is the number used by Microsoft clients.
1278  * This routine builds a UDP message.
1279  * Parameters:
1280  *   buf        : buffer to build message
1281  *   update_zone: the type of zone to update, use UPDATE_FORW for forward
1282  *                lookup zone, use UPDATE_REV for reverse lookup zone
1283  *   hostname   : fully qualified hostname to update DNS with
1284  *   ip_addr    : IP address of hostname
1285  *   life_time  : cached time of this entry by others and not within DNS
1286  *                database
1287  *   update_type: UPDATE_ADD to add entry, UPDATE_DEL to remove entry
1288  *   del_type   : DEL_ONE for deleting one entry, DEL_ALL for deleting all
1289  *                entries of the same resource name.  Only valid for UPDATE_DEL.
1290  *   key_name   : same key name used in TKEY message
1291  *   id         : DNS message ID.  If a positive value then this ID value is
1292  *                used, otherwise the next incremented value is used
1293  *   level      : This is the domain level which we send the request to, level
1294  *                zero is the default level, it can go upto 2 in reverse zone
1295  *                and virtually to any level in forward zone.
1296  * Returns:
1297  *   buf      : buffer containing update message
1298  *   id       : DNS message ID
1299  *   int      : size of update message
1300  *   -1       : error
1301  */
1302 static int
1303 dyndns_build_unsigned_tsig_msg(char *buf, int update_zone, const char *hostname,
1304 	const char *ip_addr, int life_time, int update_type, int del_type,
1305 	char *key_name, uint16_t *id, int level)
1306 {
1307 	char *bufptr;
1308 	int buf_sz;
1309 
1310 	if ((buf_sz = dyndns_build_add_remove_msg(buf, update_zone, hostname,
1311 	    ip_addr, life_time, update_type, del_type, 0, id, level)) <= 0) {
1312 		return (-1);
1313 	}
1314 
1315 	bufptr = buf + buf_sz;
1316 
1317 	if (dyndns_build_tsig(&bufptr, BUFLEN_UDP(bufptr, buf), 0,
1318 	    key_name, 300, NULL, 0, TSIG_UNSIGNED) == -1) {
1319 		return (-1);
1320 	}
1321 
1322 	return (bufptr - buf);
1323 }
1324 
1325 /*
1326  * dyndns_build_signed_tsig_msg
1327  * This routine build the signed TSIG message which contains the update
1328  * request message encrypted.  An error time of 300 seconds is used for fudge
1329  * time.  This is the number used by Microsoft clients.
1330  * This routine builds a UDP message.
1331  * Parameters:
1332  *   buf        : buffer to build message
1333  *   update_zone: the type of zone to update, use UPDATE_FORW for forward
1334  *                lookup zone, use UPDATE_REV for reverse lookup zone
1335  *   hostname   : fully qualified hostname to update DNS with
1336  *   ip_addr    : IP address of hostname
1337  *   life_time  : cached time of this entry by others and not within DNS
1338  *                database
1339  *   update_type: UPDATE_ADD to add entry, UPDATE_DEL to remove entry
1340  *   del_type   : DEL_ONE for deleting one entry, DEL_ALL for deleting all
1341  *                entries of the same resource name.  Only valid for UPDATE_DEL.
1342  *   key_name   : same key name used in TKEY message
1343  *   id         : DNS message ID.  If a positive value then this ID value is
1344  *                used, otherwise the next incremented value is used
1345  *   in_mic     : the update request message encrypted
1346  *   level      : This is the domain level which we send the request to, level
1347  *                zero is the default level, it can go upto 2 in reverse zone
1348  *                and virtually to any level in forward zone.
1349  *
1350  * Returns:
1351  *   buf      : buffer containing update message
1352  *   id       : DNS message ID
1353  *   int      : size of update message
1354  *   -1       : error
1355  */
1356 static int
1357 dyndns_build_signed_tsig_msg(char *buf, int update_zone, const char *hostname,
1358 	const char *ip_addr, int life_time, int update_type, int del_type,
1359 	char *key_name, uint16_t *id, gss_buffer_desc *in_mic, int level)
1360 {
1361 	char *bufptr;
1362 	int buf_sz;
1363 
1364 	if ((buf_sz = dyndns_build_add_remove_msg(buf, update_zone, hostname,
1365 	    ip_addr, life_time, update_type, del_type, 1, id, level)) <= 0) {
1366 		return (-1);
1367 	}
1368 
1369 	bufptr = buf + buf_sz;
1370 
1371 	if (dyndns_build_tsig(&bufptr, BUFLEN_UDP(bufptr, buf),
1372 	    *id, key_name, 300, in_mic->value,
1373 	    in_mic->length, TSIG_SIGNED) == -1) {
1374 		return (-1);
1375 	}
1376 
1377 	return (bufptr - buf);
1378 }
1379 
1380 /*
1381  * dyndns_udp_send_recv
1382  * This routine sends and receives UDP DNS request and reply messages.
1383  *
1384  * Pre-condition: Caller must call dyndns_open_init_socket() before calling
1385  * this function.
1386  *
1387  * Parameters:
1388  *   s        : socket descriptor
1389  *   buf      : buffer containing data to send
1390  *   buf_sz   : size of data to send
1391  * Returns:
1392  *   -1     : error
1393  *   rec_buf: reply dat
1394  *    0     : success
1395  */
1396 static int
1397 dyndns_udp_send_recv(int s, char *buf, int buf_sz, char *rec_buf)
1398 {
1399 	int i, retval, addr_len;
1400 	struct timeval tv, timeout;
1401 	fd_set rfds;
1402 	struct sockaddr_in from_addr;
1403 
1404 	timeout.tv_usec = 0;
1405 	timeout.tv_sec = DYNDNS_QUERY_TIMEOUT;
1406 
1407 	for (i = 0; i <= DYNDNS_MAX_QUERY_RETRIES; i++) {
1408 		if (send(s, buf, buf_sz, 0) == -1) {
1409 			syslog(LOG_ERR, "dyndns: UDP send error (%s)",
1410 			    strerror(errno));
1411 			return (-1);
1412 		}
1413 
1414 		FD_ZERO(&rfds);
1415 		FD_SET(s, &rfds);
1416 
1417 		tv = timeout;
1418 
1419 		retval = select(s+1, &rfds, NULL, NULL, &tv);
1420 
1421 		if (retval == -1) {
1422 			return (-1);
1423 		} else if (retval > 0) {
1424 			bzero(rec_buf, NS_PACKETSZ);
1425 			/* required by recvfrom */
1426 			addr_len = sizeof (struct sockaddr_in);
1427 			if (recvfrom(s, rec_buf, NS_PACKETSZ, 0,
1428 			    (struct sockaddr *)&from_addr, &addr_len) == -1) {
1429 				syslog(LOG_ERR, "dyndns: UDP recv error");
1430 				return (-1);
1431 			}
1432 			break;
1433 		}
1434 	}
1435 
1436 	/* did not receive anything */
1437 	if (i == (DYNDNS_MAX_QUERY_RETRIES + 1)) {
1438 		syslog(LOG_ERR, "dyndns: max retries for UDP recv reached");
1439 		return (-1);
1440 	}
1441 
1442 	return (0);
1443 }
1444 
1445 /*
1446  * dyndns_sec_add_remove_entry
1447  * Perform secure dynamic DNS update after getting security context.
1448  * This routine opens a UDP socket to the DNS sever, gets the security context,
1449  * builds the unsigned TSIG message and signed TSIG message.  The signed TSIG
1450  * message containing the encrypted update request message is sent to the DNS
1451  * server.  The response is received and check for error.  If there is no
1452  * error then credential handle and security context are released and the local
1453  * NSS cached is purged.
1454  * Parameters:
1455  *   update_zone : UPDATE_FORW for forward zone, UPDATE_REV for reverse zone
1456  *   hostname    : fully qualified hostname
1457  *   ip_addr     : ip address of hostname in string format
1458  *   life_time   : cached time of this entry by others and not within DNS
1459  *                 database
1460  *   max_retries : maximum retries for sending DNS update request
1461  *   recv_timeout: receive timeout
1462  *   update_type : UPDATE_ADD for adding entry, UPDATE_DEL for removing entry
1463  *   del_type    : DEL_ONE for deleting one entry, DEL_ALL for deleting all
1464  *                 entries of the same resource name.  Only valid for UPDATE_DEL
1465  *   dns_str     : DNS IP address in string format
1466  * Returns:
1467  *   -1: error
1468  *    0: success
1469  *
1470  * This function is enhanced to handle the case of NOTAUTH error when DNS server
1471  * is not authoritative for specified zone. In this case we need to resend the
1472  * same request to the higher authoritative domains.
1473  * This is true for both secure and unsecure dynamic DNS updates.
1474  */
1475 static int
1476 dyndns_sec_add_remove_entry(int update_zone, const char *hostname,
1477     const char *ip_addr, int life_time, int update_type, int del_type,
1478     char *dns_str)
1479 {
1480 	int s2;
1481 	uint16_t id, rid;
1482 	char buf[NS_PACKETSZ], buf2[NS_PACKETSZ];
1483 	int ret;
1484 	OM_uint32 min, maj;
1485 	gss_buffer_desc in_mic, out_mic;
1486 	gss_ctx_id_t gss_context;
1487 	int dns_ip;
1488 	char *key_name;
1489 	int buf_sz;
1490 	int level = 0;
1491 
1492 	assert(dns_str);
1493 	assert(*dns_str);
1494 
1495 	dns_ip = inet_addr(dns_str);
1496 
1497 sec_retry_higher:
1498 
1499 	if ((gss_context = dyndns_get_sec_context(hostname,
1500 	    dns_ip)) == NULL) {
1501 		return (-1);
1502 	}
1503 
1504 	key_name = (char *)hostname;
1505 
1506 	if ((s2 = dyndns_open_init_socket(SOCK_DGRAM, dns_ip, 53)) < 0) {
1507 		if (gss_context != GSS_C_NO_CONTEXT)
1508 			(void) gss_delete_sec_context(&min, &gss_context, NULL);
1509 		return (-1);
1510 	}
1511 
1512 	id = 0;
1513 	if ((buf_sz = dyndns_build_unsigned_tsig_msg(buf, update_zone, hostname,
1514 	    ip_addr, life_time, update_type, del_type,
1515 	    key_name, &id, level)) <= 0) {
1516 		(void) close(s2);
1517 		if (gss_context != GSS_C_NO_CONTEXT)
1518 			(void) gss_delete_sec_context(&min, &gss_context, NULL);
1519 		return (-1);
1520 	}
1521 
1522 	in_mic.length = buf_sz;
1523 	in_mic.value = buf;
1524 
1525 	/* sign update message */
1526 	if ((maj = gss_get_mic(&min, gss_context, 0, &in_mic, &out_mic)) !=
1527 	    GSS_S_COMPLETE) {
1528 		display_stat(maj, min);
1529 		(void) close(s2);
1530 		if (gss_context != GSS_C_NO_CONTEXT)
1531 			(void) gss_delete_sec_context(&min, &gss_context, NULL);
1532 		return (-1);
1533 	}
1534 
1535 	if ((buf_sz = dyndns_build_signed_tsig_msg(buf, update_zone, hostname,
1536 	    ip_addr, life_time, update_type, del_type, key_name, &id,
1537 	    &out_mic, level)) <= 0) {
1538 		(void) close(s2);
1539 		(void) gss_release_buffer(&min, &out_mic);
1540 		if (gss_context != GSS_C_NO_CONTEXT)
1541 			(void) gss_delete_sec_context(&min, &gss_context, NULL);
1542 		return (-1);
1543 	}
1544 
1545 	(void) gss_release_buffer(&min, &out_mic);
1546 
1547 	if (dyndns_udp_send_recv(s2, buf, buf_sz, buf2)) {
1548 		(void) close(s2);
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) close(s2);
1555 
1556 	if (gss_context != GSS_C_NO_CONTEXT)
1557 		(void) gss_delete_sec_context(&min, &gss_context, NULL);
1558 
1559 	ret = buf2[3] & 0xf;	/* error field in UDP */
1560 
1561 	/*
1562 	 * If it is a NOTAUTH error we should retry with higher domains
1563 	 * until we get a successful reply or the maximum retries is met.
1564 	 */
1565 	if (ret == NOTAUTH && level++ < MAX_AUTH_RETRIES)
1566 		goto sec_retry_higher;
1567 
1568 	/* check here for update request is successful */
1569 	if (ret != NOERROR) {
1570 		dyndns_syslog(LOG_ERR, ret, "TSIG reply");
1571 		return (-1);
1572 	}
1573 
1574 	(void) dyndns_get_nshort(buf2, &rid);
1575 	if (id != rid)
1576 		return (-1);
1577 
1578 	return (0);
1579 }
1580 
1581 /*
1582  * dyndns_seach_entry
1583  * Query DNS server for entry.  This routine can indicate if an entry exist
1584  * or not during forward or reverse lookup.  Also can indicate if the data
1585  * of the entry matched.  For example, for forward lookup, the entry is
1586  * searched using the hostname and the data is the IP address.  For reverse
1587  * lookup, the entry is searched using the IP address and the data is the
1588  * hostname.
1589  * Parameters:
1590  *   update_zone: UPDATE_FORW for forward zone, UPDATE_REV for reverse zone
1591  *   hostname   : fully qualified hostname
1592  *   ip_addr    : ip address of hostname in string format
1593  *   update_type: UPDATE_ADD for adding entry, UPDATE_DEL for removing entry
1594  * Returns:
1595  *   time_out: no use
1596  *   is_match: is 1 for found matching entry, otherwise 0
1597  *   1       : an entry exist but not necessarily match
1598  *   0       : an entry does not exist
1599  */
1600 /*ARGSUSED*/
1601 static int
1602 dyndns_search_entry(int update_zone, const char *hostname, const char *ip_addr,
1603     int update_type, struct timeval *time_out, int *is_match)
1604 {
1605 	struct hostent *hentry;
1606 	struct in_addr in;
1607 	in_addr_t ip;
1608 	int i;
1609 
1610 	*is_match = 0;
1611 	if (update_zone == UPDATE_FORW) {
1612 		hentry = gethostbyname(hostname);
1613 		if (hentry) {
1614 			ip = inet_addr(ip_addr);
1615 			for (i = 0; hentry->h_addr_list[i]; i++) {
1616 				(void) memcpy(&in.s_addr,
1617 				    hentry->h_addr_list[i], sizeof (in.s_addr));
1618 				if (ip == in.s_addr) {
1619 					*is_match = 1;
1620 					break;
1621 				}
1622 			}
1623 			return (1);
1624 		}
1625 	} else {
1626 		int dns_ip = inet_addr(ip_addr);
1627 		hentry = gethostbyaddr((char *)&dns_ip, 4, AF_INET);
1628 		if (hentry) {
1629 			if (strncasecmp(hentry->h_name, hostname,
1630 			    strlen(hostname)) == 0) {
1631 				*is_match = 1;
1632 			}
1633 			return (1);
1634 		}
1635 	}
1636 
1637 	/* entry does not exist */
1638 	return (0);
1639 }
1640 
1641 /*
1642  * dyndns_add_remove_entry
1643  * Perform non-secure dynamic DNS update.  If it fails and host principal
1644  * keys can be found in the local keytab file, secure update will be performed.
1645  *
1646  * This routine opens a UDP socket to the DNS sever, build the update request
1647  * message, and sends the message to the DNS server.  The response is received
1648  * and check for error.  If there is no error then the local NSS cached is
1649  * purged.  DNS may be used to check to see if an entry already exist before
1650  * adding or to see if an entry does exist before removing it.  Adding
1651  * duplicate entries or removing non-existing entries does not cause any
1652  * problems.  DNS is not check when doing a delete all.
1653  * Parameters:
1654  *   update_zone: UPDATE_FORW for forward zone, UPDATE_REV for reverse zone
1655  *   hostname   : fully qualified hostname
1656  *   ip_addr    : ip address of hostname in string format
1657  *   life_time  : cached time of this entry by others and not within DNS
1658  *                database
1659  *   update_type: UPDATE_ADD to add entry, UPDATE_DEL to remove entry
1660  *   do_check   : DNS_CHECK to check first in DNS, DNS_NOCHECK for no DNS
1661  *                checking before update
1662  *   del_type   : DEL_ONE for deleting one entry, DEL_ALL for deleting all
1663  *                entries of the same resource name.  Only valid for UPDATE_DEL.
1664  *   dns_str    : DNS IP address in string format
1665  * Returns:
1666  *   -1: error
1667  *    0: success
1668  *
1669  * This function is enhanced to handle the case of NOTAUTH error when DNS server
1670  * is not authoritative for specified zone. In this case we need to resend the
1671  * same request to the higher authoritative domains.
1672  * This is true for both secure and unsecure dynamic DNS updates.
1673  */
1674 static int
1675 dyndns_add_remove_entry(int update_zone, const char *hostname,
1676     const char *ip_addr, int life_time, int update_type,
1677     int do_check, int del_type, char *dns_str)
1678 {
1679 	int s;
1680 	uint16_t id, rid;
1681 	char buf[NS_PACKETSZ], buf2[NS_PACKETSZ];
1682 	int ret, dns_ip;
1683 	int is_exist, is_match;
1684 	struct timeval timeout;
1685 	int buf_sz;
1686 	int level = 0;
1687 
1688 	assert(dns_str);
1689 	assert(*dns_str);
1690 
1691 	dns_ip = inet_addr(dns_str);
1692 
1693 	if (do_check == DNS_CHECK && del_type != DEL_ALL) {
1694 		is_exist = dyndns_search_entry(update_zone, hostname, ip_addr,
1695 		    update_type, &timeout, &is_match);
1696 
1697 		if (update_type == UPDATE_ADD && is_exist && is_match) {
1698 			return (0);
1699 		} else if (update_type == UPDATE_DEL && !is_exist) {
1700 			return (0);
1701 		}
1702 	}
1703 
1704 retry_higher:
1705 	if ((s = dyndns_open_init_socket(SOCK_DGRAM, dns_ip, 53)) < 0) {
1706 		return (-1);
1707 	}
1708 
1709 	id = 0;
1710 	if ((buf_sz = dyndns_build_add_remove_msg(buf, update_zone, hostname,
1711 	    ip_addr, life_time, update_type, del_type, 0, &id, level)) <= 0) {
1712 		(void) close(s);
1713 		return (-1);
1714 	}
1715 
1716 	if (dyndns_udp_send_recv(s, buf, buf_sz, buf2)) {
1717 		(void) close(s);
1718 		return (-1);
1719 	}
1720 
1721 	(void) close(s);
1722 
1723 	ret = buf2[3] & 0xf;	/* error field in UDP */
1724 
1725 	/*
1726 	 * If it is a NOTAUTH error we should retry with higher domains
1727 	 * until we get a successful reply
1728 	 */
1729 	if (ret == NOTAUTH && level++ < MAX_AUTH_RETRIES)
1730 		goto retry_higher;
1731 
1732 	/* check here for update request is successful */
1733 	if (ret == NOERROR) {
1734 		(void) dyndns_get_nshort(buf2, &rid);
1735 		if (id != rid)
1736 			return (-1);
1737 		return (0);
1738 	}
1739 
1740 	if (ret == NOTIMP) {
1741 		dyndns_syslog(LOG_NOTICE, NOTIMP, "dynamic updates");
1742 		return (-1);
1743 	} else if (ret == NOTAUTH) {
1744 		dyndns_syslog(LOG_NOTICE, NOTAUTH, "DNS");
1745 		return (-1);
1746 	}
1747 
1748 	if (smb_krb5_find_keytab_entries(hostname, SMBNS_KRB5_KEYTAB))
1749 		ret = dyndns_sec_add_remove_entry(update_zone, hostname,
1750 		    ip_addr, life_time, update_type, del_type, dns_str);
1751 
1752 	return (ret);
1753 }
1754 
1755 /*
1756  * dyndns_add_entry
1757  * Main routine to add an entry into DNS.  The attempt will be made on the
1758  * the servers returned by smb_get_nameserver().  Upon a successful
1759  * attempt on any one of the server, the function will exit with 0.
1760  * Otherwise, -1 is retuned to indicate the update attempt on all the
1761  * nameservers has failed.
1762  *
1763  * Parameters:
1764  *   update_zone: the type of zone to update, use UPDATE_FORW for forward
1765  *                lookup zone, use UPDATE_REV for reverse lookup zone
1766  *   hostname   : fully qualified hostname
1767  *   ip_addr    : ip address of hostname in string format
1768  *   life_time  : cached time of this entry by others and not within DNS
1769  *                database
1770  * Returns:
1771  *   -1: error
1772  *    0: success
1773  */
1774 static int
1775 dyndns_add_entry(int update_zone, const char *hostname, const char *ip_addr,
1776     int life_time)
1777 {
1778 	char *dns_str;
1779 	char *which_zone;
1780 	struct in_addr ns_list[MAXNS];
1781 	int i, cnt;
1782 	int addr, rc = 0;
1783 
1784 	if (hostname == NULL || ip_addr == NULL)
1785 		return (-1);
1786 
1787 	addr = (int)inet_addr(ip_addr);
1788 	if ((addr == -1) || (addr == 0))
1789 		return (-1);
1790 
1791 	cnt = smb_get_nameservers(ns_list, MAXNS);
1792 
1793 	for (i = 0; i < cnt; i++) {
1794 		dns_str = inet_ntoa(ns_list[i]);
1795 		if ((dns_str == NULL) ||
1796 		    (strcmp(dns_str, "0.0.0.0") == 0)) {
1797 			continue;
1798 		}
1799 
1800 		which_zone = (update_zone == UPDATE_FORW) ?
1801 		    "forward" : "reverse";
1802 
1803 		syslog(LOG_DEBUG, "dyndns %s lookup zone update %s (%s)",
1804 		    which_zone, hostname, ip_addr);
1805 
1806 		if (dyndns_add_remove_entry(update_zone, hostname,
1807 		    ip_addr, life_time,
1808 		    UPDATE_ADD, DNS_NOCHECK, DEL_NONE, dns_str) != -1) {
1809 			rc = 1;
1810 			break;
1811 		}
1812 	}
1813 
1814 	return (rc ? 0 : -1);
1815 }
1816 
1817 /*
1818  * dyndns_remove_entry
1819  * Main routine to remove an entry or all entries of the same resource name
1820  * from DNS.  The update attempt will be made on the primary DNS server.  If
1821  * there is a failure then another attempt will be made on the secondary DNS
1822  * server.
1823  * Parameters:
1824  *   update_zone: the type of zone to update, use UPDATE_FORW for forward
1825  *                lookup zone, use UPDATE_REV for reverse lookup zone
1826  *   hostname   : fully qualified hostname
1827  *   ip_addr    : ip address of hostname in string format
1828  *   del_type   : DEL_ONE for deleting one entry, DEL_ALL for deleting all
1829  *                entries of the same resource name.  Only valid for UPDATE_DEL
1830  * Returns:
1831  *   -1: error
1832  *    0: success
1833  */
1834 static int
1835 dyndns_remove_entry(int update_zone, const char *hostname, const char *ip_addr,
1836 	int del_type)
1837 {
1838 	char *dns_str;
1839 	char *which_zone;
1840 	struct in_addr ns_list[MAXNS];
1841 	int i, cnt, scnt;
1842 	int addr;
1843 
1844 	if ((hostname == NULL || ip_addr == NULL)) {
1845 		return (-1);
1846 	}
1847 
1848 	addr = (int)inet_addr(ip_addr);
1849 	if ((addr == -1) || (addr == 0)) {
1850 		return (-1);
1851 	}
1852 
1853 	cnt = smb_get_nameservers(ns_list, MAXNS);
1854 	scnt = 0;
1855 
1856 	for (i = 0; i < cnt; i++) {
1857 		dns_str = inet_ntoa(ns_list[i]);
1858 		if ((dns_str == NULL) ||
1859 		    (strcmp(dns_str, "0.0.0.0") == 0)) {
1860 			continue;
1861 		}
1862 
1863 		which_zone = (update_zone == UPDATE_FORW) ?
1864 		    "forward" : "reverse";
1865 
1866 		if (del_type == DEL_ONE) {
1867 			syslog(LOG_DEBUG,
1868 			    "dyndns %s lookup zone remove %s (%s)",
1869 			    which_zone, hostname, ip_addr);
1870 		} else {
1871 			syslog(LOG_DEBUG,
1872 			    "dyndns %s lookup zone remove all %s",
1873 			    which_zone, hostname);
1874 		}
1875 
1876 		if (dyndns_add_remove_entry(update_zone, hostname, ip_addr, 0,
1877 		    UPDATE_DEL, DNS_NOCHECK, del_type, dns_str) != -1) {
1878 			scnt++;
1879 			break;
1880 		}
1881 	}
1882 	if (scnt)
1883 		return (0);
1884 	return (-1);
1885 }
1886 
1887 /*
1888  * dyndns_update_core
1889  * Perform dynamic update on both forward and reverse lookup zone using
1890  * the specified hostname and IP addresses.  Before updating DNS, existing
1891  * host entries with the same hostname in the forward lookup zone are removed
1892  * and existing pointer entries with the same IP addresses in the reverse
1893  * lookup zone are removed.  After DNS update, host entries for current
1894  * hostname will show current IP addresses and pointer entries for current
1895  * IP addresses will show current hostname.
1896  * Parameters:
1897  *  fqhn - fully-qualified hostname
1898  *
1899  * Returns:
1900  *   -1: some dynamic DNS updates errors
1901  *    0: successful or DDNS disabled.
1902  */
1903 static int
1904 dyndns_update_core(char *fqdn)
1905 {
1906 	int forw_update_ok, error;
1907 	char *my_ip;
1908 	struct in_addr addr;
1909 	smb_niciter_t ni;
1910 	int rc;
1911 	char fqhn[MAXHOSTNAMELEN];
1912 
1913 	if (fqdn == NULL || *fqdn == '\0')
1914 		return (0);
1915 
1916 	if (!smb_config_getbool(SMB_CI_DYNDNS_ENABLE))
1917 		return (0);
1918 
1919 	if (smb_gethostname(fqhn, MAXHOSTNAMELEN, 0) != 0)
1920 		return (-1);
1921 
1922 	(void) snprintf(fqhn, MAXHOSTNAMELEN, "%s.%s", fqhn, fqdn);
1923 	error = 0;
1924 	forw_update_ok = 0;
1925 
1926 	/*
1927 	 * Dummy IP is okay since we are removing all using the hostname.
1928 	 */
1929 	if (dyndns_remove_entry(UPDATE_FORW, fqhn, "1.1.1.1", DEL_ALL) == 0) {
1930 		forw_update_ok = 1;
1931 	} else {
1932 		error++;
1933 	}
1934 
1935 	if (smb_nic_getfirst(&ni) != 0)
1936 		return (-1);
1937 
1938 	do {
1939 		if (ni.ni_nic.nic_sysflags & (IFF_STANDBY | IFF_PRIVATE))
1940 			continue;
1941 
1942 		addr.s_addr = ni.ni_nic.nic_ip;
1943 		my_ip = (char *)strdup(inet_ntoa(addr));
1944 		if (my_ip == NULL) {
1945 			error++;
1946 			continue;
1947 		}
1948 
1949 		if (forw_update_ok) {
1950 			rc = dyndns_add_entry(UPDATE_FORW, fqhn, my_ip,
1951 			    DDNS_TTL);
1952 
1953 			if (rc == -1)
1954 				error++;
1955 		}
1956 
1957 		rc = dyndns_remove_entry(UPDATE_REV, fqhn, my_ip, DEL_ALL);
1958 		if (rc == 0) {
1959 			rc = dyndns_add_entry(UPDATE_REV, fqhn, my_ip,
1960 			    DDNS_TTL);
1961 		}
1962 
1963 		if (rc == -1)
1964 			error++;
1965 
1966 		(void) free(my_ip);
1967 	} while (smb_nic_getnext(&ni) == 0);
1968 
1969 	return ((error == 0) ? 0 : -1);
1970 }
1971 
1972 /*
1973  * dyndns_clear_rev_zone
1974  * Clear the rev zone records. Must be called to clear the OLD if list
1975  * of down records prior to updating the list with new information.
1976  *
1977  * Parameters:
1978  *   fqhn - fully-qualified hostname
1979  * Returns:
1980  *   -1: some dynamic DNS updates errors
1981  *    0: successful or DDNS disabled.
1982  */
1983 static int
1984 dyndns_clear_rev_zone(char *fqdn)
1985 {
1986 	int error;
1987 	char *my_ip;
1988 	struct in_addr addr;
1989 	smb_niciter_t ni;
1990 	int rc;
1991 	char fqhn[MAXHOSTNAMELEN];
1992 
1993 	if (!smb_config_getbool(SMB_CI_DYNDNS_ENABLE))
1994 		return (0);
1995 
1996 	if (smb_gethostname(fqhn, MAXHOSTNAMELEN, 0) != 0)
1997 		return (-1);
1998 
1999 	(void) snprintf(fqhn, MAXHOSTNAMELEN, "%s.%s", fqhn, fqdn);
2000 	error = 0;
2001 
2002 	if (smb_nic_getfirst(&ni) != 0)
2003 		return (-1);
2004 
2005 	do {
2006 		if (ni.ni_nic.nic_sysflags & (IFF_STANDBY | IFF_PRIVATE))
2007 			continue;
2008 
2009 		addr.s_addr = ni.ni_nic.nic_ip;
2010 		my_ip = (char *)strdup(inet_ntoa(addr));
2011 		if (my_ip == NULL) {
2012 			error++;
2013 			continue;
2014 		}
2015 
2016 		rc = dyndns_remove_entry(UPDATE_REV, fqhn, my_ip, DEL_ALL);
2017 		if (rc != 0)
2018 			error++;
2019 
2020 		(void) free(my_ip);
2021 	} while (smb_nic_getnext(&ni) == 0);
2022 
2023 	return ((error == 0) ? 0 : -1);
2024 }
2025