xref: /onnv-gate/usr/src/lib/smbsrv/libsmbns/common/smbns_dyndns.c (revision 13082:81ec56bf6147)
15331Samw /*
25331Samw  * CDDL HEADER START
35331Samw  *
45331Samw  * The contents of this file are subject to the terms of the
55331Samw  * Common Development and Distribution License (the "License").
65331Samw  * You may not use this file except in compliance with the License.
75331Samw  *
85331Samw  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
95331Samw  * or http://www.opensolaris.org/os/licensing.
105331Samw  * See the License for the specific language governing permissions
115331Samw  * and limitations under the License.
125331Samw  *
135331Samw  * When distributing Covered Code, include this CDDL HEADER in each
145331Samw  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
155331Samw  * If applicable, add the following below this CDDL HEADER, with the
165331Samw  * fields enclosed by brackets "[]" replaced with your own identifying
175331Samw  * information: Portions Copyright [yyyy] [name of copyright owner]
185331Samw  *
195331Samw  * CDDL HEADER END
205331Samw  */
215331Samw /*
2212508Samw@Sun.COM  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
235331Samw  */
245331Samw 
255331Samw #include <assert.h>
265331Samw #include <errno.h>
275331Samw #include <stdio.h>
285331Samw #include <stdlib.h>
295331Samw #include <strings.h>
305331Samw #include <sys/types.h>
315331Samw #include <sys/socket.h>
325331Samw #include <netinet/in.h>
335331Samw #include <arpa/inet.h>
348334SJose.Borrego@Sun.COM #include <arpa/nameser.h>
358334SJose.Borrego@Sun.COM #include <net/if.h>
368334SJose.Borrego@Sun.COM #include <resolv.h>
375331Samw #include <sys/time.h>
385331Samw #include <unistd.h>
395331Samw #include <string.h>
408334SJose.Borrego@Sun.COM #include <pthread.h>
415331Samw #include <netdb.h>
425331Samw #include <rpc/rpc.h>
435331Samw #include <syslog.h>
445331Samw #include <gssapi/gssapi.h>
455331Samw #include <kerberosv5/krb5.h>
465331Samw 
475331Samw #include <smbns_dyndns.h>
486139Sjb150015 #include <smbns_krb.h>
495331Samw 
507348SJose.Borrego@Sun.COM /*
518334SJose.Borrego@Sun.COM  * The following can be removed once head/arpa/nameser_compat.h
528334SJose.Borrego@Sun.COM  * defines BADSIG, BADKEY and BADTIME.
535331Samw  */
545331Samw #ifndef	BADSIG
555331Samw #define	BADSIG ns_r_badsig
565331Samw #endif /* BADSIG */
575331Samw 
585331Samw #ifndef	BADKEY
595331Samw #define	BADKEY ns_r_badkey
605331Samw #endif /* BADKEY */
615331Samw 
625331Samw #ifndef	BADTIME
635331Samw #define	BADTIME ns_r_badtime
645331Samw #endif /* BADTIME */
655331Samw 
668334SJose.Borrego@Sun.COM /* internal use, in dyndns_add_entry */
678334SJose.Borrego@Sun.COM #define	DEL_NONE			2
688334SJose.Borrego@Sun.COM 
698334SJose.Borrego@Sun.COM /* Maximum retires if not authoritative */
708334SJose.Borrego@Sun.COM #define	MAX_AUTH_RETRIES		3
718334SJose.Borrego@Sun.COM 
728334SJose.Borrego@Sun.COM /* Number of times to retry a DNS query */
738334SJose.Borrego@Sun.COM #define	DYNDNS_MAX_QUERY_RETRIES	3
748334SJose.Borrego@Sun.COM 
758334SJose.Borrego@Sun.COM /* Timeout value, in seconds, for DNS query responses */
768334SJose.Borrego@Sun.COM #define	DYNDNS_QUERY_TIMEOUT		2
778334SJose.Borrego@Sun.COM 
788334SJose.Borrego@Sun.COM static uint16_t dns_msgid;
798334SJose.Borrego@Sun.COM mutex_t dns_msgid_mtx;
808334SJose.Borrego@Sun.COM 
818334SJose.Borrego@Sun.COM #define	DYNDNS_OP_CLEAR			1
828334SJose.Borrego@Sun.COM #define	DYNDNS_OP_UPDATE		2
838334SJose.Borrego@Sun.COM 
848334SJose.Borrego@Sun.COM #define	DYNDNS_STATE_INIT		0
858334SJose.Borrego@Sun.COM #define	DYNDNS_STATE_READY		1
868334SJose.Borrego@Sun.COM #define	DYNDNS_STATE_PUBLISHING		2
878334SJose.Borrego@Sun.COM #define	DYNDNS_STATE_STOPPING		3
888334SJose.Borrego@Sun.COM 
898334SJose.Borrego@Sun.COM typedef struct dyndns_qentry {
908334SJose.Borrego@Sun.COM 	list_node_t	dqe_lnd;
918334SJose.Borrego@Sun.COM 	int		dqe_op;
9211963SAfshin.Ardakani@Sun.COM 	/* fully-qualified domain name is in lower case */
938334SJose.Borrego@Sun.COM 	char		dqe_fqdn[MAXHOSTNAMELEN];
948334SJose.Borrego@Sun.COM } dyndns_qentry_t;
958334SJose.Borrego@Sun.COM 
968334SJose.Borrego@Sun.COM typedef struct dyndns_queue {
978334SJose.Borrego@Sun.COM 	list_t		ddq_list;
988334SJose.Borrego@Sun.COM 	mutex_t		ddq_mtx;
998334SJose.Borrego@Sun.COM 	cond_t		ddq_cv;
1008334SJose.Borrego@Sun.COM 	uint32_t	ddq_state;
1018334SJose.Borrego@Sun.COM } dyndns_queue_t;
1028334SJose.Borrego@Sun.COM 
1038334SJose.Borrego@Sun.COM static dyndns_queue_t dyndns_queue;
1048334SJose.Borrego@Sun.COM 
1058334SJose.Borrego@Sun.COM static void dyndns_queue_request(int, const char *);
1068334SJose.Borrego@Sun.COM static void dyndns_queue_flush(list_t *);
1078334SJose.Borrego@Sun.COM static void dyndns_process(list_t *);
1088334SJose.Borrego@Sun.COM static int dyndns_update_core(char *);
1098334SJose.Borrego@Sun.COM static int dyndns_clear_rev_zone(char *);
1108334SJose.Borrego@Sun.COM static void dyndns_msgid_init(void);
1118334SJose.Borrego@Sun.COM static int dyndns_get_msgid(void);
1128334SJose.Borrego@Sun.COM static void dyndns_syslog(int, int, const char *);
1138334SJose.Borrego@Sun.COM 
114*13082SJoyce.McIntosh@Sun.COM void
dyndns_start(void)1158334SJose.Borrego@Sun.COM dyndns_start(void)
1168334SJose.Borrego@Sun.COM {
117*13082SJoyce.McIntosh@Sun.COM 	(void) mutex_lock(&dyndns_queue.ddq_mtx);
1188334SJose.Borrego@Sun.COM 
1198334SJose.Borrego@Sun.COM 	if (dyndns_queue.ddq_state != DYNDNS_STATE_INIT) {
1208334SJose.Borrego@Sun.COM 		(void) mutex_unlock(&dyndns_queue.ddq_mtx);
121*13082SJoyce.McIntosh@Sun.COM 		return;
1228334SJose.Borrego@Sun.COM 	}
1238334SJose.Borrego@Sun.COM 
1248334SJose.Borrego@Sun.COM 	dyndns_msgid_init();
1258334SJose.Borrego@Sun.COM 
1268334SJose.Borrego@Sun.COM 	list_create(&dyndns_queue.ddq_list, sizeof (dyndns_qentry_t),
1278334SJose.Borrego@Sun.COM 	    offsetof(dyndns_qentry_t, dqe_lnd));
1288334SJose.Borrego@Sun.COM 	dyndns_queue.ddq_state = DYNDNS_STATE_READY;
129*13082SJoyce.McIntosh@Sun.COM 
1308334SJose.Borrego@Sun.COM 	(void) mutex_unlock(&dyndns_queue.ddq_mtx);
1318334SJose.Borrego@Sun.COM }
1328334SJose.Borrego@Sun.COM 
1338334SJose.Borrego@Sun.COM void
dyndns_stop(void)1348334SJose.Borrego@Sun.COM dyndns_stop(void)
1358334SJose.Borrego@Sun.COM {
1368334SJose.Borrego@Sun.COM 	(void) mutex_lock(&dyndns_queue.ddq_mtx);
1378334SJose.Borrego@Sun.COM 
1388334SJose.Borrego@Sun.COM 	switch (dyndns_queue.ddq_state) {
1398334SJose.Borrego@Sun.COM 	case DYNDNS_STATE_READY:
1408334SJose.Borrego@Sun.COM 	case DYNDNS_STATE_PUBLISHING:
1418334SJose.Borrego@Sun.COM 		dyndns_queue.ddq_state = DYNDNS_STATE_STOPPING;
1428334SJose.Borrego@Sun.COM 		(void) cond_signal(&dyndns_queue.ddq_cv);
1438334SJose.Borrego@Sun.COM 		break;
1448334SJose.Borrego@Sun.COM 	default:
1458334SJose.Borrego@Sun.COM 		break;
1468334SJose.Borrego@Sun.COM 	}
1478334SJose.Borrego@Sun.COM 
1488334SJose.Borrego@Sun.COM 	(void) mutex_unlock(&dyndns_queue.ddq_mtx);
1498334SJose.Borrego@Sun.COM }
1508334SJose.Borrego@Sun.COM 
1515331Samw /*
1528334SJose.Borrego@Sun.COM  * Clear all records in both zones.
1538334SJose.Borrego@Sun.COM  */
1548334SJose.Borrego@Sun.COM void
dyndns_clear_zones(void)1558334SJose.Borrego@Sun.COM dyndns_clear_zones(void)
1568334SJose.Borrego@Sun.COM {
1578334SJose.Borrego@Sun.COM 	char fqdn[MAXHOSTNAMELEN];
1588334SJose.Borrego@Sun.COM 
1598334SJose.Borrego@Sun.COM 	if (smb_getfqdomainname(fqdn, MAXHOSTNAMELEN) != 0) {
1608334SJose.Borrego@Sun.COM 		syslog(LOG_ERR, "dyndns: failed to get domainname");
1618334SJose.Borrego@Sun.COM 		return;
1628334SJose.Borrego@Sun.COM 	}
1638334SJose.Borrego@Sun.COM 
1648334SJose.Borrego@Sun.COM 	dyndns_queue_request(DYNDNS_OP_CLEAR, fqdn);
1658334SJose.Borrego@Sun.COM }
1668334SJose.Borrego@Sun.COM 
1678334SJose.Borrego@Sun.COM /*
1688334SJose.Borrego@Sun.COM  * Update all records in both zones.
1698334SJose.Borrego@Sun.COM  */
1708334SJose.Borrego@Sun.COM void
dyndns_update_zones(void)1718334SJose.Borrego@Sun.COM dyndns_update_zones(void)
1728334SJose.Borrego@Sun.COM {
1738334SJose.Borrego@Sun.COM 	char fqdn[MAXHOSTNAMELEN];
1748334SJose.Borrego@Sun.COM 
1758334SJose.Borrego@Sun.COM 	if (smb_getfqdomainname(fqdn, MAXHOSTNAMELEN) != 0) {
1768334SJose.Borrego@Sun.COM 		syslog(LOG_ERR, "dyndns: failed to get domainname");
1778334SJose.Borrego@Sun.COM 		return;
1788334SJose.Borrego@Sun.COM 	}
1798334SJose.Borrego@Sun.COM 
1808334SJose.Borrego@Sun.COM 	dyndns_queue_request(DYNDNS_OP_UPDATE, fqdn);
1818334SJose.Borrego@Sun.COM }
1828334SJose.Borrego@Sun.COM 
1838334SJose.Borrego@Sun.COM /*
1848334SJose.Borrego@Sun.COM  * Add a request to the queue.
185*13082SJoyce.McIntosh@Sun.COM  *
186*13082SJoyce.McIntosh@Sun.COM  * To comply with RFC 4120 section 6.2.1, entry->dqe_fqdn is converted
187*13082SJoyce.McIntosh@Sun.COM  * to lower case.
1888334SJose.Borrego@Sun.COM  */
1898334SJose.Borrego@Sun.COM static void
dyndns_queue_request(int op,const char * fqdn)1908334SJose.Borrego@Sun.COM dyndns_queue_request(int op, const char *fqdn)
1918334SJose.Borrego@Sun.COM {
1928334SJose.Borrego@Sun.COM 	dyndns_qentry_t *entry;
1938334SJose.Borrego@Sun.COM 
1948334SJose.Borrego@Sun.COM 	if (!smb_config_getbool(SMB_CI_DYNDNS_ENABLE))
1958334SJose.Borrego@Sun.COM 		return;
1968334SJose.Borrego@Sun.COM 
197*13082SJoyce.McIntosh@Sun.COM 	if ((entry = malloc(sizeof (dyndns_qentry_t))) == NULL)
198*13082SJoyce.McIntosh@Sun.COM 		return;
199*13082SJoyce.McIntosh@Sun.COM 
200*13082SJoyce.McIntosh@Sun.COM 	bzero(entry, sizeof (dyndns_qentry_t));
201*13082SJoyce.McIntosh@Sun.COM 	entry->dqe_op = op;
202*13082SJoyce.McIntosh@Sun.COM 	(void) strlcpy(entry->dqe_fqdn, fqdn, MAXNAMELEN);
203*13082SJoyce.McIntosh@Sun.COM 	(void) smb_strlwr(entry->dqe_fqdn);
204*13082SJoyce.McIntosh@Sun.COM 
2058334SJose.Borrego@Sun.COM 	(void) mutex_lock(&dyndns_queue.ddq_mtx);
2068334SJose.Borrego@Sun.COM 
2078334SJose.Borrego@Sun.COM 	switch (dyndns_queue.ddq_state) {
2088334SJose.Borrego@Sun.COM 	case DYNDNS_STATE_READY:
2098334SJose.Borrego@Sun.COM 	case DYNDNS_STATE_PUBLISHING:
210*13082SJoyce.McIntosh@Sun.COM 		list_insert_tail(&dyndns_queue.ddq_list, entry);
211*13082SJoyce.McIntosh@Sun.COM 		(void) cond_signal(&dyndns_queue.ddq_cv);
2128334SJose.Borrego@Sun.COM 		break;
2138334SJose.Borrego@Sun.COM 	default:
214*13082SJoyce.McIntosh@Sun.COM 		free(entry);
215*13082SJoyce.McIntosh@Sun.COM 		break;
2168334SJose.Borrego@Sun.COM 	}
2178334SJose.Borrego@Sun.COM 
2188334SJose.Borrego@Sun.COM 	(void) mutex_unlock(&dyndns_queue.ddq_mtx);
2198334SJose.Borrego@Sun.COM }
2208334SJose.Borrego@Sun.COM 
2218334SJose.Borrego@Sun.COM /*
2228334SJose.Borrego@Sun.COM  * Flush all remaining items from the specified list/queue.
2235331Samw  */
2247052Samw static void
dyndns_queue_flush(list_t * lst)2258334SJose.Borrego@Sun.COM dyndns_queue_flush(list_t *lst)
2268334SJose.Borrego@Sun.COM {
2278334SJose.Borrego@Sun.COM 	dyndns_qentry_t *entry;
2288334SJose.Borrego@Sun.COM 
2298334SJose.Borrego@Sun.COM 	while ((entry = list_head(lst)) != NULL) {
2308334SJose.Borrego@Sun.COM 		list_remove(lst, entry);
2318334SJose.Borrego@Sun.COM 		free(entry);
2328334SJose.Borrego@Sun.COM 	}
2338334SJose.Borrego@Sun.COM }
2348334SJose.Borrego@Sun.COM 
2358334SJose.Borrego@Sun.COM /*
2368334SJose.Borrego@Sun.COM  * Dyndns update thread.  While running, the thread waits on a condition
2378334SJose.Borrego@Sun.COM  * variable until notified that an entry needs to be updated.
2388334SJose.Borrego@Sun.COM  *
2398334SJose.Borrego@Sun.COM  * If the outgoing queue is not empty, the thread wakes up every 60 seconds
2408334SJose.Borrego@Sun.COM  * to retry.
2418334SJose.Borrego@Sun.COM  */
2428334SJose.Borrego@Sun.COM /*ARGSUSED*/
243*13082SJoyce.McIntosh@Sun.COM void *
dyndns_publisher(void * arg)2448334SJose.Borrego@Sun.COM dyndns_publisher(void *arg)
2458334SJose.Borrego@Sun.COM {
2468334SJose.Borrego@Sun.COM 	dyndns_qentry_t *entry;
2478334SJose.Borrego@Sun.COM 	list_t publist;
2488334SJose.Borrego@Sun.COM 
2498334SJose.Borrego@Sun.COM 	(void) mutex_lock(&dyndns_queue.ddq_mtx);
2508334SJose.Borrego@Sun.COM 	if (dyndns_queue.ddq_state != DYNDNS_STATE_READY) {
2518334SJose.Borrego@Sun.COM 		(void) mutex_unlock(&dyndns_queue.ddq_mtx);
2528334SJose.Borrego@Sun.COM 		return (NULL);
2538334SJose.Borrego@Sun.COM 	}
2548334SJose.Borrego@Sun.COM 	dyndns_queue.ddq_state = DYNDNS_STATE_PUBLISHING;
2558334SJose.Borrego@Sun.COM 	(void) mutex_unlock(&dyndns_queue.ddq_mtx);
2568334SJose.Borrego@Sun.COM 
2578334SJose.Borrego@Sun.COM 	list_create(&publist, sizeof (dyndns_qentry_t),
2588334SJose.Borrego@Sun.COM 	    offsetof(dyndns_qentry_t, dqe_lnd));
2598334SJose.Borrego@Sun.COM 
2608334SJose.Borrego@Sun.COM 	for (;;) {
2618334SJose.Borrego@Sun.COM 		(void) mutex_lock(&dyndns_queue.ddq_mtx);
2628334SJose.Borrego@Sun.COM 
2638334SJose.Borrego@Sun.COM 		while (list_is_empty(&dyndns_queue.ddq_list) &&
2648334SJose.Borrego@Sun.COM 		    (dyndns_queue.ddq_state == DYNDNS_STATE_PUBLISHING)) {
2658334SJose.Borrego@Sun.COM 			(void) cond_wait(&dyndns_queue.ddq_cv,
2668334SJose.Borrego@Sun.COM 			    &dyndns_queue.ddq_mtx);
2678334SJose.Borrego@Sun.COM 		}
2688334SJose.Borrego@Sun.COM 
2698334SJose.Borrego@Sun.COM 		if (dyndns_queue.ddq_state != DYNDNS_STATE_PUBLISHING) {
2708334SJose.Borrego@Sun.COM 			(void) mutex_unlock(&dyndns_queue.ddq_mtx);
2718334SJose.Borrego@Sun.COM 			break;
2728334SJose.Borrego@Sun.COM 		}
2738334SJose.Borrego@Sun.COM 
2748334SJose.Borrego@Sun.COM 		/*
2758334SJose.Borrego@Sun.COM 		 * Transfer queued items to the local list so that
2768334SJose.Borrego@Sun.COM 		 * the mutex can be released.
2778334SJose.Borrego@Sun.COM 		 */
2788334SJose.Borrego@Sun.COM 		while ((entry = list_head(&dyndns_queue.ddq_list)) != NULL) {
2798334SJose.Borrego@Sun.COM 			list_remove(&dyndns_queue.ddq_list, entry);
2808334SJose.Borrego@Sun.COM 			list_insert_tail(&publist, entry);
2818334SJose.Borrego@Sun.COM 		}
2828334SJose.Borrego@Sun.COM 
2838334SJose.Borrego@Sun.COM 		(void) mutex_unlock(&dyndns_queue.ddq_mtx);
2848334SJose.Borrego@Sun.COM 
2858334SJose.Borrego@Sun.COM 		dyndns_process(&publist);
2868334SJose.Borrego@Sun.COM 	}
2878334SJose.Borrego@Sun.COM 
2888334SJose.Borrego@Sun.COM 	(void) mutex_lock(&dyndns_queue.ddq_mtx);
2898334SJose.Borrego@Sun.COM 	dyndns_queue_flush(&dyndns_queue.ddq_list);
2908334SJose.Borrego@Sun.COM 	list_destroy(&dyndns_queue.ddq_list);
2918334SJose.Borrego@Sun.COM 	dyndns_queue.ddq_state = DYNDNS_STATE_INIT;
2928334SJose.Borrego@Sun.COM 	(void) mutex_unlock(&dyndns_queue.ddq_mtx);
2938334SJose.Borrego@Sun.COM 
2948334SJose.Borrego@Sun.COM 	dyndns_queue_flush(&publist);
2958334SJose.Borrego@Sun.COM 	list_destroy(&publist);
2968334SJose.Borrego@Sun.COM 	return (NULL);
2978334SJose.Borrego@Sun.COM }
2988334SJose.Borrego@Sun.COM 
2998334SJose.Borrego@Sun.COM /*
3008334SJose.Borrego@Sun.COM  * Remove items from the queue and process them.
3018334SJose.Borrego@Sun.COM  */
3028334SJose.Borrego@Sun.COM static void
dyndns_process(list_t * publist)3038334SJose.Borrego@Sun.COM dyndns_process(list_t *publist)
3045331Samw {
3058334SJose.Borrego@Sun.COM 	dyndns_qentry_t *entry;
3068334SJose.Borrego@Sun.COM 
3078334SJose.Borrego@Sun.COM 	while ((entry = list_head(publist)) != NULL) {
3088334SJose.Borrego@Sun.COM 		(void) mutex_lock(&dyndns_queue.ddq_mtx);
3098334SJose.Borrego@Sun.COM 		if (dyndns_queue.ddq_state != DYNDNS_STATE_PUBLISHING) {
3108334SJose.Borrego@Sun.COM 			(void) mutex_unlock(&dyndns_queue.ddq_mtx);
3118334SJose.Borrego@Sun.COM 			dyndns_queue_flush(publist);
3128334SJose.Borrego@Sun.COM 			return;
3138334SJose.Borrego@Sun.COM 		}
3148334SJose.Borrego@Sun.COM 		(void) mutex_unlock(&dyndns_queue.ddq_mtx);
3158334SJose.Borrego@Sun.COM 
3168334SJose.Borrego@Sun.COM 		list_remove(publist, entry);
3178334SJose.Borrego@Sun.COM 
3188334SJose.Borrego@Sun.COM 		switch (entry->dqe_op) {
3198334SJose.Borrego@Sun.COM 		case DYNDNS_OP_CLEAR:
3208334SJose.Borrego@Sun.COM 			(void) dyndns_clear_rev_zone(entry->dqe_fqdn);
3218334SJose.Borrego@Sun.COM 			break;
3228334SJose.Borrego@Sun.COM 		case DYNDNS_OP_UPDATE:
3238334SJose.Borrego@Sun.COM 			(void) dyndns_update_core(entry->dqe_fqdn);
3248334SJose.Borrego@Sun.COM 			break;
3258334SJose.Borrego@Sun.COM 		default:
3268334SJose.Borrego@Sun.COM 			break;
3278334SJose.Borrego@Sun.COM 		}
3288334SJose.Borrego@Sun.COM 
3298334SJose.Borrego@Sun.COM 		free(entry);
3308334SJose.Borrego@Sun.COM 	}
3318334SJose.Borrego@Sun.COM }
3328334SJose.Borrego@Sun.COM 
3338334SJose.Borrego@Sun.COM /*
3348334SJose.Borrego@Sun.COM  * Dynamic DNS update API for kclient.
3358334SJose.Borrego@Sun.COM  *
3368334SJose.Borrego@Sun.COM  * Returns 0 upon success.  Otherwise, returns -1.
3378334SJose.Borrego@Sun.COM  */
3388334SJose.Borrego@Sun.COM int
dyndns_update(char * fqdn)3398334SJose.Borrego@Sun.COM dyndns_update(char *fqdn)
3408334SJose.Borrego@Sun.COM {
3418334SJose.Borrego@Sun.COM 	int rc;
3428334SJose.Borrego@Sun.COM 
34311963SAfshin.Ardakani@Sun.COM 	if (smb_nic_init() != SMB_NIC_SUCCESS)
3448334SJose.Borrego@Sun.COM 		return (-1);
3458334SJose.Borrego@Sun.COM 
3468334SJose.Borrego@Sun.COM 	dyndns_msgid_init();
34711963SAfshin.Ardakani@Sun.COM 	(void) smb_strlwr(fqdn);
3488334SJose.Borrego@Sun.COM 	rc = dyndns_update_core(fqdn);
3498334SJose.Borrego@Sun.COM 	smb_nic_fini();
3508334SJose.Borrego@Sun.COM 	return (rc);
3518334SJose.Borrego@Sun.COM }
3528334SJose.Borrego@Sun.COM 
3538334SJose.Borrego@Sun.COM /*
3548334SJose.Borrego@Sun.COM  * Initializes the DNS message ID counter using the algorithm
3558334SJose.Borrego@Sun.COM  * that resolver library uses to initialize the ID field of any res
3568334SJose.Borrego@Sun.COM  * structure.
3578334SJose.Borrego@Sun.COM  */
3588334SJose.Borrego@Sun.COM static void
dyndns_msgid_init(void)3598334SJose.Borrego@Sun.COM dyndns_msgid_init(void)
3608334SJose.Borrego@Sun.COM {
3618334SJose.Borrego@Sun.COM 	struct timeval now;
3625331Samw 
3638334SJose.Borrego@Sun.COM 	(void) gettimeofday(&now, NULL);
3648334SJose.Borrego@Sun.COM 	(void) mutex_lock(&dns_msgid_mtx);
3658334SJose.Borrego@Sun.COM 	dns_msgid = (0xffff & (now.tv_sec ^ now.tv_usec ^ getpid()));
3668334SJose.Borrego@Sun.COM 	(void) mutex_unlock(&dns_msgid_mtx);
3678334SJose.Borrego@Sun.COM }
3688334SJose.Borrego@Sun.COM 
3698334SJose.Borrego@Sun.COM static int
dyndns_get_msgid(void)3708334SJose.Borrego@Sun.COM dyndns_get_msgid(void)
3718334SJose.Borrego@Sun.COM {
3728334SJose.Borrego@Sun.COM 	uint16_t id;
3738334SJose.Borrego@Sun.COM 
3748334SJose.Borrego@Sun.COM 	(void) mutex_lock(&dns_msgid_mtx);
3758334SJose.Borrego@Sun.COM 	id = ++dns_msgid;
3768334SJose.Borrego@Sun.COM 	(void) mutex_unlock(&dns_msgid_mtx);
3778334SJose.Borrego@Sun.COM 	return (id);
3788334SJose.Borrego@Sun.COM }
3798334SJose.Borrego@Sun.COM 
3808334SJose.Borrego@Sun.COM /*
3818334SJose.Borrego@Sun.COM  * Log a DNS error message
3828334SJose.Borrego@Sun.COM  */
3838334SJose.Borrego@Sun.COM static void
dyndns_syslog(int severity,int errnum,const char * text)3848334SJose.Borrego@Sun.COM dyndns_syslog(int severity, int errnum, const char *text)
3858334SJose.Borrego@Sun.COM {
3868334SJose.Borrego@Sun.COM 	struct {
3878334SJose.Borrego@Sun.COM 		int errnum;
3888334SJose.Borrego@Sun.COM 		char *errmsg;
3898334SJose.Borrego@Sun.COM 	} errtab[] = {
3908334SJose.Borrego@Sun.COM 		{ FORMERR,  "message format error" },
3918334SJose.Borrego@Sun.COM 		{ SERVFAIL, "server internal error" },
3928334SJose.Borrego@Sun.COM 		{ NXDOMAIN, "entry should exist but does not exist" },
3938334SJose.Borrego@Sun.COM 		{ NOTIMP,   "not supported" },
3948334SJose.Borrego@Sun.COM 		{ REFUSED,  "operation refused" },
3958334SJose.Borrego@Sun.COM 		{ YXDOMAIN, "entry should not exist but does exist" },
3968334SJose.Borrego@Sun.COM 		{ YXRRSET,  "RRSet should not exist but does exist" },
3978334SJose.Borrego@Sun.COM 		{ NXRRSET,  "RRSet should exist but does not exist" },
3988334SJose.Borrego@Sun.COM 		{ NOTAUTH,  "server is not authoritative for specified zone" },
3998334SJose.Borrego@Sun.COM 		{ NOTZONE,  "name not within specified zone" },
4008334SJose.Borrego@Sun.COM 		{ BADSIG,   "bad transaction signature (TSIG)" },
4018334SJose.Borrego@Sun.COM 		{ BADKEY,   "bad transaction key (TKEY)" },
4028334SJose.Borrego@Sun.COM 		{ BADTIME,  "time not synchronized" },
4038334SJose.Borrego@Sun.COM 	};
4048334SJose.Borrego@Sun.COM 
4058334SJose.Borrego@Sun.COM 	char *errmsg = "unknown error";
4068334SJose.Borrego@Sun.COM 	int i;
4078334SJose.Borrego@Sun.COM 
4088334SJose.Borrego@Sun.COM 	if (errnum == NOERROR)
4098334SJose.Borrego@Sun.COM 		return;
4108334SJose.Borrego@Sun.COM 
4118334SJose.Borrego@Sun.COM 	for (i = 0; i < (sizeof (errtab) / sizeof (errtab[0])); ++i) {
4128334SJose.Borrego@Sun.COM 		if (errtab[i].errnum == errnum) {
4138334SJose.Borrego@Sun.COM 			errmsg = errtab[i].errmsg;
4148334SJose.Borrego@Sun.COM 			break;
4158334SJose.Borrego@Sun.COM 		}
4165331Samw 	}
4178334SJose.Borrego@Sun.COM 
4188334SJose.Borrego@Sun.COM 	syslog(severity, "dyndns: %s: %s: %d", text, errmsg, errnum);
4195331Samw }
4205331Samw 
4215331Samw /*
4225331Samw  * display_stat
4235331Samw  * Display GSS error message from error code.  This routine is used to display
4245331Samw  * the mechanism independent and mechanism specific error messages for GSS
4255331Samw  * routines.  The major status error code is the mechanism independent error
4265331Samw  * code and the minor status error code is the mechanism specific error code.
4275331Samw  * Parameters:
4285331Samw  *   maj: GSS major status
4295331Samw  *   min: GSS minor status
4305331Samw  * Returns:
4315331Samw  *   None
4325331Samw  */
4335331Samw static void
display_stat(OM_uint32 maj,OM_uint32 min)4345331Samw display_stat(OM_uint32 maj, OM_uint32 min)
4355331Samw {
4365331Samw 	gss_buffer_desc msg;
4375331Samw 	OM_uint32 msg_ctx = 0;
4385331Samw 	OM_uint32 min2;
4397961SNatalie.Li@Sun.COM 
4405331Samw 	(void) gss_display_status(&min2, maj, GSS_C_GSS_CODE, GSS_C_NULL_OID,
4415331Samw 	    &msg_ctx, &msg);
4428334SJose.Borrego@Sun.COM 	syslog(LOG_ERR, "dyndns: GSS major status error: %s",
4435331Samw 	    (char *)msg.value);
4447961SNatalie.Li@Sun.COM 	(void) gss_release_buffer(&min2, &msg);
4457961SNatalie.Li@Sun.COM 
4465331Samw 	(void) gss_display_status(&min2, min, GSS_C_MECH_CODE, GSS_C_NULL_OID,
4475331Samw 	    &msg_ctx, &msg);
4488334SJose.Borrego@Sun.COM 	syslog(LOG_ERR, "dyndns: GSS minor status error: %s",
4495331Samw 	    (char *)msg.value);
4507961SNatalie.Li@Sun.COM 	(void) gss_release_buffer(&min2, &msg);
4515331Samw }
4525331Samw 
4535331Samw static char *
dyndns_put_nshort(char * buf,uint16_t val)4545331Samw dyndns_put_nshort(char *buf, uint16_t val)
4555331Samw {
4565331Samw 	uint16_t nval;
4575331Samw 
4585331Samw 	nval = htons(val);
4595331Samw 	(void) memcpy(buf, &nval, sizeof (uint16_t));
4605331Samw 	buf += sizeof (uint16_t);
4615331Samw 	return (buf);
4625331Samw }
4635331Samw 
4647052Samw static char *
dyndns_get_nshort(char * buf,uint16_t * val)4655331Samw dyndns_get_nshort(char *buf, uint16_t *val)
4665331Samw {
4675331Samw 	uint16_t nval;
4685331Samw 
4695331Samw 	(void) memcpy(&nval, buf, sizeof (uint16_t));
4705331Samw 	*val = ntohs(nval);
4715331Samw 	buf += sizeof (uint16_t);
4725331Samw 	return (buf);
4735331Samw }
4745331Samw 
4755331Samw static char *
dyndns_put_nlong(char * buf,uint32_t val)4765331Samw dyndns_put_nlong(char *buf, uint32_t val)
4775331Samw {
4785331Samw 	uint32_t lval;
4795331Samw 
4805331Samw 	lval = htonl(val);
4815331Samw 	(void) memcpy(buf, &lval, sizeof (uint32_t));
4825331Samw 	buf += sizeof (uint32_t);
4835331Samw 	return (buf);
4845331Samw }
4855331Samw 
4865331Samw static char *
dyndns_put_byte(char * buf,char val)4875331Samw dyndns_put_byte(char *buf, char val)
4885331Samw {
4895331Samw 	*buf = val;
4905331Samw 	buf++;
4915331Samw 	return (buf);
4925331Samw }
4935331Samw 
4948670SJose.Borrego@Sun.COM 
4958670SJose.Borrego@Sun.COM 
4968670SJose.Borrego@Sun.COM 
4975331Samw static char *
dyndns_put_int(char * buf,int val)4985331Samw dyndns_put_int(char *buf, int val)
4995331Samw {
5005331Samw 	(void) memcpy(buf, &val, sizeof (int));
5015331Samw 	buf += sizeof (int);
5025331Samw 	return (buf);
5035331Samw }
5045331Samw 
5058670SJose.Borrego@Sun.COM static char *
dyndns_put_v6addr(char * buf,smb_inaddr_t * val)5068670SJose.Borrego@Sun.COM dyndns_put_v6addr(char *buf, smb_inaddr_t *val)
5078670SJose.Borrego@Sun.COM {
5088670SJose.Borrego@Sun.COM 
5098670SJose.Borrego@Sun.COM 	val->a_family = AF_INET6;
5108670SJose.Borrego@Sun.COM 	(void) memcpy(buf, &val->a_ipv6, IN6ADDRSZ);
5118670SJose.Borrego@Sun.COM 	buf += IN6ADDRSZ;
5128670SJose.Borrego@Sun.COM 	return (buf);
5138670SJose.Borrego@Sun.COM }
5145331Samw /*
5155331Samw  * dyndns_stuff_str
5165331Samw  * Converts a domain string by removing periods and replacing with a byte value
5175331Samw  * of how many characters following period.  A byte value is placed in front
5185331Samw  * to indicate how many characters before first period.  A NULL character is
5195331Samw  * placed at the end. i.e. host.procom.com -> 4host5procom3com0
5205331Samw  * Buffer space checking is done by caller.
5215331Samw  * Parameters:
5225331Samw  *   ptr : address of pointer to buffer to store converted string
5235331Samw  *   zone: domain name string
5245331Samw  * Returns:
5255331Samw  *   ptr: address of pointer to next available buffer space
5265331Samw  *   -1 : error
5275331Samw  *    0 : success
5285331Samw  */
5295331Samw static int
dyndns_stuff_str(char ** ptr,char * zone)5305331Samw dyndns_stuff_str(char **ptr, char *zone)
5315331Samw {
5325331Samw 	int len;
5335331Samw 	char *lenPtr, *zonePtr;
5345331Samw 
5355331Samw 	for (zonePtr = zone; *zonePtr; ) {
5365331Samw 		lenPtr = *ptr;
5375331Samw 		*ptr = *ptr + 1;
5385331Samw 		len = 0;
5395331Samw 		while (*zonePtr != '.' && *zonePtr != 0) {
5405331Samw 			*ptr = dyndns_put_byte(*ptr, *zonePtr);
5415331Samw 			zonePtr++;
5425331Samw 			len++;
5435331Samw 		}
5445331Samw 		*lenPtr = len;
5455331Samw 		if (*zonePtr == '.')
5465331Samw 			zonePtr++;
5475331Samw 	}
5485331Samw 	*ptr = dyndns_put_byte(*ptr, 0);
5495331Samw 	return (0);
5505331Samw }
5515331Samw 
5525331Samw /*
5535331Samw  * dyndns_build_header
5545331Samw  * Build the header for DNS query and DNS update request message.
5555331Samw  * Parameters:
5565331Samw  *   ptr               : address of pointer to buffer to store header
5575331Samw  *   buf_len           : buffer length
5585331Samw  *   msg_id            : message id
5595331Samw  *   query_req         : use REQ_QUERY for query message or REQ_UPDATE for
5605331Samw  *                       update message
5615331Samw  *   quest_zone_cnt    : number of question record for query message or
5625331Samw  *                       number of zone record for update message
5635331Samw  *   ans_prereq_cnt    : number of answer record for query message or
5645331Samw  *                       number of prerequisite record for update message
5655331Samw  *   nameser_update_cnt: number of name server for query message or
5665331Samw  *                       number of update record for update message
5675331Samw  *   addit_cnt         : number of additional record
5685331Samw  *   flags             : query flags word
5695331Samw  * Returns:
5705331Samw  *   ptr: address of pointer to next available buffer space
5715331Samw  *   -1 : error
5725331Samw  *    0 : success
5735331Samw  */
5747052Samw static int
dyndns_build_header(char ** ptr,int buf_len,uint16_t msg_id,int query_req,uint16_t quest_zone_cnt,uint16_t ans_prereq_cnt,uint16_t nameser_update_cnt,uint16_t addit_cnt,int flags)5755331Samw dyndns_build_header(char **ptr, int buf_len, uint16_t msg_id, int query_req,
5765331Samw     uint16_t quest_zone_cnt, uint16_t ans_prereq_cnt,
5775331Samw     uint16_t nameser_update_cnt, uint16_t addit_cnt, int flags)
5785331Samw {
5795331Samw 	uint16_t opcode;
5805331Samw 
5815331Samw 	if (buf_len < 12) {
5828334SJose.Borrego@Sun.COM 		syslog(LOG_ERR, "dyndns header section: buffer too small");
5835331Samw 		return (-1);
5845331Samw 	}
5855331Samw 
5865331Samw 	*ptr = dyndns_put_nshort(*ptr, msg_id);	/* mesg ID */
5875331Samw 	if (query_req == REQ_QUERY)
5885331Samw 		opcode = ns_o_query;	/* query msg */
5895331Samw 	else
5905331Samw 		opcode = ns_o_update << 11;	/* update msg */
5915331Samw 	opcode |= flags;
5925331Samw 	/* mesg opcode */
5935331Samw 	*ptr = dyndns_put_nshort(*ptr, opcode);
5945331Samw 	/* zone record count */
5955331Samw 	*ptr = dyndns_put_nshort(*ptr, quest_zone_cnt);
5965331Samw 	/* prerequiste record count */
5975331Samw 	*ptr = dyndns_put_nshort(*ptr, ans_prereq_cnt);
5985331Samw 	/* update record count */
5995331Samw 	*ptr = dyndns_put_nshort(*ptr, nameser_update_cnt);
6005331Samw 	/* additional record count */
6015331Samw 	*ptr = dyndns_put_nshort(*ptr, addit_cnt);
6025331Samw 
6035331Samw 	return (0);
6045331Samw }
6055331Samw 
6065331Samw /*
6075331Samw  * dyndns_build_quest_zone
6085331Samw  * Build the question section for query message or zone section for
6095331Samw  * update message.
6105331Samw  * Parameters:
6115331Samw  *   ptr    : address of pointer to buffer to store question or zone section
6125331Samw  *   buf_len: buffer length
6135331Samw  *   name   : question or zone name
6145331Samw  *   type   : type of question or zone
6155331Samw  *   class  : class of question or zone
6165331Samw  * Returns:
6175331Samw  *   ptr: address of pointer to next available buffer space
6185331Samw  *   -1 : error
6195331Samw  *    0 : success
6205331Samw  */
6217052Samw static int
dyndns_build_quest_zone(char ** ptr,int buf_len,char * name,int type,int class)6225331Samw dyndns_build_quest_zone(char **ptr, int buf_len, char *name, int type,
6235331Samw 	int class)
6245331Samw {
6255331Samw 	char *zonePtr;
6265331Samw 
6275331Samw 	if ((strlen(name) + 6) > buf_len) {
6288334SJose.Borrego@Sun.COM 		syslog(LOG_ERR, "dyndns question section: buffer too small");
6295331Samw 		return (-1);
6305331Samw 	}
6315331Samw 
6325331Samw 	zonePtr = *ptr;
6335331Samw 	(void) dyndns_stuff_str(&zonePtr, name);
6345331Samw 	*ptr = zonePtr;
6355331Samw 	*ptr = dyndns_put_nshort(*ptr, type);
6365331Samw 	*ptr = dyndns_put_nshort(*ptr, class);
6375331Samw 	return (0);
6385331Samw }
6395331Samw 
6405331Samw /*
6415331Samw  * dyndns_build_update
6425331Samw  * Build update section of update message for adding and removing a record.
6435331Samw  * If the ttl value is 0 then this message is for record deletion.
6445331Samw  *
6455331Samw  * Parameters:
6465331Samw  *   ptr     : address of pointer to buffer to store update section
6475331Samw  *   buf_len : buffer length
6485331Samw  *   name    : resource name of this record
6495331Samw  *   type    : type of this record
6505331Samw  *   class   : class of this record
6515331Samw  *   ttl     : time-to-live, cached time of this entry by others and not
6525331Samw  *             within DNS database, a zero value for record(s) deletion
6535331Samw  *   data    : data of this resource record
6545331Samw  *   forw_rev: UPDATE_FORW for forward zone, UPDATE_REV for reverse zone
6555331Samw  *   add_del : UPDATE_ADD for adding entry, UPDATE_DEL for removing zone
6565331Samw  *   del_type: DEL_ONE for deleting one entry, DEL_ALL for deleting all
6575331Samw  *             entries of the same resource name.  Only valid for UPDATE_DEL.
6585331Samw  * Returns:
6595331Samw  *   ptr: address of pointer to next available buffer space
6605331Samw  *   -1 : error
6615331Samw  *    0 : success
6625331Samw  */
6635331Samw static int
dyndns_build_update(char ** ptr,int buf_len,char * name,int type,int class,uint32_t ttl,char * data,int forw_rev,int add_del,int del_type)6645331Samw dyndns_build_update(char **ptr, int buf_len, char *name, int type, int class,
6655331Samw 	uint32_t ttl, char *data, int forw_rev, int add_del, int del_type)
6665331Samw {
6675331Samw 	char *namePtr;
6685331Samw 	int rec_len, data_len;
6698670SJose.Borrego@Sun.COM 	smb_inaddr_t ipaddr;
6708670SJose.Borrego@Sun.COM 	int isv4 = 1;
6715331Samw 
6725331Samw 	rec_len = strlen(name) + 10;
6738670SJose.Borrego@Sun.COM 	if (inet_pton(AF_INET, data, &ipaddr) == 1)
6748670SJose.Borrego@Sun.COM 		isv4 = 1;
6758670SJose.Borrego@Sun.COM 	else if (inet_pton(AF_INET6, data, &ipaddr) == 1)
6768670SJose.Borrego@Sun.COM 		isv4 = 0;
6778670SJose.Borrego@Sun.COM 
6785331Samw 	if (add_del == UPDATE_ADD) {
6795331Samw 		if (forw_rev == UPDATE_FORW)
6808670SJose.Borrego@Sun.COM 			data_len = isv4 ? 4 : 16;
6815331Samw 		else
6825331Samw 			data_len = strlen(data) + 2;
6835331Samw 	} else {
6845331Samw 		if (del_type == DEL_ALL)
6855331Samw 			data_len = 0;
6865331Samw 		else if (forw_rev == UPDATE_FORW)
6878670SJose.Borrego@Sun.COM 			data_len = isv4 ? 4 : 16;
6885331Samw 		else
6895331Samw 			data_len = strlen(data) + 2;
6905331Samw 	}
6915331Samw 	if (rec_len + data_len > buf_len) {
6928334SJose.Borrego@Sun.COM 		syslog(LOG_ERR, "dyndns update section: buffer too small");
6935331Samw 		return (-1);
6945331Samw 	}
6955331Samw 
6965331Samw 	namePtr = *ptr;
6975331Samw 	(void) dyndns_stuff_str(&namePtr, name);
6985331Samw 	*ptr = namePtr;
6998670SJose.Borrego@Sun.COM 	if (isv4)
7008670SJose.Borrego@Sun.COM 		*ptr = dyndns_put_nshort(*ptr, type);
7018670SJose.Borrego@Sun.COM 	else
7028670SJose.Borrego@Sun.COM 		*ptr = dyndns_put_nshort(*ptr, ns_t_aaaa);
7035331Samw 	*ptr = dyndns_put_nshort(*ptr, class);
7045331Samw 	*ptr = dyndns_put_nlong(*ptr, ttl);
7055331Samw 
7065331Samw 	if (add_del == UPDATE_DEL && del_type == DEL_ALL) {
7075331Samw 		*ptr = dyndns_put_nshort(*ptr, 0);
7085331Samw 		return (0);
7095331Samw 	}
7105331Samw 
7115331Samw 	if (forw_rev == UPDATE_FORW) {
7128670SJose.Borrego@Sun.COM 		if (isv4) {
7138670SJose.Borrego@Sun.COM 			*ptr = dyndns_put_nshort(*ptr, 4);
7148670SJose.Borrego@Sun.COM 			*ptr = dyndns_put_int(*ptr, ipaddr.a_ipv4);
7158670SJose.Borrego@Sun.COM 		} else {
7168670SJose.Borrego@Sun.COM 			*ptr = dyndns_put_nshort(*ptr, 16);
7178670SJose.Borrego@Sun.COM 			*ptr = dyndns_put_v6addr(*ptr, &ipaddr);
7188670SJose.Borrego@Sun.COM 		}
7195331Samw 	} else {
7205331Samw 		*ptr = dyndns_put_nshort(*ptr, strlen(data)+2);
7215331Samw 		namePtr = *ptr;
7225331Samw 		(void) dyndns_stuff_str(&namePtr, data);	/* hostname */
7235331Samw 		*ptr = namePtr;
7245331Samw 	}
7255331Samw 	return (0);
7265331Samw }
7275331Samw 
7285331Samw /*
7295331Samw  * dyndns_build_tkey
7305331Samw  * Build TKEY section to establish security context for secure dynamic DNS
7315331Samw  * update.  DNS header and question sections need to be build before this
7325331Samw  * section.  The TKEY data are the tokens generated during security context
7335331Samw  * establishment and the TKEY message is used to transmit those tokens, one
7345331Samw  * at a time, to the DNS server.
7355331Samw  * Parameters:
7365331Samw  *   ptr       : address of pointer to buffer to store TKEY
7375331Samw  *   buf_len   : buffer length
7385331Samw  *   name      : key name, must be unique and same as for TSIG record
7395331Samw  *   key_expire: expiration time of this key in second
7405331Samw  *   data      : TKEY data
7415331Samw  *   data_size : data size
7425331Samw  * Returns:
7435331Samw  *   ptr: address of the pointer to the next available buffer space
7445331Samw  *   -1 : error
7455331Samw  *    0 : success
7465331Samw  */
7475331Samw static int
dyndns_build_tkey(char ** ptr,int buf_len,char * name,int key_expire,char * data,int data_size)7485331Samw dyndns_build_tkey(char **ptr, int buf_len, char *name, int key_expire,
7495331Samw 	char *data, int data_size)
7505331Samw {
7515331Samw 	char *namePtr;
7525331Samw 	struct timeval tp;
7535331Samw 
7545331Samw 	if (strlen(name)+2 + 45 + data_size > buf_len) {
7558334SJose.Borrego@Sun.COM 		syslog(LOG_ERR, "dyndns TKEY: buffer too small");
7565331Samw 		return (-1);
7575331Samw 	}
7585331Samw 
7595331Samw 	namePtr = *ptr;
7605331Samw 	(void) dyndns_stuff_str(&namePtr, name);	/* unique global name */
7615331Samw 	*ptr = namePtr;
7625331Samw 	*ptr = dyndns_put_nshort(*ptr, ns_t_tkey);
7635331Samw 	*ptr = dyndns_put_nshort(*ptr, ns_c_any);
7645331Samw 	*ptr = dyndns_put_nlong(*ptr, 0);
7655331Samw 	/* 19 + 14 + data_size + 2 */
7665331Samw 	*ptr = dyndns_put_nshort(*ptr, 35 + data_size);
7675331Samw 	namePtr = *ptr;
7685331Samw 	(void) dyndns_stuff_str(&namePtr, "gss.microsoft.com");
7695331Samw 	*ptr = namePtr;
7705331Samw 	(void) gettimeofday(&tp, 0);
7715331Samw 	*ptr = dyndns_put_nlong(*ptr, tp.tv_sec);	/* inception */
7725331Samw 	/* expiration, 86400 */
7735331Samw 	*ptr = dyndns_put_nlong(*ptr, tp.tv_sec + key_expire);
7745331Samw 	*ptr = dyndns_put_nshort(*ptr, MODE_GSS_API);	/* mode: gss-api */
7755331Samw 	*ptr = dyndns_put_nshort(*ptr, 0);		/* error */
7765331Samw 	*ptr = dyndns_put_nshort(*ptr, data_size);	/* key size */
7775331Samw 	(void) memcpy(*ptr, data, data_size);	/* key data */
7785331Samw 	*ptr += data_size;
7795331Samw 	*ptr = dyndns_put_nshort(*ptr, 0);	/* other */
7805331Samw 	return (0);
7815331Samw }
7825331Samw 
7835331Samw /*
7845331Samw  * dyndns_build_tsig
7855331Samw  * Build TSIG section for secure dynamic DNS update.  This routine will be
7865331Samw  * called twice.  First called with TSIG_UNSIGNED, and second with TSIG_SIGNED.
7875331Samw  * The TSIG data is NULL and ignored for TSIG_UNSIGNED and is the update request
7885331Samw  * message encrypted for TSIG_SIGNED.  The message id must be the same id as the
7895331Samw  * one in the update request before it is encrypted.
7905331Samw  * Parameters:
7915331Samw  *   ptr        : address of pointer to buffer to store TSIG
7925331Samw  *   buf_len    : buffer length
7935331Samw  *   msg_id     : message id
7945331Samw  *   name       : key name, must be the same as in TKEY record
7955331Samw  *   fudge_time : amount of error time allow in seconds
7965331Samw  *   data       : TSIG data if TSIG_SIGNED, otherwise NULL
7975331Samw  *   data_size  : size of data, otherwise 0 if data is NULL
7985331Samw  *   data_signed: TSIG_SIGNED to indicate data is signed and encrypted,
7995331Samw  *                otherwise TSIG_UNSIGNED
8005331Samw  * Returns:
8015331Samw  *   ptr: address of pointer to next available buffer space
8025331Samw  *   -1 : error
8035331Samw  *    0 : success
8045331Samw  */
8055331Samw static int
dyndns_build_tsig(char ** ptr,int buf_len,int msg_id,char * name,int fudge_time,char * data,int data_size,int data_signed)8065331Samw dyndns_build_tsig(char **ptr, int buf_len, int msg_id, char *name,
8075331Samw 	int fudge_time, char *data, int data_size, int data_signed)
8085331Samw {
8095331Samw 	char *namePtr;
8105331Samw 	struct timeval tp;
8115331Samw 	int signtime, fudge, rec_len;
8125331Samw 
8135331Samw 	if (data_signed == TSIG_UNSIGNED)
8145331Samw 		rec_len = strlen(name)+2 + 37;
8155331Samw 	else
8165331Samw 		rec_len = strlen(name)+2 + 45 + data_size;
8175331Samw 
8185331Samw 	if (rec_len > buf_len) {
8198334SJose.Borrego@Sun.COM 		syslog(LOG_ERR, "dyndns TSIG: buffer too small");
8205331Samw 		return (-1);
8215331Samw 	}
8225331Samw 
8235331Samw 	namePtr = *ptr;
8245331Samw 	(void) dyndns_stuff_str(&namePtr, name);	/* unique global name */
8255331Samw 	*ptr = namePtr;
8265331Samw 	if (data_signed == TSIG_SIGNED)
8275331Samw 		*ptr = dyndns_put_nshort(*ptr, ns_t_tsig);
8285331Samw 	*ptr = dyndns_put_nshort(*ptr, ns_c_any);
8295331Samw 	*ptr = dyndns_put_nlong(*ptr, 0);
8305331Samw 	if (data_signed == TSIG_SIGNED) {
8315331Samw 		/* 19 + 10 + data_size + 6 */
8325331Samw 		*ptr = dyndns_put_nshort(*ptr, 35 + data_size);
8335331Samw 	}
8345331Samw 	namePtr = *ptr;
8355331Samw 	(void) dyndns_stuff_str(&namePtr, "gss.microsoft.com");
8365331Samw 	*ptr = namePtr;
8375331Samw 	(void) gettimeofday(&tp, 0);
8385331Samw 	signtime = tp.tv_sec >> 16;
8395331Samw 	*ptr = dyndns_put_nlong(*ptr, signtime);	/* sign time */
8405331Samw 	fudge = tp.tv_sec << 16;
8415331Samw 	fudge |= fudge_time;
8425331Samw 	*ptr = dyndns_put_nlong(*ptr, fudge);	/* fudge time */
8435331Samw 	if (data_signed == TSIG_SIGNED) {
8445331Samw 		/* signed data size */
8455331Samw 		*ptr = dyndns_put_nshort(*ptr, data_size);
8465331Samw 		(void) memcpy(*ptr, data, data_size);	/* signed data */
8475331Samw 		*ptr += data_size;
8485331Samw 		*ptr = dyndns_put_nshort(*ptr, msg_id);	/* original id */
8495331Samw 	}
8505331Samw 	*ptr = dyndns_put_nshort(*ptr, 0);	/* error */
8515331Samw 	*ptr = dyndns_put_nshort(*ptr, 0);	/* other */
8525331Samw 	return (0);
8535331Samw }
8545331Samw 
8555331Samw /*
8565331Samw  * dyndns_open_init_socket
8575331Samw  * This routine creates a SOCK_STREAM or SOCK_DGRAM socket and initializes it
8585331Samw  * by doing bind() and setting linger option to off.
8595331Samw  *
8605331Samw  * Parameters:
8615331Samw  *   sock_type: SOCK_STREAM for TCP or SOCK_DGRAM for UDP
8625331Samw  *   dest_addr: destination address in network byte order
8635331Samw  *   port     : destination port number
8645331Samw  * Returns:
8655331Samw  *   descriptor: descriptor referencing the created socket
8665331Samw  *   -1        : error
8675331Samw  */
8688670SJose.Borrego@Sun.COM 
8697052Samw static int
dyndns_open_init_socket(int sock_type,smb_inaddr_t * dest_addr,int port)8708670SJose.Borrego@Sun.COM dyndns_open_init_socket(int sock_type, smb_inaddr_t *dest_addr, int port)
8715331Samw {
8725331Samw 	int s;
8735331Samw 	struct sockaddr_in my_addr;
8748670SJose.Borrego@Sun.COM 	struct sockaddr_in6 my6_addr;
8755331Samw 	struct sockaddr_in serv_addr;
8768670SJose.Borrego@Sun.COM 	struct sockaddr_in6 serv6_addr;
8778670SJose.Borrego@Sun.COM 	int family;
8785331Samw 
8798670SJose.Borrego@Sun.COM 	family = dest_addr->a_family;
8805331Samw 
8818670SJose.Borrego@Sun.COM 	if ((s = socket(family, sock_type, 0)) == -1) {
8828670SJose.Borrego@Sun.COM 		syslog(LOG_ERR, "dyndns: socket error\n");
8835331Samw 		return (-1);
8845331Samw 	}
8858670SJose.Borrego@Sun.COM 	if (family == AF_INET) {
8868670SJose.Borrego@Sun.COM 		bzero(&my_addr, sizeof (my_addr));
8878670SJose.Borrego@Sun.COM 		my_addr.sin_family = family;
8888670SJose.Borrego@Sun.COM 		my_addr.sin_port = htons(0);
8898670SJose.Borrego@Sun.COM 		my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
8908670SJose.Borrego@Sun.COM 		if (bind(s, (struct sockaddr *)&my_addr,
8918670SJose.Borrego@Sun.COM 		    sizeof (my_addr)) < 0) {
8928670SJose.Borrego@Sun.COM 			syslog(LOG_ERR, "dyndns: client bind err\n");
8938670SJose.Borrego@Sun.COM 			(void) close(s);
8948670SJose.Borrego@Sun.COM 			return (-1);
8958670SJose.Borrego@Sun.COM 		}
8968670SJose.Borrego@Sun.COM 		serv_addr.sin_family = family;
8978670SJose.Borrego@Sun.COM 		serv_addr.sin_port = htons(port);
8988670SJose.Borrego@Sun.COM 		serv_addr.sin_addr.s_addr = dest_addr->a_ipv4;
8998670SJose.Borrego@Sun.COM 		if (connect(s, (struct sockaddr *)&serv_addr,
9008670SJose.Borrego@Sun.COM 		    sizeof (struct sockaddr_in)) < 0) {
9018670SJose.Borrego@Sun.COM 			syslog(LOG_ERR, "dyndns: client connect (%s)\n",
9028670SJose.Borrego@Sun.COM 			    strerror(errno));
9038670SJose.Borrego@Sun.COM 			(void) close(s);
9048670SJose.Borrego@Sun.COM 			return (-1);
9058670SJose.Borrego@Sun.COM 		}
9068670SJose.Borrego@Sun.COM 	} else {
9078670SJose.Borrego@Sun.COM 		bzero(&my6_addr, sizeof (my6_addr));
9088670SJose.Borrego@Sun.COM 		my6_addr.sin6_family = family;
9098670SJose.Borrego@Sun.COM 		my6_addr.sin6_port = htons(0);
9108670SJose.Borrego@Sun.COM 		bzero(&my6_addr.sin6_addr.s6_addr, IN6ADDRSZ);
9118670SJose.Borrego@Sun.COM 		if (bind(s, (struct sockaddr *)&my6_addr,
9128670SJose.Borrego@Sun.COM 		    sizeof (my6_addr)) < 0) {
9138670SJose.Borrego@Sun.COM 			syslog(LOG_ERR, "dyndns: client bind err\n");
9148670SJose.Borrego@Sun.COM 			(void) close(s);
9158670SJose.Borrego@Sun.COM 			return (-1);
9168670SJose.Borrego@Sun.COM 		}
9178670SJose.Borrego@Sun.COM 		serv6_addr.sin6_family = family;
9188670SJose.Borrego@Sun.COM 		serv6_addr.sin6_port = htons(port);
9198670SJose.Borrego@Sun.COM 		bcopy(&serv6_addr.sin6_addr.s6_addr, &dest_addr->a_ipv6,
9208670SJose.Borrego@Sun.COM 		    IN6ADDRSZ);
9218670SJose.Borrego@Sun.COM 		if (connect(s, (struct sockaddr *)&serv6_addr,
9228670SJose.Borrego@Sun.COM 		    sizeof (struct sockaddr_in6)) < 0) {
9238670SJose.Borrego@Sun.COM 			syslog(LOG_ERR, "dyndns: client connect err (%s)\n",
9248670SJose.Borrego@Sun.COM 			    strerror(errno));
9258670SJose.Borrego@Sun.COM 			(void) close(s);
9268670SJose.Borrego@Sun.COM 			return (-1);
9278670SJose.Borrego@Sun.COM 		}
9285331Samw 	}
9295331Samw 	return (s);
9305331Samw }
9315331Samw /*
9325331Samw  * dyndns_build_tkey_msg
9335331Samw  * This routine is used to build the TKEY message to transmit GSS tokens
9345331Samw  * during GSS security context establishment for secure DNS update.  The
9355331Samw  * TKEY message format uses the DNS query message format.  The TKEY section
9365331Samw  * is the answer section of the query message format.
9375331Samw  * Microsoft uses a value of 86400 seconds (24 hours) for key expiration time.
9385331Samw  * Parameters:
9395331Samw  *   buf     : buffer to build and store TKEY message
9405331Samw  *   key_name: a unique key name, this same key name must be also be used in
9415331Samw  *             the TSIG message
9425331Samw  *   out_tok : TKEY message data (GSS tokens)
9435331Samw  * Returns:
9445331Samw  *   id          : message id of this TKEY message
9455331Samw  *   message size: the size of the TKEY message
9465331Samw  *   -1          : error
9475331Samw  */
9485331Samw static int
dyndns_build_tkey_msg(char * buf,char * key_name,uint16_t * id,gss_buffer_desc * out_tok)9495331Samw dyndns_build_tkey_msg(char *buf, char *key_name, uint16_t *id,
9505331Samw 	gss_buffer_desc *out_tok)
9515331Samw {
9525331Samw 	int queryReq, zoneCount, preqCount, updateCount, additionalCount;
9535331Samw 	int zoneType, zoneClass;
9545331Samw 	char *bufptr;
9555331Samw 
9565331Samw 	queryReq = REQ_QUERY;
9575331Samw 	/* query section of query request */
9585331Samw 	zoneCount = 1;
9595331Samw 	/* answer section of query request */
9605331Samw 	preqCount = 1;
9615331Samw 	updateCount = 0;
9625331Samw 	additionalCount = 0;
9635331Samw 
9645331Samw 	(void) memset(buf, 0, MAX_TCP_SIZE);
9655331Samw 	bufptr = buf;
9668334SJose.Borrego@Sun.COM 	*id = dyndns_get_msgid();
9675331Samw 
9685331Samw 	/* add TCP length info that follows this field */
9695331Samw 	bufptr = dyndns_put_nshort(bufptr,
9705331Samw 	    26 + (strlen(key_name)+2)*2 + 35 + out_tok->length);
9715331Samw 
9725331Samw 	if (dyndns_build_header(&bufptr, BUFLEN_TCP(bufptr, buf), *id, queryReq,
9735331Samw 	    zoneCount, preqCount, updateCount, additionalCount, 0) == -1) {
9745331Samw 		return (-1);
9755331Samw 	}
9765331Samw 
9775331Samw 	zoneType = ns_t_tkey;
9785331Samw 	zoneClass = ns_c_in;
9795331Samw 	if (dyndns_build_quest_zone(&bufptr, BUFLEN_TCP(bufptr, buf), key_name,
9805331Samw 	    zoneType, zoneClass) == -1) {
9815331Samw 		return (-1);
9825331Samw 	}
9835331Samw 
9845331Samw 	if (dyndns_build_tkey(&bufptr, BUFLEN_TCP(bufptr, buf), key_name,
9855331Samw 	    86400, out_tok->value, out_tok->length) == -1) {
9865331Samw 		return (-1);
9875331Samw 	}
9885331Samw 
9895331Samw 	return (bufptr - buf);
9905331Samw }
9915331Samw 
9925331Samw /*
9935331Samw  * dyndns_establish_sec_ctx
9945331Samw  * This routine is used to establish a security context with the DNS server
9955331Samw  * by building TKEY messages and sending them to the DNS server.  TKEY messages
9965331Samw  * are also received from the DNS server for processing.   The security context
9975331Samw  * establishment is done with the GSS client on the system producing a token
9985331Samw  * and sending the token within the TKEY message to the GSS server on the DNS
9995331Samw  * server.  The GSS server then processes the token and then send a TKEY reply
10005331Samw  * message with a new token to be processed by the GSS client.  The GSS client
10015331Samw  * processes the new token and then generates a new token to be sent to the
10025331Samw  * GSS server.  This cycle is continued until the security establishment is
10035331Samw  * done.  TCP is used to send and receive TKEY messages.
10045331Samw  * Parameters:
10055521Sas200622  *   cred_handle  : handle to credential
10065331Samw  *   s           : socket descriptor to DNS server
10075331Samw  *   key_name    : TKEY key name
100811963SAfshin.Ardakani@Sun.COM  *   dns_hostname: fully qualified DNS hostname which will be used for
100911963SAfshin.Ardakani@Sun.COM  *                 constructing the DNS service principal name.
10105331Samw  *   oid         : contains Kerberos 5 object identifier
10115331Samw  * Returns:
10125331Samw  *   gss_context    : handle to security context
10135331Samw  */
10145331Samw static int
dyndns_establish_sec_ctx(gss_ctx_id_t * gss_context,gss_cred_id_t cred_handle,int s,char * key_name,char * dns_hostname,gss_OID oid)10155521Sas200622 dyndns_establish_sec_ctx(gss_ctx_id_t *gss_context, gss_cred_id_t cred_handle,
10165521Sas200622     int s, char *key_name, char *dns_hostname, gss_OID oid)
10175331Samw {
10185331Samw 	uint16_t id, rid, rsz;
10195331Samw 	char buf[MAX_TCP_SIZE], buf2[MAX_TCP_SIZE];
10205331Samw 	int ret;
10215331Samw 	char *service_name, *tmpptr;
10225331Samw 	int service_sz;
10235331Samw 	OM_uint32 min, maj, time_rec;
10245331Samw 	gss_buffer_desc service_buf, in_tok, out_tok;
10255331Samw 	gss_name_t target_name;
10265331Samw 	gss_buffer_desc *inputptr;
10275331Samw 	int gss_flags;
10285331Samw 	OM_uint32 ret_flags;
10295331Samw 	int buf_sz;
10305331Samw 
10315331Samw 	service_sz = strlen(dns_hostname) + 5;
10325331Samw 	service_name = (char *)malloc(sizeof (char) * service_sz);
10338334SJose.Borrego@Sun.COM 	if (service_name == NULL)
10345331Samw 		return (-1);
10358334SJose.Borrego@Sun.COM 
10365331Samw 	(void) snprintf(service_name, service_sz, "DNS@%s", dns_hostname);
10375331Samw 	service_buf.value = service_name;
10385331Samw 	service_buf.length = strlen(service_name)+1;
10395331Samw 	if ((maj = gss_import_name(&min, &service_buf,
10405521Sas200622 	    GSS_C_NT_HOSTBASED_SERVICE, &target_name)) != GSS_S_COMPLETE) {
10415331Samw 		display_stat(maj, min);
10425331Samw 		(void) free(service_name);
10435331Samw 		return (-1);
10445331Samw 	}
10455331Samw 	(void) free(service_name);
10465331Samw 
10475331Samw 	inputptr = GSS_C_NO_BUFFER;
10485331Samw 	*gss_context = GSS_C_NO_CONTEXT;
10495331Samw 	gss_flags = GSS_C_MUTUAL_FLAG | GSS_C_DELEG_FLAG | GSS_C_REPLAY_FLAG |
10505331Samw 	    GSS_C_SEQUENCE_FLAG | GSS_C_CONF_FLAG | GSS_C_INTEG_FLAG;
10515331Samw 	do {
10525521Sas200622 		maj = gss_init_sec_context(&min, cred_handle, gss_context,
10535521Sas200622 		    target_name, oid, gss_flags, 0, NULL, inputptr, NULL,
10545521Sas200622 		    &out_tok, &ret_flags, &time_rec);
10555521Sas200622 
10565521Sas200622 		if (maj != GSS_S_COMPLETE && maj != GSS_S_CONTINUE_NEEDED) {
10575521Sas200622 			assert(gss_context);
10585521Sas200622 			if (*gss_context != GSS_C_NO_CONTEXT)
10595521Sas200622 				(void) gss_delete_sec_context(&min,
10605521Sas200622 				    gss_context, NULL);
10615521Sas200622 
10625521Sas200622 			display_stat(maj, min);
10635331Samw 			(void) gss_release_name(&min, &target_name);
10645331Samw 			return (-1);
10655331Samw 		}
10665331Samw 
10675331Samw 		if ((maj == GSS_S_COMPLETE) &&
10685331Samw 		    !(ret_flags & GSS_C_REPLAY_FLAG)) {
10698334SJose.Borrego@Sun.COM 			syslog(LOG_ERR, "dyndns: No GSS_C_REPLAY_FLAG");
10705331Samw 			if (out_tok.length > 0)
10715331Samw 				(void) gss_release_buffer(&min, &out_tok);
10725331Samw 			(void) gss_release_name(&min, &target_name);
10735331Samw 			return (-1);
10745331Samw 		}
10755331Samw 
10765331Samw 		if ((maj == GSS_S_COMPLETE) &&
10775331Samw 		    !(ret_flags & GSS_C_MUTUAL_FLAG)) {
10788334SJose.Borrego@Sun.COM 			syslog(LOG_ERR, "dyndns: No GSS_C_MUTUAL_FLAG");
10795331Samw 			if (out_tok.length > 0)
10805331Samw 				(void) gss_release_buffer(&min, &out_tok);
10815331Samw 			(void) gss_release_name(&min, &target_name);
10825331Samw 			return (-1);
10835331Samw 		}
10845331Samw 
10855331Samw 		if (out_tok.length > 0) {
10865331Samw 			if ((buf_sz = dyndns_build_tkey_msg(buf, key_name,
10875331Samw 			    &id, &out_tok)) <= 0) {
10885331Samw 				(void) gss_release_buffer(&min, &out_tok);
10895331Samw 				(void) gss_release_name(&min, &target_name);
10905331Samw 				return (-1);
10915331Samw 			}
10925331Samw 
10935331Samw 			(void) gss_release_buffer(&min, &out_tok);
10945331Samw 
10955331Samw 			if (send(s, buf, buf_sz, 0) == -1) {
10968334SJose.Borrego@Sun.COM 				syslog(LOG_ERR, "dyndns: TKEY send error");
10975331Samw 				(void) gss_release_name(&min, &target_name);
10985331Samw 				return (-1);
10995331Samw 			}
11005331Samw 
11015331Samw 			bzero(buf2, MAX_TCP_SIZE);
11025331Samw 			if (recv(s, buf2, MAX_TCP_SIZE, 0) == -1) {
11038334SJose.Borrego@Sun.COM 				syslog(LOG_ERR, "dyndns: TKEY recv error");
11045331Samw 				(void) gss_release_name(&min, &target_name);
11055331Samw 				return (-1);
11065331Samw 			}
11075331Samw 
11085331Samw 			ret = buf2[5] & 0xf;	/* error field in TCP */
11095331Samw 			if (ret != NOERROR) {
11108334SJose.Borrego@Sun.COM 				dyndns_syslog(LOG_ERR, ret, "TKEY reply");
11115331Samw 				(void) gss_release_name(&min, &target_name);
11125331Samw 				return (-1);
11135331Samw 			}
11145331Samw 
11155331Samw 			tmpptr = &buf2[2];
11165331Samw 			(void) dyndns_get_nshort(tmpptr, &rid);
11175331Samw 			if (id != rid) {
11185331Samw 				(void) gss_release_name(&min, &target_name);
11195331Samw 				return (-1);
11205331Samw 			}
11215331Samw 
11225331Samw 			tmpptr = &buf2[59+(strlen(key_name)+2)*2];
11235331Samw 			(void) dyndns_get_nshort(tmpptr, &rsz);
11245331Samw 			in_tok.length = rsz;
11255331Samw 
11265331Samw 			/* bsd38 -> 2*7=14 */
11275331Samw 			in_tok.value = &buf2[61+(strlen(key_name)+2)*2];
11285331Samw 			inputptr = &in_tok;
11295331Samw 		}
11305331Samw 
11315331Samw 	} while (maj != GSS_S_COMPLETE);
11325331Samw 
11335331Samw 	(void) gss_release_name(&min, &target_name);
11345331Samw 
11355331Samw 	return (0);
11365331Samw }
11375331Samw 
11385331Samw /*
11395331Samw  * dyndns_get_sec_context
11405331Samw  * Get security context for secure dynamic DNS update.  This routine opens
11415521Sas200622  * a TCP socket to the DNS server and establishes a security context with
11425521Sas200622  * the DNS server using host principal to perform secure dynamic DNS update.
11435331Samw  * Parameters:
11445331Samw  *   hostname: fully qualified hostname
11455331Samw  *   dns_ip  : ip address of hostname in network byte order
11465331Samw  * Returns:
11475331Samw  *   gss_handle: gss credential handle
11485331Samw  *   gss_context: gss security context
11495331Samw  *   -1: error
11505331Samw  *    0: success
11515331Samw  */
11528670SJose.Borrego@Sun.COM 
11535331Samw static gss_ctx_id_t
dyndns_get_sec_context(const char * hostname,smb_inaddr_t * dns_ip)11548670SJose.Borrego@Sun.COM dyndns_get_sec_context(const char *hostname, smb_inaddr_t *dns_ip)
11555331Samw {
11565331Samw 	int s;
11575521Sas200622 	gss_cred_id_t cred_handle;
11585331Samw 	gss_ctx_id_t gss_context;
11595331Samw 	gss_OID oid;
11605521Sas200622 	char *key_name, dns_hostname[MAXHOSTNAMELEN];
11615331Samw 
11625521Sas200622 	cred_handle = GSS_C_NO_CREDENTIAL;
11635521Sas200622 	oid = GSS_C_NO_OID;
11645331Samw 	key_name = (char *)hostname;
11655331Samw 
11669832Samw@Sun.COM 	if (smb_getnameinfo(dns_ip, dns_hostname,
11678670SJose.Borrego@Sun.COM 	    sizeof (dns_hostname), 0)) {
11685331Samw 		return (NULL);
11695331Samw 	}
11705331Samw 	if ((s = dyndns_open_init_socket(SOCK_STREAM, dns_ip, 53)) < 0) {
11715331Samw 		return (NULL);
11725331Samw 	}
11735331Samw 
11745521Sas200622 	if (dyndns_establish_sec_ctx(&gss_context, cred_handle, s, key_name,
11755521Sas200622 	    dns_hostname, oid))
11765521Sas200622 		gss_context = NULL;
11775331Samw 
11785331Samw 	(void) close(s);
11795331Samw 	return (gss_context);
11805331Samw }
11815331Samw 
11825331Samw /*
11835331Samw  * dyndns_build_add_remove_msg
11845331Samw  * This routine builds the update request message for adding and removing DNS
11855331Samw  * entries which is used for non-secure and secure DNS update.
11865331Samw  * This routine builds an UDP message.
11875331Samw  * Parameters:
11885331Samw  *   buf        : buffer to build message
11895331Samw  *   update_zone: the type of zone to update, use UPDATE_FORW for forward
11905331Samw  *                lookup zone, use UPDATE_REV for reverse lookup zone
11915331Samw  *   hostname   : fully qualified hostname to update DNS with
11925331Samw  *   ip_addr    : IP address of hostname
11935331Samw  *   life_time  : cached time of this entry by others and not within DNS
11945331Samw  *                database
11955331Samw  *   update_type: UPDATE_ADD to add entry, UPDATE_DEL to remove entry
11965331Samw  *   del_type   : DEL_ONE for deleting one entry, DEL_ALL for deleting all
11975331Samw  *                entries of the same resource name.  Only valid for UPDATE_DEL.
11985331Samw  *   addit_cnt  : Indicate how many record is in the additional section of
11995331Samw  *                the DNS message.  A value of zero is always used with
12005331Samw  *                non-secure update message. For secure update message,
12015331Samw  *                the value will be one because the signed TSIG message
12025331Samw  *                is added as the additional record of the DNS update message.
12035331Samw  *   id         : DNS message ID.  If a positive value then this ID value is
12045331Samw  *                used, otherwise the next incremented value is used
12055331Samw  *   level      : This is the domain level which we send the request to, level
12065331Samw  *                zero is the default level, it can go upto 2 in reverse zone
12075331Samw  *                and virtually to any level in forward zone.
12085331Samw  * Returns:
12095331Samw  *   buf      : buffer containing update message
12105331Samw  *   id       : DNS message ID
12115331Samw  *   int      : size of update message
12125331Samw  *   -1       : error
12135331Samw  *
12145331Samw  * This function is changed to handle dynamic DNS update retires to higher
12155331Samw  * authoritative domains.
12165331Samw  */
12175331Samw static int
dyndns_build_add_remove_msg(char * buf,int update_zone,const char * hostname,const char * ip_addr,int life_time,int update_type,int del_type,int addit_cnt,uint16_t * id,int level)12185331Samw dyndns_build_add_remove_msg(char *buf, int update_zone, const char *hostname,
12195331Samw 	const char *ip_addr, int life_time, int update_type, int del_type,
12205331Samw 	int addit_cnt, uint16_t *id, int level)
12215331Samw {
12225331Samw 	int a, b, c, d;
12235331Samw 	char *bufptr;
12245331Samw 	int queryReq, zoneCount, preqCount, updateCount, additionalCount;
12255331Samw 	char *zone, *resource, *data, zone_buf[100], resrc_buf[100];
12265331Samw 	int zoneType, zoneClass, type, class, ttl;
12275331Samw 	char *p;
12288670SJose.Borrego@Sun.COM 	smb_inaddr_t tmp_addr;
12298670SJose.Borrego@Sun.COM 	int i, j, k;
12308670SJose.Borrego@Sun.COM 	int fourcnt;
12315331Samw 
12325331Samw 	queryReq = REQ_UPDATE;
12335331Samw 	zoneCount = 1;
12345331Samw 	preqCount = 0;
12355331Samw 	updateCount = 1;
12365331Samw 	additionalCount = addit_cnt;
12375331Samw 
12385331Samw 	(void) memset(buf, 0, NS_PACKETSZ);
12395331Samw 	bufptr = buf;
12405331Samw 
12415331Samw 	if (*id == 0)
12428334SJose.Borrego@Sun.COM 		*id = dyndns_get_msgid();
12435331Samw 
12445331Samw 	if (dyndns_build_header(&bufptr, BUFLEN_UDP(bufptr, buf), *id, queryReq,
12455331Samw 	    zoneCount, preqCount, updateCount, additionalCount, 0) == -1) {
12465331Samw 		return (-1);
12475331Samw 	}
12485331Samw 
12495331Samw 	zoneType = ns_t_soa;
12505331Samw 	zoneClass = ns_c_in;
12515331Samw 
12525331Samw 	if (update_zone == UPDATE_FORW) {
12535331Samw 		p = (char *)hostname;
12545331Samw 
12555331Samw 		/* Try higher domains according to the level requested */
12565331Samw 		do {
12575331Samw 			/* domain */
12585331Samw 			if ((zone = (char *)strchr(p, '.')) == NULL)
12595331Samw 				return (-1);
12605331Samw 			zone += 1;
12615331Samw 			p = zone;
12625331Samw 		} while (--level >= 0);
12635331Samw 		resource = (char *)hostname;
12645331Samw 		data = (char *)ip_addr;
12655331Samw 	} else {
12668670SJose.Borrego@Sun.COM 		if (inet_pton(AF_INET, ip_addr, &tmp_addr) == 1) {
12678670SJose.Borrego@Sun.COM 			(void) sscanf(ip_addr, "%d.%d.%d.%d", &a, &b, &c, &d);
12688670SJose.Borrego@Sun.COM 			(void) sprintf(zone_buf, "%d.%d.%d.in-addr.arpa",
12698670SJose.Borrego@Sun.COM 			    c, b, a);
12708670SJose.Borrego@Sun.COM 			zone = p = zone_buf;
12715331Samw 
12728670SJose.Borrego@Sun.COM 			/* Try higher domains based on level requested */
12738670SJose.Borrego@Sun.COM 			while (--level >= 0) {
12748670SJose.Borrego@Sun.COM 				/* domain */
12758670SJose.Borrego@Sun.COM 				if ((zone = (char *)strchr(p, '.')) == NULL) {
12768670SJose.Borrego@Sun.COM 					return (-1);
12778670SJose.Borrego@Sun.COM 				}
12788670SJose.Borrego@Sun.COM 				zone += 1;
12798670SJose.Borrego@Sun.COM 				p = zone;
12805331Samw 			}
12818670SJose.Borrego@Sun.COM 			(void) sprintf(resrc_buf, "%d.%d.%d.%d.in-addr.arpa",
12828670SJose.Borrego@Sun.COM 			    d, c, b, a);
12838670SJose.Borrego@Sun.COM 		} else {
12848670SJose.Borrego@Sun.COM 			/*
12858670SJose.Borrego@Sun.COM 			 * create reverse nibble ipv6 format
12868670SJose.Borrego@Sun.COM 			 */
12878670SJose.Borrego@Sun.COM 			bzero(resrc_buf, 100);
12888670SJose.Borrego@Sun.COM 			i = 0;
12898670SJose.Borrego@Sun.COM 			j = 0;
12908670SJose.Borrego@Sun.COM 			while (ip_addr[i] != 0)
12918670SJose.Borrego@Sun.COM 				i++;
12928670SJose.Borrego@Sun.COM 			i--;
12938670SJose.Borrego@Sun.COM 			while (i >= 0) {
12948670SJose.Borrego@Sun.COM 				fourcnt = 3;
12958670SJose.Borrego@Sun.COM 				while ((i >= 0) && (ip_addr[i] != ':')) {
12968670SJose.Borrego@Sun.COM 					resrc_buf[j++] = ip_addr[i];
12978670SJose.Borrego@Sun.COM 					(void) strcat(&resrc_buf[j++], ".");
12988670SJose.Borrego@Sun.COM 					fourcnt --;
12998670SJose.Borrego@Sun.COM 					i--;
13008670SJose.Borrego@Sun.COM 				}
13018670SJose.Borrego@Sun.COM 				for (k = 0; k <= fourcnt; k++) {
13028670SJose.Borrego@Sun.COM 					resrc_buf[j++] = '0';
13038670SJose.Borrego@Sun.COM 					(void) strcat(&resrc_buf[j++], ".");
13048670SJose.Borrego@Sun.COM 				}
13058670SJose.Borrego@Sun.COM 				i--;
13068670SJose.Borrego@Sun.COM 			}
13078670SJose.Borrego@Sun.COM 			(void) strcat(resrc_buf, "ip6.arpa");
13088670SJose.Borrego@Sun.COM 			(void) strcpy(zone_buf, &resrc_buf[32]);
13098670SJose.Borrego@Sun.COM 			zone = zone_buf;
13105331Samw 		}
13115331Samw 		resource = resrc_buf;	/* ip info */
13125331Samw 		data = (char *)hostname;
13135331Samw 	}
13145331Samw 	if (dyndns_build_quest_zone(&bufptr, BUFLEN_UDP(bufptr, buf), zone,
13155331Samw 	    zoneType, zoneClass) == -1) {
13165331Samw 		return (-1);
13175331Samw 	}
13185331Samw 
13195331Samw 	if (update_zone == UPDATE_FORW)
13205331Samw 		type = ns_t_a;
13215331Samw 	else
13225331Samw 		type = ns_t_ptr;
13235331Samw 
13245331Samw 	if (update_type == UPDATE_ADD) {
13255331Samw 		class = ns_c_in;
13265331Samw 		ttl = life_time;
13275331Samw 	} else {
13285331Samw 		if (del_type == DEL_ONE)
13295331Samw 			class = ns_c_none;	/* remove one */
13305331Samw 		else
13315331Samw 			class = ns_c_any;	/* remove all */
13325331Samw 		ttl = 0;
13335331Samw 	}
13345331Samw 	if (dyndns_build_update(&bufptr, BUFLEN_UDP(bufptr, buf),
13355331Samw 	    resource, type, class, ttl, data, update_zone,
13365331Samw 	    update_type, del_type) == -1) {
13375331Samw 		return (-1);
13385331Samw 	}
13395331Samw 
13405331Samw 	return (bufptr - buf);
13415331Samw }
13425331Samw 
13435331Samw /*
13445331Samw  * dyndns_build_unsigned_tsig_msg
13455331Samw  * This routine is used to build the unsigned TSIG message for signing.  The
13465331Samw  * unsigned TSIG message contains the update request message with certain TSIG
13475331Samw  * fields included.  An error time of 300 seconds is used for fudge time.  This
13485331Samw  * is the number used by Microsoft clients.
13495331Samw  * This routine builds a UDP message.
13505331Samw  * Parameters:
13515331Samw  *   buf        : buffer to build message
13525331Samw  *   update_zone: the type of zone to update, use UPDATE_FORW for forward
13535331Samw  *                lookup zone, use UPDATE_REV for reverse lookup zone
13545331Samw  *   hostname   : fully qualified hostname to update DNS with
13555331Samw  *   ip_addr    : IP address of hostname
13565331Samw  *   life_time  : cached time of this entry by others and not within DNS
13575331Samw  *                database
13585331Samw  *   update_type: UPDATE_ADD to add entry, UPDATE_DEL to remove entry
13595331Samw  *   del_type   : DEL_ONE for deleting one entry, DEL_ALL for deleting all
13605331Samw  *                entries of the same resource name.  Only valid for UPDATE_DEL.
13615331Samw  *   key_name   : same key name used in TKEY message
13625331Samw  *   id         : DNS message ID.  If a positive value then this ID value is
13635331Samw  *                used, otherwise the next incremented value is used
13645331Samw  *   level      : This is the domain level which we send the request to, level
13655331Samw  *                zero is the default level, it can go upto 2 in reverse zone
13665331Samw  *                and virtually to any level in forward zone.
13675331Samw  * Returns:
13685331Samw  *   buf      : buffer containing update message
13695331Samw  *   id       : DNS message ID
13705331Samw  *   int      : size of update message
13715331Samw  *   -1       : error
13725331Samw  */
13735331Samw static int
dyndns_build_unsigned_tsig_msg(char * buf,int update_zone,const char * hostname,const char * ip_addr,int life_time,int update_type,int del_type,char * key_name,uint16_t * id,int level)13745331Samw dyndns_build_unsigned_tsig_msg(char *buf, int update_zone, const char *hostname,
13755331Samw 	const char *ip_addr, int life_time, int update_type, int del_type,
13765331Samw 	char *key_name, uint16_t *id, int level)
13775331Samw {
13785331Samw 	char *bufptr;
13795331Samw 	int buf_sz;
13805331Samw 
13815331Samw 	if ((buf_sz = dyndns_build_add_remove_msg(buf, update_zone, hostname,
13825331Samw 	    ip_addr, life_time, update_type, del_type, 0, id, level)) <= 0) {
13835331Samw 		return (-1);
13845331Samw 	}
13855331Samw 
13865331Samw 	bufptr = buf + buf_sz;
13875331Samw 
13885331Samw 	if (dyndns_build_tsig(&bufptr, BUFLEN_UDP(bufptr, buf), 0,
13895331Samw 	    key_name, 300, NULL, 0, TSIG_UNSIGNED) == -1) {
13905331Samw 		return (-1);
13915331Samw 	}
13925331Samw 
13935331Samw 	return (bufptr - buf);
13945331Samw }
13955331Samw 
13965331Samw /*
13975331Samw  * dyndns_build_signed_tsig_msg
13985331Samw  * This routine build the signed TSIG message which contains the update
13995331Samw  * request message encrypted.  An error time of 300 seconds is used for fudge
14005331Samw  * time.  This is the number used by Microsoft clients.
14015331Samw  * This routine builds a UDP message.
14025331Samw  * Parameters:
14035331Samw  *   buf        : buffer to build message
14045331Samw  *   update_zone: the type of zone to update, use UPDATE_FORW for forward
14055331Samw  *                lookup zone, use UPDATE_REV for reverse lookup zone
14065331Samw  *   hostname   : fully qualified hostname to update DNS with
14075331Samw  *   ip_addr    : IP address of hostname
14085331Samw  *   life_time  : cached time of this entry by others and not within DNS
14095331Samw  *                database
14105331Samw  *   update_type: UPDATE_ADD to add entry, UPDATE_DEL to remove entry
14115331Samw  *   del_type   : DEL_ONE for deleting one entry, DEL_ALL for deleting all
14125331Samw  *                entries of the same resource name.  Only valid for UPDATE_DEL.
14135331Samw  *   key_name   : same key name used in TKEY message
14145331Samw  *   id         : DNS message ID.  If a positive value then this ID value is
14155331Samw  *                used, otherwise the next incremented value is used
14165331Samw  *   in_mic     : the update request message encrypted
14175331Samw  *   level      : This is the domain level which we send the request to, level
14185331Samw  *                zero is the default level, it can go upto 2 in reverse zone
14195331Samw  *                and virtually to any level in forward zone.
14205331Samw  *
14215331Samw  * Returns:
14225331Samw  *   buf      : buffer containing update message
14235331Samw  *   id       : DNS message ID
14245331Samw  *   int      : size of update message
14255331Samw  *   -1       : error
14265331Samw  */
14275331Samw static int
dyndns_build_signed_tsig_msg(char * buf,int update_zone,const char * hostname,const char * ip_addr,int life_time,int update_type,int del_type,char * key_name,uint16_t * id,gss_buffer_desc * in_mic,int level)14285331Samw dyndns_build_signed_tsig_msg(char *buf, int update_zone, const char *hostname,
14295331Samw 	const char *ip_addr, int life_time, int update_type, int del_type,
14305331Samw 	char *key_name, uint16_t *id, gss_buffer_desc *in_mic, int level)
14315331Samw {
14325331Samw 	char *bufptr;
14335331Samw 	int buf_sz;
14345331Samw 
14355331Samw 	if ((buf_sz = dyndns_build_add_remove_msg(buf, update_zone, hostname,
14365331Samw 	    ip_addr, life_time, update_type, del_type, 1, id, level)) <= 0) {
14375331Samw 		return (-1);
14385331Samw 	}
14395331Samw 
14405331Samw 	bufptr = buf + buf_sz;
14415331Samw 
14425331Samw 	if (dyndns_build_tsig(&bufptr, BUFLEN_UDP(bufptr, buf),
14435331Samw 	    *id, key_name, 300, in_mic->value,
14445331Samw 	    in_mic->length, TSIG_SIGNED) == -1) {
14455331Samw 		return (-1);
14465331Samw 	}
14475331Samw 
14485331Samw 	return (bufptr - buf);
14495331Samw }
14505331Samw 
14515331Samw /*
14525331Samw  * dyndns_udp_send_recv
14535772Sas200622  * This routine sends and receives UDP DNS request and reply messages.
14545331Samw  *
14555331Samw  * Pre-condition: Caller must call dyndns_open_init_socket() before calling
14565331Samw  * this function.
14575331Samw  *
14585331Samw  * Parameters:
14595331Samw  *   s        : socket descriptor
14605331Samw  *   buf      : buffer containing data to send
14615331Samw  *   buf_sz   : size of data to send
14625331Samw  * Returns:
14635331Samw  *   -1     : error
14645331Samw  *   rec_buf: reply dat
14655331Samw  *    0     : success
14665331Samw  */
14678670SJose.Borrego@Sun.COM 
14687052Samw static int
dyndns_udp_send_recv(int s,char * buf,int buf_sz,char * rec_buf)14695331Samw dyndns_udp_send_recv(int s, char *buf, int buf_sz, char *rec_buf)
14705331Samw {
14715772Sas200622 	int i, retval, addr_len;
14725331Samw 	struct timeval tv, timeout;
14735331Samw 	fd_set rfds;
14748670SJose.Borrego@Sun.COM 	struct sockaddr_in6 from_addr;
14755331Samw 
14765331Samw 	timeout.tv_usec = 0;
14775772Sas200622 	timeout.tv_sec = DYNDNS_QUERY_TIMEOUT;
14785331Samw 
14795772Sas200622 	for (i = 0; i <= DYNDNS_MAX_QUERY_RETRIES; i++) {
14805331Samw 		if (send(s, buf, buf_sz, 0) == -1) {
14818334SJose.Borrego@Sun.COM 			syslog(LOG_ERR, "dyndns: UDP send error (%s)",
14825331Samw 			    strerror(errno));
14835331Samw 			return (-1);
14845331Samw 		}
14855331Samw 
14865331Samw 		FD_ZERO(&rfds);
14875331Samw 		FD_SET(s, &rfds);
14885331Samw 
14895331Samw 		tv = timeout;
14905331Samw 
14915331Samw 		retval = select(s+1, &rfds, NULL, NULL, &tv);
14925331Samw 
14935331Samw 		if (retval == -1) {
14945331Samw 			return (-1);
14955331Samw 		} else if (retval > 0) {
14965331Samw 			bzero(rec_buf, NS_PACKETSZ);
14978670SJose.Borrego@Sun.COM 			addr_len = sizeof (struct sockaddr_in6);
14985331Samw 			if (recvfrom(s, rec_buf, NS_PACKETSZ, 0,
14995331Samw 			    (struct sockaddr *)&from_addr, &addr_len) == -1) {
15008670SJose.Borrego@Sun.COM 				syslog(LOG_ERR, "dyndns: UDP recv error ");
15015331Samw 				return (-1);
15025331Samw 			}
15035331Samw 			break;
15045331Samw 		}
15055331Samw 	}
15065331Samw 
15075772Sas200622 	/* did not receive anything */
15085772Sas200622 	if (i == (DYNDNS_MAX_QUERY_RETRIES + 1)) {
15098334SJose.Borrego@Sun.COM 		syslog(LOG_ERR, "dyndns: max retries for UDP recv reached");
15105331Samw 		return (-1);
15115331Samw 	}
15125331Samw 
15135331Samw 	return (0);
15145331Samw }
15155331Samw /*
15165331Samw  * dyndns_sec_add_remove_entry
15175331Samw  * Perform secure dynamic DNS update after getting security context.
15185331Samw  * This routine opens a UDP socket to the DNS sever, gets the security context,
15195331Samw  * builds the unsigned TSIG message and signed TSIG message.  The signed TSIG
15205331Samw  * message containing the encrypted update request message is sent to the DNS
15215331Samw  * server.  The response is received and check for error.  If there is no
15225331Samw  * error then credential handle and security context are released and the local
15235331Samw  * NSS cached is purged.
15245331Samw  * Parameters:
15255331Samw  *   update_zone : UPDATE_FORW for forward zone, UPDATE_REV for reverse zone
15265331Samw  *   hostname    : fully qualified hostname
15275331Samw  *   ip_addr     : ip address of hostname in string format
15285331Samw  *   life_time   : cached time of this entry by others and not within DNS
15295331Samw  *                 database
15305331Samw  *   max_retries : maximum retries for sending DNS update request
15315331Samw  *   recv_timeout: receive timeout
15325331Samw  *   update_type : UPDATE_ADD for adding entry, UPDATE_DEL for removing entry
15335331Samw  *   del_type    : DEL_ONE for deleting one entry, DEL_ALL for deleting all
15345331Samw  *                 entries of the same resource name.  Only valid for UPDATE_DEL
15355331Samw  *   dns_str     : DNS IP address in string format
15365331Samw  * Returns:
15375331Samw  *   -1: error
15385331Samw  *    0: success
15395331Samw  *
15405331Samw  * This function is enhanced to handle the case of NOTAUTH error when DNS server
15415331Samw  * is not authoritative for specified zone. In this case we need to resend the
15425331Samw  * same request to the higher authoritative domains.
15435331Samw  * This is true for both secure and unsecure dynamic DNS updates.
15445331Samw  */
15455331Samw static int
dyndns_sec_add_remove_entry(int update_zone,const char * hostname,const char * ip_addr,int life_time,int update_type,int del_type,char * dns_str)15465331Samw dyndns_sec_add_remove_entry(int update_zone, const char *hostname,
15475331Samw     const char *ip_addr, int life_time, int update_type, int del_type,
15485331Samw     char *dns_str)
15495331Samw {
15505331Samw 	int s2;
15515331Samw 	uint16_t id, rid;
15525331Samw 	char buf[NS_PACKETSZ], buf2[NS_PACKETSZ];
15535331Samw 	int ret;
15545331Samw 	OM_uint32 min, maj;
15555331Samw 	gss_buffer_desc in_mic, out_mic;
15565331Samw 	gss_ctx_id_t gss_context;
15578670SJose.Borrego@Sun.COM 	smb_inaddr_t dns_ip;
15585331Samw 	char *key_name;
15595331Samw 	int buf_sz;
15605331Samw 	int level = 0;
15615331Samw 
15625331Samw 	assert(dns_str);
15635331Samw 	assert(*dns_str);
15645331Samw 
15658670SJose.Borrego@Sun.COM 	if (inet_pton(AF_INET, dns_str, &dns_ip) == 1)
15668670SJose.Borrego@Sun.COM 		dns_ip.a_family = AF_INET;
15678670SJose.Borrego@Sun.COM 	else if (inet_pton(AF_INET6, dns_str, &dns_ip) == 1)
15688670SJose.Borrego@Sun.COM 		dns_ip.a_family = AF_INET6;
15695331Samw 
15705331Samw sec_retry_higher:
15715331Samw 
15725331Samw 	if ((gss_context = dyndns_get_sec_context(hostname,
15738670SJose.Borrego@Sun.COM 	    &dns_ip)) == NULL) {
15745331Samw 		return (-1);
15755331Samw 	}
15765331Samw 
15775331Samw 	key_name = (char *)hostname;
15785331Samw 
15798670SJose.Borrego@Sun.COM 	if ((s2 = dyndns_open_init_socket(SOCK_DGRAM, &dns_ip, 53)) < 0) {
15805521Sas200622 		if (gss_context != GSS_C_NO_CONTEXT)
15815521Sas200622 			(void) gss_delete_sec_context(&min, &gss_context, NULL);
15825331Samw 		return (-1);
15835331Samw 	}
15845331Samw 
15855331Samw 	id = 0;
15865331Samw 	if ((buf_sz = dyndns_build_unsigned_tsig_msg(buf, update_zone, hostname,
15875331Samw 	    ip_addr, life_time, update_type, del_type,
15885331Samw 	    key_name, &id, level)) <= 0) {
15895331Samw 		(void) close(s2);
15905521Sas200622 		if (gss_context != GSS_C_NO_CONTEXT)
15915521Sas200622 			(void) gss_delete_sec_context(&min, &gss_context, NULL);
15925331Samw 		return (-1);
15935331Samw 	}
15945331Samw 
15955331Samw 	in_mic.length = buf_sz;
15965331Samw 	in_mic.value = buf;
15975331Samw 
15985331Samw 	/* sign update message */
15995331Samw 	if ((maj = gss_get_mic(&min, gss_context, 0, &in_mic, &out_mic)) !=
16005331Samw 	    GSS_S_COMPLETE) {
16015331Samw 		display_stat(maj, min);
16025331Samw 		(void) close(s2);
16035521Sas200622 		if (gss_context != GSS_C_NO_CONTEXT)
16045521Sas200622 			(void) gss_delete_sec_context(&min, &gss_context, NULL);
16055331Samw 		return (-1);
16065331Samw 	}
16075331Samw 
16085331Samw 	if ((buf_sz = dyndns_build_signed_tsig_msg(buf, update_zone, hostname,
16095331Samw 	    ip_addr, life_time, update_type, del_type, key_name, &id,
16105331Samw 	    &out_mic, level)) <= 0) {
16115331Samw 		(void) close(s2);
16125331Samw 		(void) gss_release_buffer(&min, &out_mic);
16135521Sas200622 		if (gss_context != GSS_C_NO_CONTEXT)
16145521Sas200622 			(void) gss_delete_sec_context(&min, &gss_context, NULL);
16155331Samw 		return (-1);
16165331Samw 	}
16175331Samw 
16185331Samw 	(void) gss_release_buffer(&min, &out_mic);
16195331Samw 
16205331Samw 	if (dyndns_udp_send_recv(s2, buf, buf_sz, buf2)) {
16215331Samw 		(void) close(s2);
16225521Sas200622 		if (gss_context != GSS_C_NO_CONTEXT)
16235521Sas200622 			(void) gss_delete_sec_context(&min, &gss_context, NULL);
16245331Samw 		return (-1);
16255331Samw 	}
16265331Samw 
16275331Samw 	(void) close(s2);
16285331Samw 
16295521Sas200622 	if (gss_context != GSS_C_NO_CONTEXT)
16305521Sas200622 		(void) gss_delete_sec_context(&min, &gss_context, NULL);
16315331Samw 
16325331Samw 	ret = buf2[3] & 0xf;	/* error field in UDP */
16335331Samw 
16345331Samw 	/*
16355331Samw 	 * If it is a NOTAUTH error we should retry with higher domains
16365331Samw 	 * until we get a successful reply or the maximum retries is met.
16375331Samw 	 */
16385331Samw 	if (ret == NOTAUTH && level++ < MAX_AUTH_RETRIES)
16395331Samw 		goto sec_retry_higher;
16405331Samw 
16415331Samw 	/* check here for update request is successful */
16425331Samw 	if (ret != NOERROR) {
16438334SJose.Borrego@Sun.COM 		dyndns_syslog(LOG_ERR, ret, "TSIG reply");
16445331Samw 		return (-1);
16455331Samw 	}
16465331Samw 
16475331Samw 	(void) dyndns_get_nshort(buf2, &rid);
16485331Samw 	if (id != rid)
16495331Samw 		return (-1);
16505331Samw 
16515331Samw 	return (0);
16525331Samw }
16535331Samw 
16545331Samw /*
16555331Samw  * dyndns_seach_entry
16565331Samw  * Query DNS server for entry.  This routine can indicate if an entry exist
16575331Samw  * or not during forward or reverse lookup.  Also can indicate if the data
16585331Samw  * of the entry matched.  For example, for forward lookup, the entry is
16595331Samw  * searched using the hostname and the data is the IP address.  For reverse
16605331Samw  * lookup, the entry is searched using the IP address and the data is the
16615331Samw  * hostname.
16625331Samw  * Parameters:
16635331Samw  *   update_zone: UPDATE_FORW for forward zone, UPDATE_REV for reverse zone
16645331Samw  *   hostname   : fully qualified hostname
16655331Samw  *   ip_addr    : ip address of hostname in string format
16665331Samw  *   update_type: UPDATE_ADD for adding entry, UPDATE_DEL for removing entry
16675331Samw  * Returns:
16685331Samw  *   time_out: no use
16695331Samw  *   is_match: is 1 for found matching entry, otherwise 0
16705331Samw  *   1       : an entry exist but not necessarily match
16715331Samw  *   0       : an entry does not exist
16725331Samw  */
16735331Samw /*ARGSUSED*/
16748670SJose.Borrego@Sun.COM 
16755331Samw static int
dyndns_search_entry(int update_zone,const char * hostname,const char * ip_addr,int update_type,struct timeval * time_out,int * is_match)16765331Samw dyndns_search_entry(int update_zone, const char *hostname, const char *ip_addr,
16775331Samw     int update_type, struct timeval *time_out, int *is_match)
16785331Samw {
16798670SJose.Borrego@Sun.COM 	smb_inaddr_t ipaddr, dnsip;
16808670SJose.Borrego@Sun.COM 	char dns_hostname[NI_MAXHOST];
16818670SJose.Borrego@Sun.COM 	struct addrinfo hints, *res = NULL;
16828670SJose.Borrego@Sun.COM 	int salen;
16838670SJose.Borrego@Sun.COM 	int family;
16845331Samw 
16855331Samw 	*is_match = 0;
16868670SJose.Borrego@Sun.COM 	if (inet_pton(AF_INET, ip_addr, &ipaddr) == 1) {
16878670SJose.Borrego@Sun.COM 		salen = sizeof (ipaddr.a_ipv4);
16888670SJose.Borrego@Sun.COM 		family = AF_INET;
16898670SJose.Borrego@Sun.COM 	} else if (inet_pton(AF_INET6, ip_addr, &ipaddr) == 1) {
16908670SJose.Borrego@Sun.COM 		salen = sizeof (ipaddr.a_ipv6);
16918670SJose.Borrego@Sun.COM 		family = AF_INET6;
16928670SJose.Borrego@Sun.COM 	}
16935331Samw 	if (update_zone == UPDATE_FORW) {
16948670SJose.Borrego@Sun.COM 		bzero((char *)&hints, sizeof (hints));
16958670SJose.Borrego@Sun.COM 		hints.ai_family = family;
16968670SJose.Borrego@Sun.COM 		hints.ai_flags = AI_NUMERICHOST;
16978670SJose.Borrego@Sun.COM 		if (getaddrinfo(hostname, NULL, &hints, &res)) {
16988670SJose.Borrego@Sun.COM 			return (NULL);
16998670SJose.Borrego@Sun.COM 		}
17008670SJose.Borrego@Sun.COM 		if (res) {
17018670SJose.Borrego@Sun.COM 			/*
17028670SJose.Borrego@Sun.COM 			 * if both ips aren't the same family skip to
17038670SJose.Borrego@Sun.COM 			 * the next record
17048670SJose.Borrego@Sun.COM 			 */
17058670SJose.Borrego@Sun.COM 			do {
17068670SJose.Borrego@Sun.COM 				if ((res->ai_family == AF_INET) &&
17078670SJose.Borrego@Sun.COM 				    (family == AF_INET)) {
17088670SJose.Borrego@Sun.COM 					(void) memcpy(&dnsip, &res->ai_addr[0],
17098670SJose.Borrego@Sun.COM 					    salen);
17108670SJose.Borrego@Sun.COM 					if (ipaddr.a_ipv4 ==
17118670SJose.Borrego@Sun.COM 					    dnsip.a_ipv4) {
17128670SJose.Borrego@Sun.COM 						*is_match = 1;
17138670SJose.Borrego@Sun.COM 						break;
17148670SJose.Borrego@Sun.COM 					}
17158670SJose.Borrego@Sun.COM 				} else if ((res->ai_family == AF_INET6) &&
17168670SJose.Borrego@Sun.COM 				    (family == AF_INET6)) {
17178670SJose.Borrego@Sun.COM 					(void) memcpy(&dnsip, &res->ai_addr[0],
17188670SJose.Borrego@Sun.COM 					    salen);
17198670SJose.Borrego@Sun.COM 					/* need compare macro here */
17208670SJose.Borrego@Sun.COM 					if (!memcmp(&ipaddr, &dnsip,
17218670SJose.Borrego@Sun.COM 					    IN6ADDRSZ)) {
17228670SJose.Borrego@Sun.COM 						*is_match = 1;
17238670SJose.Borrego@Sun.COM 						break;
17248670SJose.Borrego@Sun.COM 					}
17255331Samw 				}
17268670SJose.Borrego@Sun.COM 			} while (res->ai_next);
17278670SJose.Borrego@Sun.COM 			freeaddrinfo(res);
17285331Samw 			return (1);
17295331Samw 		}
17305331Samw 	} else {
17319832Samw@Sun.COM 		if (smb_getnameinfo(&ipaddr, dns_hostname, NI_MAXHOST, 0))
17328670SJose.Borrego@Sun.COM 			return (NULL);
17339832Samw@Sun.COM 
17348670SJose.Borrego@Sun.COM 		if (strncasecmp(dns_hostname, hostname,
17358670SJose.Borrego@Sun.COM 		    strlen(hostname)) == 0) {
17368670SJose.Borrego@Sun.COM 			*is_match = 1;
17378670SJose.Borrego@Sun.COM 		}
17388670SJose.Borrego@Sun.COM 		return (1);
17395331Samw 	}
17405331Samw 
17415331Samw 	/* entry does not exist */
17425331Samw 	return (0);
17435331Samw }
17445331Samw 
17455331Samw /*
17465331Samw  * dyndns_add_remove_entry
17476139Sjb150015  * Perform non-secure dynamic DNS update.  If it fails and host principal
17486139Sjb150015  * keys can be found in the local keytab file, secure update will be performed.
17496139Sjb150015  *
17505331Samw  * This routine opens a UDP socket to the DNS sever, build the update request
17515331Samw  * message, and sends the message to the DNS server.  The response is received
17525331Samw  * and check for error.  If there is no error then the local NSS cached is
17535331Samw  * purged.  DNS may be used to check to see if an entry already exist before
17545331Samw  * adding or to see if an entry does exist before removing it.  Adding
17555331Samw  * duplicate entries or removing non-existing entries does not cause any
17565331Samw  * problems.  DNS is not check when doing a delete all.
17575331Samw  * Parameters:
17585331Samw  *   update_zone: UPDATE_FORW for forward zone, UPDATE_REV for reverse zone
17595331Samw  *   hostname   : fully qualified hostname
17605331Samw  *   ip_addr    : ip address of hostname in string format
17615331Samw  *   life_time  : cached time of this entry by others and not within DNS
17625331Samw  *                database
17635331Samw  *   update_type: UPDATE_ADD to add entry, UPDATE_DEL to remove entry
17645331Samw  *   do_check   : DNS_CHECK to check first in DNS, DNS_NOCHECK for no DNS
17655331Samw  *                checking before update
17665331Samw  *   del_type   : DEL_ONE for deleting one entry, DEL_ALL for deleting all
17675331Samw  *                entries of the same resource name.  Only valid for UPDATE_DEL.
17685331Samw  *   dns_str    : DNS IP address in string format
17695331Samw  * Returns:
17705331Samw  *   -1: error
17715331Samw  *    0: success
17725331Samw  *
17735331Samw  * This function is enhanced to handle the case of NOTAUTH error when DNS server
17745331Samw  * is not authoritative for specified zone. In this case we need to resend the
17755331Samw  * same request to the higher authoritative domains.
17765331Samw  * This is true for both secure and unsecure dynamic DNS updates.
17775331Samw  */
17785331Samw static int
dyndns_add_remove_entry(int update_zone,const char * hostname,const char * ip_addr,int life_time,int update_type,int do_check,int del_type,char * dns_str)17795331Samw dyndns_add_remove_entry(int update_zone, const char *hostname,
17805331Samw     const char *ip_addr, int life_time, int update_type,
17815331Samw     int do_check, int del_type, char *dns_str)
17825331Samw {
17835331Samw 	int s;
17845331Samw 	uint16_t id, rid;
17855331Samw 	char buf[NS_PACKETSZ], buf2[NS_PACKETSZ];
17868670SJose.Borrego@Sun.COM 	int ret;
17875331Samw 	int is_exist, is_match;
17885331Samw 	struct timeval timeout;
17895331Samw 	int buf_sz;
17905331Samw 	int level = 0;
17918670SJose.Borrego@Sun.COM 	smb_inaddr_t dns_ip;
179212508Samw@Sun.COM 	char *fqdn;
179312508Samw@Sun.COM 	char *p;
17945331Samw 
17955331Samw 	assert(dns_str);
17965331Samw 	assert(*dns_str);
17975331Samw 
17985331Samw 	if (do_check == DNS_CHECK && del_type != DEL_ALL) {
17995331Samw 		is_exist = dyndns_search_entry(update_zone, hostname, ip_addr,
18005331Samw 		    update_type, &timeout, &is_match);
18015331Samw 
18025331Samw 		if (update_type == UPDATE_ADD && is_exist && is_match) {
18035331Samw 			return (0);
18045331Samw 		} else if (update_type == UPDATE_DEL && !is_exist) {
18055331Samw 			return (0);
18065331Samw 		}
18075331Samw 	}
18085331Samw 
18098670SJose.Borrego@Sun.COM 	if (inet_pton(AF_INET, dns_str, &dns_ip) == 1)
18108670SJose.Borrego@Sun.COM 		dns_ip.a_family = AF_INET;
18118670SJose.Borrego@Sun.COM 	else if (inet_pton(AF_INET6, dns_str, &dns_ip) == 1)
18128670SJose.Borrego@Sun.COM 		dns_ip.a_family = AF_INET6;
18138670SJose.Borrego@Sun.COM 
18145331Samw retry_higher:
18158670SJose.Borrego@Sun.COM 	if ((s = dyndns_open_init_socket(SOCK_DGRAM, &dns_ip, 53)) < 0)
18165331Samw 		return (-1);
18175331Samw 
18185331Samw 	id = 0;
18195331Samw 	if ((buf_sz = dyndns_build_add_remove_msg(buf, update_zone, hostname,
18205331Samw 	    ip_addr, life_time, update_type, del_type, 0, &id, level)) <= 0) {
18215331Samw 		(void) close(s);
18225331Samw 		return (-1);
18235331Samw 	}
18245331Samw 
18255331Samw 	if (dyndns_udp_send_recv(s, buf, buf_sz, buf2)) {
18265331Samw 		(void) close(s);
18275331Samw 		return (-1);
18285331Samw 	}
18295331Samw 
18305331Samw 	(void) close(s);
18315331Samw 
18325331Samw 	ret = buf2[3] & 0xf;	/* error field in UDP */
18335331Samw 
18345331Samw 	/*
18355331Samw 	 * If it is a NOTAUTH error we should retry with higher domains
18365331Samw 	 * until we get a successful reply
18375331Samw 	 */
18385331Samw 	if (ret == NOTAUTH && level++ < MAX_AUTH_RETRIES)
18395331Samw 		goto retry_higher;
18405331Samw 
18415331Samw 	/* check here for update request is successful */
18425331Samw 	if (ret == NOERROR) {
18435331Samw 		(void) dyndns_get_nshort(buf2, &rid);
18445331Samw 		if (id != rid)
18455331Samw 			return (-1);
18465331Samw 		return (0);
18475331Samw 	}
18485331Samw 
18495331Samw 	if (ret == NOTIMP) {
18508334SJose.Borrego@Sun.COM 		dyndns_syslog(LOG_NOTICE, NOTIMP, "dynamic updates");
18515331Samw 		return (-1);
18525331Samw 	} else if (ret == NOTAUTH) {
18538334SJose.Borrego@Sun.COM 		dyndns_syslog(LOG_NOTICE, NOTAUTH, "DNS");
18545331Samw 		return (-1);
18555331Samw 	}
18565331Samw 
185712508Samw@Sun.COM 	if ((p = strchr(hostname, '.')) == NULL)
185812508Samw@Sun.COM 		return (-1);
185912508Samw@Sun.COM 
186012508Samw@Sun.COM 	fqdn = ++p;
186112508Samw@Sun.COM 	if (smb_krb5_kt_find(SMB_KRB5_PN_ID_HOST_FQHN, fqdn,
186212508Samw@Sun.COM 	    SMBNS_KRB5_KEYTAB)) {
18635521Sas200622 		ret = dyndns_sec_add_remove_entry(update_zone, hostname,
18645521Sas200622 		    ip_addr, life_time, update_type, del_type, dns_str);
186512508Samw@Sun.COM 	} else {
186612508Samw@Sun.COM 		syslog(LOG_NOTICE, "dyndns: secure update failed: cannot find "
186712508Samw@Sun.COM 		    "host principal \"%s\" in local keytab file.", hostname);
186812508Samw@Sun.COM 	}
18695331Samw 
18705331Samw 	return (ret);
18715331Samw }
18725331Samw 
18735331Samw /*
18745331Samw  * dyndns_add_entry
18755331Samw  * Main routine to add an entry into DNS.  The attempt will be made on the
18765331Samw  * the servers returned by smb_get_nameserver().  Upon a successful
18775331Samw  * attempt on any one of the server, the function will exit with 0.
18785331Samw  * Otherwise, -1 is retuned to indicate the update attempt on all the
18795331Samw  * nameservers has failed.
18805331Samw  *
18815331Samw  * Parameters:
18825331Samw  *   update_zone: the type of zone to update, use UPDATE_FORW for forward
18835331Samw  *                lookup zone, use UPDATE_REV for reverse lookup zone
18845331Samw  *   hostname   : fully qualified hostname
18855331Samw  *   ip_addr    : ip address of hostname in string format
18865331Samw  *   life_time  : cached time of this entry by others and not within DNS
18875331Samw  *                database
18885331Samw  * Returns:
18895331Samw  *   -1: error
18905331Samw  *    0: success
18915331Samw  */
18925331Samw static int
dyndns_add_entry(int update_zone,const char * hostname,const char * ip_addr,int life_time)18935331Samw dyndns_add_entry(int update_zone, const char *hostname, const char *ip_addr,
18945331Samw     int life_time)
18955331Samw {
18968670SJose.Borrego@Sun.COM 	const char *dns_str;
18978334SJose.Borrego@Sun.COM 	char *which_zone;
18988670SJose.Borrego@Sun.COM 	smb_inaddr_t ns_list[MAXNS];
18998670SJose.Borrego@Sun.COM 	char dns_buf[INET6_ADDRSTRLEN];
19005331Samw 	int i, cnt;
19018670SJose.Borrego@Sun.COM 	int rc = 0;
19025331Samw 
19038670SJose.Borrego@Sun.COM 	if (hostname == NULL || ip_addr == NULL) {
19045331Samw 		return (-1);
19058670SJose.Borrego@Sun.COM 	}
19068670SJose.Borrego@Sun.COM 	cnt = smb_get_nameservers(&ns_list[0], MAXNS);
19075331Samw 
19085331Samw 	for (i = 0; i < cnt; i++) {
19098670SJose.Borrego@Sun.COM 		dns_str = smb_inet_ntop(&ns_list[i], dns_buf,
19108670SJose.Borrego@Sun.COM 		    SMB_IPSTRLEN(ns_list[i].a_family));
19118670SJose.Borrego@Sun.COM 		if (dns_str == NULL)
19125331Samw 			continue;
19135331Samw 
19148334SJose.Borrego@Sun.COM 		which_zone = (update_zone == UPDATE_FORW) ?
19158334SJose.Borrego@Sun.COM 		    "forward" : "reverse";
19168334SJose.Borrego@Sun.COM 		syslog(LOG_DEBUG, "dyndns %s lookup zone update %s (%s)",
19178334SJose.Borrego@Sun.COM 		    which_zone, hostname, ip_addr);
19188334SJose.Borrego@Sun.COM 
19195331Samw 		if (dyndns_add_remove_entry(update_zone, hostname,
19205331Samw 		    ip_addr, life_time,
19218670SJose.Borrego@Sun.COM 		    UPDATE_ADD, DNS_NOCHECK, DEL_NONE, dns_buf) != -1) {
19225331Samw 			rc = 1;
19235331Samw 			break;
19245331Samw 		}
19255331Samw 	}
19265331Samw 
19275331Samw 	return (rc ? 0 : -1);
19285331Samw }
19295331Samw 
19305331Samw /*
19315331Samw  * dyndns_remove_entry
19325331Samw  * Main routine to remove an entry or all entries of the same resource name
19335331Samw  * from DNS.  The update attempt will be made on the primary DNS server.  If
19345331Samw  * there is a failure then another attempt will be made on the secondary DNS
19355331Samw  * server.
19365331Samw  * Parameters:
19375331Samw  *   update_zone: the type of zone to update, use UPDATE_FORW for forward
19385331Samw  *                lookup zone, use UPDATE_REV for reverse lookup zone
19395331Samw  *   hostname   : fully qualified hostname
19405331Samw  *   ip_addr    : ip address of hostname in string format
19415331Samw  *   del_type   : DEL_ONE for deleting one entry, DEL_ALL for deleting all
19425331Samw  *                entries of the same resource name.  Only valid for UPDATE_DEL
19435331Samw  * Returns:
19445331Samw  *   -1: error
19455331Samw  *    0: success
19465331Samw  */
19475331Samw static int
dyndns_remove_entry(int update_zone,const char * hostname,const char * ip_addr,int del_type)19485331Samw dyndns_remove_entry(int update_zone, const char *hostname, const char *ip_addr,
19495331Samw 	int del_type)
19505331Samw {
19518670SJose.Borrego@Sun.COM 	const char *dns_str;
19528670SJose.Borrego@Sun.COM 	smb_inaddr_t ns_list[MAXNS];
19538670SJose.Borrego@Sun.COM 	char dns_buf[INET6_ADDRSTRLEN];
19545331Samw 	int i, cnt, scnt;
19555331Samw 
19565331Samw 	if ((hostname == NULL || ip_addr == NULL)) {
19575331Samw 		return (-1);
19585331Samw 	}
19595331Samw 	cnt = smb_get_nameservers(ns_list, MAXNS);
19605331Samw 	scnt = 0;
19615331Samw 	for (i = 0; i < cnt; i++) {
19628670SJose.Borrego@Sun.COM 		dns_str = smb_inet_ntop(&ns_list[i], dns_buf,
19638670SJose.Borrego@Sun.COM 		    SMB_IPSTRLEN(ns_list[i].a_family));
19648670SJose.Borrego@Sun.COM 		if (dns_str == NULL)
19655331Samw 			continue;
19668670SJose.Borrego@Sun.COM 		if (update_zone == UPDATE_FORW) {
19678670SJose.Borrego@Sun.COM 			if (del_type == DEL_ONE) {
19688670SJose.Borrego@Sun.COM 				syslog(LOG_DEBUG, "Dynamic update "
19698670SJose.Borrego@Sun.COM 				    "on forward lookup "
19708670SJose.Borrego@Sun.COM 				    "zone for %s (%s)...\n", hostname, ip_addr);
19718670SJose.Borrego@Sun.COM 			} else {
19728670SJose.Borrego@Sun.COM 				syslog(LOG_DEBUG, "Removing all "
19738670SJose.Borrego@Sun.COM 				    "entries of %s "
19748670SJose.Borrego@Sun.COM 				    "in forward lookup zone...\n", hostname);
19758670SJose.Borrego@Sun.COM 			}
19768670SJose.Borrego@Sun.COM 		} else {
19778670SJose.Borrego@Sun.COM 			if (del_type == DEL_ONE) {
19788670SJose.Borrego@Sun.COM 				syslog(LOG_DEBUG, "Dynamic update "
19798670SJose.Borrego@Sun.COM 				    "on reverse lookup "
19808670SJose.Borrego@Sun.COM 				    "zone for %s (%s)...\n", hostname, ip_addr);
19818670SJose.Borrego@Sun.COM 			} else {
19828670SJose.Borrego@Sun.COM 				syslog(LOG_DEBUG, "Removing all "
19838670SJose.Borrego@Sun.COM 				    "entries of %s "
19848670SJose.Borrego@Sun.COM 				    "in reverse lookup zone...\n", ip_addr);
19858670SJose.Borrego@Sun.COM 			}
19865331Samw 		}
19875331Samw 		if (dyndns_add_remove_entry(update_zone, hostname, ip_addr, 0,
19888670SJose.Borrego@Sun.COM 		    UPDATE_DEL, DNS_NOCHECK, del_type, dns_buf) != -1) {
19895331Samw 			scnt++;
19905331Samw 			break;
19915331Samw 		}
19925331Samw 	}
19935331Samw 	if (scnt)
19945331Samw 		return (0);
19955331Samw 	return (-1);
19965331Samw }
19975331Samw 
19985331Samw /*
19996600Sas200622  * dyndns_update_core
20005331Samw  * Perform dynamic update on both forward and reverse lookup zone using
20016139Sjb150015  * the specified hostname and IP addresses.  Before updating DNS, existing
20026139Sjb150015  * host entries with the same hostname in the forward lookup zone are removed
20035331Samw  * and existing pointer entries with the same IP addresses in the reverse
20045331Samw  * lookup zone are removed.  After DNS update, host entries for current
20055331Samw  * hostname will show current IP addresses and pointer entries for current
20065331Samw  * IP addresses will show current hostname.
20075331Samw  * Parameters:
200811963SAfshin.Ardakani@Sun.COM  *  fqdn - fully-qualified domain name (in lower case)
20096600Sas200622  *
20105331Samw  * Returns:
20115331Samw  *   -1: some dynamic DNS updates errors
20126432Sas200622  *    0: successful or DDNS disabled.
20135331Samw  */
20148670SJose.Borrego@Sun.COM int
dyndns_update_core(char * fqdn)20156600Sas200622 dyndns_update_core(char *fqdn)
20165331Samw {
20176030Sjb150015 	int forw_update_ok, error;
20188670SJose.Borrego@Sun.COM 	char my_ip[INET6_ADDRSTRLEN];
20198670SJose.Borrego@Sun.COM 	const char *my_str;
20206030Sjb150015 	smb_niciter_t ni;
20215331Samw 	int rc;
20226139Sjb150015 	char fqhn[MAXHOSTNAMELEN];
20235331Samw 
20248334SJose.Borrego@Sun.COM 	if (fqdn == NULL || *fqdn == '\0')
20258334SJose.Borrego@Sun.COM 		return (0);
20268334SJose.Borrego@Sun.COM 
20275772Sas200622 	if (!smb_config_getbool(SMB_CI_DYNDNS_ENABLE))
20286432Sas200622 		return (0);
202911963SAfshin.Ardakani@Sun.COM 	if (smb_gethostname(fqhn, MAXHOSTNAMELEN, SMB_CASE_LOWER) != 0)
20305331Samw 		return (-1);
20315331Samw 
203211963SAfshin.Ardakani@Sun.COM 	/*
203311963SAfshin.Ardakani@Sun.COM 	 * To comply with RFC 4120 section 6.2.1, the fully-qualified hostname
203411963SAfshin.Ardakani@Sun.COM 	 * must be set to lower case.
203511963SAfshin.Ardakani@Sun.COM 	 */
20366139Sjb150015 	(void) snprintf(fqhn, MAXHOSTNAMELEN, "%s.%s", fqhn, fqdn);
203711963SAfshin.Ardakani@Sun.COM 
20385331Samw 	error = 0;
20395331Samw 	forw_update_ok = 0;
20405331Samw 
20415331Samw 	/*
20425331Samw 	 * Dummy IP is okay since we are removing all using the hostname.
20435331Samw 	 */
20446139Sjb150015 	if (dyndns_remove_entry(UPDATE_FORW, fqhn, "1.1.1.1", DEL_ALL) == 0) {
20455331Samw 		forw_update_ok = 1;
20465331Samw 	} else {
20475331Samw 		error++;
20485331Samw 	}
20495331Samw 
205011963SAfshin.Ardakani@Sun.COM 	if (smb_nic_getfirst(&ni) != SMB_NIC_SUCCESS)
20516030Sjb150015 		return (-1);
20526030Sjb150015 
20536030Sjb150015 	do {
20548485SPeter.Memishian@Sun.COM 		if (ni.ni_nic.nic_sysflags & IFF_PRIVATE)
20555331Samw 			continue;
20568670SJose.Borrego@Sun.COM 		/* first try ipv4, then ipv6 */
20578670SJose.Borrego@Sun.COM 		my_str = smb_inet_ntop(&ni.ni_nic.nic_ip, my_ip,
20588670SJose.Borrego@Sun.COM 		    SMB_IPSTRLEN(ni.ni_nic.nic_ip.a_family));
20598670SJose.Borrego@Sun.COM 		if (my_str == NULL) {
20605331Samw 			error++;
20615331Samw 			continue;
20625331Samw 		}
20635331Samw 
20645331Samw 		if (forw_update_ok) {
20658670SJose.Borrego@Sun.COM 			rc = dyndns_add_entry(UPDATE_FORW, fqhn, my_str,
20665331Samw 			    DDNS_TTL);
20675331Samw 
20685331Samw 			if (rc == -1)
20695331Samw 				error++;
20705331Samw 		}
20715331Samw 
20728670SJose.Borrego@Sun.COM 		rc = dyndns_remove_entry(UPDATE_REV, fqhn, my_str, DEL_ALL);
20735331Samw 		if (rc == 0) {
20748670SJose.Borrego@Sun.COM 			rc = dyndns_add_entry(UPDATE_REV, fqhn, my_str,
20755331Samw 			    DDNS_TTL);
20765331Samw 		}
20775331Samw 
20785331Samw 		if (rc == -1)
20795331Samw 			error++;
20805331Samw 
208111963SAfshin.Ardakani@Sun.COM 	} while (smb_nic_getnext(&ni) == SMB_NIC_SUCCESS);
20825331Samw 
20835331Samw 	return ((error == 0) ? 0 : -1);
20845331Samw }
20855331Samw 
20865331Samw /*
20875331Samw  * dyndns_clear_rev_zone
20885331Samw  * Clear the rev zone records. Must be called to clear the OLD if list
20895331Samw  * of down records prior to updating the list with new information.
20905331Samw  *
20915331Samw  * Parameters:
209211963SAfshin.Ardakani@Sun.COM  *   fqdn - fully-qualified domain name (in lower case)
20935331Samw  * Returns:
20945331Samw  *   -1: some dynamic DNS updates errors
20956432Sas200622  *    0: successful or DDNS disabled.
20965331Samw  */
20978670SJose.Borrego@Sun.COM int
dyndns_clear_rev_zone(char * fqdn)20986139Sjb150015 dyndns_clear_rev_zone(char *fqdn)
20995331Samw {
21006030Sjb150015 	int error;
21018670SJose.Borrego@Sun.COM 	char my_ip[INET6_ADDRSTRLEN];
21026030Sjb150015 	smb_niciter_t ni;
21035331Samw 	int rc;
21046139Sjb150015 	char fqhn[MAXHOSTNAMELEN];
21058670SJose.Borrego@Sun.COM 	const char *my_str;
21065331Samw 
21075772Sas200622 	if (!smb_config_getbool(SMB_CI_DYNDNS_ENABLE))
21086432Sas200622 		return (0);
21095331Samw 
211011963SAfshin.Ardakani@Sun.COM 	if (smb_gethostname(fqhn, MAXHOSTNAMELEN, SMB_CASE_LOWER) != 0)
21115331Samw 		return (-1);
21125331Samw 
211311963SAfshin.Ardakani@Sun.COM 	/*
211411963SAfshin.Ardakani@Sun.COM 	 * To comply with RFC 4120 section 6.2.1, the fully-qualified hostname
211511963SAfshin.Ardakani@Sun.COM 	 * must be set to lower case.
211611963SAfshin.Ardakani@Sun.COM 	 */
21176139Sjb150015 	(void) snprintf(fqhn, MAXHOSTNAMELEN, "%s.%s", fqhn, fqdn);
211811963SAfshin.Ardakani@Sun.COM 
21195331Samw 	error = 0;
21205331Samw 
212111963SAfshin.Ardakani@Sun.COM 	if (smb_nic_getfirst(&ni) != SMB_NIC_SUCCESS)
21226030Sjb150015 		return (-1);
21236030Sjb150015 
21246030Sjb150015 	do {
21258485SPeter.Memishian@Sun.COM 		if (ni.ni_nic.nic_sysflags & IFF_PRIVATE)
21265331Samw 			continue;
21278670SJose.Borrego@Sun.COM 		my_str = smb_inet_ntop(&ni.ni_nic.nic_ip, my_ip,
21288670SJose.Borrego@Sun.COM 		    SMB_IPSTRLEN(ni.ni_nic.nic_ip.a_family));
21298670SJose.Borrego@Sun.COM 		if (my_str == NULL) {
21305331Samw 			error++;
21315331Samw 			continue;
21325331Samw 		}
21335331Samw 
21346139Sjb150015 		rc = dyndns_remove_entry(UPDATE_REV, fqhn, my_ip, DEL_ALL);
21355331Samw 		if (rc != 0)
21365331Samw 			error++;
21375331Samw 
213811963SAfshin.Ardakani@Sun.COM 	} while (smb_nic_getnext(&ni) == SMB_NIC_SUCCESS);
21395331Samw 
21405331Samw 	return ((error == 0) ? 0 : -1);
21415331Samw }
2142