1da6c28aaSamw /*
2da6c28aaSamw * CDDL HEADER START
3da6c28aaSamw *
4da6c28aaSamw * The contents of this file are subject to the terms of the
5da6c28aaSamw * Common Development and Distribution License (the "License").
6da6c28aaSamw * You may not use this file except in compliance with the License.
7da6c28aaSamw *
8da6c28aaSamw * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9da6c28aaSamw * or http://www.opensolaris.org/os/licensing.
10da6c28aaSamw * See the License for the specific language governing permissions
11da6c28aaSamw * and limitations under the License.
12da6c28aaSamw *
13da6c28aaSamw * When distributing Covered Code, include this CDDL HEADER in each
14da6c28aaSamw * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15da6c28aaSamw * If applicable, add the following below this CDDL HEADER, with the
16da6c28aaSamw * fields enclosed by brackets "[]" replaced with your own identifying
17da6c28aaSamw * information: Portions Copyright [yyyy] [name of copyright owner]
18da6c28aaSamw *
19da6c28aaSamw * CDDL HEADER END
20da6c28aaSamw */
21da6c28aaSamw /*
22148c5f43SAlan Wright * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
23da6c28aaSamw */
24da6c28aaSamw
25da6c28aaSamw #include <assert.h>
26da6c28aaSamw #include <errno.h>
27da6c28aaSamw #include <stdio.h>
28da6c28aaSamw #include <stdlib.h>
29da6c28aaSamw #include <strings.h>
30da6c28aaSamw #include <sys/types.h>
31da6c28aaSamw #include <sys/socket.h>
32da6c28aaSamw #include <netinet/in.h>
33da6c28aaSamw #include <arpa/inet.h>
348d7e4166Sjose borrego #include <arpa/nameser.h>
358d7e4166Sjose borrego #include <net/if.h>
368d7e4166Sjose borrego #include <resolv.h>
37da6c28aaSamw #include <sys/time.h>
38da6c28aaSamw #include <unistd.h>
39da6c28aaSamw #include <string.h>
408d7e4166Sjose borrego #include <pthread.h>
41da6c28aaSamw #include <netdb.h>
42da6c28aaSamw #include <rpc/rpc.h>
43da6c28aaSamw #include <syslog.h>
44da6c28aaSamw #include <gssapi/gssapi.h>
45da6c28aaSamw #include <kerberosv5/krb5.h>
46da6c28aaSamw
47da6c28aaSamw #include <smbns_dyndns.h>
48faa1795aSjb150015 #include <smbns_krb.h>
49da6c28aaSamw
50c8ec8eeaSjose borrego /*
518d7e4166Sjose borrego * The following can be removed once head/arpa/nameser_compat.h
528d7e4166Sjose borrego * defines BADSIG, BADKEY and BADTIME.
53da6c28aaSamw */
54da6c28aaSamw #ifndef BADSIG
55da6c28aaSamw #define BADSIG ns_r_badsig
56da6c28aaSamw #endif /* BADSIG */
57da6c28aaSamw
58da6c28aaSamw #ifndef BADKEY
59da6c28aaSamw #define BADKEY ns_r_badkey
60da6c28aaSamw #endif /* BADKEY */
61da6c28aaSamw
62da6c28aaSamw #ifndef BADTIME
63da6c28aaSamw #define BADTIME ns_r_badtime
64da6c28aaSamw #endif /* BADTIME */
65da6c28aaSamw
668d7e4166Sjose borrego /* internal use, in dyndns_add_entry */
678d7e4166Sjose borrego #define DEL_NONE 2
688d7e4166Sjose borrego
698d7e4166Sjose borrego /* Maximum retires if not authoritative */
708d7e4166Sjose borrego #define MAX_AUTH_RETRIES 3
718d7e4166Sjose borrego
728d7e4166Sjose borrego /* Number of times to retry a DNS query */
738d7e4166Sjose borrego #define DYNDNS_MAX_QUERY_RETRIES 3
748d7e4166Sjose borrego
758d7e4166Sjose borrego /* Timeout value, in seconds, for DNS query responses */
768d7e4166Sjose borrego #define DYNDNS_QUERY_TIMEOUT 2
778d7e4166Sjose borrego
788d7e4166Sjose borrego static uint16_t dns_msgid;
798d7e4166Sjose borrego mutex_t dns_msgid_mtx;
808d7e4166Sjose borrego
818d7e4166Sjose borrego #define DYNDNS_OP_CLEAR 1
828d7e4166Sjose borrego #define DYNDNS_OP_UPDATE 2
838d7e4166Sjose borrego
848d7e4166Sjose borrego #define DYNDNS_STATE_INIT 0
858d7e4166Sjose borrego #define DYNDNS_STATE_READY 1
868d7e4166Sjose borrego #define DYNDNS_STATE_PUBLISHING 2
878d7e4166Sjose borrego #define DYNDNS_STATE_STOPPING 3
888d7e4166Sjose borrego
898d7e4166Sjose borrego typedef struct dyndns_qentry {
908d7e4166Sjose borrego list_node_t dqe_lnd;
918d7e4166Sjose borrego int dqe_op;
929fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States /* fully-qualified domain name is in lower case */
938d7e4166Sjose borrego char dqe_fqdn[MAXHOSTNAMELEN];
948d7e4166Sjose borrego } dyndns_qentry_t;
958d7e4166Sjose borrego
968d7e4166Sjose borrego typedef struct dyndns_queue {
978d7e4166Sjose borrego list_t ddq_list;
988d7e4166Sjose borrego mutex_t ddq_mtx;
998d7e4166Sjose borrego cond_t ddq_cv;
1008d7e4166Sjose borrego uint32_t ddq_state;
1018d7e4166Sjose borrego } dyndns_queue_t;
1028d7e4166Sjose borrego
1038d7e4166Sjose borrego static dyndns_queue_t dyndns_queue;
1048d7e4166Sjose borrego
1058d7e4166Sjose borrego static void dyndns_queue_request(int, const char *);
1068d7e4166Sjose borrego static void dyndns_queue_flush(list_t *);
1078d7e4166Sjose borrego static void dyndns_process(list_t *);
1088d7e4166Sjose borrego static int dyndns_update_core(char *);
1098d7e4166Sjose borrego static int dyndns_clear_rev_zone(char *);
1108d7e4166Sjose borrego static void dyndns_msgid_init(void);
1118d7e4166Sjose borrego static int dyndns_get_msgid(void);
1128d7e4166Sjose borrego static void dyndns_syslog(int, int, const char *);
1138d7e4166Sjose borrego
114*fd9ee8b5Sjoyce mcintosh void
dyndns_start(void)1158d7e4166Sjose borrego dyndns_start(void)
1168d7e4166Sjose borrego {
1178d7e4166Sjose borrego (void) mutex_lock(&dyndns_queue.ddq_mtx);
118*fd9ee8b5Sjoyce mcintosh
1198d7e4166Sjose borrego if (dyndns_queue.ddq_state != DYNDNS_STATE_INIT) {
1208d7e4166Sjose borrego (void) mutex_unlock(&dyndns_queue.ddq_mtx);
121*fd9ee8b5Sjoyce mcintosh return;
1228d7e4166Sjose borrego }
1238d7e4166Sjose borrego
1248d7e4166Sjose borrego dyndns_msgid_init();
1258d7e4166Sjose borrego
1268d7e4166Sjose borrego list_create(&dyndns_queue.ddq_list, sizeof (dyndns_qentry_t),
1278d7e4166Sjose borrego offsetof(dyndns_qentry_t, dqe_lnd));
1288d7e4166Sjose borrego dyndns_queue.ddq_state = DYNDNS_STATE_READY;
1298d7e4166Sjose borrego
130*fd9ee8b5Sjoyce mcintosh (void) mutex_unlock(&dyndns_queue.ddq_mtx);
1318d7e4166Sjose borrego }
1328d7e4166Sjose borrego
1338d7e4166Sjose borrego void
dyndns_stop(void)1348d7e4166Sjose borrego dyndns_stop(void)
1358d7e4166Sjose borrego {
1368d7e4166Sjose borrego (void) mutex_lock(&dyndns_queue.ddq_mtx);
1378d7e4166Sjose borrego
1388d7e4166Sjose borrego switch (dyndns_queue.ddq_state) {
1398d7e4166Sjose borrego case DYNDNS_STATE_READY:
1408d7e4166Sjose borrego case DYNDNS_STATE_PUBLISHING:
1418d7e4166Sjose borrego dyndns_queue.ddq_state = DYNDNS_STATE_STOPPING;
1428d7e4166Sjose borrego (void) cond_signal(&dyndns_queue.ddq_cv);
1438d7e4166Sjose borrego break;
1448d7e4166Sjose borrego default:
1458d7e4166Sjose borrego break;
1468d7e4166Sjose borrego }
1478d7e4166Sjose borrego
1488d7e4166Sjose borrego (void) mutex_unlock(&dyndns_queue.ddq_mtx);
1498d7e4166Sjose borrego }
1508d7e4166Sjose borrego
151da6c28aaSamw /*
1528d7e4166Sjose borrego * Clear all records in both zones.
1538d7e4166Sjose borrego */
1548d7e4166Sjose borrego void
dyndns_clear_zones(void)1558d7e4166Sjose borrego dyndns_clear_zones(void)
1568d7e4166Sjose borrego {
1578d7e4166Sjose borrego char fqdn[MAXHOSTNAMELEN];
1588d7e4166Sjose borrego
1598d7e4166Sjose borrego if (smb_getfqdomainname(fqdn, MAXHOSTNAMELEN) != 0) {
1608d7e4166Sjose borrego syslog(LOG_ERR, "dyndns: failed to get domainname");
1618d7e4166Sjose borrego return;
1628d7e4166Sjose borrego }
1638d7e4166Sjose borrego
1648d7e4166Sjose borrego dyndns_queue_request(DYNDNS_OP_CLEAR, fqdn);
1658d7e4166Sjose borrego }
1668d7e4166Sjose borrego
1678d7e4166Sjose borrego /*
1688d7e4166Sjose borrego * Update all records in both zones.
1698d7e4166Sjose borrego */
1708d7e4166Sjose borrego void
dyndns_update_zones(void)1718d7e4166Sjose borrego dyndns_update_zones(void)
1728d7e4166Sjose borrego {
1738d7e4166Sjose borrego char fqdn[MAXHOSTNAMELEN];
1748d7e4166Sjose borrego
1758d7e4166Sjose borrego if (smb_getfqdomainname(fqdn, MAXHOSTNAMELEN) != 0) {
1768d7e4166Sjose borrego syslog(LOG_ERR, "dyndns: failed to get domainname");
1778d7e4166Sjose borrego return;
1788d7e4166Sjose borrego }
1798d7e4166Sjose borrego
1808d7e4166Sjose borrego dyndns_queue_request(DYNDNS_OP_UPDATE, fqdn);
1818d7e4166Sjose borrego }
1828d7e4166Sjose borrego
1838d7e4166Sjose borrego /*
1848d7e4166Sjose borrego * Add a request to the queue.
185*fd9ee8b5Sjoyce mcintosh *
186*fd9ee8b5Sjoyce mcintosh * To comply with RFC 4120 section 6.2.1, entry->dqe_fqdn is converted
187*fd9ee8b5Sjoyce mcintosh * to lower case.
188da6c28aaSamw */
1893db3f65cSamw static void
dyndns_queue_request(int op,const char * fqdn)1908d7e4166Sjose borrego dyndns_queue_request(int op, const char *fqdn)
191da6c28aaSamw {
1928d7e4166Sjose borrego dyndns_qentry_t *entry;
193da6c28aaSamw
1948d7e4166Sjose borrego if (!smb_config_getbool(SMB_CI_DYNDNS_ENABLE))
1958d7e4166Sjose borrego return;
1968d7e4166Sjose borrego
197*fd9ee8b5Sjoyce mcintosh if ((entry = malloc(sizeof (dyndns_qentry_t))) == NULL)
198*fd9ee8b5Sjoyce mcintosh return;
199*fd9ee8b5Sjoyce mcintosh
200*fd9ee8b5Sjoyce mcintosh bzero(entry, sizeof (dyndns_qentry_t));
201*fd9ee8b5Sjoyce mcintosh entry->dqe_op = op;
202*fd9ee8b5Sjoyce mcintosh (void) strlcpy(entry->dqe_fqdn, fqdn, MAXNAMELEN);
203*fd9ee8b5Sjoyce mcintosh (void) smb_strlwr(entry->dqe_fqdn);
204*fd9ee8b5Sjoyce mcintosh
2058d7e4166Sjose borrego (void) mutex_lock(&dyndns_queue.ddq_mtx);
2068d7e4166Sjose borrego
2078d7e4166Sjose borrego switch (dyndns_queue.ddq_state) {
2088d7e4166Sjose borrego case DYNDNS_STATE_READY:
2098d7e4166Sjose borrego case DYNDNS_STATE_PUBLISHING:
2108d7e4166Sjose borrego list_insert_tail(&dyndns_queue.ddq_list, entry);
2118d7e4166Sjose borrego (void) cond_signal(&dyndns_queue.ddq_cv);
212*fd9ee8b5Sjoyce mcintosh break;
213*fd9ee8b5Sjoyce mcintosh default:
214*fd9ee8b5Sjoyce mcintosh free(entry);
215*fd9ee8b5Sjoyce mcintosh break;
216*fd9ee8b5Sjoyce mcintosh }
217*fd9ee8b5Sjoyce mcintosh
2188d7e4166Sjose borrego (void) mutex_unlock(&dyndns_queue.ddq_mtx);
2198d7e4166Sjose borrego }
2208d7e4166Sjose borrego
2218d7e4166Sjose borrego /*
2228d7e4166Sjose borrego * Flush all remaining items from the specified list/queue.
2238d7e4166Sjose borrego */
2248d7e4166Sjose borrego static void
dyndns_queue_flush(list_t * lst)2258d7e4166Sjose borrego dyndns_queue_flush(list_t *lst)
2268d7e4166Sjose borrego {
2278d7e4166Sjose borrego dyndns_qentry_t *entry;
2288d7e4166Sjose borrego
2298d7e4166Sjose borrego while ((entry = list_head(lst)) != NULL) {
2308d7e4166Sjose borrego list_remove(lst, entry);
2318d7e4166Sjose borrego free(entry);
2328d7e4166Sjose borrego }
2338d7e4166Sjose borrego }
2348d7e4166Sjose borrego
2358d7e4166Sjose borrego /*
2368d7e4166Sjose borrego * Dyndns update thread. While running, the thread waits on a condition
2378d7e4166Sjose borrego * variable until notified that an entry needs to be updated.
2388d7e4166Sjose borrego *
2398d7e4166Sjose borrego * If the outgoing queue is not empty, the thread wakes up every 60 seconds
2408d7e4166Sjose borrego * to retry.
2418d7e4166Sjose borrego */
2428d7e4166Sjose borrego /*ARGSUSED*/
243*fd9ee8b5Sjoyce mcintosh void *
dyndns_publisher(void * arg)2448d7e4166Sjose borrego dyndns_publisher(void *arg)
2458d7e4166Sjose borrego {
2468d7e4166Sjose borrego dyndns_qentry_t *entry;
2478d7e4166Sjose borrego list_t publist;
2488d7e4166Sjose borrego
2498d7e4166Sjose borrego (void) mutex_lock(&dyndns_queue.ddq_mtx);
2508d7e4166Sjose borrego if (dyndns_queue.ddq_state != DYNDNS_STATE_READY) {
2518d7e4166Sjose borrego (void) mutex_unlock(&dyndns_queue.ddq_mtx);
2528d7e4166Sjose borrego return (NULL);
2538d7e4166Sjose borrego }
2548d7e4166Sjose borrego dyndns_queue.ddq_state = DYNDNS_STATE_PUBLISHING;
2558d7e4166Sjose borrego (void) mutex_unlock(&dyndns_queue.ddq_mtx);
2568d7e4166Sjose borrego
2578d7e4166Sjose borrego list_create(&publist, sizeof (dyndns_qentry_t),
2588d7e4166Sjose borrego offsetof(dyndns_qentry_t, dqe_lnd));
2598d7e4166Sjose borrego
2608d7e4166Sjose borrego for (;;) {
2618d7e4166Sjose borrego (void) mutex_lock(&dyndns_queue.ddq_mtx);
2628d7e4166Sjose borrego
2638d7e4166Sjose borrego while (list_is_empty(&dyndns_queue.ddq_list) &&
2648d7e4166Sjose borrego (dyndns_queue.ddq_state == DYNDNS_STATE_PUBLISHING)) {
2658d7e4166Sjose borrego (void) cond_wait(&dyndns_queue.ddq_cv,
2668d7e4166Sjose borrego &dyndns_queue.ddq_mtx);
2678d7e4166Sjose borrego }
2688d7e4166Sjose borrego
2698d7e4166Sjose borrego if (dyndns_queue.ddq_state != DYNDNS_STATE_PUBLISHING) {
2708d7e4166Sjose borrego (void) mutex_unlock(&dyndns_queue.ddq_mtx);
2718d7e4166Sjose borrego break;
2728d7e4166Sjose borrego }
2738d7e4166Sjose borrego
2748d7e4166Sjose borrego /*
2758d7e4166Sjose borrego * Transfer queued items to the local list so that
2768d7e4166Sjose borrego * the mutex can be released.
2778d7e4166Sjose borrego */
2788d7e4166Sjose borrego while ((entry = list_head(&dyndns_queue.ddq_list)) != NULL) {
2798d7e4166Sjose borrego list_remove(&dyndns_queue.ddq_list, entry);
2808d7e4166Sjose borrego list_insert_tail(&publist, entry);
2818d7e4166Sjose borrego }
2828d7e4166Sjose borrego
2838d7e4166Sjose borrego (void) mutex_unlock(&dyndns_queue.ddq_mtx);
2848d7e4166Sjose borrego
2858d7e4166Sjose borrego dyndns_process(&publist);
2868d7e4166Sjose borrego }
2878d7e4166Sjose borrego
2888d7e4166Sjose borrego (void) mutex_lock(&dyndns_queue.ddq_mtx);
2898d7e4166Sjose borrego dyndns_queue_flush(&dyndns_queue.ddq_list);
2908d7e4166Sjose borrego list_destroy(&dyndns_queue.ddq_list);
2918d7e4166Sjose borrego dyndns_queue.ddq_state = DYNDNS_STATE_INIT;
2928d7e4166Sjose borrego (void) mutex_unlock(&dyndns_queue.ddq_mtx);
2938d7e4166Sjose borrego
2948d7e4166Sjose borrego dyndns_queue_flush(&publist);
2958d7e4166Sjose borrego list_destroy(&publist);
2968d7e4166Sjose borrego return (NULL);
2978d7e4166Sjose borrego }
2988d7e4166Sjose borrego
2998d7e4166Sjose borrego /*
3008d7e4166Sjose borrego * Remove items from the queue and process them.
3018d7e4166Sjose borrego */
3028d7e4166Sjose borrego static void
dyndns_process(list_t * publist)3038d7e4166Sjose borrego dyndns_process(list_t *publist)
3048d7e4166Sjose borrego {
3058d7e4166Sjose borrego dyndns_qentry_t *entry;
3068d7e4166Sjose borrego
3078d7e4166Sjose borrego while ((entry = list_head(publist)) != NULL) {
3088d7e4166Sjose borrego (void) mutex_lock(&dyndns_queue.ddq_mtx);
3098d7e4166Sjose borrego if (dyndns_queue.ddq_state != DYNDNS_STATE_PUBLISHING) {
3108d7e4166Sjose borrego (void) mutex_unlock(&dyndns_queue.ddq_mtx);
3118d7e4166Sjose borrego dyndns_queue_flush(publist);
3128d7e4166Sjose borrego return;
3138d7e4166Sjose borrego }
3148d7e4166Sjose borrego (void) mutex_unlock(&dyndns_queue.ddq_mtx);
3158d7e4166Sjose borrego
3168d7e4166Sjose borrego list_remove(publist, entry);
3178d7e4166Sjose borrego
3188d7e4166Sjose borrego switch (entry->dqe_op) {
3198d7e4166Sjose borrego case DYNDNS_OP_CLEAR:
3208d7e4166Sjose borrego (void) dyndns_clear_rev_zone(entry->dqe_fqdn);
3218d7e4166Sjose borrego break;
3228d7e4166Sjose borrego case DYNDNS_OP_UPDATE:
3238d7e4166Sjose borrego (void) dyndns_update_core(entry->dqe_fqdn);
3248d7e4166Sjose borrego break;
3258d7e4166Sjose borrego default:
3268d7e4166Sjose borrego break;
3278d7e4166Sjose borrego }
3288d7e4166Sjose borrego
3298d7e4166Sjose borrego free(entry);
3308d7e4166Sjose borrego }
3318d7e4166Sjose borrego }
3328d7e4166Sjose borrego
3338d7e4166Sjose borrego /*
3348d7e4166Sjose borrego * Dynamic DNS update API for kclient.
3358d7e4166Sjose borrego *
3368d7e4166Sjose borrego * Returns 0 upon success. Otherwise, returns -1.
3378d7e4166Sjose borrego */
3388d7e4166Sjose borrego int
dyndns_update(char * fqdn)3398d7e4166Sjose borrego dyndns_update(char *fqdn)
3408d7e4166Sjose borrego {
3418d7e4166Sjose borrego int rc;
3428d7e4166Sjose borrego
3439fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States if (smb_nic_init() != SMB_NIC_SUCCESS)
3448d7e4166Sjose borrego return (-1);
3458d7e4166Sjose borrego
3468d7e4166Sjose borrego dyndns_msgid_init();
3479fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States (void) smb_strlwr(fqdn);
3488d7e4166Sjose borrego rc = dyndns_update_core(fqdn);
3498d7e4166Sjose borrego smb_nic_fini();
3508d7e4166Sjose borrego return (rc);
3518d7e4166Sjose borrego }
3528d7e4166Sjose borrego
3538d7e4166Sjose borrego /*
3548d7e4166Sjose borrego * Initializes the DNS message ID counter using the algorithm
3558d7e4166Sjose borrego * that resolver library uses to initialize the ID field of any res
3568d7e4166Sjose borrego * structure.
3578d7e4166Sjose borrego */
3588d7e4166Sjose borrego static void
dyndns_msgid_init(void)3598d7e4166Sjose borrego dyndns_msgid_init(void)
3608d7e4166Sjose borrego {
3618d7e4166Sjose borrego struct timeval now;
3628d7e4166Sjose borrego
3638d7e4166Sjose borrego (void) gettimeofday(&now, NULL);
3648d7e4166Sjose borrego (void) mutex_lock(&dns_msgid_mtx);
3658d7e4166Sjose borrego dns_msgid = (0xffff & (now.tv_sec ^ now.tv_usec ^ getpid()));
3668d7e4166Sjose borrego (void) mutex_unlock(&dns_msgid_mtx);
3678d7e4166Sjose borrego }
3688d7e4166Sjose borrego
3698d7e4166Sjose borrego static int
dyndns_get_msgid(void)3708d7e4166Sjose borrego dyndns_get_msgid(void)
3718d7e4166Sjose borrego {
3728d7e4166Sjose borrego uint16_t id;
3738d7e4166Sjose borrego
3748d7e4166Sjose borrego (void) mutex_lock(&dns_msgid_mtx);
3758d7e4166Sjose borrego id = ++dns_msgid;
3768d7e4166Sjose borrego (void) mutex_unlock(&dns_msgid_mtx);
3778d7e4166Sjose borrego return (id);
3788d7e4166Sjose borrego }
3798d7e4166Sjose borrego
3808d7e4166Sjose borrego /*
3818d7e4166Sjose borrego * Log a DNS error message
3828d7e4166Sjose borrego */
3838d7e4166Sjose borrego static void
dyndns_syslog(int severity,int errnum,const char * text)3848d7e4166Sjose borrego dyndns_syslog(int severity, int errnum, const char *text)
3858d7e4166Sjose borrego {
3868d7e4166Sjose borrego struct {
3878d7e4166Sjose borrego int errnum;
3888d7e4166Sjose borrego char *errmsg;
3898d7e4166Sjose borrego } errtab[] = {
3908d7e4166Sjose borrego { FORMERR, "message format error" },
3918d7e4166Sjose borrego { SERVFAIL, "server internal error" },
3928d7e4166Sjose borrego { NXDOMAIN, "entry should exist but does not exist" },
3938d7e4166Sjose borrego { NOTIMP, "not supported" },
3948d7e4166Sjose borrego { REFUSED, "operation refused" },
3958d7e4166Sjose borrego { YXDOMAIN, "entry should not exist but does exist" },
3968d7e4166Sjose borrego { YXRRSET, "RRSet should not exist but does exist" },
3978d7e4166Sjose borrego { NXRRSET, "RRSet should exist but does not exist" },
3988d7e4166Sjose borrego { NOTAUTH, "server is not authoritative for specified zone" },
3998d7e4166Sjose borrego { NOTZONE, "name not within specified zone" },
4008d7e4166Sjose borrego { BADSIG, "bad transaction signature (TSIG)" },
4018d7e4166Sjose borrego { BADKEY, "bad transaction key (TKEY)" },
4028d7e4166Sjose borrego { BADTIME, "time not synchronized" },
4038d7e4166Sjose borrego };
4048d7e4166Sjose borrego
4058d7e4166Sjose borrego char *errmsg = "unknown error";
4068d7e4166Sjose borrego int i;
4078d7e4166Sjose borrego
4088d7e4166Sjose borrego if (errnum == NOERROR)
4098d7e4166Sjose borrego return;
4108d7e4166Sjose borrego
4118d7e4166Sjose borrego for (i = 0; i < (sizeof (errtab) / sizeof (errtab[0])); ++i) {
4128d7e4166Sjose borrego if (errtab[i].errnum == errnum) {
4138d7e4166Sjose borrego errmsg = errtab[i].errmsg;
4148d7e4166Sjose borrego break;
4158d7e4166Sjose borrego }
4168d7e4166Sjose borrego }
4178d7e4166Sjose borrego
4188d7e4166Sjose borrego syslog(severity, "dyndns: %s: %s: %d", text, errmsg, errnum);
419da6c28aaSamw }
420da6c28aaSamw
421da6c28aaSamw /*
422da6c28aaSamw * display_stat
423da6c28aaSamw * Display GSS error message from error code. This routine is used to display
424da6c28aaSamw * the mechanism independent and mechanism specific error messages for GSS
425da6c28aaSamw * routines. The major status error code is the mechanism independent error
426da6c28aaSamw * code and the minor status error code is the mechanism specific error code.
427da6c28aaSamw * Parameters:
428da6c28aaSamw * maj: GSS major status
429da6c28aaSamw * min: GSS minor status
430da6c28aaSamw * Returns:
431da6c28aaSamw * None
432da6c28aaSamw */
433da6c28aaSamw static void
display_stat(OM_uint32 maj,OM_uint32 min)434da6c28aaSamw display_stat(OM_uint32 maj, OM_uint32 min)
435da6c28aaSamw {
436da6c28aaSamw gss_buffer_desc msg;
437da6c28aaSamw OM_uint32 msg_ctx = 0;
438da6c28aaSamw OM_uint32 min2;
439b89a8333Snatalie li - Sun Microsystems - Irvine United States
440da6c28aaSamw (void) gss_display_status(&min2, maj, GSS_C_GSS_CODE, GSS_C_NULL_OID,
441da6c28aaSamw &msg_ctx, &msg);
4428d7e4166Sjose borrego syslog(LOG_ERR, "dyndns: GSS major status error: %s",
443da6c28aaSamw (char *)msg.value);
444b89a8333Snatalie li - Sun Microsystems - Irvine United States (void) gss_release_buffer(&min2, &msg);
445b89a8333Snatalie li - Sun Microsystems - Irvine United States
446da6c28aaSamw (void) gss_display_status(&min2, min, GSS_C_MECH_CODE, GSS_C_NULL_OID,
447da6c28aaSamw &msg_ctx, &msg);
4488d7e4166Sjose borrego syslog(LOG_ERR, "dyndns: GSS minor status error: %s",
449da6c28aaSamw (char *)msg.value);
450b89a8333Snatalie li - Sun Microsystems - Irvine United States (void) gss_release_buffer(&min2, &msg);
451da6c28aaSamw }
452da6c28aaSamw
453da6c28aaSamw static char *
dyndns_put_nshort(char * buf,uint16_t val)454da6c28aaSamw dyndns_put_nshort(char *buf, uint16_t val)
455da6c28aaSamw {
456da6c28aaSamw uint16_t nval;
457da6c28aaSamw
458da6c28aaSamw nval = htons(val);
459da6c28aaSamw (void) memcpy(buf, &nval, sizeof (uint16_t));
460da6c28aaSamw buf += sizeof (uint16_t);
461da6c28aaSamw return (buf);
462da6c28aaSamw }
463da6c28aaSamw
4643db3f65cSamw static char *
dyndns_get_nshort(char * buf,uint16_t * val)465da6c28aaSamw dyndns_get_nshort(char *buf, uint16_t *val)
466da6c28aaSamw {
467da6c28aaSamw uint16_t nval;
468da6c28aaSamw
469da6c28aaSamw (void) memcpy(&nval, buf, sizeof (uint16_t));
470da6c28aaSamw *val = ntohs(nval);
471da6c28aaSamw buf += sizeof (uint16_t);
472da6c28aaSamw return (buf);
473da6c28aaSamw }
474da6c28aaSamw
475da6c28aaSamw static char *
dyndns_put_nlong(char * buf,uint32_t val)476da6c28aaSamw dyndns_put_nlong(char *buf, uint32_t val)
477da6c28aaSamw {
478da6c28aaSamw uint32_t lval;
479da6c28aaSamw
480da6c28aaSamw lval = htonl(val);
481da6c28aaSamw (void) memcpy(buf, &lval, sizeof (uint32_t));
482da6c28aaSamw buf += sizeof (uint32_t);
483da6c28aaSamw return (buf);
484da6c28aaSamw }
485da6c28aaSamw
486da6c28aaSamw static char *
dyndns_put_byte(char * buf,char val)487da6c28aaSamw dyndns_put_byte(char *buf, char val)
488da6c28aaSamw {
489da6c28aaSamw *buf = val;
490da6c28aaSamw buf++;
491da6c28aaSamw return (buf);
492da6c28aaSamw }
493da6c28aaSamw
4947f667e74Sjose borrego
4957f667e74Sjose borrego
4967f667e74Sjose borrego
497da6c28aaSamw static char *
dyndns_put_int(char * buf,int val)498da6c28aaSamw dyndns_put_int(char *buf, int val)
499da6c28aaSamw {
500da6c28aaSamw (void) memcpy(buf, &val, sizeof (int));
501da6c28aaSamw buf += sizeof (int);
502da6c28aaSamw return (buf);
503da6c28aaSamw }
504da6c28aaSamw
5057f667e74Sjose borrego static char *
dyndns_put_v6addr(char * buf,smb_inaddr_t * val)5067f667e74Sjose borrego dyndns_put_v6addr(char *buf, smb_inaddr_t *val)
5077f667e74Sjose borrego {
5087f667e74Sjose borrego
5097f667e74Sjose borrego val->a_family = AF_INET6;
5107f667e74Sjose borrego (void) memcpy(buf, &val->a_ipv6, IN6ADDRSZ);
5117f667e74Sjose borrego buf += IN6ADDRSZ;
5127f667e74Sjose borrego return (buf);
5137f667e74Sjose borrego }
514da6c28aaSamw /*
515da6c28aaSamw * dyndns_stuff_str
516da6c28aaSamw * Converts a domain string by removing periods and replacing with a byte value
517da6c28aaSamw * of how many characters following period. A byte value is placed in front
518da6c28aaSamw * to indicate how many characters before first period. A NULL character is
519da6c28aaSamw * placed at the end. i.e. host.procom.com -> 4host5procom3com0
520da6c28aaSamw * Buffer space checking is done by caller.
521da6c28aaSamw * Parameters:
522da6c28aaSamw * ptr : address of pointer to buffer to store converted string
523da6c28aaSamw * zone: domain name string
524da6c28aaSamw * Returns:
525da6c28aaSamw * ptr: address of pointer to next available buffer space
526da6c28aaSamw * -1 : error
527da6c28aaSamw * 0 : success
528da6c28aaSamw */
529da6c28aaSamw static int
dyndns_stuff_str(char ** ptr,char * zone)530da6c28aaSamw dyndns_stuff_str(char **ptr, char *zone)
531da6c28aaSamw {
532da6c28aaSamw int len;
533da6c28aaSamw char *lenPtr, *zonePtr;
534da6c28aaSamw
535da6c28aaSamw for (zonePtr = zone; *zonePtr; ) {
536da6c28aaSamw lenPtr = *ptr;
537da6c28aaSamw *ptr = *ptr + 1;
538da6c28aaSamw len = 0;
539da6c28aaSamw while (*zonePtr != '.' && *zonePtr != 0) {
540da6c28aaSamw *ptr = dyndns_put_byte(*ptr, *zonePtr);
541da6c28aaSamw zonePtr++;
542da6c28aaSamw len++;
543da6c28aaSamw }
544da6c28aaSamw *lenPtr = len;
545da6c28aaSamw if (*zonePtr == '.')
546da6c28aaSamw zonePtr++;
547da6c28aaSamw }
548da6c28aaSamw *ptr = dyndns_put_byte(*ptr, 0);
549da6c28aaSamw return (0);
550da6c28aaSamw }
551da6c28aaSamw
552da6c28aaSamw /*
553da6c28aaSamw * dyndns_build_header
554da6c28aaSamw * Build the header for DNS query and DNS update request message.
555da6c28aaSamw * Parameters:
556da6c28aaSamw * ptr : address of pointer to buffer to store header
557da6c28aaSamw * buf_len : buffer length
558da6c28aaSamw * msg_id : message id
559da6c28aaSamw * query_req : use REQ_QUERY for query message or REQ_UPDATE for
560da6c28aaSamw * update message
561da6c28aaSamw * quest_zone_cnt : number of question record for query message or
562da6c28aaSamw * number of zone record for update message
563da6c28aaSamw * ans_prereq_cnt : number of answer record for query message or
564da6c28aaSamw * number of prerequisite record for update message
565da6c28aaSamw * nameser_update_cnt: number of name server for query message or
566da6c28aaSamw * number of update record for update message
567da6c28aaSamw * addit_cnt : number of additional record
568da6c28aaSamw * flags : query flags word
569da6c28aaSamw * Returns:
570da6c28aaSamw * ptr: address of pointer to next available buffer space
571da6c28aaSamw * -1 : error
572da6c28aaSamw * 0 : success
573da6c28aaSamw */
5743db3f65cSamw 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)575da6c28aaSamw dyndns_build_header(char **ptr, int buf_len, uint16_t msg_id, int query_req,
576da6c28aaSamw uint16_t quest_zone_cnt, uint16_t ans_prereq_cnt,
577da6c28aaSamw uint16_t nameser_update_cnt, uint16_t addit_cnt, int flags)
578da6c28aaSamw {
579da6c28aaSamw uint16_t opcode;
580da6c28aaSamw
581da6c28aaSamw if (buf_len < 12) {
5828d7e4166Sjose borrego syslog(LOG_ERR, "dyndns header section: buffer too small");
583da6c28aaSamw return (-1);
584da6c28aaSamw }
585da6c28aaSamw
586da6c28aaSamw *ptr = dyndns_put_nshort(*ptr, msg_id); /* mesg ID */
587da6c28aaSamw if (query_req == REQ_QUERY)
588da6c28aaSamw opcode = ns_o_query; /* query msg */
589da6c28aaSamw else
590da6c28aaSamw opcode = ns_o_update << 11; /* update msg */
591da6c28aaSamw opcode |= flags;
592da6c28aaSamw /* mesg opcode */
593da6c28aaSamw *ptr = dyndns_put_nshort(*ptr, opcode);
594da6c28aaSamw /* zone record count */
595da6c28aaSamw *ptr = dyndns_put_nshort(*ptr, quest_zone_cnt);
596da6c28aaSamw /* prerequiste record count */
597da6c28aaSamw *ptr = dyndns_put_nshort(*ptr, ans_prereq_cnt);
598da6c28aaSamw /* update record count */
599da6c28aaSamw *ptr = dyndns_put_nshort(*ptr, nameser_update_cnt);
600da6c28aaSamw /* additional record count */
601da6c28aaSamw *ptr = dyndns_put_nshort(*ptr, addit_cnt);
602da6c28aaSamw
603da6c28aaSamw return (0);
604da6c28aaSamw }
605da6c28aaSamw
606da6c28aaSamw /*
607da6c28aaSamw * dyndns_build_quest_zone
608da6c28aaSamw * Build the question section for query message or zone section for
609da6c28aaSamw * update message.
610da6c28aaSamw * Parameters:
611da6c28aaSamw * ptr : address of pointer to buffer to store question or zone section
612da6c28aaSamw * buf_len: buffer length
613da6c28aaSamw * name : question or zone name
614da6c28aaSamw * type : type of question or zone
615da6c28aaSamw * class : class of question or zone
616da6c28aaSamw * Returns:
617da6c28aaSamw * ptr: address of pointer to next available buffer space
618da6c28aaSamw * -1 : error
619da6c28aaSamw * 0 : success
620da6c28aaSamw */
6213db3f65cSamw static int
dyndns_build_quest_zone(char ** ptr,int buf_len,char * name,int type,int class)622da6c28aaSamw dyndns_build_quest_zone(char **ptr, int buf_len, char *name, int type,
623da6c28aaSamw int class)
624da6c28aaSamw {
625da6c28aaSamw char *zonePtr;
626da6c28aaSamw
627da6c28aaSamw if ((strlen(name) + 6) > buf_len) {
6288d7e4166Sjose borrego syslog(LOG_ERR, "dyndns question section: buffer too small");
629da6c28aaSamw return (-1);
630da6c28aaSamw }
631da6c28aaSamw
632da6c28aaSamw zonePtr = *ptr;
633da6c28aaSamw (void) dyndns_stuff_str(&zonePtr, name);
634da6c28aaSamw *ptr = zonePtr;
635da6c28aaSamw *ptr = dyndns_put_nshort(*ptr, type);
636da6c28aaSamw *ptr = dyndns_put_nshort(*ptr, class);
637da6c28aaSamw return (0);
638da6c28aaSamw }
639da6c28aaSamw
640da6c28aaSamw /*
641da6c28aaSamw * dyndns_build_update
642da6c28aaSamw * Build update section of update message for adding and removing a record.
643da6c28aaSamw * If the ttl value is 0 then this message is for record deletion.
644da6c28aaSamw *
645da6c28aaSamw * Parameters:
646da6c28aaSamw * ptr : address of pointer to buffer to store update section
647da6c28aaSamw * buf_len : buffer length
648da6c28aaSamw * name : resource name of this record
649da6c28aaSamw * type : type of this record
650da6c28aaSamw * class : class of this record
651da6c28aaSamw * ttl : time-to-live, cached time of this entry by others and not
652da6c28aaSamw * within DNS database, a zero value for record(s) deletion
653da6c28aaSamw * data : data of this resource record
654da6c28aaSamw * forw_rev: UPDATE_FORW for forward zone, UPDATE_REV for reverse zone
655da6c28aaSamw * add_del : UPDATE_ADD for adding entry, UPDATE_DEL for removing zone
656da6c28aaSamw * del_type: DEL_ONE for deleting one entry, DEL_ALL for deleting all
657da6c28aaSamw * entries of the same resource name. Only valid for UPDATE_DEL.
658da6c28aaSamw * Returns:
659da6c28aaSamw * ptr: address of pointer to next available buffer space
660da6c28aaSamw * -1 : error
661da6c28aaSamw * 0 : success
662da6c28aaSamw */
663da6c28aaSamw 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)664da6c28aaSamw dyndns_build_update(char **ptr, int buf_len, char *name, int type, int class,
665da6c28aaSamw uint32_t ttl, char *data, int forw_rev, int add_del, int del_type)
666da6c28aaSamw {
667da6c28aaSamw char *namePtr;
668da6c28aaSamw int rec_len, data_len;
6697f667e74Sjose borrego smb_inaddr_t ipaddr;
6707f667e74Sjose borrego int isv4 = 1;
671da6c28aaSamw
672da6c28aaSamw rec_len = strlen(name) + 10;
6737f667e74Sjose borrego if (inet_pton(AF_INET, data, &ipaddr) == 1)
6747f667e74Sjose borrego isv4 = 1;
6757f667e74Sjose borrego else if (inet_pton(AF_INET6, data, &ipaddr) == 1)
6767f667e74Sjose borrego isv4 = 0;
6777f667e74Sjose borrego
678da6c28aaSamw if (add_del == UPDATE_ADD) {
679da6c28aaSamw if (forw_rev == UPDATE_FORW)
6807f667e74Sjose borrego data_len = isv4 ? 4 : 16;
681da6c28aaSamw else
682da6c28aaSamw data_len = strlen(data) + 2;
683da6c28aaSamw } else {
684da6c28aaSamw if (del_type == DEL_ALL)
685da6c28aaSamw data_len = 0;
686da6c28aaSamw else if (forw_rev == UPDATE_FORW)
6877f667e74Sjose borrego data_len = isv4 ? 4 : 16;
688da6c28aaSamw else
689da6c28aaSamw data_len = strlen(data) + 2;
690da6c28aaSamw }
691da6c28aaSamw if (rec_len + data_len > buf_len) {
6928d7e4166Sjose borrego syslog(LOG_ERR, "dyndns update section: buffer too small");
693da6c28aaSamw return (-1);
694da6c28aaSamw }
695da6c28aaSamw
696da6c28aaSamw namePtr = *ptr;
697da6c28aaSamw (void) dyndns_stuff_str(&namePtr, name);
698da6c28aaSamw *ptr = namePtr;
6997f667e74Sjose borrego if (isv4)
700da6c28aaSamw *ptr = dyndns_put_nshort(*ptr, type);
7017f667e74Sjose borrego else
7027f667e74Sjose borrego *ptr = dyndns_put_nshort(*ptr, ns_t_aaaa);
703da6c28aaSamw *ptr = dyndns_put_nshort(*ptr, class);
704da6c28aaSamw *ptr = dyndns_put_nlong(*ptr, ttl);
705da6c28aaSamw
706da6c28aaSamw if (add_del == UPDATE_DEL && del_type == DEL_ALL) {
707da6c28aaSamw *ptr = dyndns_put_nshort(*ptr, 0);
708da6c28aaSamw return (0);
709da6c28aaSamw }
710da6c28aaSamw
711da6c28aaSamw if (forw_rev == UPDATE_FORW) {
7127f667e74Sjose borrego if (isv4) {
713da6c28aaSamw *ptr = dyndns_put_nshort(*ptr, 4);
7147f667e74Sjose borrego *ptr = dyndns_put_int(*ptr, ipaddr.a_ipv4);
7157f667e74Sjose borrego } else {
7167f667e74Sjose borrego *ptr = dyndns_put_nshort(*ptr, 16);
7177f667e74Sjose borrego *ptr = dyndns_put_v6addr(*ptr, &ipaddr);
7187f667e74Sjose borrego }
719da6c28aaSamw } else {
720da6c28aaSamw *ptr = dyndns_put_nshort(*ptr, strlen(data)+2);
721da6c28aaSamw namePtr = *ptr;
722da6c28aaSamw (void) dyndns_stuff_str(&namePtr, data); /* hostname */
723da6c28aaSamw *ptr = namePtr;
724da6c28aaSamw }
725da6c28aaSamw return (0);
726da6c28aaSamw }
727da6c28aaSamw
728da6c28aaSamw /*
729da6c28aaSamw * dyndns_build_tkey
730da6c28aaSamw * Build TKEY section to establish security context for secure dynamic DNS
731da6c28aaSamw * update. DNS header and question sections need to be build before this
732da6c28aaSamw * section. The TKEY data are the tokens generated during security context
733da6c28aaSamw * establishment and the TKEY message is used to transmit those tokens, one
734da6c28aaSamw * at a time, to the DNS server.
735da6c28aaSamw * Parameters:
736da6c28aaSamw * ptr : address of pointer to buffer to store TKEY
737da6c28aaSamw * buf_len : buffer length
738da6c28aaSamw * name : key name, must be unique and same as for TSIG record
739da6c28aaSamw * key_expire: expiration time of this key in second
740da6c28aaSamw * data : TKEY data
741da6c28aaSamw * data_size : data size
742da6c28aaSamw * Returns:
743da6c28aaSamw * ptr: address of the pointer to the next available buffer space
744da6c28aaSamw * -1 : error
745da6c28aaSamw * 0 : success
746da6c28aaSamw */
747da6c28aaSamw static int
dyndns_build_tkey(char ** ptr,int buf_len,char * name,int key_expire,char * data,int data_size)748da6c28aaSamw dyndns_build_tkey(char **ptr, int buf_len, char *name, int key_expire,
749da6c28aaSamw char *data, int data_size)
750da6c28aaSamw {
751da6c28aaSamw char *namePtr;
752da6c28aaSamw struct timeval tp;
753da6c28aaSamw
754da6c28aaSamw if (strlen(name)+2 + 45 + data_size > buf_len) {
7558d7e4166Sjose borrego syslog(LOG_ERR, "dyndns TKEY: buffer too small");
756da6c28aaSamw return (-1);
757da6c28aaSamw }
758da6c28aaSamw
759da6c28aaSamw namePtr = *ptr;
760da6c28aaSamw (void) dyndns_stuff_str(&namePtr, name); /* unique global name */
761da6c28aaSamw *ptr = namePtr;
762da6c28aaSamw *ptr = dyndns_put_nshort(*ptr, ns_t_tkey);
763da6c28aaSamw *ptr = dyndns_put_nshort(*ptr, ns_c_any);
764da6c28aaSamw *ptr = dyndns_put_nlong(*ptr, 0);
765da6c28aaSamw /* 19 + 14 + data_size + 2 */
766da6c28aaSamw *ptr = dyndns_put_nshort(*ptr, 35 + data_size);
767da6c28aaSamw namePtr = *ptr;
768da6c28aaSamw (void) dyndns_stuff_str(&namePtr, "gss.microsoft.com");
769da6c28aaSamw *ptr = namePtr;
770da6c28aaSamw (void) gettimeofday(&tp, 0);
771da6c28aaSamw *ptr = dyndns_put_nlong(*ptr, tp.tv_sec); /* inception */
772da6c28aaSamw /* expiration, 86400 */
773da6c28aaSamw *ptr = dyndns_put_nlong(*ptr, tp.tv_sec + key_expire);
774da6c28aaSamw *ptr = dyndns_put_nshort(*ptr, MODE_GSS_API); /* mode: gss-api */
775da6c28aaSamw *ptr = dyndns_put_nshort(*ptr, 0); /* error */
776da6c28aaSamw *ptr = dyndns_put_nshort(*ptr, data_size); /* key size */
777da6c28aaSamw (void) memcpy(*ptr, data, data_size); /* key data */
778da6c28aaSamw *ptr += data_size;
779da6c28aaSamw *ptr = dyndns_put_nshort(*ptr, 0); /* other */
780da6c28aaSamw return (0);
781da6c28aaSamw }
782da6c28aaSamw
783da6c28aaSamw /*
784da6c28aaSamw * dyndns_build_tsig
785da6c28aaSamw * Build TSIG section for secure dynamic DNS update. This routine will be
786da6c28aaSamw * called twice. First called with TSIG_UNSIGNED, and second with TSIG_SIGNED.
787da6c28aaSamw * The TSIG data is NULL and ignored for TSIG_UNSIGNED and is the update request
788da6c28aaSamw * message encrypted for TSIG_SIGNED. The message id must be the same id as the
789da6c28aaSamw * one in the update request before it is encrypted.
790da6c28aaSamw * Parameters:
791da6c28aaSamw * ptr : address of pointer to buffer to store TSIG
792da6c28aaSamw * buf_len : buffer length
793da6c28aaSamw * msg_id : message id
794da6c28aaSamw * name : key name, must be the same as in TKEY record
795da6c28aaSamw * fudge_time : amount of error time allow in seconds
796da6c28aaSamw * data : TSIG data if TSIG_SIGNED, otherwise NULL
797da6c28aaSamw * data_size : size of data, otherwise 0 if data is NULL
798da6c28aaSamw * data_signed: TSIG_SIGNED to indicate data is signed and encrypted,
799da6c28aaSamw * otherwise TSIG_UNSIGNED
800da6c28aaSamw * Returns:
801da6c28aaSamw * ptr: address of pointer to next available buffer space
802da6c28aaSamw * -1 : error
803da6c28aaSamw * 0 : success
804da6c28aaSamw */
805da6c28aaSamw 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)806da6c28aaSamw dyndns_build_tsig(char **ptr, int buf_len, int msg_id, char *name,
807da6c28aaSamw int fudge_time, char *data, int data_size, int data_signed)
808da6c28aaSamw {
809da6c28aaSamw char *namePtr;
810da6c28aaSamw struct timeval tp;
811da6c28aaSamw int signtime, fudge, rec_len;
812da6c28aaSamw
813da6c28aaSamw if (data_signed == TSIG_UNSIGNED)
814da6c28aaSamw rec_len = strlen(name)+2 + 37;
815da6c28aaSamw else
816da6c28aaSamw rec_len = strlen(name)+2 + 45 + data_size;
817da6c28aaSamw
818da6c28aaSamw if (rec_len > buf_len) {
8198d7e4166Sjose borrego syslog(LOG_ERR, "dyndns TSIG: buffer too small");
820da6c28aaSamw return (-1);
821da6c28aaSamw }
822da6c28aaSamw
823da6c28aaSamw namePtr = *ptr;
824da6c28aaSamw (void) dyndns_stuff_str(&namePtr, name); /* unique global name */
825da6c28aaSamw *ptr = namePtr;
826da6c28aaSamw if (data_signed == TSIG_SIGNED)
827da6c28aaSamw *ptr = dyndns_put_nshort(*ptr, ns_t_tsig);
828da6c28aaSamw *ptr = dyndns_put_nshort(*ptr, ns_c_any);
829da6c28aaSamw *ptr = dyndns_put_nlong(*ptr, 0);
830da6c28aaSamw if (data_signed == TSIG_SIGNED) {
831da6c28aaSamw /* 19 + 10 + data_size + 6 */
832da6c28aaSamw *ptr = dyndns_put_nshort(*ptr, 35 + data_size);
833da6c28aaSamw }
834da6c28aaSamw namePtr = *ptr;
835da6c28aaSamw (void) dyndns_stuff_str(&namePtr, "gss.microsoft.com");
836da6c28aaSamw *ptr = namePtr;
837da6c28aaSamw (void) gettimeofday(&tp, 0);
838da6c28aaSamw signtime = tp.tv_sec >> 16;
839da6c28aaSamw *ptr = dyndns_put_nlong(*ptr, signtime); /* sign time */
840da6c28aaSamw fudge = tp.tv_sec << 16;
841da6c28aaSamw fudge |= fudge_time;
842da6c28aaSamw *ptr = dyndns_put_nlong(*ptr, fudge); /* fudge time */
843da6c28aaSamw if (data_signed == TSIG_SIGNED) {
844da6c28aaSamw /* signed data size */
845da6c28aaSamw *ptr = dyndns_put_nshort(*ptr, data_size);
846da6c28aaSamw (void) memcpy(*ptr, data, data_size); /* signed data */
847da6c28aaSamw *ptr += data_size;
848da6c28aaSamw *ptr = dyndns_put_nshort(*ptr, msg_id); /* original id */
849da6c28aaSamw }
850da6c28aaSamw *ptr = dyndns_put_nshort(*ptr, 0); /* error */
851da6c28aaSamw *ptr = dyndns_put_nshort(*ptr, 0); /* other */
852da6c28aaSamw return (0);
853da6c28aaSamw }
854da6c28aaSamw
855da6c28aaSamw /*
856da6c28aaSamw * dyndns_open_init_socket
857da6c28aaSamw * This routine creates a SOCK_STREAM or SOCK_DGRAM socket and initializes it
858da6c28aaSamw * by doing bind() and setting linger option to off.
859da6c28aaSamw *
860da6c28aaSamw * Parameters:
861da6c28aaSamw * sock_type: SOCK_STREAM for TCP or SOCK_DGRAM for UDP
862da6c28aaSamw * dest_addr: destination address in network byte order
863da6c28aaSamw * port : destination port number
864da6c28aaSamw * Returns:
865da6c28aaSamw * descriptor: descriptor referencing the created socket
866da6c28aaSamw * -1 : error
867da6c28aaSamw */
8687f667e74Sjose borrego
8693db3f65cSamw static int
dyndns_open_init_socket(int sock_type,smb_inaddr_t * dest_addr,int port)8707f667e74Sjose borrego dyndns_open_init_socket(int sock_type, smb_inaddr_t *dest_addr, int port)
871da6c28aaSamw {
872da6c28aaSamw int s;
873da6c28aaSamw struct sockaddr_in my_addr;
8747f667e74Sjose borrego struct sockaddr_in6 my6_addr;
875da6c28aaSamw struct sockaddr_in serv_addr;
8767f667e74Sjose borrego struct sockaddr_in6 serv6_addr;
8777f667e74Sjose borrego int family;
878da6c28aaSamw
8797f667e74Sjose borrego family = dest_addr->a_family;
8807f667e74Sjose borrego
8817f667e74Sjose borrego if ((s = socket(family, sock_type, 0)) == -1) {
8827f667e74Sjose borrego syslog(LOG_ERR, "dyndns: socket error\n");
883da6c28aaSamw return (-1);
884da6c28aaSamw }
8857f667e74Sjose borrego if (family == AF_INET) {
886da6c28aaSamw bzero(&my_addr, sizeof (my_addr));
8877f667e74Sjose borrego my_addr.sin_family = family;
888da6c28aaSamw my_addr.sin_port = htons(0);
889da6c28aaSamw my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
8907f667e74Sjose borrego if (bind(s, (struct sockaddr *)&my_addr,
8917f667e74Sjose borrego sizeof (my_addr)) < 0) {
8927f667e74Sjose borrego syslog(LOG_ERR, "dyndns: client bind err\n");
893da6c28aaSamw (void) close(s);
894da6c28aaSamw return (-1);
895da6c28aaSamw }
8967f667e74Sjose borrego serv_addr.sin_family = family;
897da6c28aaSamw serv_addr.sin_port = htons(port);
8987f667e74Sjose borrego serv_addr.sin_addr.s_addr = dest_addr->a_ipv4;
899da6c28aaSamw if (connect(s, (struct sockaddr *)&serv_addr,
900da6c28aaSamw sizeof (struct sockaddr_in)) < 0) {
9017f667e74Sjose borrego syslog(LOG_ERR, "dyndns: client connect (%s)\n",
902da6c28aaSamw strerror(errno));
903da6c28aaSamw (void) close(s);
904da6c28aaSamw return (-1);
905da6c28aaSamw }
9067f667e74Sjose borrego } else {
9077f667e74Sjose borrego bzero(&my6_addr, sizeof (my6_addr));
9087f667e74Sjose borrego my6_addr.sin6_family = family;
9097f667e74Sjose borrego my6_addr.sin6_port = htons(0);
9107f667e74Sjose borrego bzero(&my6_addr.sin6_addr.s6_addr, IN6ADDRSZ);
9117f667e74Sjose borrego if (bind(s, (struct sockaddr *)&my6_addr,
9127f667e74Sjose borrego sizeof (my6_addr)) < 0) {
9137f667e74Sjose borrego syslog(LOG_ERR, "dyndns: client bind err\n");
9147f667e74Sjose borrego (void) close(s);
9157f667e74Sjose borrego return (-1);
9167f667e74Sjose borrego }
9177f667e74Sjose borrego serv6_addr.sin6_family = family;
9187f667e74Sjose borrego serv6_addr.sin6_port = htons(port);
9197f667e74Sjose borrego bcopy(&serv6_addr.sin6_addr.s6_addr, &dest_addr->a_ipv6,
9207f667e74Sjose borrego IN6ADDRSZ);
9217f667e74Sjose borrego if (connect(s, (struct sockaddr *)&serv6_addr,
9227f667e74Sjose borrego sizeof (struct sockaddr_in6)) < 0) {
9237f667e74Sjose borrego syslog(LOG_ERR, "dyndns: client connect err (%s)\n",
9247f667e74Sjose borrego strerror(errno));
9257f667e74Sjose borrego (void) close(s);
9267f667e74Sjose borrego return (-1);
9277f667e74Sjose borrego }
9287f667e74Sjose borrego }
929da6c28aaSamw return (s);
930da6c28aaSamw }
931da6c28aaSamw /*
932da6c28aaSamw * dyndns_build_tkey_msg
933da6c28aaSamw * This routine is used to build the TKEY message to transmit GSS tokens
934da6c28aaSamw * during GSS security context establishment for secure DNS update. The
935da6c28aaSamw * TKEY message format uses the DNS query message format. The TKEY section
936da6c28aaSamw * is the answer section of the query message format.
937da6c28aaSamw * Microsoft uses a value of 86400 seconds (24 hours) for key expiration time.
938da6c28aaSamw * Parameters:
939da6c28aaSamw * buf : buffer to build and store TKEY message
940da6c28aaSamw * key_name: a unique key name, this same key name must be also be used in
941da6c28aaSamw * the TSIG message
942da6c28aaSamw * out_tok : TKEY message data (GSS tokens)
943da6c28aaSamw * Returns:
944da6c28aaSamw * id : message id of this TKEY message
945da6c28aaSamw * message size: the size of the TKEY message
946da6c28aaSamw * -1 : error
947da6c28aaSamw */
948da6c28aaSamw static int
dyndns_build_tkey_msg(char * buf,char * key_name,uint16_t * id,gss_buffer_desc * out_tok)949da6c28aaSamw dyndns_build_tkey_msg(char *buf, char *key_name, uint16_t *id,
950da6c28aaSamw gss_buffer_desc *out_tok)
951da6c28aaSamw {
952da6c28aaSamw int queryReq, zoneCount, preqCount, updateCount, additionalCount;
953da6c28aaSamw int zoneType, zoneClass;
954da6c28aaSamw char *bufptr;
955da6c28aaSamw
956da6c28aaSamw queryReq = REQ_QUERY;
957da6c28aaSamw /* query section of query request */
958da6c28aaSamw zoneCount = 1;
959da6c28aaSamw /* answer section of query request */
960da6c28aaSamw preqCount = 1;
961da6c28aaSamw updateCount = 0;
962da6c28aaSamw additionalCount = 0;
963da6c28aaSamw
964da6c28aaSamw (void) memset(buf, 0, MAX_TCP_SIZE);
965da6c28aaSamw bufptr = buf;
9668d7e4166Sjose borrego *id = dyndns_get_msgid();
967da6c28aaSamw
968da6c28aaSamw /* add TCP length info that follows this field */
969da6c28aaSamw bufptr = dyndns_put_nshort(bufptr,
970da6c28aaSamw 26 + (strlen(key_name)+2)*2 + 35 + out_tok->length);
971da6c28aaSamw
972da6c28aaSamw if (dyndns_build_header(&bufptr, BUFLEN_TCP(bufptr, buf), *id, queryReq,
973da6c28aaSamw zoneCount, preqCount, updateCount, additionalCount, 0) == -1) {
974da6c28aaSamw return (-1);
975da6c28aaSamw }
976da6c28aaSamw
977da6c28aaSamw zoneType = ns_t_tkey;
978da6c28aaSamw zoneClass = ns_c_in;
979da6c28aaSamw if (dyndns_build_quest_zone(&bufptr, BUFLEN_TCP(bufptr, buf), key_name,
980da6c28aaSamw zoneType, zoneClass) == -1) {
981da6c28aaSamw return (-1);
982da6c28aaSamw }
983da6c28aaSamw
984da6c28aaSamw if (dyndns_build_tkey(&bufptr, BUFLEN_TCP(bufptr, buf), key_name,
985da6c28aaSamw 86400, out_tok->value, out_tok->length) == -1) {
986da6c28aaSamw return (-1);
987da6c28aaSamw }
988da6c28aaSamw
989da6c28aaSamw return (bufptr - buf);
990da6c28aaSamw }
991da6c28aaSamw
992da6c28aaSamw /*
993da6c28aaSamw * dyndns_establish_sec_ctx
994da6c28aaSamw * This routine is used to establish a security context with the DNS server
995da6c28aaSamw * by building TKEY messages and sending them to the DNS server. TKEY messages
996da6c28aaSamw * are also received from the DNS server for processing. The security context
997da6c28aaSamw * establishment is done with the GSS client on the system producing a token
998da6c28aaSamw * and sending the token within the TKEY message to the GSS server on the DNS
999da6c28aaSamw * server. The GSS server then processes the token and then send a TKEY reply
1000da6c28aaSamw * message with a new token to be processed by the GSS client. The GSS client
1001da6c28aaSamw * processes the new token and then generates a new token to be sent to the
1002da6c28aaSamw * GSS server. This cycle is continued until the security establishment is
1003da6c28aaSamw * done. TCP is used to send and receive TKEY messages.
1004da6c28aaSamw * Parameters:
100555bf511dSas200622 * cred_handle : handle to credential
1006da6c28aaSamw * s : socket descriptor to DNS server
1007da6c28aaSamw * key_name : TKEY key name
10089fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States * dns_hostname: fully qualified DNS hostname which will be used for
10099fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States * constructing the DNS service principal name.
1010da6c28aaSamw * oid : contains Kerberos 5 object identifier
1011da6c28aaSamw * Returns:
1012da6c28aaSamw * gss_context : handle to security context
1013da6c28aaSamw */
1014da6c28aaSamw 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)101555bf511dSas200622 dyndns_establish_sec_ctx(gss_ctx_id_t *gss_context, gss_cred_id_t cred_handle,
101655bf511dSas200622 int s, char *key_name, char *dns_hostname, gss_OID oid)
1017da6c28aaSamw {
1018da6c28aaSamw uint16_t id, rid, rsz;
1019da6c28aaSamw char buf[MAX_TCP_SIZE], buf2[MAX_TCP_SIZE];
1020da6c28aaSamw int ret;
1021da6c28aaSamw char *service_name, *tmpptr;
1022da6c28aaSamw int service_sz;
1023da6c28aaSamw OM_uint32 min, maj, time_rec;
1024da6c28aaSamw gss_buffer_desc service_buf, in_tok, out_tok;
1025da6c28aaSamw gss_name_t target_name;
1026da6c28aaSamw gss_buffer_desc *inputptr;
1027da6c28aaSamw int gss_flags;
1028da6c28aaSamw OM_uint32 ret_flags;
1029da6c28aaSamw int buf_sz;
1030da6c28aaSamw
1031da6c28aaSamw service_sz = strlen(dns_hostname) + 5;
1032da6c28aaSamw service_name = (char *)malloc(sizeof (char) * service_sz);
10338d7e4166Sjose borrego if (service_name == NULL)
1034da6c28aaSamw return (-1);
10358d7e4166Sjose borrego
1036da6c28aaSamw (void) snprintf(service_name, service_sz, "DNS@%s", dns_hostname);
1037da6c28aaSamw service_buf.value = service_name;
1038da6c28aaSamw service_buf.length = strlen(service_name)+1;
1039da6c28aaSamw if ((maj = gss_import_name(&min, &service_buf,
104055bf511dSas200622 GSS_C_NT_HOSTBASED_SERVICE, &target_name)) != GSS_S_COMPLETE) {
1041da6c28aaSamw display_stat(maj, min);
1042da6c28aaSamw (void) free(service_name);
1043da6c28aaSamw return (-1);
1044da6c28aaSamw }
1045da6c28aaSamw (void) free(service_name);
1046da6c28aaSamw
1047da6c28aaSamw inputptr = GSS_C_NO_BUFFER;
1048da6c28aaSamw *gss_context = GSS_C_NO_CONTEXT;
1049da6c28aaSamw gss_flags = GSS_C_MUTUAL_FLAG | GSS_C_DELEG_FLAG | GSS_C_REPLAY_FLAG |
1050da6c28aaSamw GSS_C_SEQUENCE_FLAG | GSS_C_CONF_FLAG | GSS_C_INTEG_FLAG;
1051da6c28aaSamw do {
105255bf511dSas200622 maj = gss_init_sec_context(&min, cred_handle, gss_context,
105355bf511dSas200622 target_name, oid, gss_flags, 0, NULL, inputptr, NULL,
105455bf511dSas200622 &out_tok, &ret_flags, &time_rec);
105555bf511dSas200622
105655bf511dSas200622 if (maj != GSS_S_COMPLETE && maj != GSS_S_CONTINUE_NEEDED) {
105755bf511dSas200622 assert(gss_context);
105855bf511dSas200622 if (*gss_context != GSS_C_NO_CONTEXT)
105955bf511dSas200622 (void) gss_delete_sec_context(&min,
106055bf511dSas200622 gss_context, NULL);
106155bf511dSas200622
106255bf511dSas200622 display_stat(maj, min);
1063da6c28aaSamw (void) gss_release_name(&min, &target_name);
1064da6c28aaSamw return (-1);
1065da6c28aaSamw }
1066da6c28aaSamw
1067da6c28aaSamw if ((maj == GSS_S_COMPLETE) &&
1068da6c28aaSamw !(ret_flags & GSS_C_REPLAY_FLAG)) {
10698d7e4166Sjose borrego syslog(LOG_ERR, "dyndns: No GSS_C_REPLAY_FLAG");
1070da6c28aaSamw if (out_tok.length > 0)
1071da6c28aaSamw (void) gss_release_buffer(&min, &out_tok);
1072da6c28aaSamw (void) gss_release_name(&min, &target_name);
1073da6c28aaSamw return (-1);
1074da6c28aaSamw }
1075da6c28aaSamw
1076da6c28aaSamw if ((maj == GSS_S_COMPLETE) &&
1077da6c28aaSamw !(ret_flags & GSS_C_MUTUAL_FLAG)) {
10788d7e4166Sjose borrego syslog(LOG_ERR, "dyndns: No GSS_C_MUTUAL_FLAG");
1079da6c28aaSamw if (out_tok.length > 0)
1080da6c28aaSamw (void) gss_release_buffer(&min, &out_tok);
1081da6c28aaSamw (void) gss_release_name(&min, &target_name);
1082da6c28aaSamw return (-1);
1083da6c28aaSamw }
1084da6c28aaSamw
1085da6c28aaSamw if (out_tok.length > 0) {
1086da6c28aaSamw if ((buf_sz = dyndns_build_tkey_msg(buf, key_name,
1087da6c28aaSamw &id, &out_tok)) <= 0) {
1088da6c28aaSamw (void) gss_release_buffer(&min, &out_tok);
1089da6c28aaSamw (void) gss_release_name(&min, &target_name);
1090da6c28aaSamw return (-1);
1091da6c28aaSamw }
1092da6c28aaSamw
1093da6c28aaSamw (void) gss_release_buffer(&min, &out_tok);
1094da6c28aaSamw
1095da6c28aaSamw if (send(s, buf, buf_sz, 0) == -1) {
10968d7e4166Sjose borrego syslog(LOG_ERR, "dyndns: TKEY send error");
1097da6c28aaSamw (void) gss_release_name(&min, &target_name);
1098da6c28aaSamw return (-1);
1099da6c28aaSamw }
1100da6c28aaSamw
1101da6c28aaSamw bzero(buf2, MAX_TCP_SIZE);
1102da6c28aaSamw if (recv(s, buf2, MAX_TCP_SIZE, 0) == -1) {
11038d7e4166Sjose borrego syslog(LOG_ERR, "dyndns: TKEY recv error");
1104da6c28aaSamw (void) gss_release_name(&min, &target_name);
1105da6c28aaSamw return (-1);
1106da6c28aaSamw }
1107da6c28aaSamw
1108da6c28aaSamw ret = buf2[5] & 0xf; /* error field in TCP */
1109da6c28aaSamw if (ret != NOERROR) {
11108d7e4166Sjose borrego dyndns_syslog(LOG_ERR, ret, "TKEY reply");
1111da6c28aaSamw (void) gss_release_name(&min, &target_name);
1112da6c28aaSamw return (-1);
1113da6c28aaSamw }
1114da6c28aaSamw
1115da6c28aaSamw tmpptr = &buf2[2];
1116da6c28aaSamw (void) dyndns_get_nshort(tmpptr, &rid);
1117da6c28aaSamw if (id != rid) {
1118da6c28aaSamw (void) gss_release_name(&min, &target_name);
1119da6c28aaSamw return (-1);
1120da6c28aaSamw }
1121da6c28aaSamw
1122da6c28aaSamw tmpptr = &buf2[59+(strlen(key_name)+2)*2];
1123da6c28aaSamw (void) dyndns_get_nshort(tmpptr, &rsz);
1124da6c28aaSamw in_tok.length = rsz;
1125da6c28aaSamw
1126da6c28aaSamw /* bsd38 -> 2*7=14 */
1127da6c28aaSamw in_tok.value = &buf2[61+(strlen(key_name)+2)*2];
1128da6c28aaSamw inputptr = &in_tok;
1129da6c28aaSamw }
1130da6c28aaSamw
1131da6c28aaSamw } while (maj != GSS_S_COMPLETE);
1132da6c28aaSamw
1133da6c28aaSamw (void) gss_release_name(&min, &target_name);
1134da6c28aaSamw
1135da6c28aaSamw return (0);
1136da6c28aaSamw }
1137da6c28aaSamw
1138da6c28aaSamw /*
1139da6c28aaSamw * dyndns_get_sec_context
1140da6c28aaSamw * Get security context for secure dynamic DNS update. This routine opens
114155bf511dSas200622 * a TCP socket to the DNS server and establishes a security context with
114255bf511dSas200622 * the DNS server using host principal to perform secure dynamic DNS update.
1143da6c28aaSamw * Parameters:
1144da6c28aaSamw * hostname: fully qualified hostname
1145da6c28aaSamw * dns_ip : ip address of hostname in network byte order
1146da6c28aaSamw * Returns:
1147da6c28aaSamw * gss_handle: gss credential handle
1148da6c28aaSamw * gss_context: gss security context
1149da6c28aaSamw * -1: error
1150da6c28aaSamw * 0: success
1151da6c28aaSamw */
11527f667e74Sjose borrego
1153da6c28aaSamw static gss_ctx_id_t
dyndns_get_sec_context(const char * hostname,smb_inaddr_t * dns_ip)11547f667e74Sjose borrego dyndns_get_sec_context(const char *hostname, smb_inaddr_t *dns_ip)
1155da6c28aaSamw {
1156da6c28aaSamw int s;
115755bf511dSas200622 gss_cred_id_t cred_handle;
1158da6c28aaSamw gss_ctx_id_t gss_context;
1159da6c28aaSamw gss_OID oid;
116055bf511dSas200622 char *key_name, dns_hostname[MAXHOSTNAMELEN];
1161da6c28aaSamw
116255bf511dSas200622 cred_handle = GSS_C_NO_CREDENTIAL;
116355bf511dSas200622 oid = GSS_C_NO_OID;
1164da6c28aaSamw key_name = (char *)hostname;
1165da6c28aaSamw
116629bd2886SAlan Wright if (smb_getnameinfo(dns_ip, dns_hostname,
11677f667e74Sjose borrego sizeof (dns_hostname), 0)) {
1168da6c28aaSamw return (NULL);
1169da6c28aaSamw }
1170da6c28aaSamw if ((s = dyndns_open_init_socket(SOCK_STREAM, dns_ip, 53)) < 0) {
1171da6c28aaSamw return (NULL);
1172da6c28aaSamw }
1173da6c28aaSamw
117455bf511dSas200622 if (dyndns_establish_sec_ctx(&gss_context, cred_handle, s, key_name,
117555bf511dSas200622 dns_hostname, oid))
117655bf511dSas200622 gss_context = NULL;
1177da6c28aaSamw
1178da6c28aaSamw (void) close(s);
1179da6c28aaSamw return (gss_context);
1180da6c28aaSamw }
1181da6c28aaSamw
1182da6c28aaSamw /*
1183da6c28aaSamw * dyndns_build_add_remove_msg
1184da6c28aaSamw * This routine builds the update request message for adding and removing DNS
1185da6c28aaSamw * entries which is used for non-secure and secure DNS update.
1186da6c28aaSamw * This routine builds an UDP message.
1187da6c28aaSamw * Parameters:
1188da6c28aaSamw * buf : buffer to build message
1189da6c28aaSamw * update_zone: the type of zone to update, use UPDATE_FORW for forward
1190da6c28aaSamw * lookup zone, use UPDATE_REV for reverse lookup zone
1191da6c28aaSamw * hostname : fully qualified hostname to update DNS with
1192da6c28aaSamw * ip_addr : IP address of hostname
1193da6c28aaSamw * life_time : cached time of this entry by others and not within DNS
1194da6c28aaSamw * database
1195da6c28aaSamw * update_type: UPDATE_ADD to add entry, UPDATE_DEL to remove entry
1196da6c28aaSamw * del_type : DEL_ONE for deleting one entry, DEL_ALL for deleting all
1197da6c28aaSamw * entries of the same resource name. Only valid for UPDATE_DEL.
1198da6c28aaSamw * addit_cnt : Indicate how many record is in the additional section of
1199da6c28aaSamw * the DNS message. A value of zero is always used with
1200da6c28aaSamw * non-secure update message. For secure update message,
1201da6c28aaSamw * the value will be one because the signed TSIG message
1202da6c28aaSamw * is added as the additional record of the DNS update message.
1203da6c28aaSamw * id : DNS message ID. If a positive value then this ID value is
1204da6c28aaSamw * used, otherwise the next incremented value is used
1205da6c28aaSamw * level : This is the domain level which we send the request to, level
1206da6c28aaSamw * zero is the default level, it can go upto 2 in reverse zone
1207da6c28aaSamw * and virtually to any level in forward zone.
1208da6c28aaSamw * Returns:
1209da6c28aaSamw * buf : buffer containing update message
1210da6c28aaSamw * id : DNS message ID
1211da6c28aaSamw * int : size of update message
1212da6c28aaSamw * -1 : error
1213da6c28aaSamw *
1214da6c28aaSamw * This function is changed to handle dynamic DNS update retires to higher
1215da6c28aaSamw * authoritative domains.
1216da6c28aaSamw */
1217da6c28aaSamw 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)1218da6c28aaSamw dyndns_build_add_remove_msg(char *buf, int update_zone, const char *hostname,
1219da6c28aaSamw const char *ip_addr, int life_time, int update_type, int del_type,
1220da6c28aaSamw int addit_cnt, uint16_t *id, int level)
1221da6c28aaSamw {
1222da6c28aaSamw int a, b, c, d;
1223da6c28aaSamw char *bufptr;
1224da6c28aaSamw int queryReq, zoneCount, preqCount, updateCount, additionalCount;
1225da6c28aaSamw char *zone, *resource, *data, zone_buf[100], resrc_buf[100];
1226da6c28aaSamw int zoneType, zoneClass, type, class, ttl;
1227da6c28aaSamw char *p;
12287f667e74Sjose borrego smb_inaddr_t tmp_addr;
12297f667e74Sjose borrego int i, j, k;
12307f667e74Sjose borrego int fourcnt;
1231da6c28aaSamw
1232da6c28aaSamw queryReq = REQ_UPDATE;
1233da6c28aaSamw zoneCount = 1;
1234da6c28aaSamw preqCount = 0;
1235da6c28aaSamw updateCount = 1;
1236da6c28aaSamw additionalCount = addit_cnt;
1237da6c28aaSamw
1238da6c28aaSamw (void) memset(buf, 0, NS_PACKETSZ);
1239da6c28aaSamw bufptr = buf;
1240da6c28aaSamw
1241da6c28aaSamw if (*id == 0)
12428d7e4166Sjose borrego *id = dyndns_get_msgid();
1243da6c28aaSamw
1244da6c28aaSamw if (dyndns_build_header(&bufptr, BUFLEN_UDP(bufptr, buf), *id, queryReq,
1245da6c28aaSamw zoneCount, preqCount, updateCount, additionalCount, 0) == -1) {
1246da6c28aaSamw return (-1);
1247da6c28aaSamw }
1248da6c28aaSamw
1249da6c28aaSamw zoneType = ns_t_soa;
1250da6c28aaSamw zoneClass = ns_c_in;
1251da6c28aaSamw
1252da6c28aaSamw if (update_zone == UPDATE_FORW) {
1253da6c28aaSamw p = (char *)hostname;
1254da6c28aaSamw
1255da6c28aaSamw /* Try higher domains according to the level requested */
1256da6c28aaSamw do {
1257da6c28aaSamw /* domain */
1258da6c28aaSamw if ((zone = (char *)strchr(p, '.')) == NULL)
1259da6c28aaSamw return (-1);
1260da6c28aaSamw zone += 1;
1261da6c28aaSamw p = zone;
1262da6c28aaSamw } while (--level >= 0);
1263da6c28aaSamw resource = (char *)hostname;
1264da6c28aaSamw data = (char *)ip_addr;
1265da6c28aaSamw } else {
12667f667e74Sjose borrego if (inet_pton(AF_INET, ip_addr, &tmp_addr) == 1) {
1267da6c28aaSamw (void) sscanf(ip_addr, "%d.%d.%d.%d", &a, &b, &c, &d);
12687f667e74Sjose borrego (void) sprintf(zone_buf, "%d.%d.%d.in-addr.arpa",
12697f667e74Sjose borrego c, b, a);
1270da6c28aaSamw zone = p = zone_buf;
1271da6c28aaSamw
12727f667e74Sjose borrego /* Try higher domains based on level requested */
1273da6c28aaSamw while (--level >= 0) {
1274da6c28aaSamw /* domain */
1275da6c28aaSamw if ((zone = (char *)strchr(p, '.')) == NULL) {
1276da6c28aaSamw return (-1);
1277da6c28aaSamw }
1278da6c28aaSamw zone += 1;
1279da6c28aaSamw p = zone;
1280da6c28aaSamw }
1281da6c28aaSamw (void) sprintf(resrc_buf, "%d.%d.%d.%d.in-addr.arpa",
1282da6c28aaSamw d, c, b, a);
12837f667e74Sjose borrego } else {
12847f667e74Sjose borrego /*
12857f667e74Sjose borrego * create reverse nibble ipv6 format
12867f667e74Sjose borrego */
12877f667e74Sjose borrego bzero(resrc_buf, 100);
12887f667e74Sjose borrego i = 0;
12897f667e74Sjose borrego j = 0;
12907f667e74Sjose borrego while (ip_addr[i] != 0)
12917f667e74Sjose borrego i++;
12927f667e74Sjose borrego i--;
12937f667e74Sjose borrego while (i >= 0) {
12947f667e74Sjose borrego fourcnt = 3;
12957f667e74Sjose borrego while ((i >= 0) && (ip_addr[i] != ':')) {
12967f667e74Sjose borrego resrc_buf[j++] = ip_addr[i];
12977f667e74Sjose borrego (void) strcat(&resrc_buf[j++], ".");
12987f667e74Sjose borrego fourcnt --;
12997f667e74Sjose borrego i--;
13007f667e74Sjose borrego }
13017f667e74Sjose borrego for (k = 0; k <= fourcnt; k++) {
13027f667e74Sjose borrego resrc_buf[j++] = '0';
13037f667e74Sjose borrego (void) strcat(&resrc_buf[j++], ".");
13047f667e74Sjose borrego }
13057f667e74Sjose borrego i--;
13067f667e74Sjose borrego }
13077f667e74Sjose borrego (void) strcat(resrc_buf, "ip6.arpa");
13087f667e74Sjose borrego (void) strcpy(zone_buf, &resrc_buf[32]);
13097f667e74Sjose borrego zone = zone_buf;
13107f667e74Sjose borrego }
1311da6c28aaSamw resource = resrc_buf; /* ip info */
1312da6c28aaSamw data = (char *)hostname;
1313da6c28aaSamw }
1314da6c28aaSamw if (dyndns_build_quest_zone(&bufptr, BUFLEN_UDP(bufptr, buf), zone,
1315da6c28aaSamw zoneType, zoneClass) == -1) {
1316da6c28aaSamw return (-1);
1317da6c28aaSamw }
1318da6c28aaSamw
1319da6c28aaSamw if (update_zone == UPDATE_FORW)
1320da6c28aaSamw type = ns_t_a;
1321da6c28aaSamw else
1322da6c28aaSamw type = ns_t_ptr;
1323da6c28aaSamw
1324da6c28aaSamw if (update_type == UPDATE_ADD) {
1325da6c28aaSamw class = ns_c_in;
1326da6c28aaSamw ttl = life_time;
1327da6c28aaSamw } else {
1328da6c28aaSamw if (del_type == DEL_ONE)
1329da6c28aaSamw class = ns_c_none; /* remove one */
1330da6c28aaSamw else
1331da6c28aaSamw class = ns_c_any; /* remove all */
1332da6c28aaSamw ttl = 0;
1333da6c28aaSamw }
1334da6c28aaSamw if (dyndns_build_update(&bufptr, BUFLEN_UDP(bufptr, buf),
1335da6c28aaSamw resource, type, class, ttl, data, update_zone,
1336da6c28aaSamw update_type, del_type) == -1) {
1337da6c28aaSamw return (-1);
1338da6c28aaSamw }
1339da6c28aaSamw
1340da6c28aaSamw return (bufptr - buf);
1341da6c28aaSamw }
1342da6c28aaSamw
1343da6c28aaSamw /*
1344da6c28aaSamw * dyndns_build_unsigned_tsig_msg
1345da6c28aaSamw * This routine is used to build the unsigned TSIG message for signing. The
1346da6c28aaSamw * unsigned TSIG message contains the update request message with certain TSIG
1347da6c28aaSamw * fields included. An error time of 300 seconds is used for fudge time. This
1348da6c28aaSamw * is the number used by Microsoft clients.
1349da6c28aaSamw * This routine builds a UDP message.
1350da6c28aaSamw * Parameters:
1351da6c28aaSamw * buf : buffer to build message
1352da6c28aaSamw * update_zone: the type of zone to update, use UPDATE_FORW for forward
1353da6c28aaSamw * lookup zone, use UPDATE_REV for reverse lookup zone
1354da6c28aaSamw * hostname : fully qualified hostname to update DNS with
1355da6c28aaSamw * ip_addr : IP address of hostname
1356da6c28aaSamw * life_time : cached time of this entry by others and not within DNS
1357da6c28aaSamw * database
1358da6c28aaSamw * update_type: UPDATE_ADD to add entry, UPDATE_DEL to remove entry
1359da6c28aaSamw * del_type : DEL_ONE for deleting one entry, DEL_ALL for deleting all
1360da6c28aaSamw * entries of the same resource name. Only valid for UPDATE_DEL.
1361da6c28aaSamw * key_name : same key name used in TKEY message
1362da6c28aaSamw * id : DNS message ID. If a positive value then this ID value is
1363da6c28aaSamw * used, otherwise the next incremented value is used
1364da6c28aaSamw * level : This is the domain level which we send the request to, level
1365da6c28aaSamw * zero is the default level, it can go upto 2 in reverse zone
1366da6c28aaSamw * and virtually to any level in forward zone.
1367da6c28aaSamw * Returns:
1368da6c28aaSamw * buf : buffer containing update message
1369da6c28aaSamw * id : DNS message ID
1370da6c28aaSamw * int : size of update message
1371da6c28aaSamw * -1 : error
1372da6c28aaSamw */
1373da6c28aaSamw 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)1374da6c28aaSamw dyndns_build_unsigned_tsig_msg(char *buf, int update_zone, const char *hostname,
1375da6c28aaSamw const char *ip_addr, int life_time, int update_type, int del_type,
1376da6c28aaSamw char *key_name, uint16_t *id, int level)
1377da6c28aaSamw {
1378da6c28aaSamw char *bufptr;
1379da6c28aaSamw int buf_sz;
1380da6c28aaSamw
1381da6c28aaSamw if ((buf_sz = dyndns_build_add_remove_msg(buf, update_zone, hostname,
1382da6c28aaSamw ip_addr, life_time, update_type, del_type, 0, id, level)) <= 0) {
1383da6c28aaSamw return (-1);
1384da6c28aaSamw }
1385da6c28aaSamw
1386da6c28aaSamw bufptr = buf + buf_sz;
1387da6c28aaSamw
1388da6c28aaSamw if (dyndns_build_tsig(&bufptr, BUFLEN_UDP(bufptr, buf), 0,
1389da6c28aaSamw key_name, 300, NULL, 0, TSIG_UNSIGNED) == -1) {
1390da6c28aaSamw return (-1);
1391da6c28aaSamw }
1392da6c28aaSamw
1393da6c28aaSamw return (bufptr - buf);
1394da6c28aaSamw }
1395da6c28aaSamw
1396da6c28aaSamw /*
1397da6c28aaSamw * dyndns_build_signed_tsig_msg
1398da6c28aaSamw * This routine build the signed TSIG message which contains the update
1399da6c28aaSamw * request message encrypted. An error time of 300 seconds is used for fudge
1400da6c28aaSamw * time. This is the number used by Microsoft clients.
1401da6c28aaSamw * This routine builds a UDP message.
1402da6c28aaSamw * Parameters:
1403da6c28aaSamw * buf : buffer to build message
1404da6c28aaSamw * update_zone: the type of zone to update, use UPDATE_FORW for forward
1405da6c28aaSamw * lookup zone, use UPDATE_REV for reverse lookup zone
1406da6c28aaSamw * hostname : fully qualified hostname to update DNS with
1407da6c28aaSamw * ip_addr : IP address of hostname
1408da6c28aaSamw * life_time : cached time of this entry by others and not within DNS
1409da6c28aaSamw * database
1410da6c28aaSamw * update_type: UPDATE_ADD to add entry, UPDATE_DEL to remove entry
1411da6c28aaSamw * del_type : DEL_ONE for deleting one entry, DEL_ALL for deleting all
1412da6c28aaSamw * entries of the same resource name. Only valid for UPDATE_DEL.
1413da6c28aaSamw * key_name : same key name used in TKEY message
1414da6c28aaSamw * id : DNS message ID. If a positive value then this ID value is
1415da6c28aaSamw * used, otherwise the next incremented value is used
1416da6c28aaSamw * in_mic : the update request message encrypted
1417da6c28aaSamw * level : This is the domain level which we send the request to, level
1418da6c28aaSamw * zero is the default level, it can go upto 2 in reverse zone
1419da6c28aaSamw * and virtually to any level in forward zone.
1420da6c28aaSamw *
1421da6c28aaSamw * Returns:
1422da6c28aaSamw * buf : buffer containing update message
1423da6c28aaSamw * id : DNS message ID
1424da6c28aaSamw * int : size of update message
1425da6c28aaSamw * -1 : error
1426da6c28aaSamw */
1427da6c28aaSamw 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)1428da6c28aaSamw dyndns_build_signed_tsig_msg(char *buf, int update_zone, const char *hostname,
1429da6c28aaSamw const char *ip_addr, int life_time, int update_type, int del_type,
1430da6c28aaSamw char *key_name, uint16_t *id, gss_buffer_desc *in_mic, int level)
1431da6c28aaSamw {
1432da6c28aaSamw char *bufptr;
1433da6c28aaSamw int buf_sz;
1434da6c28aaSamw
1435da6c28aaSamw if ((buf_sz = dyndns_build_add_remove_msg(buf, update_zone, hostname,
1436da6c28aaSamw ip_addr, life_time, update_type, del_type, 1, id, level)) <= 0) {
1437da6c28aaSamw return (-1);
1438da6c28aaSamw }
1439da6c28aaSamw
1440da6c28aaSamw bufptr = buf + buf_sz;
1441da6c28aaSamw
1442da6c28aaSamw if (dyndns_build_tsig(&bufptr, BUFLEN_UDP(bufptr, buf),
1443da6c28aaSamw *id, key_name, 300, in_mic->value,
1444da6c28aaSamw in_mic->length, TSIG_SIGNED) == -1) {
1445da6c28aaSamw return (-1);
1446da6c28aaSamw }
1447da6c28aaSamw
1448da6c28aaSamw return (bufptr - buf);
1449da6c28aaSamw }
1450da6c28aaSamw
1451da6c28aaSamw /*
1452da6c28aaSamw * dyndns_udp_send_recv
1453dc20a302Sas200622 * This routine sends and receives UDP DNS request and reply messages.
1454da6c28aaSamw *
1455da6c28aaSamw * Pre-condition: Caller must call dyndns_open_init_socket() before calling
1456da6c28aaSamw * this function.
1457da6c28aaSamw *
1458da6c28aaSamw * Parameters:
1459da6c28aaSamw * s : socket descriptor
1460da6c28aaSamw * buf : buffer containing data to send
1461da6c28aaSamw * buf_sz : size of data to send
1462da6c28aaSamw * Returns:
1463da6c28aaSamw * -1 : error
1464da6c28aaSamw * rec_buf: reply dat
1465da6c28aaSamw * 0 : success
1466da6c28aaSamw */
14677f667e74Sjose borrego
14683db3f65cSamw static int
dyndns_udp_send_recv(int s,char * buf,int buf_sz,char * rec_buf)1469da6c28aaSamw dyndns_udp_send_recv(int s, char *buf, int buf_sz, char *rec_buf)
1470da6c28aaSamw {
1471dc20a302Sas200622 int i, retval, addr_len;
1472da6c28aaSamw struct timeval tv, timeout;
1473da6c28aaSamw fd_set rfds;
14747f667e74Sjose borrego struct sockaddr_in6 from_addr;
1475da6c28aaSamw
1476da6c28aaSamw timeout.tv_usec = 0;
1477dc20a302Sas200622 timeout.tv_sec = DYNDNS_QUERY_TIMEOUT;
1478da6c28aaSamw
1479dc20a302Sas200622 for (i = 0; i <= DYNDNS_MAX_QUERY_RETRIES; i++) {
1480da6c28aaSamw if (send(s, buf, buf_sz, 0) == -1) {
14818d7e4166Sjose borrego syslog(LOG_ERR, "dyndns: UDP send error (%s)",
1482da6c28aaSamw strerror(errno));
1483da6c28aaSamw return (-1);
1484da6c28aaSamw }
1485da6c28aaSamw
1486da6c28aaSamw FD_ZERO(&rfds);
1487da6c28aaSamw FD_SET(s, &rfds);
1488da6c28aaSamw
1489da6c28aaSamw tv = timeout;
1490da6c28aaSamw
1491da6c28aaSamw retval = select(s+1, &rfds, NULL, NULL, &tv);
1492da6c28aaSamw
1493da6c28aaSamw if (retval == -1) {
1494da6c28aaSamw return (-1);
1495da6c28aaSamw } else if (retval > 0) {
1496da6c28aaSamw bzero(rec_buf, NS_PACKETSZ);
14977f667e74Sjose borrego addr_len = sizeof (struct sockaddr_in6);
1498da6c28aaSamw if (recvfrom(s, rec_buf, NS_PACKETSZ, 0,
1499da6c28aaSamw (struct sockaddr *)&from_addr, &addr_len) == -1) {
15008d7e4166Sjose borrego syslog(LOG_ERR, "dyndns: UDP recv error ");
1501da6c28aaSamw return (-1);
1502da6c28aaSamw }
1503da6c28aaSamw break;
1504da6c28aaSamw }
1505da6c28aaSamw }
1506da6c28aaSamw
1507dc20a302Sas200622 /* did not receive anything */
1508dc20a302Sas200622 if (i == (DYNDNS_MAX_QUERY_RETRIES + 1)) {
15098d7e4166Sjose borrego syslog(LOG_ERR, "dyndns: max retries for UDP recv reached");
1510da6c28aaSamw return (-1);
1511da6c28aaSamw }
1512da6c28aaSamw
1513da6c28aaSamw return (0);
1514da6c28aaSamw }
1515da6c28aaSamw /*
1516da6c28aaSamw * dyndns_sec_add_remove_entry
1517da6c28aaSamw * Perform secure dynamic DNS update after getting security context.
1518da6c28aaSamw * This routine opens a UDP socket to the DNS sever, gets the security context,
1519da6c28aaSamw * builds the unsigned TSIG message and signed TSIG message. The signed TSIG
1520da6c28aaSamw * message containing the encrypted update request message is sent to the DNS
1521da6c28aaSamw * server. The response is received and check for error. If there is no
1522da6c28aaSamw * error then credential handle and security context are released and the local
1523da6c28aaSamw * NSS cached is purged.
1524da6c28aaSamw * Parameters:
1525da6c28aaSamw * update_zone : UPDATE_FORW for forward zone, UPDATE_REV for reverse zone
1526da6c28aaSamw * hostname : fully qualified hostname
1527da6c28aaSamw * ip_addr : ip address of hostname in string format
1528da6c28aaSamw * life_time : cached time of this entry by others and not within DNS
1529da6c28aaSamw * database
1530da6c28aaSamw * max_retries : maximum retries for sending DNS update request
1531da6c28aaSamw * recv_timeout: receive timeout
1532da6c28aaSamw * update_type : UPDATE_ADD for adding entry, UPDATE_DEL for removing entry
1533da6c28aaSamw * del_type : DEL_ONE for deleting one entry, DEL_ALL for deleting all
1534da6c28aaSamw * entries of the same resource name. Only valid for UPDATE_DEL
1535da6c28aaSamw * dns_str : DNS IP address in string format
1536da6c28aaSamw * Returns:
1537da6c28aaSamw * -1: error
1538da6c28aaSamw * 0: success
1539da6c28aaSamw *
1540da6c28aaSamw * This function is enhanced to handle the case of NOTAUTH error when DNS server
1541da6c28aaSamw * is not authoritative for specified zone. In this case we need to resend the
1542da6c28aaSamw * same request to the higher authoritative domains.
1543da6c28aaSamw * This is true for both secure and unsecure dynamic DNS updates.
1544da6c28aaSamw */
1545da6c28aaSamw 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)1546da6c28aaSamw dyndns_sec_add_remove_entry(int update_zone, const char *hostname,
1547da6c28aaSamw const char *ip_addr, int life_time, int update_type, int del_type,
1548da6c28aaSamw char *dns_str)
1549da6c28aaSamw {
1550da6c28aaSamw int s2;
1551da6c28aaSamw uint16_t id, rid;
1552da6c28aaSamw char buf[NS_PACKETSZ], buf2[NS_PACKETSZ];
1553da6c28aaSamw int ret;
1554da6c28aaSamw OM_uint32 min, maj;
1555da6c28aaSamw gss_buffer_desc in_mic, out_mic;
1556da6c28aaSamw gss_ctx_id_t gss_context;
15577f667e74Sjose borrego smb_inaddr_t dns_ip;
1558da6c28aaSamw char *key_name;
1559da6c28aaSamw int buf_sz;
1560da6c28aaSamw int level = 0;
1561da6c28aaSamw
1562da6c28aaSamw assert(dns_str);
1563da6c28aaSamw assert(*dns_str);
1564da6c28aaSamw
15657f667e74Sjose borrego if (inet_pton(AF_INET, dns_str, &dns_ip) == 1)
15667f667e74Sjose borrego dns_ip.a_family = AF_INET;
15677f667e74Sjose borrego else if (inet_pton(AF_INET6, dns_str, &dns_ip) == 1)
15687f667e74Sjose borrego dns_ip.a_family = AF_INET6;
1569da6c28aaSamw
1570da6c28aaSamw sec_retry_higher:
1571da6c28aaSamw
1572da6c28aaSamw if ((gss_context = dyndns_get_sec_context(hostname,
15737f667e74Sjose borrego &dns_ip)) == NULL) {
1574da6c28aaSamw return (-1);
1575da6c28aaSamw }
1576da6c28aaSamw
1577da6c28aaSamw key_name = (char *)hostname;
1578da6c28aaSamw
15797f667e74Sjose borrego if ((s2 = dyndns_open_init_socket(SOCK_DGRAM, &dns_ip, 53)) < 0) {
158055bf511dSas200622 if (gss_context != GSS_C_NO_CONTEXT)
1581da6c28aaSamw (void) gss_delete_sec_context(&min, &gss_context, NULL);
1582da6c28aaSamw return (-1);
1583da6c28aaSamw }
1584da6c28aaSamw
1585da6c28aaSamw id = 0;
1586da6c28aaSamw if ((buf_sz = dyndns_build_unsigned_tsig_msg(buf, update_zone, hostname,
1587da6c28aaSamw ip_addr, life_time, update_type, del_type,
1588da6c28aaSamw key_name, &id, level)) <= 0) {
1589da6c28aaSamw (void) close(s2);
159055bf511dSas200622 if (gss_context != GSS_C_NO_CONTEXT)
1591da6c28aaSamw (void) gss_delete_sec_context(&min, &gss_context, NULL);
1592da6c28aaSamw return (-1);
1593da6c28aaSamw }
1594da6c28aaSamw
1595da6c28aaSamw in_mic.length = buf_sz;
1596da6c28aaSamw in_mic.value = buf;
1597da6c28aaSamw
1598da6c28aaSamw /* sign update message */
1599da6c28aaSamw if ((maj = gss_get_mic(&min, gss_context, 0, &in_mic, &out_mic)) !=
1600da6c28aaSamw GSS_S_COMPLETE) {
1601da6c28aaSamw display_stat(maj, min);
1602da6c28aaSamw (void) close(s2);
160355bf511dSas200622 if (gss_context != GSS_C_NO_CONTEXT)
1604da6c28aaSamw (void) gss_delete_sec_context(&min, &gss_context, NULL);
1605da6c28aaSamw return (-1);
1606da6c28aaSamw }
1607da6c28aaSamw
1608da6c28aaSamw if ((buf_sz = dyndns_build_signed_tsig_msg(buf, update_zone, hostname,
1609da6c28aaSamw ip_addr, life_time, update_type, del_type, key_name, &id,
1610da6c28aaSamw &out_mic, level)) <= 0) {
1611da6c28aaSamw (void) close(s2);
1612da6c28aaSamw (void) gss_release_buffer(&min, &out_mic);
161355bf511dSas200622 if (gss_context != GSS_C_NO_CONTEXT)
1614da6c28aaSamw (void) gss_delete_sec_context(&min, &gss_context, NULL);
1615da6c28aaSamw return (-1);
1616da6c28aaSamw }
1617da6c28aaSamw
1618da6c28aaSamw (void) gss_release_buffer(&min, &out_mic);
1619da6c28aaSamw
1620da6c28aaSamw if (dyndns_udp_send_recv(s2, buf, buf_sz, buf2)) {
1621da6c28aaSamw (void) close(s2);
162255bf511dSas200622 if (gss_context != GSS_C_NO_CONTEXT)
1623da6c28aaSamw (void) gss_delete_sec_context(&min, &gss_context, NULL);
1624da6c28aaSamw return (-1);
1625da6c28aaSamw }
1626da6c28aaSamw
1627da6c28aaSamw (void) close(s2);
1628da6c28aaSamw
162955bf511dSas200622 if (gss_context != GSS_C_NO_CONTEXT)
1630da6c28aaSamw (void) gss_delete_sec_context(&min, &gss_context, NULL);
1631da6c28aaSamw
1632da6c28aaSamw ret = buf2[3] & 0xf; /* error field in UDP */
1633da6c28aaSamw
1634da6c28aaSamw /*
1635da6c28aaSamw * If it is a NOTAUTH error we should retry with higher domains
1636da6c28aaSamw * until we get a successful reply or the maximum retries is met.
1637da6c28aaSamw */
1638da6c28aaSamw if (ret == NOTAUTH && level++ < MAX_AUTH_RETRIES)
1639da6c28aaSamw goto sec_retry_higher;
1640da6c28aaSamw
1641da6c28aaSamw /* check here for update request is successful */
1642da6c28aaSamw if (ret != NOERROR) {
16438d7e4166Sjose borrego dyndns_syslog(LOG_ERR, ret, "TSIG reply");
1644da6c28aaSamw return (-1);
1645da6c28aaSamw }
1646da6c28aaSamw
1647da6c28aaSamw (void) dyndns_get_nshort(buf2, &rid);
1648da6c28aaSamw if (id != rid)
1649da6c28aaSamw return (-1);
1650da6c28aaSamw
1651da6c28aaSamw return (0);
1652da6c28aaSamw }
1653da6c28aaSamw
1654da6c28aaSamw /*
1655da6c28aaSamw * dyndns_seach_entry
1656da6c28aaSamw * Query DNS server for entry. This routine can indicate if an entry exist
1657da6c28aaSamw * or not during forward or reverse lookup. Also can indicate if the data
1658da6c28aaSamw * of the entry matched. For example, for forward lookup, the entry is
1659da6c28aaSamw * searched using the hostname and the data is the IP address. For reverse
1660da6c28aaSamw * lookup, the entry is searched using the IP address and the data is the
1661da6c28aaSamw * hostname.
1662da6c28aaSamw * Parameters:
1663da6c28aaSamw * update_zone: UPDATE_FORW for forward zone, UPDATE_REV for reverse zone
1664da6c28aaSamw * hostname : fully qualified hostname
1665da6c28aaSamw * ip_addr : ip address of hostname in string format
1666da6c28aaSamw * update_type: UPDATE_ADD for adding entry, UPDATE_DEL for removing entry
1667da6c28aaSamw * Returns:
1668da6c28aaSamw * time_out: no use
1669da6c28aaSamw * is_match: is 1 for found matching entry, otherwise 0
1670da6c28aaSamw * 1 : an entry exist but not necessarily match
1671da6c28aaSamw * 0 : an entry does not exist
1672da6c28aaSamw */
1673da6c28aaSamw /*ARGSUSED*/
16747f667e74Sjose borrego
1675da6c28aaSamw 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)1676da6c28aaSamw dyndns_search_entry(int update_zone, const char *hostname, const char *ip_addr,
1677da6c28aaSamw int update_type, struct timeval *time_out, int *is_match)
1678da6c28aaSamw {
16797f667e74Sjose borrego smb_inaddr_t ipaddr, dnsip;
16807f667e74Sjose borrego char dns_hostname[NI_MAXHOST];
16817f667e74Sjose borrego struct addrinfo hints, *res = NULL;
16827f667e74Sjose borrego int salen;
16837f667e74Sjose borrego int family;
1684da6c28aaSamw
1685da6c28aaSamw *is_match = 0;
16867f667e74Sjose borrego if (inet_pton(AF_INET, ip_addr, &ipaddr) == 1) {
16877f667e74Sjose borrego salen = sizeof (ipaddr.a_ipv4);
16887f667e74Sjose borrego family = AF_INET;
16897f667e74Sjose borrego } else if (inet_pton(AF_INET6, ip_addr, &ipaddr) == 1) {
16907f667e74Sjose borrego salen = sizeof (ipaddr.a_ipv6);
16917f667e74Sjose borrego family = AF_INET6;
16927f667e74Sjose borrego }
1693da6c28aaSamw if (update_zone == UPDATE_FORW) {
16947f667e74Sjose borrego bzero((char *)&hints, sizeof (hints));
16957f667e74Sjose borrego hints.ai_family = family;
16967f667e74Sjose borrego hints.ai_flags = AI_NUMERICHOST;
16977f667e74Sjose borrego if (getaddrinfo(hostname, NULL, &hints, &res)) {
16987f667e74Sjose borrego return (NULL);
16997f667e74Sjose borrego }
17007f667e74Sjose borrego if (res) {
17017f667e74Sjose borrego /*
17027f667e74Sjose borrego * if both ips aren't the same family skip to
17037f667e74Sjose borrego * the next record
17047f667e74Sjose borrego */
17057f667e74Sjose borrego do {
17067f667e74Sjose borrego if ((res->ai_family == AF_INET) &&
17077f667e74Sjose borrego (family == AF_INET)) {
17087f667e74Sjose borrego (void) memcpy(&dnsip, &res->ai_addr[0],
17097f667e74Sjose borrego salen);
17107f667e74Sjose borrego if (ipaddr.a_ipv4 ==
17117f667e74Sjose borrego dnsip.a_ipv4) {
17127f667e74Sjose borrego *is_match = 1;
17137f667e74Sjose borrego break;
17147f667e74Sjose borrego }
17157f667e74Sjose borrego } else if ((res->ai_family == AF_INET6) &&
17167f667e74Sjose borrego (family == AF_INET6)) {
17177f667e74Sjose borrego (void) memcpy(&dnsip, &res->ai_addr[0],
17187f667e74Sjose borrego salen);
17197f667e74Sjose borrego /* need compare macro here */
17207f667e74Sjose borrego if (!memcmp(&ipaddr, &dnsip,
17217f667e74Sjose borrego IN6ADDRSZ)) {
1722da6c28aaSamw *is_match = 1;
1723da6c28aaSamw break;
1724da6c28aaSamw }
1725da6c28aaSamw }
17267f667e74Sjose borrego } while (res->ai_next);
17277f667e74Sjose borrego freeaddrinfo(res);
1728da6c28aaSamw return (1);
1729da6c28aaSamw }
1730da6c28aaSamw } else {
173129bd2886SAlan Wright if (smb_getnameinfo(&ipaddr, dns_hostname, NI_MAXHOST, 0))
17327f667e74Sjose borrego return (NULL);
173329bd2886SAlan Wright
17347f667e74Sjose borrego if (strncasecmp(dns_hostname, hostname,
1735da6c28aaSamw strlen(hostname)) == 0) {
1736da6c28aaSamw *is_match = 1;
1737da6c28aaSamw }
1738da6c28aaSamw return (1);
1739da6c28aaSamw }
1740da6c28aaSamw
1741da6c28aaSamw /* entry does not exist */
1742da6c28aaSamw return (0);
1743da6c28aaSamw }
1744da6c28aaSamw
1745da6c28aaSamw /*
1746da6c28aaSamw * dyndns_add_remove_entry
1747faa1795aSjb150015 * Perform non-secure dynamic DNS update. If it fails and host principal
1748faa1795aSjb150015 * keys can be found in the local keytab file, secure update will be performed.
1749faa1795aSjb150015 *
1750da6c28aaSamw * This routine opens a UDP socket to the DNS sever, build the update request
1751da6c28aaSamw * message, and sends the message to the DNS server. The response is received
1752da6c28aaSamw * and check for error. If there is no error then the local NSS cached is
1753da6c28aaSamw * purged. DNS may be used to check to see if an entry already exist before
1754da6c28aaSamw * adding or to see if an entry does exist before removing it. Adding
1755da6c28aaSamw * duplicate entries or removing non-existing entries does not cause any
1756da6c28aaSamw * problems. DNS is not check when doing a delete all.
1757da6c28aaSamw * Parameters:
1758da6c28aaSamw * update_zone: UPDATE_FORW for forward zone, UPDATE_REV for reverse zone
1759da6c28aaSamw * hostname : fully qualified hostname
1760da6c28aaSamw * ip_addr : ip address of hostname in string format
1761da6c28aaSamw * life_time : cached time of this entry by others and not within DNS
1762da6c28aaSamw * database
1763da6c28aaSamw * update_type: UPDATE_ADD to add entry, UPDATE_DEL to remove entry
1764da6c28aaSamw * do_check : DNS_CHECK to check first in DNS, DNS_NOCHECK for no DNS
1765da6c28aaSamw * checking before update
1766da6c28aaSamw * del_type : DEL_ONE for deleting one entry, DEL_ALL for deleting all
1767da6c28aaSamw * entries of the same resource name. Only valid for UPDATE_DEL.
1768da6c28aaSamw * dns_str : DNS IP address in string format
1769da6c28aaSamw * Returns:
1770da6c28aaSamw * -1: error
1771da6c28aaSamw * 0: success
1772da6c28aaSamw *
1773da6c28aaSamw * This function is enhanced to handle the case of NOTAUTH error when DNS server
1774da6c28aaSamw * is not authoritative for specified zone. In this case we need to resend the
1775da6c28aaSamw * same request to the higher authoritative domains.
1776da6c28aaSamw * This is true for both secure and unsecure dynamic DNS updates.
1777da6c28aaSamw */
1778da6c28aaSamw 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)1779da6c28aaSamw dyndns_add_remove_entry(int update_zone, const char *hostname,
1780da6c28aaSamw const char *ip_addr, int life_time, int update_type,
1781da6c28aaSamw int do_check, int del_type, char *dns_str)
1782da6c28aaSamw {
1783da6c28aaSamw int s;
1784da6c28aaSamw uint16_t id, rid;
1785da6c28aaSamw char buf[NS_PACKETSZ], buf2[NS_PACKETSZ];
17867f667e74Sjose borrego int ret;
1787da6c28aaSamw int is_exist, is_match;
1788da6c28aaSamw struct timeval timeout;
1789da6c28aaSamw int buf_sz;
1790da6c28aaSamw int level = 0;
17917f667e74Sjose borrego smb_inaddr_t dns_ip;
1792148c5f43SAlan Wright char *fqdn;
1793148c5f43SAlan Wright char *p;
1794da6c28aaSamw
1795da6c28aaSamw assert(dns_str);
1796da6c28aaSamw assert(*dns_str);
1797da6c28aaSamw
1798da6c28aaSamw if (do_check == DNS_CHECK && del_type != DEL_ALL) {
1799da6c28aaSamw is_exist = dyndns_search_entry(update_zone, hostname, ip_addr,
1800da6c28aaSamw update_type, &timeout, &is_match);
1801da6c28aaSamw
1802da6c28aaSamw if (update_type == UPDATE_ADD && is_exist && is_match) {
1803da6c28aaSamw return (0);
1804da6c28aaSamw } else if (update_type == UPDATE_DEL && !is_exist) {
1805da6c28aaSamw return (0);
1806da6c28aaSamw }
1807da6c28aaSamw }
1808da6c28aaSamw
18097f667e74Sjose borrego if (inet_pton(AF_INET, dns_str, &dns_ip) == 1)
18107f667e74Sjose borrego dns_ip.a_family = AF_INET;
18117f667e74Sjose borrego else if (inet_pton(AF_INET6, dns_str, &dns_ip) == 1)
18127f667e74Sjose borrego dns_ip.a_family = AF_INET6;
18137f667e74Sjose borrego
1814da6c28aaSamw retry_higher:
18157f667e74Sjose borrego if ((s = dyndns_open_init_socket(SOCK_DGRAM, &dns_ip, 53)) < 0)
1816da6c28aaSamw return (-1);
1817da6c28aaSamw
1818da6c28aaSamw id = 0;
1819da6c28aaSamw if ((buf_sz = dyndns_build_add_remove_msg(buf, update_zone, hostname,
1820da6c28aaSamw ip_addr, life_time, update_type, del_type, 0, &id, level)) <= 0) {
1821da6c28aaSamw (void) close(s);
1822da6c28aaSamw return (-1);
1823da6c28aaSamw }
1824da6c28aaSamw
1825da6c28aaSamw if (dyndns_udp_send_recv(s, buf, buf_sz, buf2)) {
1826da6c28aaSamw (void) close(s);
1827da6c28aaSamw return (-1);
1828da6c28aaSamw }
1829da6c28aaSamw
1830da6c28aaSamw (void) close(s);
1831da6c28aaSamw
1832da6c28aaSamw ret = buf2[3] & 0xf; /* error field in UDP */
1833da6c28aaSamw
1834da6c28aaSamw /*
1835da6c28aaSamw * If it is a NOTAUTH error we should retry with higher domains
1836da6c28aaSamw * until we get a successful reply
1837da6c28aaSamw */
1838da6c28aaSamw if (ret == NOTAUTH && level++ < MAX_AUTH_RETRIES)
1839da6c28aaSamw goto retry_higher;
1840da6c28aaSamw
1841da6c28aaSamw /* check here for update request is successful */
1842da6c28aaSamw if (ret == NOERROR) {
1843da6c28aaSamw (void) dyndns_get_nshort(buf2, &rid);
1844da6c28aaSamw if (id != rid)
1845da6c28aaSamw return (-1);
1846da6c28aaSamw return (0);
1847da6c28aaSamw }
1848da6c28aaSamw
1849da6c28aaSamw if (ret == NOTIMP) {
18508d7e4166Sjose borrego dyndns_syslog(LOG_NOTICE, NOTIMP, "dynamic updates");
1851da6c28aaSamw return (-1);
1852da6c28aaSamw } else if (ret == NOTAUTH) {
18538d7e4166Sjose borrego dyndns_syslog(LOG_NOTICE, NOTAUTH, "DNS");
1854da6c28aaSamw return (-1);
1855da6c28aaSamw }
1856da6c28aaSamw
1857148c5f43SAlan Wright if ((p = strchr(hostname, '.')) == NULL)
1858148c5f43SAlan Wright return (-1);
1859148c5f43SAlan Wright
1860148c5f43SAlan Wright fqdn = ++p;
1861148c5f43SAlan Wright if (smb_krb5_kt_find(SMB_KRB5_PN_ID_HOST_FQHN, fqdn,
1862148c5f43SAlan Wright SMBNS_KRB5_KEYTAB)) {
1863da6c28aaSamw ret = dyndns_sec_add_remove_entry(update_zone, hostname,
1864da6c28aaSamw ip_addr, life_time, update_type, del_type, dns_str);
1865148c5f43SAlan Wright } else {
1866148c5f43SAlan Wright syslog(LOG_NOTICE, "dyndns: secure update failed: cannot find "
1867148c5f43SAlan Wright "host principal \"%s\" in local keytab file.", hostname);
1868148c5f43SAlan Wright }
1869da6c28aaSamw
1870da6c28aaSamw return (ret);
1871da6c28aaSamw }
1872da6c28aaSamw
1873da6c28aaSamw /*
1874da6c28aaSamw * dyndns_add_entry
1875da6c28aaSamw * Main routine to add an entry into DNS. The attempt will be made on the
1876da6c28aaSamw * the servers returned by smb_get_nameserver(). Upon a successful
1877da6c28aaSamw * attempt on any one of the server, the function will exit with 0.
1878da6c28aaSamw * Otherwise, -1 is retuned to indicate the update attempt on all the
1879da6c28aaSamw * nameservers has failed.
1880da6c28aaSamw *
1881da6c28aaSamw * Parameters:
1882da6c28aaSamw * update_zone: the type of zone to update, use UPDATE_FORW for forward
1883da6c28aaSamw * lookup zone, use UPDATE_REV for reverse lookup zone
1884da6c28aaSamw * hostname : fully qualified hostname
1885da6c28aaSamw * ip_addr : ip address of hostname in string format
1886da6c28aaSamw * life_time : cached time of this entry by others and not within DNS
1887da6c28aaSamw * database
1888da6c28aaSamw * Returns:
1889da6c28aaSamw * -1: error
1890da6c28aaSamw * 0: success
1891da6c28aaSamw */
1892da6c28aaSamw static int
dyndns_add_entry(int update_zone,const char * hostname,const char * ip_addr,int life_time)1893da6c28aaSamw dyndns_add_entry(int update_zone, const char *hostname, const char *ip_addr,
1894da6c28aaSamw int life_time)
1895da6c28aaSamw {
18967f667e74Sjose borrego const char *dns_str;
18978d7e4166Sjose borrego char *which_zone;
18987f667e74Sjose borrego smb_inaddr_t ns_list[MAXNS];
18997f667e74Sjose borrego char dns_buf[INET6_ADDRSTRLEN];
1900da6c28aaSamw int i, cnt;
19017f667e74Sjose borrego int rc = 0;
1902da6c28aaSamw
19037f667e74Sjose borrego if (hostname == NULL || ip_addr == NULL) {
1904da6c28aaSamw return (-1);
19057f667e74Sjose borrego }
19067f667e74Sjose borrego cnt = smb_get_nameservers(&ns_list[0], MAXNS);
1907da6c28aaSamw
1908da6c28aaSamw for (i = 0; i < cnt; i++) {
19097f667e74Sjose borrego dns_str = smb_inet_ntop(&ns_list[i], dns_buf,
19107f667e74Sjose borrego SMB_IPSTRLEN(ns_list[i].a_family));
19117f667e74Sjose borrego if (dns_str == NULL)
1912da6c28aaSamw continue;
1913da6c28aaSamw
19148d7e4166Sjose borrego which_zone = (update_zone == UPDATE_FORW) ?
19158d7e4166Sjose borrego "forward" : "reverse";
19168d7e4166Sjose borrego syslog(LOG_DEBUG, "dyndns %s lookup zone update %s (%s)",
19178d7e4166Sjose borrego which_zone, hostname, ip_addr);
19188d7e4166Sjose borrego
1919da6c28aaSamw if (dyndns_add_remove_entry(update_zone, hostname,
1920da6c28aaSamw ip_addr, life_time,
19217f667e74Sjose borrego UPDATE_ADD, DNS_NOCHECK, DEL_NONE, dns_buf) != -1) {
1922da6c28aaSamw rc = 1;
1923da6c28aaSamw break;
1924da6c28aaSamw }
1925da6c28aaSamw }
1926da6c28aaSamw
1927da6c28aaSamw return (rc ? 0 : -1);
1928da6c28aaSamw }
1929da6c28aaSamw
1930da6c28aaSamw /*
1931da6c28aaSamw * dyndns_remove_entry
1932da6c28aaSamw * Main routine to remove an entry or all entries of the same resource name
1933da6c28aaSamw * from DNS. The update attempt will be made on the primary DNS server. If
1934da6c28aaSamw * there is a failure then another attempt will be made on the secondary DNS
1935da6c28aaSamw * server.
1936da6c28aaSamw * Parameters:
1937da6c28aaSamw * update_zone: the type of zone to update, use UPDATE_FORW for forward
1938da6c28aaSamw * lookup zone, use UPDATE_REV for reverse lookup zone
1939da6c28aaSamw * hostname : fully qualified hostname
1940da6c28aaSamw * ip_addr : ip address of hostname in string format
1941da6c28aaSamw * del_type : DEL_ONE for deleting one entry, DEL_ALL for deleting all
1942da6c28aaSamw * entries of the same resource name. Only valid for UPDATE_DEL
1943da6c28aaSamw * Returns:
1944da6c28aaSamw * -1: error
1945da6c28aaSamw * 0: success
1946da6c28aaSamw */
1947da6c28aaSamw static int
dyndns_remove_entry(int update_zone,const char * hostname,const char * ip_addr,int del_type)1948da6c28aaSamw dyndns_remove_entry(int update_zone, const char *hostname, const char *ip_addr,
1949da6c28aaSamw int del_type)
1950da6c28aaSamw {
19517f667e74Sjose borrego const char *dns_str;
19527f667e74Sjose borrego smb_inaddr_t ns_list[MAXNS];
19537f667e74Sjose borrego char dns_buf[INET6_ADDRSTRLEN];
1954da6c28aaSamw int i, cnt, scnt;
1955da6c28aaSamw
1956da6c28aaSamw if ((hostname == NULL || ip_addr == NULL)) {
1957da6c28aaSamw return (-1);
1958da6c28aaSamw }
1959da6c28aaSamw cnt = smb_get_nameservers(ns_list, MAXNS);
1960da6c28aaSamw scnt = 0;
1961da6c28aaSamw for (i = 0; i < cnt; i++) {
19627f667e74Sjose borrego dns_str = smb_inet_ntop(&ns_list[i], dns_buf,
19637f667e74Sjose borrego SMB_IPSTRLEN(ns_list[i].a_family));
19647f667e74Sjose borrego if (dns_str == NULL)
1965da6c28aaSamw continue;
19667f667e74Sjose borrego if (update_zone == UPDATE_FORW) {
1967da6c28aaSamw if (del_type == DEL_ONE) {
19687f667e74Sjose borrego syslog(LOG_DEBUG, "Dynamic update "
19697f667e74Sjose borrego "on forward lookup "
19707f667e74Sjose borrego "zone for %s (%s)...\n", hostname, ip_addr);
1971da6c28aaSamw } else {
19727f667e74Sjose borrego syslog(LOG_DEBUG, "Removing all "
19737f667e74Sjose borrego "entries of %s "
19747f667e74Sjose borrego "in forward lookup zone...\n", hostname);
1975da6c28aaSamw }
19767f667e74Sjose borrego } else {
19777f667e74Sjose borrego if (del_type == DEL_ONE) {
19787f667e74Sjose borrego syslog(LOG_DEBUG, "Dynamic update "
19797f667e74Sjose borrego "on reverse lookup "
19807f667e74Sjose borrego "zone for %s (%s)...\n", hostname, ip_addr);
19817f667e74Sjose borrego } else {
19827f667e74Sjose borrego syslog(LOG_DEBUG, "Removing all "
19837f667e74Sjose borrego "entries of %s "
19847f667e74Sjose borrego "in reverse lookup zone...\n", ip_addr);
19857f667e74Sjose borrego }
19867f667e74Sjose borrego }
1987da6c28aaSamw if (dyndns_add_remove_entry(update_zone, hostname, ip_addr, 0,
19887f667e74Sjose borrego UPDATE_DEL, DNS_NOCHECK, del_type, dns_buf) != -1) {
1989da6c28aaSamw scnt++;
1990da6c28aaSamw break;
1991da6c28aaSamw }
1992da6c28aaSamw }
1993da6c28aaSamw if (scnt)
1994da6c28aaSamw return (0);
1995da6c28aaSamw return (-1);
1996da6c28aaSamw }
1997da6c28aaSamw
1998da6c28aaSamw /*
19998c10a865Sas200622 * dyndns_update_core
2000da6c28aaSamw * Perform dynamic update on both forward and reverse lookup zone using
2001faa1795aSjb150015 * the specified hostname and IP addresses. Before updating DNS, existing
2002faa1795aSjb150015 * host entries with the same hostname in the forward lookup zone are removed
2003da6c28aaSamw * and existing pointer entries with the same IP addresses in the reverse
2004da6c28aaSamw * lookup zone are removed. After DNS update, host entries for current
2005da6c28aaSamw * hostname will show current IP addresses and pointer entries for current
2006da6c28aaSamw * IP addresses will show current hostname.
2007da6c28aaSamw * Parameters:
20089fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States * fqdn - fully-qualified domain name (in lower case)
20098c10a865Sas200622 *
2010da6c28aaSamw * Returns:
2011da6c28aaSamw * -1: some dynamic DNS updates errors
20126537f381Sas200622 * 0: successful or DDNS disabled.
2013da6c28aaSamw */
20147f667e74Sjose borrego int
dyndns_update_core(char * fqdn)20158c10a865Sas200622 dyndns_update_core(char *fqdn)
2016da6c28aaSamw {
20177b59d02dSjb150015 int forw_update_ok, error;
20187f667e74Sjose borrego char my_ip[INET6_ADDRSTRLEN];
20197f667e74Sjose borrego const char *my_str;
20207b59d02dSjb150015 smb_niciter_t ni;
2021da6c28aaSamw int rc;
2022faa1795aSjb150015 char fqhn[MAXHOSTNAMELEN];
2023da6c28aaSamw
20248d7e4166Sjose borrego if (fqdn == NULL || *fqdn == '\0')
20258d7e4166Sjose borrego return (0);
20268d7e4166Sjose borrego
2027dc20a302Sas200622 if (!smb_config_getbool(SMB_CI_DYNDNS_ENABLE))
20286537f381Sas200622 return (0);
20299fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States if (smb_gethostname(fqhn, MAXHOSTNAMELEN, SMB_CASE_LOWER) != 0)
2030faa1795aSjb150015 return (-1);
2031faa1795aSjb150015
20329fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States /*
20339fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States * To comply with RFC 4120 section 6.2.1, the fully-qualified hostname
20349fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States * must be set to lower case.
20359fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States */
2036faa1795aSjb150015 (void) snprintf(fqhn, MAXHOSTNAMELEN, "%s.%s", fqhn, fqdn);
20379fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States
2038da6c28aaSamw error = 0;
2039da6c28aaSamw forw_update_ok = 0;
2040da6c28aaSamw
2041da6c28aaSamw /*
2042da6c28aaSamw * Dummy IP is okay since we are removing all using the hostname.
2043da6c28aaSamw */
2044faa1795aSjb150015 if (dyndns_remove_entry(UPDATE_FORW, fqhn, "1.1.1.1", DEL_ALL) == 0) {
2045da6c28aaSamw forw_update_ok = 1;
2046da6c28aaSamw } else {
2047da6c28aaSamw error++;
2048da6c28aaSamw }
2049da6c28aaSamw
20509fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States if (smb_nic_getfirst(&ni) != SMB_NIC_SUCCESS)
20517b59d02dSjb150015 return (-1);
20527b59d02dSjb150015
20537b59d02dSjb150015 do {
2054e11c3f44Smeem if (ni.ni_nic.nic_sysflags & IFF_PRIVATE)
2055da6c28aaSamw continue;
20567f667e74Sjose borrego /* first try ipv4, then ipv6 */
20577f667e74Sjose borrego my_str = smb_inet_ntop(&ni.ni_nic.nic_ip, my_ip,
20587f667e74Sjose borrego SMB_IPSTRLEN(ni.ni_nic.nic_ip.a_family));
20597f667e74Sjose borrego if (my_str == NULL) {
2060da6c28aaSamw error++;
2061da6c28aaSamw continue;
2062da6c28aaSamw }
2063da6c28aaSamw
2064da6c28aaSamw if (forw_update_ok) {
20657f667e74Sjose borrego rc = dyndns_add_entry(UPDATE_FORW, fqhn, my_str,
2066da6c28aaSamw DDNS_TTL);
2067da6c28aaSamw
2068da6c28aaSamw if (rc == -1)
2069da6c28aaSamw error++;
2070da6c28aaSamw }
2071da6c28aaSamw
20727f667e74Sjose borrego rc = dyndns_remove_entry(UPDATE_REV, fqhn, my_str, DEL_ALL);
2073da6c28aaSamw if (rc == 0) {
20747f667e74Sjose borrego rc = dyndns_add_entry(UPDATE_REV, fqhn, my_str,
2075da6c28aaSamw DDNS_TTL);
2076da6c28aaSamw }
2077da6c28aaSamw
2078da6c28aaSamw if (rc == -1)
2079da6c28aaSamw error++;
2080da6c28aaSamw
20819fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States } while (smb_nic_getnext(&ni) == SMB_NIC_SUCCESS);
2082da6c28aaSamw
2083da6c28aaSamw return ((error == 0) ? 0 : -1);
2084da6c28aaSamw }
2085da6c28aaSamw
2086da6c28aaSamw /*
2087da6c28aaSamw * dyndns_clear_rev_zone
2088da6c28aaSamw * Clear the rev zone records. Must be called to clear the OLD if list
2089da6c28aaSamw * of down records prior to updating the list with new information.
2090da6c28aaSamw *
2091da6c28aaSamw * Parameters:
20929fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States * fqdn - fully-qualified domain name (in lower case)
2093da6c28aaSamw * Returns:
2094da6c28aaSamw * -1: some dynamic DNS updates errors
20956537f381Sas200622 * 0: successful or DDNS disabled.
2096da6c28aaSamw */
20977f667e74Sjose borrego int
dyndns_clear_rev_zone(char * fqdn)2098faa1795aSjb150015 dyndns_clear_rev_zone(char *fqdn)
2099da6c28aaSamw {
21007b59d02dSjb150015 int error;
21017f667e74Sjose borrego char my_ip[INET6_ADDRSTRLEN];
21027b59d02dSjb150015 smb_niciter_t ni;
2103da6c28aaSamw int rc;
2104faa1795aSjb150015 char fqhn[MAXHOSTNAMELEN];
21057f667e74Sjose borrego const char *my_str;
2106da6c28aaSamw
2107dc20a302Sas200622 if (!smb_config_getbool(SMB_CI_DYNDNS_ENABLE))
21086537f381Sas200622 return (0);
2109da6c28aaSamw
21109fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States if (smb_gethostname(fqhn, MAXHOSTNAMELEN, SMB_CASE_LOWER) != 0)
2111da6c28aaSamw return (-1);
2112da6c28aaSamw
21139fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States /*
21149fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States * To comply with RFC 4120 section 6.2.1, the fully-qualified hostname
21159fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States * must be set to lower case.
21169fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States */
2117faa1795aSjb150015 (void) snprintf(fqhn, MAXHOSTNAMELEN, "%s.%s", fqhn, fqdn);
21189fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States
2119da6c28aaSamw error = 0;
2120da6c28aaSamw
21219fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States if (smb_nic_getfirst(&ni) != SMB_NIC_SUCCESS)
21227b59d02dSjb150015 return (-1);
21237b59d02dSjb150015
21247b59d02dSjb150015 do {
2125e11c3f44Smeem if (ni.ni_nic.nic_sysflags & IFF_PRIVATE)
2126da6c28aaSamw continue;
21277f667e74Sjose borrego my_str = smb_inet_ntop(&ni.ni_nic.nic_ip, my_ip,
21287f667e74Sjose borrego SMB_IPSTRLEN(ni.ni_nic.nic_ip.a_family));
21297f667e74Sjose borrego if (my_str == NULL) {
2130da6c28aaSamw error++;
2131da6c28aaSamw continue;
2132da6c28aaSamw }
2133da6c28aaSamw
2134faa1795aSjb150015 rc = dyndns_remove_entry(UPDATE_REV, fqhn, my_ip, DEL_ALL);
2135da6c28aaSamw if (rc != 0)
2136da6c28aaSamw error++;
2137da6c28aaSamw
21389fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States } while (smb_nic_getnext(&ni) == SMB_NIC_SUCCESS);
2139da6c28aaSamw
2140da6c28aaSamw return ((error == 0) ? 0 : -1);
2141da6c28aaSamw }
2142