4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
22 * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
30 #include <sys/types.h>
31 #include <sys/socket.h>
32 #include <netinet/in.h>
33 #include <arpa/inet.h>
34 #include <arpa/nameser.h>
44 #include <gssapi/gssapi.h>
45 #include <kerberosv5/krb5.h>
47 #include <smbns_dyndns.h>
48 #include <smbns_krb.h>
51 * The following can be removed once head/arpa/nameser_compat.h
52 * defines BADSIG, BADKEY and BADTIME.
55 #define BADSIG ns_r_badsig
59 #define BADKEY ns_r_badkey
63 #define BADTIME ns_r_badtime
66 /* internal use, in dyndns_add_entry */
69 /* Maximum retires if not authoritative */
70 #define MAX_AUTH_RETRIES 3
72 /* Number of times to retry a DNS query */
73 #define DYNDNS_MAX_QUERY_RETRIES 3
75 /* Timeout value, in seconds, for DNS query responses */
76 #define DYNDNS_QUERY_TIMEOUT 2
78 static uint16_t dns_msgid
;
79 mutex_t dns_msgid_mtx
;
81 #define DYNDNS_OP_CLEAR 1
82 #define DYNDNS_OP_UPDATE 2
84 #define DYNDNS_STATE_INIT 0
85 #define DYNDNS_STATE_READY 1
86 #define DYNDNS_STATE_PUBLISHING 2
87 #define DYNDNS_STATE_STOPPING 3
89 typedef struct dyndns_qentry
{
92 /* fully-qualified domain name is in lower case */
93 char dqe_fqdn
[MAXHOSTNAMELEN
];
96 typedef struct dyndns_queue
{
103 static dyndns_queue_t dyndns_queue
;
105 static void dyndns_queue_request(int, const char *);
106 static void dyndns_queue_flush(list_t
*);
107 static void dyndns_process(list_t
*);
108 static int dyndns_update_core(char *);
109 static int dyndns_clear_rev_zone(char *);
110 static void dyndns_msgid_init(void);
111 static int dyndns_get_msgid(void);
112 static void dyndns_syslog(int, int, const char *);
117 (void) mutex_lock(&dyndns_queue
.ddq_mtx
);
119 if (dyndns_queue
.ddq_state
!= DYNDNS_STATE_INIT
) {
120 (void) mutex_unlock(&dyndns_queue
.ddq_mtx
);
126 list_create(&dyndns_queue
.ddq_list
, sizeof (dyndns_qentry_t
),
127 offsetof(dyndns_qentry_t
, dqe_lnd
));
128 dyndns_queue
.ddq_state
= DYNDNS_STATE_READY
;
130 (void) mutex_unlock(&dyndns_queue
.ddq_mtx
);
136 (void) mutex_lock(&dyndns_queue
.ddq_mtx
);
138 switch (dyndns_queue
.ddq_state
) {
139 case DYNDNS_STATE_READY
:
140 case DYNDNS_STATE_PUBLISHING
:
141 dyndns_queue
.ddq_state
= DYNDNS_STATE_STOPPING
;
142 (void) cond_signal(&dyndns_queue
.ddq_cv
);
148 (void) mutex_unlock(&dyndns_queue
.ddq_mtx
);
152 * Clear all records in both zones.
155 dyndns_clear_zones(void)
157 char fqdn
[MAXHOSTNAMELEN
];
159 if (smb_getfqdomainname(fqdn
, MAXHOSTNAMELEN
) != 0) {
160 syslog(LOG_ERR
, "dyndns: failed to get domainname");
164 dyndns_queue_request(DYNDNS_OP_CLEAR
, fqdn
);
168 * Update all records in both zones.
171 dyndns_update_zones(void)
173 char fqdn
[MAXHOSTNAMELEN
];
175 if (smb_getfqdomainname(fqdn
, MAXHOSTNAMELEN
) != 0) {
176 syslog(LOG_ERR
, "dyndns: failed to get domainname");
180 dyndns_queue_request(DYNDNS_OP_UPDATE
, fqdn
);
184 * Add a request to the queue.
186 * To comply with RFC 4120 section 6.2.1, entry->dqe_fqdn is converted
190 dyndns_queue_request(int op
, const char *fqdn
)
192 dyndns_qentry_t
*entry
;
194 if (!smb_config_getbool(SMB_CI_DYNDNS_ENABLE
))
197 if ((entry
= malloc(sizeof (dyndns_qentry_t
))) == NULL
)
200 bzero(entry
, sizeof (dyndns_qentry_t
));
202 (void) strlcpy(entry
->dqe_fqdn
, fqdn
, MAXNAMELEN
);
203 (void) smb_strlwr(entry
->dqe_fqdn
);
205 (void) mutex_lock(&dyndns_queue
.ddq_mtx
);
207 switch (dyndns_queue
.ddq_state
) {
208 case DYNDNS_STATE_READY
:
209 case DYNDNS_STATE_PUBLISHING
:
210 list_insert_tail(&dyndns_queue
.ddq_list
, entry
);
211 (void) cond_signal(&dyndns_queue
.ddq_cv
);
218 (void) mutex_unlock(&dyndns_queue
.ddq_mtx
);
222 * Flush all remaining items from the specified list/queue.
225 dyndns_queue_flush(list_t
*lst
)
227 dyndns_qentry_t
*entry
;
229 while ((entry
= list_head(lst
)) != NULL
) {
230 list_remove(lst
, entry
);
236 * Dyndns update thread. While running, the thread waits on a condition
237 * variable until notified that an entry needs to be updated.
239 * If the outgoing queue is not empty, the thread wakes up every 60 seconds
244 dyndns_publisher(void *arg
)
246 dyndns_qentry_t
*entry
;
249 (void) mutex_lock(&dyndns_queue
.ddq_mtx
);
250 if (dyndns_queue
.ddq_state
!= DYNDNS_STATE_READY
) {
251 (void) mutex_unlock(&dyndns_queue
.ddq_mtx
);
254 dyndns_queue
.ddq_state
= DYNDNS_STATE_PUBLISHING
;
255 (void) mutex_unlock(&dyndns_queue
.ddq_mtx
);
257 list_create(&publist
, sizeof (dyndns_qentry_t
),
258 offsetof(dyndns_qentry_t
, dqe_lnd
));
261 (void) mutex_lock(&dyndns_queue
.ddq_mtx
);
263 while (list_is_empty(&dyndns_queue
.ddq_list
) &&
264 (dyndns_queue
.ddq_state
== DYNDNS_STATE_PUBLISHING
)) {
265 (void) cond_wait(&dyndns_queue
.ddq_cv
,
266 &dyndns_queue
.ddq_mtx
);
269 if (dyndns_queue
.ddq_state
!= DYNDNS_STATE_PUBLISHING
) {
270 (void) mutex_unlock(&dyndns_queue
.ddq_mtx
);
275 * Transfer queued items to the local list so that
276 * the mutex can be released.
278 while ((entry
= list_head(&dyndns_queue
.ddq_list
)) != NULL
) {
279 list_remove(&dyndns_queue
.ddq_list
, entry
);
280 list_insert_tail(&publist
, entry
);
283 (void) mutex_unlock(&dyndns_queue
.ddq_mtx
);
285 dyndns_process(&publist
);
288 (void) mutex_lock(&dyndns_queue
.ddq_mtx
);
289 dyndns_queue_flush(&dyndns_queue
.ddq_list
);
290 list_destroy(&dyndns_queue
.ddq_list
);
291 dyndns_queue
.ddq_state
= DYNDNS_STATE_INIT
;
292 (void) mutex_unlock(&dyndns_queue
.ddq_mtx
);
294 dyndns_queue_flush(&publist
);
295 list_destroy(&publist
);
300 * Remove items from the queue and process them.
303 dyndns_process(list_t
*publist
)
305 dyndns_qentry_t
*entry
;
307 while ((entry
= list_head(publist
)) != NULL
) {
308 (void) mutex_lock(&dyndns_queue
.ddq_mtx
);
309 if (dyndns_queue
.ddq_state
!= DYNDNS_STATE_PUBLISHING
) {
310 (void) mutex_unlock(&dyndns_queue
.ddq_mtx
);
311 dyndns_queue_flush(publist
);
314 (void) mutex_unlock(&dyndns_queue
.ddq_mtx
);
316 list_remove(publist
, entry
);
318 switch (entry
->dqe_op
) {
319 case DYNDNS_OP_CLEAR
:
320 (void) dyndns_clear_rev_zone(entry
->dqe_fqdn
);
322 case DYNDNS_OP_UPDATE
:
323 (void) dyndns_update_core(entry
->dqe_fqdn
);
334 * Dynamic DNS update API for kclient.
336 * Returns 0 upon success. Otherwise, returns -1.
339 dyndns_update(char *fqdn
)
343 if (smb_nic_init() != SMB_NIC_SUCCESS
)
347 (void) smb_strlwr(fqdn
);
348 rc
= dyndns_update_core(fqdn
);
354 * Initializes the DNS message ID counter using the algorithm
355 * that resolver library uses to initialize the ID field of any res
359 dyndns_msgid_init(void)
363 (void) gettimeofday(&now
, NULL
);
364 (void) mutex_lock(&dns_msgid_mtx
);
365 dns_msgid
= (0xffff & (now
.tv_sec
^ now
.tv_usec
^ getpid()));
366 (void) mutex_unlock(&dns_msgid_mtx
);
370 dyndns_get_msgid(void)
374 (void) mutex_lock(&dns_msgid_mtx
);
376 (void) mutex_unlock(&dns_msgid_mtx
);
381 * Log a DNS error message
384 dyndns_syslog(int severity
, int errnum
, const char *text
)
390 { FORMERR
, "message format error" },
391 { SERVFAIL
, "server internal error" },
392 { NXDOMAIN
, "entry should exist but does not exist" },
393 { NOTIMP
, "not supported" },
394 { REFUSED
, "operation refused" },
395 { YXDOMAIN
, "entry should not exist but does exist" },
396 { YXRRSET
, "RRSet should not exist but does exist" },
397 { NXRRSET
, "RRSet should exist but does not exist" },
398 { NOTAUTH
, "server is not authoritative for specified zone" },
399 { NOTZONE
, "name not within specified zone" },
400 { BADSIG
, "bad transaction signature (TSIG)" },
401 { BADKEY
, "bad transaction key (TKEY)" },
402 { BADTIME
, "time not synchronized" },
405 char *errmsg
= "unknown error";
408 if (errnum
== NOERROR
)
411 for (i
= 0; i
< (sizeof (errtab
) / sizeof (errtab
[0])); ++i
) {
412 if (errtab
[i
].errnum
== errnum
) {
413 errmsg
= errtab
[i
].errmsg
;
418 syslog(severity
, "dyndns: %s: %s: %d", text
, errmsg
, errnum
);
423 * Display GSS error message from error code. This routine is used to display
424 * the mechanism independent and mechanism specific error messages for GSS
425 * routines. The major status error code is the mechanism independent error
426 * code and the minor status error code is the mechanism specific error code.
428 * maj: GSS major status
429 * min: GSS minor status
434 display_stat(OM_uint32 maj
, OM_uint32 min
)
437 OM_uint32 msg_ctx
= 0;
440 (void) gss_display_status(&min2
, maj
, GSS_C_GSS_CODE
, GSS_C_NULL_OID
,
442 syslog(LOG_ERR
, "dyndns: GSS major status error: %s",
444 (void) gss_release_buffer(&min2
, &msg
);
446 (void) gss_display_status(&min2
, min
, GSS_C_MECH_CODE
, GSS_C_NULL_OID
,
448 syslog(LOG_ERR
, "dyndns: GSS minor status error: %s",
450 (void) gss_release_buffer(&min2
, &msg
);
454 dyndns_put_nshort(char *buf
, uint16_t val
)
459 (void) memcpy(buf
, &nval
, sizeof (uint16_t));
460 buf
+= sizeof (uint16_t);
465 dyndns_get_nshort(char *buf
, uint16_t *val
)
469 (void) memcpy(&nval
, buf
, sizeof (uint16_t));
471 buf
+= sizeof (uint16_t);
476 dyndns_put_nlong(char *buf
, uint32_t val
)
481 (void) memcpy(buf
, &lval
, sizeof (uint32_t));
482 buf
+= sizeof (uint32_t);
487 dyndns_put_byte(char *buf
, char val
)
498 dyndns_put_int(char *buf
, int val
)
500 (void) memcpy(buf
, &val
, sizeof (int));
506 dyndns_put_v6addr(char *buf
, smb_inaddr_t
*val
)
509 val
->a_family
= AF_INET6
;
510 (void) memcpy(buf
, &val
->a_ipv6
, IN6ADDRSZ
);
516 * Converts a domain string by removing periods and replacing with a byte value
517 * of how many characters following period. A byte value is placed in front
518 * to indicate how many characters before first period. A NULL character is
519 * placed at the end. i.e. host.procom.com -> 4host5procom3com0
520 * Buffer space checking is done by caller.
522 * ptr : address of pointer to buffer to store converted string
523 * zone: domain name string
525 * ptr: address of pointer to next available buffer space
530 dyndns_stuff_str(char **ptr
, char *zone
)
533 char *lenPtr
, *zonePtr
;
535 for (zonePtr
= zone
; *zonePtr
; ) {
539 while (*zonePtr
!= '.' && *zonePtr
!= 0) {
540 *ptr
= dyndns_put_byte(*ptr
, *zonePtr
);
548 *ptr
= dyndns_put_byte(*ptr
, 0);
553 * dyndns_build_header
554 * Build the header for DNS query and DNS update request message.
556 * ptr : address of pointer to buffer to store header
557 * buf_len : buffer length
558 * msg_id : message id
559 * query_req : use REQ_QUERY for query message or REQ_UPDATE for
561 * quest_zone_cnt : number of question record for query message or
562 * number of zone record for update message
563 * ans_prereq_cnt : number of answer record for query message or
564 * number of prerequisite record for update message
565 * nameser_update_cnt: number of name server for query message or
566 * number of update record for update message
567 * addit_cnt : number of additional record
568 * flags : query flags word
570 * ptr: address of pointer to next available buffer space
575 dyndns_build_header(char **ptr
, int buf_len
, uint16_t msg_id
, int query_req
,
576 uint16_t quest_zone_cnt
, uint16_t ans_prereq_cnt
,
577 uint16_t nameser_update_cnt
, uint16_t addit_cnt
, int flags
)
582 syslog(LOG_ERR
, "dyndns header section: buffer too small");
586 *ptr
= dyndns_put_nshort(*ptr
, msg_id
); /* mesg ID */
587 if (query_req
== REQ_QUERY
)
588 opcode
= ns_o_query
; /* query msg */
590 opcode
= ns_o_update
<< 11; /* update msg */
593 *ptr
= dyndns_put_nshort(*ptr
, opcode
);
594 /* zone record count */
595 *ptr
= dyndns_put_nshort(*ptr
, quest_zone_cnt
);
596 /* prerequiste record count */
597 *ptr
= dyndns_put_nshort(*ptr
, ans_prereq_cnt
);
598 /* update record count */
599 *ptr
= dyndns_put_nshort(*ptr
, nameser_update_cnt
);
600 /* additional record count */
601 *ptr
= dyndns_put_nshort(*ptr
, addit_cnt
);
607 * dyndns_build_quest_zone
608 * Build the question section for query message or zone section for
611 * ptr : address of pointer to buffer to store question or zone section
612 * buf_len: buffer length
613 * name : question or zone name
614 * type : type of question or zone
615 * class : class of question or zone
617 * ptr: address of pointer to next available buffer space
622 dyndns_build_quest_zone(char **ptr
, int buf_len
, char *name
, int type
,
627 if ((strlen(name
) + 6) > buf_len
) {
628 syslog(LOG_ERR
, "dyndns question section: buffer too small");
633 (void) dyndns_stuff_str(&zonePtr
, name
);
635 *ptr
= dyndns_put_nshort(*ptr
, type
);
636 *ptr
= dyndns_put_nshort(*ptr
, class);
641 * dyndns_build_update
642 * Build update section of update message for adding and removing a record.
643 * If the ttl value is 0 then this message is for record deletion.
646 * ptr : address of pointer to buffer to store update section
647 * buf_len : buffer length
648 * name : resource name of this record
649 * type : type of this record
650 * class : class of this record
651 * ttl : time-to-live, cached time of this entry by others and not
652 * within DNS database, a zero value for record(s) deletion
653 * data : data of this resource record
654 * forw_rev: UPDATE_FORW for forward zone, UPDATE_REV for reverse zone
655 * add_del : UPDATE_ADD for adding entry, UPDATE_DEL for removing zone
656 * del_type: DEL_ONE for deleting one entry, DEL_ALL for deleting all
657 * entries of the same resource name. Only valid for UPDATE_DEL.
659 * ptr: address of pointer to next available buffer space
664 dyndns_build_update(char **ptr
, int buf_len
, char *name
, int type
, int class,
665 uint32_t ttl
, char *data
, int forw_rev
, int add_del
, int del_type
)
668 int rec_len
, data_len
;
672 rec_len
= strlen(name
) + 10;
673 if (inet_pton(AF_INET
, data
, &ipaddr
) == 1)
675 else if (inet_pton(AF_INET6
, data
, &ipaddr
) == 1)
678 if (add_del
== UPDATE_ADD
) {
679 if (forw_rev
== UPDATE_FORW
)
680 data_len
= isv4
? 4 : 16;
682 data_len
= strlen(data
) + 2;
684 if (del_type
== DEL_ALL
)
686 else if (forw_rev
== UPDATE_FORW
)
687 data_len
= isv4
? 4 : 16;
689 data_len
= strlen(data
) + 2;
691 if (rec_len
+ data_len
> buf_len
) {
692 syslog(LOG_ERR
, "dyndns update section: buffer too small");
697 (void) dyndns_stuff_str(&namePtr
, name
);
700 *ptr
= dyndns_put_nshort(*ptr
, type
);
702 *ptr
= dyndns_put_nshort(*ptr
, ns_t_aaaa
);
703 *ptr
= dyndns_put_nshort(*ptr
, class);
704 *ptr
= dyndns_put_nlong(*ptr
, ttl
);
706 if (add_del
== UPDATE_DEL
&& del_type
== DEL_ALL
) {
707 *ptr
= dyndns_put_nshort(*ptr
, 0);
711 if (forw_rev
== UPDATE_FORW
) {
713 *ptr
= dyndns_put_nshort(*ptr
, 4);
714 *ptr
= dyndns_put_int(*ptr
, ipaddr
.a_ipv4
);
716 *ptr
= dyndns_put_nshort(*ptr
, 16);
717 *ptr
= dyndns_put_v6addr(*ptr
, &ipaddr
);
720 *ptr
= dyndns_put_nshort(*ptr
, strlen(data
)+2);
722 (void) dyndns_stuff_str(&namePtr
, data
); /* hostname */
730 * Build TKEY section to establish security context for secure dynamic DNS
731 * update. DNS header and question sections need to be build before this
732 * section. The TKEY data are the tokens generated during security context
733 * establishment and the TKEY message is used to transmit those tokens, one
734 * at a time, to the DNS server.
736 * ptr : address of pointer to buffer to store TKEY
737 * buf_len : buffer length
738 * name : key name, must be unique and same as for TSIG record
739 * key_expire: expiration time of this key in second
741 * data_size : data size
743 * ptr: address of the pointer to the next available buffer space
748 dyndns_build_tkey(char **ptr
, int buf_len
, char *name
, int key_expire
,
749 char *data
, int data_size
)
754 if (strlen(name
)+2 + 45 + data_size
> buf_len
) {
755 syslog(LOG_ERR
, "dyndns TKEY: buffer too small");
760 (void) dyndns_stuff_str(&namePtr
, name
); /* unique global name */
762 *ptr
= dyndns_put_nshort(*ptr
, ns_t_tkey
);
763 *ptr
= dyndns_put_nshort(*ptr
, ns_c_any
);
764 *ptr
= dyndns_put_nlong(*ptr
, 0);
765 /* 19 + 14 + data_size + 2 */
766 *ptr
= dyndns_put_nshort(*ptr
, 35 + data_size
);
768 (void) dyndns_stuff_str(&namePtr
, "gss.microsoft.com");
770 (void) gettimeofday(&tp
, 0);
771 *ptr
= dyndns_put_nlong(*ptr
, tp
.tv_sec
); /* inception */
772 /* expiration, 86400 */
773 *ptr
= dyndns_put_nlong(*ptr
, tp
.tv_sec
+ key_expire
);
774 *ptr
= dyndns_put_nshort(*ptr
, MODE_GSS_API
); /* mode: gss-api */
775 *ptr
= dyndns_put_nshort(*ptr
, 0); /* error */
776 *ptr
= dyndns_put_nshort(*ptr
, data_size
); /* key size */
777 (void) memcpy(*ptr
, data
, data_size
); /* key data */
779 *ptr
= dyndns_put_nshort(*ptr
, 0); /* other */
785 * Build TSIG section for secure dynamic DNS update. This routine will be
786 * called twice. First called with TSIG_UNSIGNED, and second with TSIG_SIGNED.
787 * The TSIG data is NULL and ignored for TSIG_UNSIGNED and is the update request
788 * message encrypted for TSIG_SIGNED. The message id must be the same id as the
789 * one in the update request before it is encrypted.
791 * ptr : address of pointer to buffer to store TSIG
792 * buf_len : buffer length
793 * msg_id : message id
794 * name : key name, must be the same as in TKEY record
795 * fudge_time : amount of error time allow in seconds
796 * data : TSIG data if TSIG_SIGNED, otherwise NULL
797 * data_size : size of data, otherwise 0 if data is NULL
798 * data_signed: TSIG_SIGNED to indicate data is signed and encrypted,
799 * otherwise TSIG_UNSIGNED
801 * ptr: address of pointer to next available buffer space
806 dyndns_build_tsig(char **ptr
, int buf_len
, int msg_id
, char *name
,
807 int fudge_time
, char *data
, int data_size
, int data_signed
)
811 int signtime
, fudge
, rec_len
;
813 if (data_signed
== TSIG_UNSIGNED
)
814 rec_len
= strlen(name
)+2 + 37;
816 rec_len
= strlen(name
)+2 + 45 + data_size
;
818 if (rec_len
> buf_len
) {
819 syslog(LOG_ERR
, "dyndns TSIG: buffer too small");
824 (void) dyndns_stuff_str(&namePtr
, name
); /* unique global name */
826 if (data_signed
== TSIG_SIGNED
)
827 *ptr
= dyndns_put_nshort(*ptr
, ns_t_tsig
);
828 *ptr
= dyndns_put_nshort(*ptr
, ns_c_any
);
829 *ptr
= dyndns_put_nlong(*ptr
, 0);
830 if (data_signed
== TSIG_SIGNED
) {
831 /* 19 + 10 + data_size + 6 */
832 *ptr
= dyndns_put_nshort(*ptr
, 35 + data_size
);
835 (void) dyndns_stuff_str(&namePtr
, "gss.microsoft.com");
837 (void) gettimeofday(&tp
, 0);
838 signtime
= tp
.tv_sec
>> 16;
839 *ptr
= dyndns_put_nlong(*ptr
, signtime
); /* sign time */
840 fudge
= tp
.tv_sec
<< 16;
842 *ptr
= dyndns_put_nlong(*ptr
, fudge
); /* fudge time */
843 if (data_signed
== TSIG_SIGNED
) {
844 /* signed data size */
845 *ptr
= dyndns_put_nshort(*ptr
, data_size
);
846 (void) memcpy(*ptr
, data
, data_size
); /* signed data */
848 *ptr
= dyndns_put_nshort(*ptr
, msg_id
); /* original id */
850 *ptr
= dyndns_put_nshort(*ptr
, 0); /* error */
851 *ptr
= dyndns_put_nshort(*ptr
, 0); /* other */
856 * dyndns_open_init_socket
857 * This routine creates a SOCK_STREAM or SOCK_DGRAM socket and initializes it
858 * by doing bind() and setting linger option to off.
861 * sock_type: SOCK_STREAM for TCP or SOCK_DGRAM for UDP
862 * dest_addr: destination address in network byte order
863 * port : destination port number
865 * descriptor: descriptor referencing the created socket
870 dyndns_open_init_socket(int sock_type
, smb_inaddr_t
*dest_addr
, int port
)
873 struct sockaddr_in my_addr
;
874 struct sockaddr_in6 my6_addr
;
875 struct sockaddr_in serv_addr
;
876 struct sockaddr_in6 serv6_addr
;
879 family
= dest_addr
->a_family
;
881 if ((s
= socket(family
, sock_type
, 0)) == -1) {
882 syslog(LOG_ERR
, "dyndns: socket error\n");
885 if (family
== AF_INET
) {
886 bzero(&my_addr
, sizeof (my_addr
));
887 my_addr
.sin_family
= family
;
888 my_addr
.sin_port
= htons(0);
889 my_addr
.sin_addr
.s_addr
= htonl(INADDR_ANY
);
890 if (bind(s
, (struct sockaddr
*)&my_addr
,
891 sizeof (my_addr
)) < 0) {
892 syslog(LOG_ERR
, "dyndns: client bind err\n");
896 serv_addr
.sin_family
= family
;
897 serv_addr
.sin_port
= htons(port
);
898 serv_addr
.sin_addr
.s_addr
= dest_addr
->a_ipv4
;
899 if (connect(s
, (struct sockaddr
*)&serv_addr
,
900 sizeof (struct sockaddr_in
)) < 0) {
901 syslog(LOG_ERR
, "dyndns: client connect (%s)\n",
907 bzero(&my6_addr
, sizeof (my6_addr
));
908 my6_addr
.sin6_family
= family
;
909 my6_addr
.sin6_port
= htons(0);
910 bzero(&my6_addr
.sin6_addr
.s6_addr
, IN6ADDRSZ
);
911 if (bind(s
, (struct sockaddr
*)&my6_addr
,
912 sizeof (my6_addr
)) < 0) {
913 syslog(LOG_ERR
, "dyndns: client bind err\n");
917 serv6_addr
.sin6_family
= family
;
918 serv6_addr
.sin6_port
= htons(port
);
919 bcopy(&serv6_addr
.sin6_addr
.s6_addr
, &dest_addr
->a_ipv6
,
921 if (connect(s
, (struct sockaddr
*)&serv6_addr
,
922 sizeof (struct sockaddr_in6
)) < 0) {
923 syslog(LOG_ERR
, "dyndns: client connect err (%s)\n",
932 * dyndns_build_tkey_msg
933 * This routine is used to build the TKEY message to transmit GSS tokens
934 * during GSS security context establishment for secure DNS update. The
935 * TKEY message format uses the DNS query message format. The TKEY section
936 * is the answer section of the query message format.
937 * Microsoft uses a value of 86400 seconds (24 hours) for key expiration time.
939 * buf : buffer to build and store TKEY message
940 * key_name: a unique key name, this same key name must be also be used in
942 * out_tok : TKEY message data (GSS tokens)
944 * id : message id of this TKEY message
945 * message size: the size of the TKEY message
949 dyndns_build_tkey_msg(char *buf
, char *key_name
, uint16_t *id
,
950 gss_buffer_desc
*out_tok
)
952 int queryReq
, zoneCount
, preqCount
, updateCount
, additionalCount
;
953 int zoneType
, zoneClass
;
956 queryReq
= REQ_QUERY
;
957 /* query section of query request */
959 /* answer section of query request */
964 (void) memset(buf
, 0, MAX_TCP_SIZE
);
966 *id
= dyndns_get_msgid();
968 /* add TCP length info that follows this field */
969 bufptr
= dyndns_put_nshort(bufptr
,
970 26 + (strlen(key_name
)+2)*2 + 35 + out_tok
->length
);
972 if (dyndns_build_header(&bufptr
, BUFLEN_TCP(bufptr
, buf
), *id
, queryReq
,
973 zoneCount
, preqCount
, updateCount
, additionalCount
, 0) == -1) {
977 zoneType
= ns_t_tkey
;
979 if (dyndns_build_quest_zone(&bufptr
, BUFLEN_TCP(bufptr
, buf
), key_name
,
980 zoneType
, zoneClass
) == -1) {
984 if (dyndns_build_tkey(&bufptr
, BUFLEN_TCP(bufptr
, buf
), key_name
,
985 86400, out_tok
->value
, out_tok
->length
) == -1) {
989 return (bufptr
- buf
);
993 * dyndns_establish_sec_ctx
994 * This routine is used to establish a security context with the DNS server
995 * by building TKEY messages and sending them to the DNS server. TKEY messages
996 * are also received from the DNS server for processing. The security context
997 * establishment is done with the GSS client on the system producing a token
998 * and sending the token within the TKEY message to the GSS server on the DNS
999 * server. The GSS server then processes the token and then send a TKEY reply
1000 * message with a new token to be processed by the GSS client. The GSS client
1001 * processes the new token and then generates a new token to be sent to the
1002 * GSS server. This cycle is continued until the security establishment is
1003 * done. TCP is used to send and receive TKEY messages.
1005 * cred_handle : handle to credential
1006 * s : socket descriptor to DNS server
1007 * key_name : TKEY key name
1008 * dns_hostname: fully qualified DNS hostname which will be used for
1009 * constructing the DNS service principal name.
1010 * oid : contains Kerberos 5 object identifier
1012 * gss_context : handle to security context
1015 dyndns_establish_sec_ctx(gss_ctx_id_t
*gss_context
, gss_cred_id_t cred_handle
,
1016 int s
, char *key_name
, char *dns_hostname
, gss_OID oid
)
1018 uint16_t id
, rid
, rsz
;
1019 char buf
[MAX_TCP_SIZE
], buf2
[MAX_TCP_SIZE
];
1021 char *service_name
, *tmpptr
;
1023 OM_uint32 min
, maj
, time_rec
;
1024 gss_buffer_desc service_buf
, in_tok
, out_tok
;
1025 gss_name_t target_name
;
1026 gss_buffer_desc
*inputptr
;
1028 OM_uint32 ret_flags
;
1031 service_sz
= strlen(dns_hostname
) + 5;
1032 service_name
= (char *)malloc(sizeof (char) * service_sz
);
1033 if (service_name
== NULL
)
1036 (void) snprintf(service_name
, service_sz
, "DNS@%s", dns_hostname
);
1037 service_buf
.value
= service_name
;
1038 service_buf
.length
= strlen(service_name
)+1;
1039 if ((maj
= gss_import_name(&min
, &service_buf
,
1040 GSS_C_NT_HOSTBASED_SERVICE
, &target_name
)) != GSS_S_COMPLETE
) {
1041 display_stat(maj
, min
);
1042 (void) free(service_name
);
1045 (void) free(service_name
);
1047 inputptr
= GSS_C_NO_BUFFER
;
1048 *gss_context
= GSS_C_NO_CONTEXT
;
1049 gss_flags
= GSS_C_MUTUAL_FLAG
| GSS_C_DELEG_FLAG
| GSS_C_REPLAY_FLAG
|
1050 GSS_C_SEQUENCE_FLAG
| GSS_C_CONF_FLAG
| GSS_C_INTEG_FLAG
;
1052 maj
= gss_init_sec_context(&min
, cred_handle
, gss_context
,
1053 target_name
, oid
, gss_flags
, 0, NULL
, inputptr
, NULL
,
1054 &out_tok
, &ret_flags
, &time_rec
);
1056 if (maj
!= GSS_S_COMPLETE
&& maj
!= GSS_S_CONTINUE_NEEDED
) {
1057 assert(gss_context
);
1058 if (*gss_context
!= GSS_C_NO_CONTEXT
)
1059 (void) gss_delete_sec_context(&min
,
1062 display_stat(maj
, min
);
1063 (void) gss_release_name(&min
, &target_name
);
1067 if ((maj
== GSS_S_COMPLETE
) &&
1068 !(ret_flags
& GSS_C_REPLAY_FLAG
)) {
1069 syslog(LOG_ERR
, "dyndns: No GSS_C_REPLAY_FLAG");
1070 if (out_tok
.length
> 0)
1071 (void) gss_release_buffer(&min
, &out_tok
);
1072 (void) gss_release_name(&min
, &target_name
);
1076 if ((maj
== GSS_S_COMPLETE
) &&
1077 !(ret_flags
& GSS_C_MUTUAL_FLAG
)) {
1078 syslog(LOG_ERR
, "dyndns: No GSS_C_MUTUAL_FLAG");
1079 if (out_tok
.length
> 0)
1080 (void) gss_release_buffer(&min
, &out_tok
);
1081 (void) gss_release_name(&min
, &target_name
);
1085 if (out_tok
.length
> 0) {
1086 if ((buf_sz
= dyndns_build_tkey_msg(buf
, key_name
,
1087 &id
, &out_tok
)) <= 0) {
1088 (void) gss_release_buffer(&min
, &out_tok
);
1089 (void) gss_release_name(&min
, &target_name
);
1093 (void) gss_release_buffer(&min
, &out_tok
);
1095 if (send(s
, buf
, buf_sz
, 0) == -1) {
1096 syslog(LOG_ERR
, "dyndns: TKEY send error");
1097 (void) gss_release_name(&min
, &target_name
);
1101 bzero(buf2
, MAX_TCP_SIZE
);
1102 if (recv(s
, buf2
, MAX_TCP_SIZE
, 0) == -1) {
1103 syslog(LOG_ERR
, "dyndns: TKEY recv error");
1104 (void) gss_release_name(&min
, &target_name
);
1108 ret
= buf2
[5] & 0xf; /* error field in TCP */
1109 if (ret
!= NOERROR
) {
1110 dyndns_syslog(LOG_ERR
, ret
, "TKEY reply");
1111 (void) gss_release_name(&min
, &target_name
);
1116 (void) dyndns_get_nshort(tmpptr
, &rid
);
1118 (void) gss_release_name(&min
, &target_name
);
1122 tmpptr
= &buf2
[59+(strlen(key_name
)+2)*2];
1123 (void) dyndns_get_nshort(tmpptr
, &rsz
);
1124 in_tok
.length
= rsz
;
1126 /* bsd38 -> 2*7=14 */
1127 in_tok
.value
= &buf2
[61+(strlen(key_name
)+2)*2];
1131 } while (maj
!= GSS_S_COMPLETE
);
1133 (void) gss_release_name(&min
, &target_name
);
1139 * dyndns_get_sec_context
1140 * Get security context for secure dynamic DNS update. This routine opens
1141 * a TCP socket to the DNS server and establishes a security context with
1142 * the DNS server using host principal to perform secure dynamic DNS update.
1144 * hostname: fully qualified hostname
1145 * dns_ip : ip address of hostname in network byte order
1147 * gss_handle: gss credential handle
1148 * gss_context: gss security context
1154 dyndns_get_sec_context(const char *hostname
, smb_inaddr_t
*dns_ip
)
1157 gss_cred_id_t cred_handle
;
1158 gss_ctx_id_t gss_context
;
1160 char *key_name
, dns_hostname
[MAXHOSTNAMELEN
];
1162 cred_handle
= GSS_C_NO_CREDENTIAL
;
1164 key_name
= (char *)hostname
;
1166 if (smb_getnameinfo(dns_ip
, dns_hostname
,
1167 sizeof (dns_hostname
), 0)) {
1170 if ((s
= dyndns_open_init_socket(SOCK_STREAM
, dns_ip
, 53)) < 0) {
1174 if (dyndns_establish_sec_ctx(&gss_context
, cred_handle
, s
, key_name
,
1179 return (gss_context
);
1183 * dyndns_build_add_remove_msg
1184 * This routine builds the update request message for adding and removing DNS
1185 * entries which is used for non-secure and secure DNS update.
1186 * This routine builds an UDP message.
1188 * buf : buffer to build message
1189 * update_zone: the type of zone to update, use UPDATE_FORW for forward
1190 * lookup zone, use UPDATE_REV for reverse lookup zone
1191 * hostname : fully qualified hostname to update DNS with
1192 * ip_addr : IP address of hostname
1193 * life_time : cached time of this entry by others and not within DNS
1195 * update_type: UPDATE_ADD to add entry, UPDATE_DEL to remove entry
1196 * del_type : DEL_ONE for deleting one entry, DEL_ALL for deleting all
1197 * entries of the same resource name. Only valid for UPDATE_DEL.
1198 * addit_cnt : Indicate how many record is in the additional section of
1199 * the DNS message. A value of zero is always used with
1200 * non-secure update message. For secure update message,
1201 * the value will be one because the signed TSIG message
1202 * is added as the additional record of the DNS update message.
1203 * id : DNS message ID. If a positive value then this ID value is
1204 * used, otherwise the next incremented value is used
1205 * level : This is the domain level which we send the request to, level
1206 * zero is the default level, it can go upto 2 in reverse zone
1207 * and virtually to any level in forward zone.
1209 * buf : buffer containing update message
1210 * id : DNS message ID
1211 * int : size of update message
1214 * This function is changed to handle dynamic DNS update retires to higher
1215 * authoritative domains.
1218 dyndns_build_add_remove_msg(char *buf
, int update_zone
, const char *hostname
,
1219 const char *ip_addr
, int life_time
, int update_type
, int del_type
,
1220 int addit_cnt
, uint16_t *id
, int level
)
1224 int queryReq
, zoneCount
, preqCount
, updateCount
, additionalCount
;
1225 char *zone
, *resource
, *data
, zone_buf
[100], resrc_buf
[100];
1226 int zoneType
, zoneClass
, type
, class, ttl
;
1228 smb_inaddr_t tmp_addr
;
1232 queryReq
= REQ_UPDATE
;
1236 additionalCount
= addit_cnt
;
1238 (void) memset(buf
, 0, NS_PACKETSZ
);
1242 *id
= dyndns_get_msgid();
1244 if (dyndns_build_header(&bufptr
, BUFLEN_UDP(bufptr
, buf
), *id
, queryReq
,
1245 zoneCount
, preqCount
, updateCount
, additionalCount
, 0) == -1) {
1249 zoneType
= ns_t_soa
;
1250 zoneClass
= ns_c_in
;
1252 if (update_zone
== UPDATE_FORW
) {
1253 p
= (char *)hostname
;
1255 /* Try higher domains according to the level requested */
1258 if ((zone
= (char *)strchr(p
, '.')) == NULL
)
1262 } while (--level
>= 0);
1263 resource
= (char *)hostname
;
1264 data
= (char *)ip_addr
;
1266 if (inet_pton(AF_INET
, ip_addr
, &tmp_addr
) == 1) {
1267 (void) sscanf(ip_addr
, "%d.%d.%d.%d", &a
, &b
, &c
, &d
);
1268 (void) sprintf(zone_buf
, "%d.%d.%d.in-addr.arpa",
1270 zone
= p
= zone_buf
;
1272 /* Try higher domains based on level requested */
1273 while (--level
>= 0) {
1275 if ((zone
= (char *)strchr(p
, '.')) == NULL
) {
1281 (void) sprintf(resrc_buf
, "%d.%d.%d.%d.in-addr.arpa",
1285 * create reverse nibble ipv6 format
1287 bzero(resrc_buf
, 100);
1290 while (ip_addr
[i
] != 0)
1295 while ((i
>= 0) && (ip_addr
[i
] != ':')) {
1296 resrc_buf
[j
++] = ip_addr
[i
];
1297 (void) strcat(&resrc_buf
[j
++], ".");
1301 for (k
= 0; k
<= fourcnt
; k
++) {
1302 resrc_buf
[j
++] = '0';
1303 (void) strcat(&resrc_buf
[j
++], ".");
1307 (void) strcat(resrc_buf
, "ip6.arpa");
1308 (void) strcpy(zone_buf
, &resrc_buf
[32]);
1311 resource
= resrc_buf
; /* ip info */
1312 data
= (char *)hostname
;
1314 if (dyndns_build_quest_zone(&bufptr
, BUFLEN_UDP(bufptr
, buf
), zone
,
1315 zoneType
, zoneClass
) == -1) {
1319 if (update_zone
== UPDATE_FORW
)
1324 if (update_type
== UPDATE_ADD
) {
1328 if (del_type
== DEL_ONE
)
1329 class = ns_c_none
; /* remove one */
1331 class = ns_c_any
; /* remove all */
1334 if (dyndns_build_update(&bufptr
, BUFLEN_UDP(bufptr
, buf
),
1335 resource
, type
, class, ttl
, data
, update_zone
,
1336 update_type
, del_type
) == -1) {
1340 return (bufptr
- buf
);
1344 * dyndns_build_unsigned_tsig_msg
1345 * This routine is used to build the unsigned TSIG message for signing. The
1346 * unsigned TSIG message contains the update request message with certain TSIG
1347 * fields included. An error time of 300 seconds is used for fudge time. This
1348 * is the number used by Microsoft clients.
1349 * This routine builds a UDP message.
1351 * buf : buffer to build message
1352 * update_zone: the type of zone to update, use UPDATE_FORW for forward
1353 * lookup zone, use UPDATE_REV for reverse lookup zone
1354 * hostname : fully qualified hostname to update DNS with
1355 * ip_addr : IP address of hostname
1356 * life_time : cached time of this entry by others and not within DNS
1358 * update_type: UPDATE_ADD to add entry, UPDATE_DEL to remove entry
1359 * del_type : DEL_ONE for deleting one entry, DEL_ALL for deleting all
1360 * entries of the same resource name. Only valid for UPDATE_DEL.
1361 * key_name : same key name used in TKEY message
1362 * id : DNS message ID. If a positive value then this ID value is
1363 * used, otherwise the next incremented value is used
1364 * level : This is the domain level which we send the request to, level
1365 * zero is the default level, it can go upto 2 in reverse zone
1366 * and virtually to any level in forward zone.
1368 * buf : buffer containing update message
1369 * id : DNS message ID
1370 * int : size of update message
1374 dyndns_build_unsigned_tsig_msg(char *buf
, int update_zone
, const char *hostname
,
1375 const char *ip_addr
, int life_time
, int update_type
, int del_type
,
1376 char *key_name
, uint16_t *id
, int level
)
1381 if ((buf_sz
= dyndns_build_add_remove_msg(buf
, update_zone
, hostname
,
1382 ip_addr
, life_time
, update_type
, del_type
, 0, id
, level
)) <= 0) {
1386 bufptr
= buf
+ buf_sz
;
1388 if (dyndns_build_tsig(&bufptr
, BUFLEN_UDP(bufptr
, buf
), 0,
1389 key_name
, 300, NULL
, 0, TSIG_UNSIGNED
) == -1) {
1393 return (bufptr
- buf
);
1397 * dyndns_build_signed_tsig_msg
1398 * This routine build the signed TSIG message which contains the update
1399 * request message encrypted. An error time of 300 seconds is used for fudge
1400 * time. This is the number used by Microsoft clients.
1401 * This routine builds a UDP message.
1403 * buf : buffer to build message
1404 * update_zone: the type of zone to update, use UPDATE_FORW for forward
1405 * lookup zone, use UPDATE_REV for reverse lookup zone
1406 * hostname : fully qualified hostname to update DNS with
1407 * ip_addr : IP address of hostname
1408 * life_time : cached time of this entry by others and not within DNS
1410 * update_type: UPDATE_ADD to add entry, UPDATE_DEL to remove entry
1411 * del_type : DEL_ONE for deleting one entry, DEL_ALL for deleting all
1412 * entries of the same resource name. Only valid for UPDATE_DEL.
1413 * key_name : same key name used in TKEY message
1414 * id : DNS message ID. If a positive value then this ID value is
1415 * used, otherwise the next incremented value is used
1416 * in_mic : the update request message encrypted
1417 * level : This is the domain level which we send the request to, level
1418 * zero is the default level, it can go upto 2 in reverse zone
1419 * and virtually to any level in forward zone.
1422 * buf : buffer containing update message
1423 * id : DNS message ID
1424 * int : size of update message
1428 dyndns_build_signed_tsig_msg(char *buf
, int update_zone
, const char *hostname
,
1429 const char *ip_addr
, int life_time
, int update_type
, int del_type
,
1430 char *key_name
, uint16_t *id
, gss_buffer_desc
*in_mic
, int level
)
1435 if ((buf_sz
= dyndns_build_add_remove_msg(buf
, update_zone
, hostname
,
1436 ip_addr
, life_time
, update_type
, del_type
, 1, id
, level
)) <= 0) {
1440 bufptr
= buf
+ buf_sz
;
1442 if (dyndns_build_tsig(&bufptr
, BUFLEN_UDP(bufptr
, buf
),
1443 *id
, key_name
, 300, in_mic
->value
,
1444 in_mic
->length
, TSIG_SIGNED
) == -1) {
1448 return (bufptr
- buf
);
1452 * dyndns_udp_send_recv
1453 * This routine sends and receives UDP DNS request and reply messages.
1455 * Pre-condition: Caller must call dyndns_open_init_socket() before calling
1459 * s : socket descriptor
1460 * buf : buffer containing data to send
1461 * buf_sz : size of data to send
1464 * rec_buf: reply dat
1469 dyndns_udp_send_recv(int s
, char *buf
, int buf_sz
, char *rec_buf
)
1473 struct timeval tv
, timeout
;
1475 struct sockaddr_in6 from_addr
;
1477 timeout
.tv_usec
= 0;
1478 timeout
.tv_sec
= DYNDNS_QUERY_TIMEOUT
;
1480 for (i
= 0; i
<= DYNDNS_MAX_QUERY_RETRIES
; i
++) {
1481 if (send(s
, buf
, buf_sz
, 0) == -1) {
1482 syslog(LOG_ERR
, "dyndns: UDP send error (%s)",
1492 retval
= select(s
+1, &rfds
, NULL
, NULL
, &tv
);
1496 } else if (retval
> 0) {
1497 bzero(rec_buf
, NS_PACKETSZ
);
1498 addr_len
= sizeof (struct sockaddr_in6
);
1499 if (recvfrom(s
, rec_buf
, NS_PACKETSZ
, 0,
1500 (struct sockaddr
*)&from_addr
, &addr_len
) == -1) {
1501 syslog(LOG_ERR
, "dyndns: UDP recv error ");
1508 /* did not receive anything */
1509 if (i
== (DYNDNS_MAX_QUERY_RETRIES
+ 1)) {
1510 syslog(LOG_ERR
, "dyndns: max retries for UDP recv reached");
1517 * dyndns_sec_add_remove_entry
1518 * Perform secure dynamic DNS update after getting security context.
1519 * This routine opens a UDP socket to the DNS sever, gets the security context,
1520 * builds the unsigned TSIG message and signed TSIG message. The signed TSIG
1521 * message containing the encrypted update request message is sent to the DNS
1522 * server. The response is received and check for error. If there is no
1523 * error then credential handle and security context are released and the local
1524 * NSS cached is purged.
1526 * update_zone : UPDATE_FORW for forward zone, UPDATE_REV for reverse zone
1527 * hostname : fully qualified hostname
1528 * ip_addr : ip address of hostname in string format
1529 * life_time : cached time of this entry by others and not within DNS
1531 * max_retries : maximum retries for sending DNS update request
1532 * recv_timeout: receive timeout
1533 * update_type : UPDATE_ADD for adding entry, UPDATE_DEL for removing entry
1534 * del_type : DEL_ONE for deleting one entry, DEL_ALL for deleting all
1535 * entries of the same resource name. Only valid for UPDATE_DEL
1536 * dns_str : DNS IP address in string format
1541 * This function is enhanced to handle the case of NOTAUTH error when DNS server
1542 * is not authoritative for specified zone. In this case we need to resend the
1543 * same request to the higher authoritative domains.
1544 * This is true for both secure and unsecure dynamic DNS updates.
1547 dyndns_sec_add_remove_entry(int update_zone
, const char *hostname
,
1548 const char *ip_addr
, int life_time
, int update_type
, int del_type
,
1553 char buf
[NS_PACKETSZ
], buf2
[NS_PACKETSZ
];
1556 gss_buffer_desc in_mic
, out_mic
;
1557 gss_ctx_id_t gss_context
;
1558 smb_inaddr_t dns_ip
;
1566 if (inet_pton(AF_INET
, dns_str
, &dns_ip
) == 1)
1567 dns_ip
.a_family
= AF_INET
;
1568 else if (inet_pton(AF_INET6
, dns_str
, &dns_ip
) == 1)
1569 dns_ip
.a_family
= AF_INET6
;
1573 if ((gss_context
= dyndns_get_sec_context(hostname
,
1574 &dns_ip
)) == NULL
) {
1578 key_name
= (char *)hostname
;
1580 if ((s2
= dyndns_open_init_socket(SOCK_DGRAM
, &dns_ip
, 53)) < 0) {
1581 if (gss_context
!= GSS_C_NO_CONTEXT
)
1582 (void) gss_delete_sec_context(&min
, &gss_context
, NULL
);
1587 if ((buf_sz
= dyndns_build_unsigned_tsig_msg(buf
, update_zone
, hostname
,
1588 ip_addr
, life_time
, update_type
, del_type
,
1589 key_name
, &id
, level
)) <= 0) {
1591 if (gss_context
!= GSS_C_NO_CONTEXT
)
1592 (void) gss_delete_sec_context(&min
, &gss_context
, NULL
);
1596 in_mic
.length
= buf_sz
;
1599 /* sign update message */
1600 if ((maj
= gss_get_mic(&min
, gss_context
, 0, &in_mic
, &out_mic
)) !=
1602 display_stat(maj
, min
);
1604 if (gss_context
!= GSS_C_NO_CONTEXT
)
1605 (void) gss_delete_sec_context(&min
, &gss_context
, NULL
);
1609 if ((buf_sz
= dyndns_build_signed_tsig_msg(buf
, update_zone
, hostname
,
1610 ip_addr
, life_time
, update_type
, del_type
, key_name
, &id
,
1611 &out_mic
, level
)) <= 0) {
1613 (void) gss_release_buffer(&min
, &out_mic
);
1614 if (gss_context
!= GSS_C_NO_CONTEXT
)
1615 (void) gss_delete_sec_context(&min
, &gss_context
, NULL
);
1619 (void) gss_release_buffer(&min
, &out_mic
);
1621 if (dyndns_udp_send_recv(s2
, buf
, buf_sz
, buf2
)) {
1623 if (gss_context
!= GSS_C_NO_CONTEXT
)
1624 (void) gss_delete_sec_context(&min
, &gss_context
, NULL
);
1630 if (gss_context
!= GSS_C_NO_CONTEXT
)
1631 (void) gss_delete_sec_context(&min
, &gss_context
, NULL
);
1633 ret
= buf2
[3] & 0xf; /* error field in UDP */
1636 * If it is a NOTAUTH error we should retry with higher domains
1637 * until we get a successful reply or the maximum retries is met.
1639 if (ret
== NOTAUTH
&& level
++ < MAX_AUTH_RETRIES
)
1640 goto sec_retry_higher
;
1642 /* check here for update request is successful */
1643 if (ret
!= NOERROR
) {
1644 dyndns_syslog(LOG_ERR
, ret
, "TSIG reply");
1648 (void) dyndns_get_nshort(buf2
, &rid
);
1656 * dyndns_seach_entry
1657 * Query DNS server for entry. This routine can indicate if an entry exist
1658 * or not during forward or reverse lookup. Also can indicate if the data
1659 * of the entry matched. For example, for forward lookup, the entry is
1660 * searched using the hostname and the data is the IP address. For reverse
1661 * lookup, the entry is searched using the IP address and the data is the
1664 * update_zone: UPDATE_FORW for forward zone, UPDATE_REV for reverse zone
1665 * hostname : fully qualified hostname
1666 * ip_addr : ip address of hostname in string format
1667 * update_type: UPDATE_ADD for adding entry, UPDATE_DEL for removing entry
1670 * is_match: is 1 for found matching entry, otherwise 0
1671 * 1 : an entry exist but not necessarily match
1672 * 0 : an entry does not exist
1677 dyndns_search_entry(int update_zone
, const char *hostname
, const char *ip_addr
,
1678 int update_type
, struct timeval
*time_out
, int *is_match
)
1680 smb_inaddr_t ipaddr
, dnsip
;
1681 char dns_hostname
[NI_MAXHOST
];
1682 struct addrinfo hints
, *res
= NULL
;
1687 if (inet_pton(AF_INET
, ip_addr
, &ipaddr
) == 1) {
1688 salen
= sizeof (ipaddr
.a_ipv4
);
1690 } else if (inet_pton(AF_INET6
, ip_addr
, &ipaddr
) == 1) {
1691 salen
= sizeof (ipaddr
.a_ipv6
);
1694 if (update_zone
== UPDATE_FORW
) {
1695 bzero((char *)&hints
, sizeof (hints
));
1696 hints
.ai_family
= family
;
1697 hints
.ai_flags
= AI_NUMERICHOST
;
1698 if (getaddrinfo(hostname
, NULL
, &hints
, &res
)) {
1703 * if both ips aren't the same family skip to
1707 if ((res
->ai_family
== AF_INET
) &&
1708 (family
== AF_INET
)) {
1709 (void) memcpy(&dnsip
, &res
->ai_addr
[0],
1711 if (ipaddr
.a_ipv4
==
1716 } else if ((res
->ai_family
== AF_INET6
) &&
1717 (family
== AF_INET6
)) {
1718 (void) memcpy(&dnsip
, &res
->ai_addr
[0],
1720 /* need compare macro here */
1721 if (!memcmp(&ipaddr
, &dnsip
,
1727 } while (res
->ai_next
);
1732 if (smb_getnameinfo(&ipaddr
, dns_hostname
, NI_MAXHOST
, 0))
1735 if (strncasecmp(dns_hostname
, hostname
,
1736 strlen(hostname
)) == 0) {
1742 /* entry does not exist */
1747 * dyndns_add_remove_entry
1748 * Perform non-secure dynamic DNS update. If it fails and host principal
1749 * keys can be found in the local keytab file, secure update will be performed.
1751 * This routine opens a UDP socket to the DNS sever, build the update request
1752 * message, and sends the message to the DNS server. The response is received
1753 * and check for error. If there is no error then the local NSS cached is
1754 * purged. DNS may be used to check to see if an entry already exist before
1755 * adding or to see if an entry does exist before removing it. Adding
1756 * duplicate entries or removing non-existing entries does not cause any
1757 * problems. DNS is not check when doing a delete all.
1759 * update_zone: UPDATE_FORW for forward zone, UPDATE_REV for reverse zone
1760 * hostname : fully qualified hostname
1761 * ip_addr : ip address of hostname in string format
1762 * life_time : cached time of this entry by others and not within DNS
1764 * update_type: UPDATE_ADD to add entry, UPDATE_DEL to remove entry
1765 * do_check : DNS_CHECK to check first in DNS, DNS_NOCHECK for no DNS
1766 * checking before update
1767 * del_type : DEL_ONE for deleting one entry, DEL_ALL for deleting all
1768 * entries of the same resource name. Only valid for UPDATE_DEL.
1769 * dns_str : DNS IP address in string format
1774 * This function is enhanced to handle the case of NOTAUTH error when DNS server
1775 * is not authoritative for specified zone. In this case we need to resend the
1776 * same request to the higher authoritative domains.
1777 * This is true for both secure and unsecure dynamic DNS updates.
1780 dyndns_add_remove_entry(int update_zone
, const char *hostname
,
1781 const char *ip_addr
, int life_time
, int update_type
,
1782 int do_check
, int del_type
, char *dns_str
)
1786 char buf
[NS_PACKETSZ
], buf2
[NS_PACKETSZ
];
1788 int is_exist
, is_match
;
1789 struct timeval timeout
;
1792 smb_inaddr_t dns_ip
;
1799 if (do_check
== DNS_CHECK
&& del_type
!= DEL_ALL
) {
1800 is_exist
= dyndns_search_entry(update_zone
, hostname
, ip_addr
,
1801 update_type
, &timeout
, &is_match
);
1803 if (update_type
== UPDATE_ADD
&& is_exist
&& is_match
) {
1805 } else if (update_type
== UPDATE_DEL
&& !is_exist
) {
1810 if (inet_pton(AF_INET
, dns_str
, &dns_ip
) == 1)
1811 dns_ip
.a_family
= AF_INET
;
1812 else if (inet_pton(AF_INET6
, dns_str
, &dns_ip
) == 1)
1813 dns_ip
.a_family
= AF_INET6
;
1816 if ((s
= dyndns_open_init_socket(SOCK_DGRAM
, &dns_ip
, 53)) < 0)
1820 if ((buf_sz
= dyndns_build_add_remove_msg(buf
, update_zone
, hostname
,
1821 ip_addr
, life_time
, update_type
, del_type
, 0, &id
, level
)) <= 0) {
1826 if (dyndns_udp_send_recv(s
, buf
, buf_sz
, buf2
)) {
1833 ret
= buf2
[3] & 0xf; /* error field in UDP */
1836 * If it is a NOTAUTH error we should retry with higher domains
1837 * until we get a successful reply
1839 if (ret
== NOTAUTH
&& level
++ < MAX_AUTH_RETRIES
)
1842 /* check here for update request is successful */
1843 if (ret
== NOERROR
) {
1844 (void) dyndns_get_nshort(buf2
, &rid
);
1850 if (ret
== NOTIMP
) {
1851 dyndns_syslog(LOG_NOTICE
, NOTIMP
, "dynamic updates");
1853 } else if (ret
== NOTAUTH
) {
1854 dyndns_syslog(LOG_NOTICE
, NOTAUTH
, "DNS");
1858 if ((p
= strchr(hostname
, '.')) == NULL
)
1862 if (smb_krb5_kt_find(SMB_KRB5_PN_ID_HOST_FQHN
, fqdn
,
1863 SMBNS_KRB5_KEYTAB
)) {
1864 ret
= dyndns_sec_add_remove_entry(update_zone
, hostname
,
1865 ip_addr
, life_time
, update_type
, del_type
, dns_str
);
1867 syslog(LOG_NOTICE
, "dyndns: secure update failed: cannot find "
1868 "host principal \"%s\" in local keytab file.", hostname
);
1876 * Main routine to add an entry into DNS. The attempt will be made on the
1877 * the servers returned by smb_get_nameserver(). Upon a successful
1878 * attempt on any one of the server, the function will exit with 0.
1879 * Otherwise, -1 is retuned to indicate the update attempt on all the
1880 * nameservers has failed.
1883 * update_zone: the type of zone to update, use UPDATE_FORW for forward
1884 * lookup zone, use UPDATE_REV for reverse lookup zone
1885 * hostname : fully qualified hostname
1886 * ip_addr : ip address of hostname in string format
1887 * life_time : cached time of this entry by others and not within DNS
1894 dyndns_add_entry(int update_zone
, const char *hostname
, const char *ip_addr
,
1897 const char *dns_str
;
1899 smb_inaddr_t ns_list
[MAXNS
];
1900 char dns_buf
[INET6_ADDRSTRLEN
];
1904 if (hostname
== NULL
|| ip_addr
== NULL
) {
1907 cnt
= smb_get_nameservers(&ns_list
[0], MAXNS
);
1909 for (i
= 0; i
< cnt
; i
++) {
1910 dns_str
= smb_inet_ntop(&ns_list
[i
], dns_buf
,
1911 SMB_IPSTRLEN(ns_list
[i
].a_family
));
1912 if (dns_str
== NULL
)
1915 which_zone
= (update_zone
== UPDATE_FORW
) ?
1916 "forward" : "reverse";
1917 syslog(LOG_DEBUG
, "dyndns %s lookup zone update %s (%s)",
1918 which_zone
, hostname
, ip_addr
);
1920 if (dyndns_add_remove_entry(update_zone
, hostname
,
1922 UPDATE_ADD
, DNS_NOCHECK
, DEL_NONE
, dns_buf
) != -1) {
1928 return (rc
? 0 : -1);
1932 * dyndns_remove_entry
1933 * Main routine to remove an entry or all entries of the same resource name
1934 * from DNS. The update attempt will be made on the primary DNS server. If
1935 * there is a failure then another attempt will be made on the secondary DNS
1938 * update_zone: the type of zone to update, use UPDATE_FORW for forward
1939 * lookup zone, use UPDATE_REV for reverse lookup zone
1940 * hostname : fully qualified hostname
1941 * ip_addr : ip address of hostname in string format
1942 * del_type : DEL_ONE for deleting one entry, DEL_ALL for deleting all
1943 * entries of the same resource name. Only valid for UPDATE_DEL
1949 dyndns_remove_entry(int update_zone
, const char *hostname
, const char *ip_addr
,
1952 const char *dns_str
;
1953 smb_inaddr_t ns_list
[MAXNS
];
1954 char dns_buf
[INET6_ADDRSTRLEN
];
1957 if ((hostname
== NULL
|| ip_addr
== NULL
)) {
1960 cnt
= smb_get_nameservers(ns_list
, MAXNS
);
1962 for (i
= 0; i
< cnt
; i
++) {
1963 dns_str
= smb_inet_ntop(&ns_list
[i
], dns_buf
,
1964 SMB_IPSTRLEN(ns_list
[i
].a_family
));
1965 if (dns_str
== NULL
)
1967 if (update_zone
== UPDATE_FORW
) {
1968 if (del_type
== DEL_ONE
) {
1969 syslog(LOG_DEBUG
, "Dynamic update "
1970 "on forward lookup "
1971 "zone for %s (%s)...\n", hostname
, ip_addr
);
1973 syslog(LOG_DEBUG
, "Removing all "
1975 "in forward lookup zone...\n", hostname
);
1978 if (del_type
== DEL_ONE
) {
1979 syslog(LOG_DEBUG
, "Dynamic update "
1980 "on reverse lookup "
1981 "zone for %s (%s)...\n", hostname
, ip_addr
);
1983 syslog(LOG_DEBUG
, "Removing all "
1985 "in reverse lookup zone...\n", ip_addr
);
1988 if (dyndns_add_remove_entry(update_zone
, hostname
, ip_addr
, 0,
1989 UPDATE_DEL
, DNS_NOCHECK
, del_type
, dns_buf
) != -1) {
2000 * dyndns_update_core
2001 * Perform dynamic update on both forward and reverse lookup zone using
2002 * the specified hostname and IP addresses. Before updating DNS, existing
2003 * host entries with the same hostname in the forward lookup zone are removed
2004 * and existing pointer entries with the same IP addresses in the reverse
2005 * lookup zone are removed. After DNS update, host entries for current
2006 * hostname will show current IP addresses and pointer entries for current
2007 * IP addresses will show current hostname.
2009 * fqdn - fully-qualified domain name (in lower case)
2012 * -1: some dynamic DNS updates errors
2013 * 0: successful or DDNS disabled.
2016 dyndns_update_core(char *fqdn
)
2018 int forw_update_ok
, error
;
2019 char my_ip
[INET6_ADDRSTRLEN
];
2023 char fqhn
[MAXHOSTNAMELEN
];
2025 if (fqdn
== NULL
|| *fqdn
== '\0')
2028 if (!smb_config_getbool(SMB_CI_DYNDNS_ENABLE
))
2030 if (smb_gethostname(fqhn
, MAXHOSTNAMELEN
, SMB_CASE_LOWER
) != 0)
2034 * To comply with RFC 4120 section 6.2.1, the fully-qualified hostname
2035 * must be set to lower case.
2037 (void) snprintf(fqhn
, MAXHOSTNAMELEN
, "%s.%s", fqhn
, fqdn
);
2043 * Dummy IP is okay since we are removing all using the hostname.
2045 if (dyndns_remove_entry(UPDATE_FORW
, fqhn
, "1.1.1.1", DEL_ALL
) == 0) {
2051 if (smb_nic_getfirst(&ni
) != SMB_NIC_SUCCESS
)
2055 if (ni
.ni_nic
.nic_sysflags
& IFF_PRIVATE
)
2057 /* first try ipv4, then ipv6 */
2058 my_str
= smb_inet_ntop(&ni
.ni_nic
.nic_ip
, my_ip
,
2059 SMB_IPSTRLEN(ni
.ni_nic
.nic_ip
.a_family
));
2060 if (my_str
== NULL
) {
2065 if (forw_update_ok
) {
2066 rc
= dyndns_add_entry(UPDATE_FORW
, fqhn
, my_str
,
2073 rc
= dyndns_remove_entry(UPDATE_REV
, fqhn
, my_str
, DEL_ALL
);
2075 rc
= dyndns_add_entry(UPDATE_REV
, fqhn
, my_str
,
2082 } while (smb_nic_getnext(&ni
) == SMB_NIC_SUCCESS
);
2084 return ((error
== 0) ? 0 : -1);
2088 * dyndns_clear_rev_zone
2089 * Clear the rev zone records. Must be called to clear the OLD if list
2090 * of down records prior to updating the list with new information.
2093 * fqdn - fully-qualified domain name (in lower case)
2095 * -1: some dynamic DNS updates errors
2096 * 0: successful or DDNS disabled.
2099 dyndns_clear_rev_zone(char *fqdn
)
2102 char my_ip
[INET6_ADDRSTRLEN
];
2105 char fqhn
[MAXHOSTNAMELEN
];
2108 if (!smb_config_getbool(SMB_CI_DYNDNS_ENABLE
))
2111 if (smb_gethostname(fqhn
, MAXHOSTNAMELEN
, SMB_CASE_LOWER
) != 0)
2115 * To comply with RFC 4120 section 6.2.1, the fully-qualified hostname
2116 * must be set to lower case.
2118 (void) snprintf(fqhn
, MAXHOSTNAMELEN
, "%s.%s", fqhn
, fqdn
);
2122 if (smb_nic_getfirst(&ni
) != SMB_NIC_SUCCESS
)
2126 if (ni
.ni_nic
.nic_sysflags
& IFF_PRIVATE
)
2128 my_str
= smb_inet_ntop(&ni
.ni_nic
.nic_ip
, my_ip
,
2129 SMB_IPSTRLEN(ni
.ni_nic
.nic_ip
.a_family
));
2130 if (my_str
== NULL
) {
2135 rc
= dyndns_remove_entry(UPDATE_REV
, fqhn
, my_ip
, DEL_ALL
);
2139 } while (smb_nic_getnext(&ni
) == SMB_NIC_SUCCESS
);
2141 return ((error
== 0) ? 0 : -1);