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