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