8322 nl: misleading-indentation
[unleashed/tickless.git] / usr / src / lib / smbsrv / libsmbns / common / smbns_dyndns.c
blob26aff77bb6c0a40abe6a9623c92832a00130e867
1 /*
2 * CDDL HEADER START
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]
19 * CDDL HEADER END
22 * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
25 #include <assert.h>
26 #include <errno.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <strings.h>
30 #include <sys/types.h>
31 #include <sys/socket.h>
32 #include <netinet/in.h>
33 #include <arpa/inet.h>
34 #include <arpa/nameser.h>
35 #include <net/if.h>
36 #include <resolv.h>
37 #include <sys/time.h>
38 #include <unistd.h>
39 #include <string.h>
40 #include <pthread.h>
41 #include <netdb.h>
42 #include <rpc/rpc.h>
43 #include <syslog.h>
44 #include <gssapi/gssapi.h>
45 #include <kerberosv5/krb5.h>
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.
54 #ifndef BADSIG
55 #define BADSIG ns_r_badsig
56 #endif /* BADSIG */
58 #ifndef BADKEY
59 #define BADKEY ns_r_badkey
60 #endif /* BADKEY */
62 #ifndef BADTIME
63 #define BADTIME ns_r_badtime
64 #endif /* BADTIME */
66 /* internal use, in dyndns_add_entry */
67 #define DEL_NONE 2
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 {
90 list_node_t dqe_lnd;
91 int dqe_op;
92 /* fully-qualified domain name is in lower case */
93 char dqe_fqdn[MAXHOSTNAMELEN];
94 } dyndns_qentry_t;
96 typedef struct dyndns_queue {
97 list_t ddq_list;
98 mutex_t ddq_mtx;
99 cond_t ddq_cv;
100 uint32_t ddq_state;
101 } dyndns_queue_t;
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 *);
114 void
115 dyndns_start(void)
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);
121 return;
124 dyndns_msgid_init();
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);
133 void
134 dyndns_stop(void)
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);
143 break;
144 default:
145 break;
148 (void) mutex_unlock(&dyndns_queue.ddq_mtx);
152 * Clear all records in both zones.
154 void
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");
161 return;
164 dyndns_queue_request(DYNDNS_OP_CLEAR, fqdn);
168 * Update all records in both zones.
170 void
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");
177 return;
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
187 * to lower case.
189 static void
190 dyndns_queue_request(int op, const char *fqdn)
192 dyndns_qentry_t *entry;
194 if (!smb_config_getbool(SMB_CI_DYNDNS_ENABLE))
195 return;
197 if ((entry = malloc(sizeof (dyndns_qentry_t))) == NULL)
198 return;
200 bzero(entry, sizeof (dyndns_qentry_t));
201 entry->dqe_op = op;
202 (void) strlcpy(entry->dqe_fqdn, fqdn, MAXNAMELEN);
203 (void) smb_strlwr(entry->dqe_fqdn);
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);
212 break;
213 default:
214 free(entry);
215 break;
218 (void) mutex_unlock(&dyndns_queue.ddq_mtx);
222 * Flush all remaining items from the specified list/queue.
224 static void
225 dyndns_queue_flush(list_t *lst)
227 dyndns_qentry_t *entry;
229 while ((entry = list_head(lst)) != NULL) {
230 list_remove(lst, entry);
231 free(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
240 * to retry.
242 /*ARGSUSED*/
243 void *
244 dyndns_publisher(void *arg)
246 dyndns_qentry_t *entry;
247 list_t publist;
249 (void) mutex_lock(&dyndns_queue.ddq_mtx);
250 if (dyndns_queue.ddq_state != DYNDNS_STATE_READY) {
251 (void) mutex_unlock(&dyndns_queue.ddq_mtx);
252 return (NULL);
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));
260 for (;;) {
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);
271 break;
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);
296 return (NULL);
300 * Remove items from the queue and process them.
302 static void
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);
312 return;
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);
321 break;
322 case DYNDNS_OP_UPDATE:
323 (void) dyndns_update_core(entry->dqe_fqdn);
324 break;
325 default:
326 break;
329 free(entry);
334 * Dynamic DNS update API for kclient.
336 * Returns 0 upon success. Otherwise, returns -1.
339 dyndns_update(char *fqdn)
341 int rc;
343 if (smb_nic_init() != SMB_NIC_SUCCESS)
344 return (-1);
346 dyndns_msgid_init();
347 (void) smb_strlwr(fqdn);
348 rc = dyndns_update_core(fqdn);
349 smb_nic_fini();
350 return (rc);
354 * Initializes the DNS message ID counter using the algorithm
355 * that resolver library uses to initialize the ID field of any res
356 * structure.
358 static void
359 dyndns_msgid_init(void)
361 struct timeval now;
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);
369 static int
370 dyndns_get_msgid(void)
372 uint16_t id;
374 (void) mutex_lock(&dns_msgid_mtx);
375 id = ++dns_msgid;
376 (void) mutex_unlock(&dns_msgid_mtx);
377 return (id);
381 * Log a DNS error message
383 static void
384 dyndns_syslog(int severity, int errnum, const char *text)
386 struct {
387 int errnum;
388 char *errmsg;
389 } errtab[] = {
390 { FORMERR, "message format error" },
391 { SERVFAIL, "server internal error" },
392 { NXDOMAIN, "entry should exist but does not exist" },
393 { NOTIMP, "not supported" },
394 { REFUSED, "operation refused" },
395 { YXDOMAIN, "entry should not exist but does exist" },
396 { YXRRSET, "RRSet should not exist but does exist" },
397 { NXRRSET, "RRSet should exist but does not exist" },
398 { NOTAUTH, "server is not authoritative for specified zone" },
399 { NOTZONE, "name not within specified zone" },
400 { BADSIG, "bad transaction signature (TSIG)" },
401 { BADKEY, "bad transaction key (TKEY)" },
402 { BADTIME, "time not synchronized" },
405 char *errmsg = "unknown error";
406 int i;
408 if (errnum == NOERROR)
409 return;
411 for (i = 0; i < (sizeof (errtab) / sizeof (errtab[0])); ++i) {
412 if (errtab[i].errnum == errnum) {
413 errmsg = errtab[i].errmsg;
414 break;
418 syslog(severity, "dyndns: %s: %s: %d", text, errmsg, errnum);
422 * display_stat
423 * Display GSS error message from error code. This routine is used to display
424 * the mechanism independent and mechanism specific error messages for GSS
425 * routines. The major status error code is the mechanism independent error
426 * code and the minor status error code is the mechanism specific error code.
427 * Parameters:
428 * maj: GSS major status
429 * min: GSS minor status
430 * Returns:
431 * None
433 static void
434 display_stat(OM_uint32 maj, OM_uint32 min)
436 gss_buffer_desc msg;
437 OM_uint32 msg_ctx = 0;
438 OM_uint32 min2;
440 (void) gss_display_status(&min2, maj, GSS_C_GSS_CODE, GSS_C_NULL_OID,
441 &msg_ctx, &msg);
442 syslog(LOG_ERR, "dyndns: GSS major status error: %s",
443 (char *)msg.value);
444 (void) gss_release_buffer(&min2, &msg);
446 (void) gss_display_status(&min2, min, GSS_C_MECH_CODE, GSS_C_NULL_OID,
447 &msg_ctx, &msg);
448 syslog(LOG_ERR, "dyndns: GSS minor status error: %s",
449 (char *)msg.value);
450 (void) gss_release_buffer(&min2, &msg);
453 static char *
454 dyndns_put_nshort(char *buf, uint16_t val)
456 uint16_t nval;
458 nval = htons(val);
459 (void) memcpy(buf, &nval, sizeof (uint16_t));
460 buf += sizeof (uint16_t);
461 return (buf);
464 static char *
465 dyndns_get_nshort(char *buf, uint16_t *val)
467 uint16_t nval;
469 (void) memcpy(&nval, buf, sizeof (uint16_t));
470 *val = ntohs(nval);
471 buf += sizeof (uint16_t);
472 return (buf);
475 static char *
476 dyndns_put_nlong(char *buf, uint32_t val)
478 uint32_t lval;
480 lval = htonl(val);
481 (void) memcpy(buf, &lval, sizeof (uint32_t));
482 buf += sizeof (uint32_t);
483 return (buf);
486 static char *
487 dyndns_put_byte(char *buf, char val)
489 *buf = val;
490 buf++;
491 return (buf);
497 static char *
498 dyndns_put_int(char *buf, int val)
500 (void) memcpy(buf, &val, sizeof (int));
501 buf += sizeof (int);
502 return (buf);
505 static char *
506 dyndns_put_v6addr(char *buf, smb_inaddr_t *val)
509 val->a_family = AF_INET6;
510 (void) memcpy(buf, &val->a_ipv6, IN6ADDRSZ);
511 buf += IN6ADDRSZ;
512 return (buf);
515 * dyndns_stuff_str
516 * Converts a domain string by removing periods and replacing with a byte value
517 * of how many characters following period. A byte value is placed in front
518 * to indicate how many characters before first period. A NULL character is
519 * placed at the end. i.e. host.procom.com -> 4host5procom3com0
520 * Buffer space checking is done by caller.
521 * Parameters:
522 * ptr : address of pointer to buffer to store converted string
523 * zone: domain name string
524 * Returns:
525 * ptr: address of pointer to next available buffer space
526 * -1 : error
527 * 0 : success
529 static int
530 dyndns_stuff_str(char **ptr, char *zone)
532 int len;
533 char *lenPtr, *zonePtr;
535 for (zonePtr = zone; *zonePtr; ) {
536 lenPtr = *ptr;
537 *ptr = *ptr + 1;
538 len = 0;
539 while (*zonePtr != '.' && *zonePtr != 0) {
540 *ptr = dyndns_put_byte(*ptr, *zonePtr);
541 zonePtr++;
542 len++;
544 *lenPtr = len;
545 if (*zonePtr == '.')
546 zonePtr++;
548 *ptr = dyndns_put_byte(*ptr, 0);
549 return (0);
553 * dyndns_build_header
554 * Build the header for DNS query and DNS update request message.
555 * Parameters:
556 * ptr : address of pointer to buffer to store header
557 * buf_len : buffer length
558 * msg_id : message id
559 * query_req : use REQ_QUERY for query message or REQ_UPDATE for
560 * update message
561 * quest_zone_cnt : number of question record for query message or
562 * number of zone record for update message
563 * ans_prereq_cnt : number of answer record for query message or
564 * number of prerequisite record for update message
565 * nameser_update_cnt: number of name server for query message or
566 * number of update record for update message
567 * addit_cnt : number of additional record
568 * flags : query flags word
569 * Returns:
570 * ptr: address of pointer to next available buffer space
571 * -1 : error
572 * 0 : success
574 static int
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)
579 uint16_t opcode;
581 if (buf_len < 12) {
582 syslog(LOG_ERR, "dyndns header section: buffer too small");
583 return (-1);
586 *ptr = dyndns_put_nshort(*ptr, msg_id); /* mesg ID */
587 if (query_req == REQ_QUERY)
588 opcode = ns_o_query; /* query msg */
589 else
590 opcode = ns_o_update << 11; /* update msg */
591 opcode |= flags;
592 /* mesg opcode */
593 *ptr = dyndns_put_nshort(*ptr, opcode);
594 /* zone record count */
595 *ptr = dyndns_put_nshort(*ptr, quest_zone_cnt);
596 /* prerequiste record count */
597 *ptr = dyndns_put_nshort(*ptr, ans_prereq_cnt);
598 /* update record count */
599 *ptr = dyndns_put_nshort(*ptr, nameser_update_cnt);
600 /* additional record count */
601 *ptr = dyndns_put_nshort(*ptr, addit_cnt);
603 return (0);
607 * dyndns_build_quest_zone
608 * Build the question section for query message or zone section for
609 * update message.
610 * Parameters:
611 * ptr : address of pointer to buffer to store question or zone section
612 * buf_len: buffer length
613 * name : question or zone name
614 * type : type of question or zone
615 * class : class of question or zone
616 * Returns:
617 * ptr: address of pointer to next available buffer space
618 * -1 : error
619 * 0 : success
621 static int
622 dyndns_build_quest_zone(char **ptr, int buf_len, char *name, int type,
623 int class)
625 char *zonePtr;
627 if ((strlen(name) + 6) > buf_len) {
628 syslog(LOG_ERR, "dyndns question section: buffer too small");
629 return (-1);
632 zonePtr = *ptr;
633 (void) dyndns_stuff_str(&zonePtr, name);
634 *ptr = zonePtr;
635 *ptr = dyndns_put_nshort(*ptr, type);
636 *ptr = dyndns_put_nshort(*ptr, class);
637 return (0);
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.
645 * Parameters:
646 * ptr : address of pointer to buffer to store update section
647 * buf_len : buffer length
648 * name : resource name of this record
649 * type : type of this record
650 * class : class of this record
651 * ttl : time-to-live, cached time of this entry by others and not
652 * within DNS database, a zero value for record(s) deletion
653 * data : data of this resource record
654 * forw_rev: UPDATE_FORW for forward zone, UPDATE_REV for reverse zone
655 * add_del : UPDATE_ADD for adding entry, UPDATE_DEL for removing zone
656 * del_type: DEL_ONE for deleting one entry, DEL_ALL for deleting all
657 * entries of the same resource name. Only valid for UPDATE_DEL.
658 * Returns:
659 * ptr: address of pointer to next available buffer space
660 * -1 : error
661 * 0 : success
663 static int
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)
667 char *namePtr;
668 int rec_len, data_len;
669 smb_inaddr_t ipaddr;
670 int isv4 = 1;
672 rec_len = strlen(name) + 10;
673 if (inet_pton(AF_INET, data, &ipaddr) == 1)
674 isv4 = 1;
675 else if (inet_pton(AF_INET6, data, &ipaddr) == 1)
676 isv4 = 0;
678 if (add_del == UPDATE_ADD) {
679 if (forw_rev == UPDATE_FORW)
680 data_len = isv4 ? 4 : 16;
681 else
682 data_len = strlen(data) + 2;
683 } else {
684 if (del_type == DEL_ALL)
685 data_len = 0;
686 else if (forw_rev == UPDATE_FORW)
687 data_len = isv4 ? 4 : 16;
688 else
689 data_len = strlen(data) + 2;
691 if (rec_len + data_len > buf_len) {
692 syslog(LOG_ERR, "dyndns update section: buffer too small");
693 return (-1);
696 namePtr = *ptr;
697 (void) dyndns_stuff_str(&namePtr, name);
698 *ptr = namePtr;
699 if (isv4)
700 *ptr = dyndns_put_nshort(*ptr, type);
701 else
702 *ptr = dyndns_put_nshort(*ptr, ns_t_aaaa);
703 *ptr = dyndns_put_nshort(*ptr, class);
704 *ptr = dyndns_put_nlong(*ptr, ttl);
706 if (add_del == UPDATE_DEL && del_type == DEL_ALL) {
707 *ptr = dyndns_put_nshort(*ptr, 0);
708 return (0);
711 if (forw_rev == UPDATE_FORW) {
712 if (isv4) {
713 *ptr = dyndns_put_nshort(*ptr, 4);
714 *ptr = dyndns_put_int(*ptr, ipaddr.a_ipv4);
715 } else {
716 *ptr = dyndns_put_nshort(*ptr, 16);
717 *ptr = dyndns_put_v6addr(*ptr, &ipaddr);
719 } else {
720 *ptr = dyndns_put_nshort(*ptr, strlen(data)+2);
721 namePtr = *ptr;
722 (void) dyndns_stuff_str(&namePtr, data); /* hostname */
723 *ptr = namePtr;
725 return (0);
729 * dyndns_build_tkey
730 * Build TKEY section to establish security context for secure dynamic DNS
731 * update. DNS header and question sections need to be build before this
732 * section. The TKEY data are the tokens generated during security context
733 * establishment and the TKEY message is used to transmit those tokens, one
734 * at a time, to the DNS server.
735 * Parameters:
736 * ptr : address of pointer to buffer to store TKEY
737 * buf_len : buffer length
738 * name : key name, must be unique and same as for TSIG record
739 * key_expire: expiration time of this key in second
740 * data : TKEY data
741 * data_size : data size
742 * Returns:
743 * ptr: address of the pointer to the next available buffer space
744 * -1 : error
745 * 0 : success
747 static int
748 dyndns_build_tkey(char **ptr, int buf_len, char *name, int key_expire,
749 char *data, int data_size)
751 char *namePtr;
752 struct timeval tp;
754 if (strlen(name)+2 + 45 + data_size > buf_len) {
755 syslog(LOG_ERR, "dyndns TKEY: buffer too small");
756 return (-1);
759 namePtr = *ptr;
760 (void) dyndns_stuff_str(&namePtr, name); /* unique global name */
761 *ptr = namePtr;
762 *ptr = dyndns_put_nshort(*ptr, ns_t_tkey);
763 *ptr = dyndns_put_nshort(*ptr, ns_c_any);
764 *ptr = dyndns_put_nlong(*ptr, 0);
765 /* 19 + 14 + data_size + 2 */
766 *ptr = dyndns_put_nshort(*ptr, 35 + data_size);
767 namePtr = *ptr;
768 (void) dyndns_stuff_str(&namePtr, "gss.microsoft.com");
769 *ptr = namePtr;
770 (void) gettimeofday(&tp, 0);
771 *ptr = dyndns_put_nlong(*ptr, tp.tv_sec); /* inception */
772 /* expiration, 86400 */
773 *ptr = dyndns_put_nlong(*ptr, tp.tv_sec + key_expire);
774 *ptr = dyndns_put_nshort(*ptr, MODE_GSS_API); /* mode: gss-api */
775 *ptr = dyndns_put_nshort(*ptr, 0); /* error */
776 *ptr = dyndns_put_nshort(*ptr, data_size); /* key size */
777 (void) memcpy(*ptr, data, data_size); /* key data */
778 *ptr += data_size;
779 *ptr = dyndns_put_nshort(*ptr, 0); /* other */
780 return (0);
784 * dyndns_build_tsig
785 * Build TSIG section for secure dynamic DNS update. This routine will be
786 * called twice. First called with TSIG_UNSIGNED, and second with TSIG_SIGNED.
787 * The TSIG data is NULL and ignored for TSIG_UNSIGNED and is the update request
788 * message encrypted for TSIG_SIGNED. The message id must be the same id as the
789 * one in the update request before it is encrypted.
790 * Parameters:
791 * ptr : address of pointer to buffer to store TSIG
792 * buf_len : buffer length
793 * msg_id : message id
794 * name : key name, must be the same as in TKEY record
795 * fudge_time : amount of error time allow in seconds
796 * data : TSIG data if TSIG_SIGNED, otherwise NULL
797 * data_size : size of data, otherwise 0 if data is NULL
798 * data_signed: TSIG_SIGNED to indicate data is signed and encrypted,
799 * otherwise TSIG_UNSIGNED
800 * Returns:
801 * ptr: address of pointer to next available buffer space
802 * -1 : error
803 * 0 : success
805 static int
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)
809 char *namePtr;
810 struct timeval tp;
811 int signtime, fudge, rec_len;
813 if (data_signed == TSIG_UNSIGNED)
814 rec_len = strlen(name)+2 + 37;
815 else
816 rec_len = strlen(name)+2 + 45 + data_size;
818 if (rec_len > buf_len) {
819 syslog(LOG_ERR, "dyndns TSIG: buffer too small");
820 return (-1);
823 namePtr = *ptr;
824 (void) dyndns_stuff_str(&namePtr, name); /* unique global name */
825 *ptr = namePtr;
826 if (data_signed == TSIG_SIGNED)
827 *ptr = dyndns_put_nshort(*ptr, ns_t_tsig);
828 *ptr = dyndns_put_nshort(*ptr, ns_c_any);
829 *ptr = dyndns_put_nlong(*ptr, 0);
830 if (data_signed == TSIG_SIGNED) {
831 /* 19 + 10 + data_size + 6 */
832 *ptr = dyndns_put_nshort(*ptr, 35 + data_size);
834 namePtr = *ptr;
835 (void) dyndns_stuff_str(&namePtr, "gss.microsoft.com");
836 *ptr = namePtr;
837 (void) gettimeofday(&tp, 0);
838 signtime = tp.tv_sec >> 16;
839 *ptr = dyndns_put_nlong(*ptr, signtime); /* sign time */
840 fudge = tp.tv_sec << 16;
841 fudge |= fudge_time;
842 *ptr = dyndns_put_nlong(*ptr, fudge); /* fudge time */
843 if (data_signed == TSIG_SIGNED) {
844 /* signed data size */
845 *ptr = dyndns_put_nshort(*ptr, data_size);
846 (void) memcpy(*ptr, data, data_size); /* signed data */
847 *ptr += data_size;
848 *ptr = dyndns_put_nshort(*ptr, msg_id); /* original id */
850 *ptr = dyndns_put_nshort(*ptr, 0); /* error */
851 *ptr = dyndns_put_nshort(*ptr, 0); /* other */
852 return (0);
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.
860 * Parameters:
861 * sock_type: SOCK_STREAM for TCP or SOCK_DGRAM for UDP
862 * dest_addr: destination address in network byte order
863 * port : destination port number
864 * Returns:
865 * descriptor: descriptor referencing the created socket
866 * -1 : error
869 static int
870 dyndns_open_init_socket(int sock_type, smb_inaddr_t *dest_addr, int port)
872 int s;
873 struct sockaddr_in my_addr;
874 struct sockaddr_in6 my6_addr;
875 struct sockaddr_in serv_addr;
876 struct sockaddr_in6 serv6_addr;
877 int family;
879 family = dest_addr->a_family;
881 if ((s = socket(family, sock_type, 0)) == -1) {
882 syslog(LOG_ERR, "dyndns: socket error\n");
883 return (-1);
885 if (family == AF_INET) {
886 bzero(&my_addr, sizeof (my_addr));
887 my_addr.sin_family = family;
888 my_addr.sin_port = htons(0);
889 my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
890 if (bind(s, (struct sockaddr *)&my_addr,
891 sizeof (my_addr)) < 0) {
892 syslog(LOG_ERR, "dyndns: client bind err\n");
893 (void) close(s);
894 return (-1);
896 serv_addr.sin_family = family;
897 serv_addr.sin_port = htons(port);
898 serv_addr.sin_addr.s_addr = dest_addr->a_ipv4;
899 if (connect(s, (struct sockaddr *)&serv_addr,
900 sizeof (struct sockaddr_in)) < 0) {
901 syslog(LOG_ERR, "dyndns: client connect (%s)\n",
902 strerror(errno));
903 (void) close(s);
904 return (-1);
906 } else {
907 bzero(&my6_addr, sizeof (my6_addr));
908 my6_addr.sin6_family = family;
909 my6_addr.sin6_port = htons(0);
910 bzero(&my6_addr.sin6_addr.s6_addr, IN6ADDRSZ);
911 if (bind(s, (struct sockaddr *)&my6_addr,
912 sizeof (my6_addr)) < 0) {
913 syslog(LOG_ERR, "dyndns: client bind err\n");
914 (void) close(s);
915 return (-1);
917 serv6_addr.sin6_family = family;
918 serv6_addr.sin6_port = htons(port);
919 bcopy(&serv6_addr.sin6_addr.s6_addr, &dest_addr->a_ipv6,
920 IN6ADDRSZ);
921 if (connect(s, (struct sockaddr *)&serv6_addr,
922 sizeof (struct sockaddr_in6)) < 0) {
923 syslog(LOG_ERR, "dyndns: client connect err (%s)\n",
924 strerror(errno));
925 (void) close(s);
926 return (-1);
929 return (s);
932 * dyndns_build_tkey_msg
933 * This routine is used to build the TKEY message to transmit GSS tokens
934 * during GSS security context establishment for secure DNS update. The
935 * TKEY message format uses the DNS query message format. The TKEY section
936 * is the answer section of the query message format.
937 * Microsoft uses a value of 86400 seconds (24 hours) for key expiration time.
938 * Parameters:
939 * buf : buffer to build and store TKEY message
940 * key_name: a unique key name, this same key name must be also be used in
941 * the TSIG message
942 * out_tok : TKEY message data (GSS tokens)
943 * Returns:
944 * id : message id of this TKEY message
945 * message size: the size of the TKEY message
946 * -1 : error
948 static int
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;
954 char *bufptr;
956 queryReq = REQ_QUERY;
957 /* query section of query request */
958 zoneCount = 1;
959 /* answer section of query request */
960 preqCount = 1;
961 updateCount = 0;
962 additionalCount = 0;
964 (void) memset(buf, 0, MAX_TCP_SIZE);
965 bufptr = buf;
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) {
974 return (-1);
977 zoneType = ns_t_tkey;
978 zoneClass = ns_c_in;
979 if (dyndns_build_quest_zone(&bufptr, BUFLEN_TCP(bufptr, buf), key_name,
980 zoneType, zoneClass) == -1) {
981 return (-1);
984 if (dyndns_build_tkey(&bufptr, BUFLEN_TCP(bufptr, buf), key_name,
985 86400, out_tok->value, out_tok->length) == -1) {
986 return (-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.
1004 * Parameters:
1005 * cred_handle : handle to credential
1006 * s : socket descriptor to DNS server
1007 * key_name : TKEY key name
1008 * dns_hostname: fully qualified DNS hostname which will be used for
1009 * constructing the DNS service principal name.
1010 * oid : contains Kerberos 5 object identifier
1011 * Returns:
1012 * gss_context : handle to security context
1014 static int
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];
1020 int ret;
1021 char *service_name, *tmpptr;
1022 int service_sz;
1023 OM_uint32 min, maj, time_rec;
1024 gss_buffer_desc service_buf, in_tok, out_tok;
1025 gss_name_t target_name;
1026 gss_buffer_desc *inputptr;
1027 int gss_flags;
1028 OM_uint32 ret_flags;
1029 int buf_sz;
1031 service_sz = strlen(dns_hostname) + 5;
1032 service_name = (char *)malloc(sizeof (char) * service_sz);
1033 if (service_name == NULL)
1034 return (-1);
1036 (void) snprintf(service_name, service_sz, "DNS@%s", dns_hostname);
1037 service_buf.value = service_name;
1038 service_buf.length = strlen(service_name)+1;
1039 if ((maj = gss_import_name(&min, &service_buf,
1040 GSS_C_NT_HOSTBASED_SERVICE, &target_name)) != GSS_S_COMPLETE) {
1041 display_stat(maj, min);
1042 (void) free(service_name);
1043 return (-1);
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;
1051 do {
1052 maj = gss_init_sec_context(&min, cred_handle, gss_context,
1053 target_name, oid, gss_flags, 0, NULL, inputptr, NULL,
1054 &out_tok, &ret_flags, &time_rec);
1056 if (maj != GSS_S_COMPLETE && maj != GSS_S_CONTINUE_NEEDED) {
1057 assert(gss_context);
1058 if (*gss_context != GSS_C_NO_CONTEXT)
1059 (void) gss_delete_sec_context(&min,
1060 gss_context, NULL);
1062 display_stat(maj, min);
1063 (void) gss_release_name(&min, &target_name);
1064 return (-1);
1067 if ((maj == GSS_S_COMPLETE) &&
1068 !(ret_flags & GSS_C_REPLAY_FLAG)) {
1069 syslog(LOG_ERR, "dyndns: No GSS_C_REPLAY_FLAG");
1070 if (out_tok.length > 0)
1071 (void) gss_release_buffer(&min, &out_tok);
1072 (void) gss_release_name(&min, &target_name);
1073 return (-1);
1076 if ((maj == GSS_S_COMPLETE) &&
1077 !(ret_flags & GSS_C_MUTUAL_FLAG)) {
1078 syslog(LOG_ERR, "dyndns: No GSS_C_MUTUAL_FLAG");
1079 if (out_tok.length > 0)
1080 (void) gss_release_buffer(&min, &out_tok);
1081 (void) gss_release_name(&min, &target_name);
1082 return (-1);
1085 if (out_tok.length > 0) {
1086 if ((buf_sz = dyndns_build_tkey_msg(buf, key_name,
1087 &id, &out_tok)) <= 0) {
1088 (void) gss_release_buffer(&min, &out_tok);
1089 (void) gss_release_name(&min, &target_name);
1090 return (-1);
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);
1098 return (-1);
1101 bzero(buf2, MAX_TCP_SIZE);
1102 if (recv(s, buf2, MAX_TCP_SIZE, 0) == -1) {
1103 syslog(LOG_ERR, "dyndns: TKEY recv error");
1104 (void) gss_release_name(&min, &target_name);
1105 return (-1);
1108 ret = buf2[5] & 0xf; /* error field in TCP */
1109 if (ret != NOERROR) {
1110 dyndns_syslog(LOG_ERR, ret, "TKEY reply");
1111 (void) gss_release_name(&min, &target_name);
1112 return (-1);
1115 tmpptr = &buf2[2];
1116 (void) dyndns_get_nshort(tmpptr, &rid);
1117 if (id != rid) {
1118 (void) gss_release_name(&min, &target_name);
1119 return (-1);
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];
1128 inputptr = &in_tok;
1131 } while (maj != GSS_S_COMPLETE);
1133 (void) gss_release_name(&min, &target_name);
1135 return (0);
1139 * dyndns_get_sec_context
1140 * Get security context for secure dynamic DNS update. This routine opens
1141 * a TCP socket to the DNS server and establishes a security context with
1142 * the DNS server using host principal to perform secure dynamic DNS update.
1143 * Parameters:
1144 * hostname: fully qualified hostname
1145 * dns_ip : ip address of hostname in network byte order
1146 * Returns:
1147 * gss_handle: gss credential handle
1148 * gss_context: gss security context
1149 * -1: error
1150 * 0: success
1153 static gss_ctx_id_t
1154 dyndns_get_sec_context(const char *hostname, smb_inaddr_t *dns_ip)
1156 int s;
1157 gss_cred_id_t cred_handle;
1158 gss_ctx_id_t gss_context;
1159 gss_OID oid;
1160 char *key_name, dns_hostname[MAXHOSTNAMELEN];
1162 cred_handle = GSS_C_NO_CREDENTIAL;
1163 oid = GSS_C_NO_OID;
1164 key_name = (char *)hostname;
1166 if (smb_getnameinfo(dns_ip, dns_hostname,
1167 sizeof (dns_hostname), 0)) {
1168 return (NULL);
1170 if ((s = dyndns_open_init_socket(SOCK_STREAM, dns_ip, 53)) < 0) {
1171 return (NULL);
1174 if (dyndns_establish_sec_ctx(&gss_context, cred_handle, s, key_name,
1175 dns_hostname, oid))
1176 gss_context = NULL;
1178 (void) close(s);
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.
1187 * Parameters:
1188 * buf : buffer to build message
1189 * update_zone: the type of zone to update, use UPDATE_FORW for forward
1190 * lookup zone, use UPDATE_REV for reverse lookup zone
1191 * hostname : fully qualified hostname to update DNS with
1192 * ip_addr : IP address of hostname
1193 * life_time : cached time of this entry by others and not within DNS
1194 * database
1195 * update_type: UPDATE_ADD to add entry, UPDATE_DEL to remove entry
1196 * del_type : DEL_ONE for deleting one entry, DEL_ALL for deleting all
1197 * entries of the same resource name. Only valid for UPDATE_DEL.
1198 * addit_cnt : Indicate how many record is in the additional section of
1199 * the DNS message. A value of zero is always used with
1200 * non-secure update message. For secure update message,
1201 * the value will be one because the signed TSIG message
1202 * is added as the additional record of the DNS update message.
1203 * id : DNS message ID. If a positive value then this ID value is
1204 * used, otherwise the next incremented value is used
1205 * level : This is the domain level which we send the request to, level
1206 * zero is the default level, it can go upto 2 in reverse zone
1207 * and virtually to any level in forward zone.
1208 * Returns:
1209 * buf : buffer containing update message
1210 * id : DNS message ID
1211 * int : size of update message
1212 * -1 : error
1214 * This function is changed to handle dynamic DNS update retires to higher
1215 * authoritative domains.
1217 static int
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)
1222 int a, b, c, d;
1223 char *bufptr;
1224 int queryReq, zoneCount, preqCount, updateCount, additionalCount;
1225 char *zone, *resource, *data, zone_buf[100], resrc_buf[100];
1226 int zoneType, zoneClass, type, class, ttl;
1227 char *p;
1228 smb_inaddr_t tmp_addr;
1229 int i, j, k;
1230 int fourcnt;
1232 queryReq = REQ_UPDATE;
1233 zoneCount = 1;
1234 preqCount = 0;
1235 updateCount = 1;
1236 additionalCount = addit_cnt;
1238 (void) memset(buf, 0, NS_PACKETSZ);
1239 bufptr = buf;
1241 if (*id == 0)
1242 *id = dyndns_get_msgid();
1244 if (dyndns_build_header(&bufptr, BUFLEN_UDP(bufptr, buf), *id, queryReq,
1245 zoneCount, preqCount, updateCount, additionalCount, 0) == -1) {
1246 return (-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 */
1256 do {
1257 /* domain */
1258 if ((zone = (char *)strchr(p, '.')) == NULL)
1259 return (-1);
1260 zone += 1;
1261 p = zone;
1262 } while (--level >= 0);
1263 resource = (char *)hostname;
1264 data = (char *)ip_addr;
1265 } else {
1266 if (inet_pton(AF_INET, ip_addr, &tmp_addr) == 1) {
1267 (void) sscanf(ip_addr, "%d.%d.%d.%d", &a, &b, &c, &d);
1268 (void) sprintf(zone_buf, "%d.%d.%d.in-addr.arpa",
1269 c, b, a);
1270 zone = p = zone_buf;
1272 /* Try higher domains based on level requested */
1273 while (--level >= 0) {
1274 /* domain */
1275 if ((zone = (char *)strchr(p, '.')) == NULL) {
1276 return (-1);
1278 zone += 1;
1279 p = zone;
1281 (void) sprintf(resrc_buf, "%d.%d.%d.%d.in-addr.arpa",
1282 d, c, b, a);
1283 } else {
1285 * create reverse nibble ipv6 format
1287 bzero(resrc_buf, 100);
1288 i = 0;
1289 j = 0;
1290 while (ip_addr[i] != 0)
1291 i++;
1292 i--;
1293 while (i >= 0) {
1294 fourcnt = 3;
1295 while ((i >= 0) && (ip_addr[i] != ':')) {
1296 resrc_buf[j++] = ip_addr[i];
1297 (void) strcat(&resrc_buf[j++], ".");
1298 fourcnt --;
1299 i--;
1301 for (k = 0; k <= fourcnt; k++) {
1302 resrc_buf[j++] = '0';
1303 (void) strcat(&resrc_buf[j++], ".");
1305 i--;
1307 (void) strcat(resrc_buf, "ip6.arpa");
1308 (void) strcpy(zone_buf, &resrc_buf[32]);
1309 zone = zone_buf;
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) {
1316 return (-1);
1319 if (update_zone == UPDATE_FORW)
1320 type = ns_t_a;
1321 else
1322 type = ns_t_ptr;
1324 if (update_type == UPDATE_ADD) {
1325 class = ns_c_in;
1326 ttl = life_time;
1327 } else {
1328 if (del_type == DEL_ONE)
1329 class = ns_c_none; /* remove one */
1330 else
1331 class = ns_c_any; /* remove all */
1332 ttl = 0;
1334 if (dyndns_build_update(&bufptr, BUFLEN_UDP(bufptr, buf),
1335 resource, type, class, ttl, data, update_zone,
1336 update_type, del_type) == -1) {
1337 return (-1);
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.
1350 * Parameters:
1351 * buf : buffer to build message
1352 * update_zone: the type of zone to update, use UPDATE_FORW for forward
1353 * lookup zone, use UPDATE_REV for reverse lookup zone
1354 * hostname : fully qualified hostname to update DNS with
1355 * ip_addr : IP address of hostname
1356 * life_time : cached time of this entry by others and not within DNS
1357 * database
1358 * update_type: UPDATE_ADD to add entry, UPDATE_DEL to remove entry
1359 * del_type : DEL_ONE for deleting one entry, DEL_ALL for deleting all
1360 * entries of the same resource name. Only valid for UPDATE_DEL.
1361 * key_name : same key name used in TKEY message
1362 * id : DNS message ID. If a positive value then this ID value is
1363 * used, otherwise the next incremented value is used
1364 * level : This is the domain level which we send the request to, level
1365 * zero is the default level, it can go upto 2 in reverse zone
1366 * and virtually to any level in forward zone.
1367 * Returns:
1368 * buf : buffer containing update message
1369 * id : DNS message ID
1370 * int : size of update message
1371 * -1 : error
1373 static int
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)
1378 char *bufptr;
1379 int buf_sz;
1381 if ((buf_sz = dyndns_build_add_remove_msg(buf, update_zone, hostname,
1382 ip_addr, life_time, update_type, del_type, 0, id, level)) <= 0) {
1383 return (-1);
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) {
1390 return (-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.
1402 * Parameters:
1403 * buf : buffer to build message
1404 * update_zone: the type of zone to update, use UPDATE_FORW for forward
1405 * lookup zone, use UPDATE_REV for reverse lookup zone
1406 * hostname : fully qualified hostname to update DNS with
1407 * ip_addr : IP address of hostname
1408 * life_time : cached time of this entry by others and not within DNS
1409 * database
1410 * update_type: UPDATE_ADD to add entry, UPDATE_DEL to remove entry
1411 * del_type : DEL_ONE for deleting one entry, DEL_ALL for deleting all
1412 * entries of the same resource name. Only valid for UPDATE_DEL.
1413 * key_name : same key name used in TKEY message
1414 * id : DNS message ID. If a positive value then this ID value is
1415 * used, otherwise the next incremented value is used
1416 * in_mic : the update request message encrypted
1417 * level : This is the domain level which we send the request to, level
1418 * zero is the default level, it can go upto 2 in reverse zone
1419 * and virtually to any level in forward zone.
1421 * Returns:
1422 * buf : buffer containing update message
1423 * id : DNS message ID
1424 * int : size of update message
1425 * -1 : error
1427 static int
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)
1432 char *bufptr;
1433 int buf_sz;
1435 if ((buf_sz = dyndns_build_add_remove_msg(buf, update_zone, hostname,
1436 ip_addr, life_time, update_type, del_type, 1, id, level)) <= 0) {
1437 return (-1);
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) {
1445 return (-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
1456 * this function.
1458 * Parameters:
1459 * s : socket descriptor
1460 * buf : buffer containing data to send
1461 * buf_sz : size of data to send
1462 * Returns:
1463 * -1 : error
1464 * rec_buf: reply dat
1465 * 0 : success
1468 static int
1469 dyndns_udp_send_recv(int s, char *buf, int buf_sz, char *rec_buf)
1471 int i, retval, addr_len;
1472 struct timeval tv, timeout;
1473 fd_set rfds;
1474 struct sockaddr_in6 from_addr;
1476 timeout.tv_usec = 0;
1477 timeout.tv_sec = DYNDNS_QUERY_TIMEOUT;
1479 for (i = 0; i <= DYNDNS_MAX_QUERY_RETRIES; i++) {
1480 if (send(s, buf, buf_sz, 0) == -1) {
1481 syslog(LOG_ERR, "dyndns: UDP send error (%s)",
1482 strerror(errno));
1483 return (-1);
1486 FD_ZERO(&rfds);
1487 FD_SET(s, &rfds);
1489 tv = timeout;
1491 retval = select(s+1, &rfds, NULL, NULL, &tv);
1493 if (retval == -1) {
1494 return (-1);
1495 } else if (retval > 0) {
1496 bzero(rec_buf, NS_PACKETSZ);
1497 addr_len = sizeof (struct sockaddr_in6);
1498 if (recvfrom(s, rec_buf, NS_PACKETSZ, 0,
1499 (struct sockaddr *)&from_addr, &addr_len) == -1) {
1500 syslog(LOG_ERR, "dyndns: UDP recv error ");
1501 return (-1);
1503 break;
1507 /* did not receive anything */
1508 if (i == (DYNDNS_MAX_QUERY_RETRIES + 1)) {
1509 syslog(LOG_ERR, "dyndns: max retries for UDP recv reached");
1510 return (-1);
1513 return (0);
1516 * dyndns_sec_add_remove_entry
1517 * Perform secure dynamic DNS update after getting security context.
1518 * This routine opens a UDP socket to the DNS sever, gets the security context,
1519 * builds the unsigned TSIG message and signed TSIG message. The signed TSIG
1520 * message containing the encrypted update request message is sent to the DNS
1521 * server. The response is received and check for error. If there is no
1522 * error then credential handle and security context are released and the local
1523 * NSS cached is purged.
1524 * Parameters:
1525 * update_zone : UPDATE_FORW for forward zone, UPDATE_REV for reverse zone
1526 * hostname : fully qualified hostname
1527 * ip_addr : ip address of hostname in string format
1528 * life_time : cached time of this entry by others and not within DNS
1529 * database
1530 * max_retries : maximum retries for sending DNS update request
1531 * recv_timeout: receive timeout
1532 * update_type : UPDATE_ADD for adding entry, UPDATE_DEL for removing entry
1533 * del_type : DEL_ONE for deleting one entry, DEL_ALL for deleting all
1534 * entries of the same resource name. Only valid for UPDATE_DEL
1535 * dns_str : DNS IP address in string format
1536 * Returns:
1537 * -1: error
1538 * 0: success
1540 * This function is enhanced to handle the case of NOTAUTH error when DNS server
1541 * is not authoritative for specified zone. In this case we need to resend the
1542 * same request to the higher authoritative domains.
1543 * This is true for both secure and unsecure dynamic DNS updates.
1545 static int
1546 dyndns_sec_add_remove_entry(int update_zone, const char *hostname,
1547 const char *ip_addr, int life_time, int update_type, int del_type,
1548 char *dns_str)
1550 int s2;
1551 uint16_t id, rid;
1552 char buf[NS_PACKETSZ], buf2[NS_PACKETSZ];
1553 int ret;
1554 OM_uint32 min, maj;
1555 gss_buffer_desc in_mic, out_mic;
1556 gss_ctx_id_t gss_context;
1557 smb_inaddr_t dns_ip;
1558 char *key_name;
1559 int buf_sz;
1560 int level = 0;
1562 assert(dns_str);
1563 assert(*dns_str);
1565 if (inet_pton(AF_INET, dns_str, &dns_ip) == 1)
1566 dns_ip.a_family = AF_INET;
1567 else if (inet_pton(AF_INET6, dns_str, &dns_ip) == 1)
1568 dns_ip.a_family = AF_INET6;
1570 sec_retry_higher:
1572 if ((gss_context = dyndns_get_sec_context(hostname,
1573 &dns_ip)) == NULL) {
1574 return (-1);
1577 key_name = (char *)hostname;
1579 if ((s2 = dyndns_open_init_socket(SOCK_DGRAM, &dns_ip, 53)) < 0) {
1580 if (gss_context != GSS_C_NO_CONTEXT)
1581 (void) gss_delete_sec_context(&min, &gss_context, NULL);
1582 return (-1);
1585 id = 0;
1586 if ((buf_sz = dyndns_build_unsigned_tsig_msg(buf, update_zone, hostname,
1587 ip_addr, life_time, update_type, del_type,
1588 key_name, &id, level)) <= 0) {
1589 (void) close(s2);
1590 if (gss_context != GSS_C_NO_CONTEXT)
1591 (void) gss_delete_sec_context(&min, &gss_context, NULL);
1592 return (-1);
1595 in_mic.length = buf_sz;
1596 in_mic.value = buf;
1598 /* sign update message */
1599 if ((maj = gss_get_mic(&min, gss_context, 0, &in_mic, &out_mic)) !=
1600 GSS_S_COMPLETE) {
1601 display_stat(maj, min);
1602 (void) close(s2);
1603 if (gss_context != GSS_C_NO_CONTEXT)
1604 (void) gss_delete_sec_context(&min, &gss_context, NULL);
1605 return (-1);
1608 if ((buf_sz = dyndns_build_signed_tsig_msg(buf, update_zone, hostname,
1609 ip_addr, life_time, update_type, del_type, key_name, &id,
1610 &out_mic, level)) <= 0) {
1611 (void) close(s2);
1612 (void) gss_release_buffer(&min, &out_mic);
1613 if (gss_context != GSS_C_NO_CONTEXT)
1614 (void) gss_delete_sec_context(&min, &gss_context, NULL);
1615 return (-1);
1618 (void) gss_release_buffer(&min, &out_mic);
1620 if (dyndns_udp_send_recv(s2, buf, buf_sz, buf2)) {
1621 (void) close(s2);
1622 if (gss_context != GSS_C_NO_CONTEXT)
1623 (void) gss_delete_sec_context(&min, &gss_context, NULL);
1624 return (-1);
1627 (void) close(s2);
1629 if (gss_context != GSS_C_NO_CONTEXT)
1630 (void) gss_delete_sec_context(&min, &gss_context, NULL);
1632 ret = buf2[3] & 0xf; /* error field in UDP */
1635 * If it is a NOTAUTH error we should retry with higher domains
1636 * until we get a successful reply or the maximum retries is met.
1638 if (ret == NOTAUTH && level++ < MAX_AUTH_RETRIES)
1639 goto sec_retry_higher;
1641 /* check here for update request is successful */
1642 if (ret != NOERROR) {
1643 dyndns_syslog(LOG_ERR, ret, "TSIG reply");
1644 return (-1);
1647 (void) dyndns_get_nshort(buf2, &rid);
1648 if (id != rid)
1649 return (-1);
1651 return (0);
1655 * dyndns_seach_entry
1656 * Query DNS server for entry. This routine can indicate if an entry exist
1657 * or not during forward or reverse lookup. Also can indicate if the data
1658 * of the entry matched. For example, for forward lookup, the entry is
1659 * searched using the hostname and the data is the IP address. For reverse
1660 * lookup, the entry is searched using the IP address and the data is the
1661 * hostname.
1662 * Parameters:
1663 * update_zone: UPDATE_FORW for forward zone, UPDATE_REV for reverse zone
1664 * hostname : fully qualified hostname
1665 * ip_addr : ip address of hostname in string format
1666 * update_type: UPDATE_ADD for adding entry, UPDATE_DEL for removing entry
1667 * Returns:
1668 * time_out: no use
1669 * is_match: is 1 for found matching entry, otherwise 0
1670 * 1 : an entry exist but not necessarily match
1671 * 0 : an entry does not exist
1673 /*ARGSUSED*/
1675 static int
1676 dyndns_search_entry(int update_zone, const char *hostname, const char *ip_addr,
1677 int update_type, struct timeval *time_out, int *is_match)
1679 smb_inaddr_t ipaddr, dnsip;
1680 char dns_hostname[NI_MAXHOST];
1681 struct addrinfo hints, *res = NULL;
1682 int salen;
1683 int family;
1685 *is_match = 0;
1686 if (inet_pton(AF_INET, ip_addr, &ipaddr) == 1) {
1687 salen = sizeof (ipaddr.a_ipv4);
1688 family = AF_INET;
1689 } else if (inet_pton(AF_INET6, ip_addr, &ipaddr) == 1) {
1690 salen = sizeof (ipaddr.a_ipv6);
1691 family = AF_INET6;
1693 if (update_zone == UPDATE_FORW) {
1694 bzero((char *)&hints, sizeof (hints));
1695 hints.ai_family = family;
1696 hints.ai_flags = AI_NUMERICHOST;
1697 if (getaddrinfo(hostname, NULL, &hints, &res)) {
1698 return (NULL);
1700 if (res) {
1702 * if both ips aren't the same family skip to
1703 * the next record
1705 do {
1706 if ((res->ai_family == AF_INET) &&
1707 (family == AF_INET)) {
1708 (void) memcpy(&dnsip, &res->ai_addr[0],
1709 salen);
1710 if (ipaddr.a_ipv4 ==
1711 dnsip.a_ipv4) {
1712 *is_match = 1;
1713 break;
1715 } else if ((res->ai_family == AF_INET6) &&
1716 (family == AF_INET6)) {
1717 (void) memcpy(&dnsip, &res->ai_addr[0],
1718 salen);
1719 /* need compare macro here */
1720 if (!memcmp(&ipaddr, &dnsip,
1721 IN6ADDRSZ)) {
1722 *is_match = 1;
1723 break;
1726 } while (res->ai_next);
1727 freeaddrinfo(res);
1728 return (1);
1730 } else {
1731 if (smb_getnameinfo(&ipaddr, dns_hostname, NI_MAXHOST, 0))
1732 return (NULL);
1734 if (strncasecmp(dns_hostname, hostname,
1735 strlen(hostname)) == 0) {
1736 *is_match = 1;
1738 return (1);
1741 /* entry does not exist */
1742 return (0);
1746 * dyndns_add_remove_entry
1747 * Perform non-secure dynamic DNS update. If it fails and host principal
1748 * keys can be found in the local keytab file, secure update will be performed.
1750 * This routine opens a UDP socket to the DNS sever, build the update request
1751 * message, and sends the message to the DNS server. The response is received
1752 * and check for error. If there is no error then the local NSS cached is
1753 * purged. DNS may be used to check to see if an entry already exist before
1754 * adding or to see if an entry does exist before removing it. Adding
1755 * duplicate entries or removing non-existing entries does not cause any
1756 * problems. DNS is not check when doing a delete all.
1757 * Parameters:
1758 * update_zone: UPDATE_FORW for forward zone, UPDATE_REV for reverse zone
1759 * hostname : fully qualified hostname
1760 * ip_addr : ip address of hostname in string format
1761 * life_time : cached time of this entry by others and not within DNS
1762 * database
1763 * update_type: UPDATE_ADD to add entry, UPDATE_DEL to remove entry
1764 * do_check : DNS_CHECK to check first in DNS, DNS_NOCHECK for no DNS
1765 * checking before update
1766 * del_type : DEL_ONE for deleting one entry, DEL_ALL for deleting all
1767 * entries of the same resource name. Only valid for UPDATE_DEL.
1768 * dns_str : DNS IP address in string format
1769 * Returns:
1770 * -1: error
1771 * 0: success
1773 * This function is enhanced to handle the case of NOTAUTH error when DNS server
1774 * is not authoritative for specified zone. In this case we need to resend the
1775 * same request to the higher authoritative domains.
1776 * This is true for both secure and unsecure dynamic DNS updates.
1778 static int
1779 dyndns_add_remove_entry(int update_zone, const char *hostname,
1780 const char *ip_addr, int life_time, int update_type,
1781 int do_check, int del_type, char *dns_str)
1783 int s;
1784 uint16_t id, rid;
1785 char buf[NS_PACKETSZ], buf2[NS_PACKETSZ];
1786 int ret;
1787 int is_exist, is_match;
1788 struct timeval timeout;
1789 int buf_sz;
1790 int level = 0;
1791 smb_inaddr_t dns_ip;
1792 char *fqdn;
1793 char *p;
1795 assert(dns_str);
1796 assert(*dns_str);
1798 if (do_check == DNS_CHECK && del_type != DEL_ALL) {
1799 is_exist = dyndns_search_entry(update_zone, hostname, ip_addr,
1800 update_type, &timeout, &is_match);
1802 if (update_type == UPDATE_ADD && is_exist && is_match) {
1803 return (0);
1804 } else if (update_type == UPDATE_DEL && !is_exist) {
1805 return (0);
1809 if (inet_pton(AF_INET, dns_str, &dns_ip) == 1)
1810 dns_ip.a_family = AF_INET;
1811 else if (inet_pton(AF_INET6, dns_str, &dns_ip) == 1)
1812 dns_ip.a_family = AF_INET6;
1814 retry_higher:
1815 if ((s = dyndns_open_init_socket(SOCK_DGRAM, &dns_ip, 53)) < 0)
1816 return (-1);
1818 id = 0;
1819 if ((buf_sz = dyndns_build_add_remove_msg(buf, update_zone, hostname,
1820 ip_addr, life_time, update_type, del_type, 0, &id, level)) <= 0) {
1821 (void) close(s);
1822 return (-1);
1825 if (dyndns_udp_send_recv(s, buf, buf_sz, buf2)) {
1826 (void) close(s);
1827 return (-1);
1830 (void) close(s);
1832 ret = buf2[3] & 0xf; /* error field in UDP */
1835 * If it is a NOTAUTH error we should retry with higher domains
1836 * until we get a successful reply
1838 if (ret == NOTAUTH && level++ < MAX_AUTH_RETRIES)
1839 goto retry_higher;
1841 /* check here for update request is successful */
1842 if (ret == NOERROR) {
1843 (void) dyndns_get_nshort(buf2, &rid);
1844 if (id != rid)
1845 return (-1);
1846 return (0);
1849 if (ret == NOTIMP) {
1850 dyndns_syslog(LOG_NOTICE, NOTIMP, "dynamic updates");
1851 return (-1);
1852 } else if (ret == NOTAUTH) {
1853 dyndns_syslog(LOG_NOTICE, NOTAUTH, "DNS");
1854 return (-1);
1857 if ((p = strchr(hostname, '.')) == NULL)
1858 return (-1);
1860 fqdn = ++p;
1861 if (smb_krb5_kt_find(SMB_KRB5_PN_ID_HOST_FQHN, fqdn,
1862 SMBNS_KRB5_KEYTAB)) {
1863 ret = dyndns_sec_add_remove_entry(update_zone, hostname,
1864 ip_addr, life_time, update_type, del_type, dns_str);
1865 } else {
1866 syslog(LOG_NOTICE, "dyndns: secure update failed: cannot find "
1867 "host principal \"%s\" in local keytab file.", hostname);
1870 return (ret);
1874 * dyndns_add_entry
1875 * Main routine to add an entry into DNS. The attempt will be made on the
1876 * the servers returned by smb_get_nameserver(). Upon a successful
1877 * attempt on any one of the server, the function will exit with 0.
1878 * Otherwise, -1 is retuned to indicate the update attempt on all the
1879 * nameservers has failed.
1881 * Parameters:
1882 * update_zone: the type of zone to update, use UPDATE_FORW for forward
1883 * lookup zone, use UPDATE_REV for reverse lookup zone
1884 * hostname : fully qualified hostname
1885 * ip_addr : ip address of hostname in string format
1886 * life_time : cached time of this entry by others and not within DNS
1887 * database
1888 * Returns:
1889 * -1: error
1890 * 0: success
1892 static int
1893 dyndns_add_entry(int update_zone, const char *hostname, const char *ip_addr,
1894 int life_time)
1896 const char *dns_str;
1897 char *which_zone;
1898 smb_inaddr_t ns_list[MAXNS];
1899 char dns_buf[INET6_ADDRSTRLEN];
1900 int i, cnt;
1901 int rc = 0;
1903 if (hostname == NULL || ip_addr == NULL) {
1904 return (-1);
1906 cnt = smb_get_nameservers(&ns_list[0], MAXNS);
1908 for (i = 0; i < cnt; i++) {
1909 dns_str = smb_inet_ntop(&ns_list[i], dns_buf,
1910 SMB_IPSTRLEN(ns_list[i].a_family));
1911 if (dns_str == NULL)
1912 continue;
1914 which_zone = (update_zone == UPDATE_FORW) ?
1915 "forward" : "reverse";
1916 syslog(LOG_DEBUG, "dyndns %s lookup zone update %s (%s)",
1917 which_zone, hostname, ip_addr);
1919 if (dyndns_add_remove_entry(update_zone, hostname,
1920 ip_addr, life_time,
1921 UPDATE_ADD, DNS_NOCHECK, DEL_NONE, dns_buf) != -1) {
1922 rc = 1;
1923 break;
1927 return (rc ? 0 : -1);
1931 * dyndns_remove_entry
1932 * Main routine to remove an entry or all entries of the same resource name
1933 * from DNS. The update attempt will be made on the primary DNS server. If
1934 * there is a failure then another attempt will be made on the secondary DNS
1935 * server.
1936 * Parameters:
1937 * update_zone: the type of zone to update, use UPDATE_FORW for forward
1938 * lookup zone, use UPDATE_REV for reverse lookup zone
1939 * hostname : fully qualified hostname
1940 * ip_addr : ip address of hostname in string format
1941 * del_type : DEL_ONE for deleting one entry, DEL_ALL for deleting all
1942 * entries of the same resource name. Only valid for UPDATE_DEL
1943 * Returns:
1944 * -1: error
1945 * 0: success
1947 static int
1948 dyndns_remove_entry(int update_zone, const char *hostname, const char *ip_addr,
1949 int del_type)
1951 const char *dns_str;
1952 smb_inaddr_t ns_list[MAXNS];
1953 char dns_buf[INET6_ADDRSTRLEN];
1954 int i, cnt, scnt;
1956 if ((hostname == NULL || ip_addr == NULL)) {
1957 return (-1);
1959 cnt = smb_get_nameservers(ns_list, MAXNS);
1960 scnt = 0;
1961 for (i = 0; i < cnt; i++) {
1962 dns_str = smb_inet_ntop(&ns_list[i], dns_buf,
1963 SMB_IPSTRLEN(ns_list[i].a_family));
1964 if (dns_str == NULL)
1965 continue;
1966 if (update_zone == UPDATE_FORW) {
1967 if (del_type == DEL_ONE) {
1968 syslog(LOG_DEBUG, "Dynamic update "
1969 "on forward lookup "
1970 "zone for %s (%s)...\n", hostname, ip_addr);
1971 } else {
1972 syslog(LOG_DEBUG, "Removing all "
1973 "entries of %s "
1974 "in forward lookup zone...\n", hostname);
1976 } else {
1977 if (del_type == DEL_ONE) {
1978 syslog(LOG_DEBUG, "Dynamic update "
1979 "on reverse lookup "
1980 "zone for %s (%s)...\n", hostname, ip_addr);
1981 } else {
1982 syslog(LOG_DEBUG, "Removing all "
1983 "entries of %s "
1984 "in reverse lookup zone...\n", ip_addr);
1987 if (dyndns_add_remove_entry(update_zone, hostname, ip_addr, 0,
1988 UPDATE_DEL, DNS_NOCHECK, del_type, dns_buf) != -1) {
1989 scnt++;
1990 break;
1993 if (scnt)
1994 return (0);
1995 return (-1);
1999 * dyndns_update_core
2000 * Perform dynamic update on both forward and reverse lookup zone using
2001 * the specified hostname and IP addresses. Before updating DNS, existing
2002 * host entries with the same hostname in the forward lookup zone are removed
2003 * and existing pointer entries with the same IP addresses in the reverse
2004 * lookup zone are removed. After DNS update, host entries for current
2005 * hostname will show current IP addresses and pointer entries for current
2006 * IP addresses will show current hostname.
2007 * Parameters:
2008 * fqdn - fully-qualified domain name (in lower case)
2010 * Returns:
2011 * -1: some dynamic DNS updates errors
2012 * 0: successful or DDNS disabled.
2015 dyndns_update_core(char *fqdn)
2017 int forw_update_ok, error;
2018 char my_ip[INET6_ADDRSTRLEN];
2019 const char *my_str;
2020 smb_niciter_t ni;
2021 int rc;
2022 char fqhn[MAXHOSTNAMELEN];
2024 if (fqdn == NULL || *fqdn == '\0')
2025 return (0);
2027 if (!smb_config_getbool(SMB_CI_DYNDNS_ENABLE))
2028 return (0);
2029 if (smb_gethostname(fqhn, MAXHOSTNAMELEN, SMB_CASE_LOWER) != 0)
2030 return (-1);
2033 * To comply with RFC 4120 section 6.2.1, the fully-qualified hostname
2034 * must be set to lower case.
2036 (void) snprintf(fqhn, MAXHOSTNAMELEN, "%s.%s", fqhn, fqdn);
2038 error = 0;
2039 forw_update_ok = 0;
2042 * Dummy IP is okay since we are removing all using the hostname.
2044 if (dyndns_remove_entry(UPDATE_FORW, fqhn, "1.1.1.1", DEL_ALL) == 0) {
2045 forw_update_ok = 1;
2046 } else {
2047 error++;
2050 if (smb_nic_getfirst(&ni) != SMB_NIC_SUCCESS)
2051 return (-1);
2053 do {
2054 if (ni.ni_nic.nic_sysflags & IFF_PRIVATE)
2055 continue;
2056 /* first try ipv4, then ipv6 */
2057 my_str = smb_inet_ntop(&ni.ni_nic.nic_ip, my_ip,
2058 SMB_IPSTRLEN(ni.ni_nic.nic_ip.a_family));
2059 if (my_str == NULL) {
2060 error++;
2061 continue;
2064 if (forw_update_ok) {
2065 rc = dyndns_add_entry(UPDATE_FORW, fqhn, my_str,
2066 DDNS_TTL);
2068 if (rc == -1)
2069 error++;
2072 rc = dyndns_remove_entry(UPDATE_REV, fqhn, my_str, DEL_ALL);
2073 if (rc == 0) {
2074 rc = dyndns_add_entry(UPDATE_REV, fqhn, my_str,
2075 DDNS_TTL);
2078 if (rc == -1)
2079 error++;
2081 } while (smb_nic_getnext(&ni) == SMB_NIC_SUCCESS);
2083 return ((error == 0) ? 0 : -1);
2087 * dyndns_clear_rev_zone
2088 * Clear the rev zone records. Must be called to clear the OLD if list
2089 * of down records prior to updating the list with new information.
2091 * Parameters:
2092 * fqdn - fully-qualified domain name (in lower case)
2093 * Returns:
2094 * -1: some dynamic DNS updates errors
2095 * 0: successful or DDNS disabled.
2098 dyndns_clear_rev_zone(char *fqdn)
2100 int error;
2101 char my_ip[INET6_ADDRSTRLEN];
2102 smb_niciter_t ni;
2103 int rc;
2104 char fqhn[MAXHOSTNAMELEN];
2105 const char *my_str;
2107 if (!smb_config_getbool(SMB_CI_DYNDNS_ENABLE))
2108 return (0);
2110 if (smb_gethostname(fqhn, MAXHOSTNAMELEN, SMB_CASE_LOWER) != 0)
2111 return (-1);
2114 * To comply with RFC 4120 section 6.2.1, the fully-qualified hostname
2115 * must be set to lower case.
2117 (void) snprintf(fqhn, MAXHOSTNAMELEN, "%s.%s", fqhn, fqdn);
2119 error = 0;
2121 if (smb_nic_getfirst(&ni) != SMB_NIC_SUCCESS)
2122 return (-1);
2124 do {
2125 if (ni.ni_nic.nic_sysflags & IFF_PRIVATE)
2126 continue;
2127 my_str = smb_inet_ntop(&ni.ni_nic.nic_ip, my_ip,
2128 SMB_IPSTRLEN(ni.ni_nic.nic_ip.a_family));
2129 if (my_str == NULL) {
2130 error++;
2131 continue;
2134 rc = dyndns_remove_entry(UPDATE_REV, fqhn, my_ip, DEL_ALL);
2135 if (rc != 0)
2136 error++;
2138 } while (smb_nic_getnext(&ni) == SMB_NIC_SUCCESS);
2140 return ((error == 0) ? 0 : -1);