1 /* 2 * CDDL HEADER START 3 * 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. 7 * 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. 12 * 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] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #include <assert.h> 27 #include <errno.h> 28 #include <stdio.h> 29 #include <stdlib.h> 30 #include <strings.h> 31 #include <sys/types.h> 32 #include <sys/socket.h> 33 #include <netinet/in.h> 34 #include <arpa/inet.h> 35 #include <arpa/nameser.h> 36 #include <net/if.h> 37 #include <resolv.h> 38 #include <sys/time.h> 39 #include <unistd.h> 40 #include <string.h> 41 #include <pthread.h> 42 #include <netdb.h> 43 #include <rpc/rpc.h> 44 #include <syslog.h> 45 #include <gssapi/gssapi.h> 46 #include <kerberosv5/krb5.h> 47 48 #include <smbns_dyndns.h> 49 #include <smbns_krb.h> 50 51 /* 52 * The following can be removed once head/arpa/nameser_compat.h 53 * defines BADSIG, BADKEY and BADTIME. 54 */ 55 #ifndef BADSIG 56 #define BADSIG ns_r_badsig 57 #endif /* BADSIG */ 58 59 #ifndef BADKEY 60 #define BADKEY ns_r_badkey 61 #endif /* BADKEY */ 62 63 #ifndef BADTIME 64 #define BADTIME ns_r_badtime 65 #endif /* BADTIME */ 66 67 /* internal use, in dyndns_add_entry */ 68 #define DEL_NONE 2 69 70 /* Maximum retires if not authoritative */ 71 #define MAX_AUTH_RETRIES 3 72 73 /* Number of times to retry a DNS query */ 74 #define DYNDNS_MAX_QUERY_RETRIES 3 75 76 /* Timeout value, in seconds, for DNS query responses */ 77 #define DYNDNS_QUERY_TIMEOUT 2 78 79 static uint16_t dns_msgid; 80 mutex_t dns_msgid_mtx; 81 82 #define DYNDNS_OP_CLEAR 1 83 #define DYNDNS_OP_UPDATE 2 84 85 #define DYNDNS_STATE_INIT 0 86 #define DYNDNS_STATE_READY 1 87 #define DYNDNS_STATE_PUBLISHING 2 88 #define DYNDNS_STATE_STOPPING 3 89 90 typedef struct dyndns_qentry { 91 list_node_t dqe_lnd; 92 int dqe_op; 93 char dqe_fqdn[MAXHOSTNAMELEN]; 94 } dyndns_qentry_t; 95 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; 102 103 static dyndns_queue_t dyndns_queue; 104 105 static void dyndns_queue_request(int, const char *); 106 static void dyndns_queue_flush(list_t *); 107 static void *dyndns_publisher(void *); 108 static void dyndns_process(list_t *); 109 static int dyndns_update_core(char *); 110 static int dyndns_clear_rev_zone(char *); 111 static void dyndns_msgid_init(void); 112 static int dyndns_get_msgid(void); 113 static void dyndns_syslog(int, int, const char *); 114 115 int 116 dyndns_start(void) 117 { 118 pthread_t publisher; 119 pthread_attr_t tattr; 120 int rc; 121 122 if (!smb_config_getbool(SMB_CI_DYNDNS_ENABLE)) 123 return (0); 124 125 (void) mutex_lock(&dyndns_queue.ddq_mtx); 126 if (dyndns_queue.ddq_state != DYNDNS_STATE_INIT) { 127 (void) mutex_unlock(&dyndns_queue.ddq_mtx); 128 return (0); 129 } 130 131 dyndns_msgid_init(); 132 133 list_create(&dyndns_queue.ddq_list, sizeof (dyndns_qentry_t), 134 offsetof(dyndns_qentry_t, dqe_lnd)); 135 dyndns_queue.ddq_state = DYNDNS_STATE_READY; 136 (void) mutex_unlock(&dyndns_queue.ddq_mtx); 137 138 (void) pthread_attr_init(&tattr); 139 (void) pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED); 140 rc = pthread_create(&publisher, &tattr, dyndns_publisher, 0); 141 (void) pthread_attr_destroy(&tattr); 142 return (rc); 143 } 144 145 void 146 dyndns_stop(void) 147 { 148 (void) mutex_lock(&dyndns_queue.ddq_mtx); 149 150 switch (dyndns_queue.ddq_state) { 151 case DYNDNS_STATE_READY: 152 case DYNDNS_STATE_PUBLISHING: 153 dyndns_queue.ddq_state = DYNDNS_STATE_STOPPING; 154 (void) cond_signal(&dyndns_queue.ddq_cv); 155 break; 156 default: 157 break; 158 } 159 160 (void) mutex_unlock(&dyndns_queue.ddq_mtx); 161 } 162 163 /* 164 * Clear all records in both zones. 165 */ 166 void 167 dyndns_clear_zones(void) 168 { 169 char fqdn[MAXHOSTNAMELEN]; 170 171 if (smb_getfqdomainname(fqdn, MAXHOSTNAMELEN) != 0) { 172 syslog(LOG_ERR, "dyndns: failed to get domainname"); 173 return; 174 } 175 176 dyndns_queue_request(DYNDNS_OP_CLEAR, fqdn); 177 } 178 179 /* 180 * Update all records in both zones. 181 */ 182 void 183 dyndns_update_zones(void) 184 { 185 char fqdn[MAXHOSTNAMELEN]; 186 187 if (smb_getfqdomainname(fqdn, MAXHOSTNAMELEN) != 0) { 188 syslog(LOG_ERR, "dyndns: failed to get domainname"); 189 return; 190 } 191 192 dyndns_queue_request(DYNDNS_OP_UPDATE, fqdn); 193 } 194 195 /* 196 * Add a request to the queue. 197 */ 198 static void 199 dyndns_queue_request(int op, const char *fqdn) 200 { 201 dyndns_qentry_t *entry; 202 203 if (!smb_config_getbool(SMB_CI_DYNDNS_ENABLE)) 204 return; 205 206 (void) mutex_lock(&dyndns_queue.ddq_mtx); 207 208 switch (dyndns_queue.ddq_state) { 209 case DYNDNS_STATE_READY: 210 case DYNDNS_STATE_PUBLISHING: 211 break; 212 default: 213 (void) mutex_unlock(&dyndns_queue.ddq_mtx); 214 return; 215 } 216 217 if ((entry = malloc(sizeof (dyndns_qentry_t))) == NULL) { 218 (void) mutex_unlock(&dyndns_queue.ddq_mtx); 219 return; 220 } 221 222 bzero(entry, sizeof (dyndns_qentry_t)); 223 entry->dqe_op = op; 224 (void) strlcpy(entry->dqe_fqdn, fqdn, MAXNAMELEN); 225 226 list_insert_tail(&dyndns_queue.ddq_list, entry); 227 (void) cond_signal(&dyndns_queue.ddq_cv); 228 (void) mutex_unlock(&dyndns_queue.ddq_mtx); 229 } 230 231 /* 232 * Flush all remaining items from the specified list/queue. 233 */ 234 static void 235 dyndns_queue_flush(list_t *lst) 236 { 237 dyndns_qentry_t *entry; 238 239 while ((entry = list_head(lst)) != NULL) { 240 list_remove(lst, entry); 241 free(entry); 242 } 243 } 244 245 /* 246 * Dyndns update thread. While running, the thread waits on a condition 247 * variable until notified that an entry needs to be updated. 248 * 249 * If the outgoing queue is not empty, the thread wakes up every 60 seconds 250 * to retry. 251 */ 252 /*ARGSUSED*/ 253 static void * 254 dyndns_publisher(void *arg) 255 { 256 dyndns_qentry_t *entry; 257 list_t publist; 258 259 (void) mutex_lock(&dyndns_queue.ddq_mtx); 260 if (dyndns_queue.ddq_state != DYNDNS_STATE_READY) { 261 (void) mutex_unlock(&dyndns_queue.ddq_mtx); 262 return (NULL); 263 } 264 dyndns_queue.ddq_state = DYNDNS_STATE_PUBLISHING; 265 (void) mutex_unlock(&dyndns_queue.ddq_mtx); 266 267 list_create(&publist, sizeof (dyndns_qentry_t), 268 offsetof(dyndns_qentry_t, dqe_lnd)); 269 270 for (;;) { 271 (void) mutex_lock(&dyndns_queue.ddq_mtx); 272 273 while (list_is_empty(&dyndns_queue.ddq_list) && 274 (dyndns_queue.ddq_state == DYNDNS_STATE_PUBLISHING)) { 275 (void) cond_wait(&dyndns_queue.ddq_cv, 276 &dyndns_queue.ddq_mtx); 277 } 278 279 if (dyndns_queue.ddq_state != DYNDNS_STATE_PUBLISHING) { 280 (void) mutex_unlock(&dyndns_queue.ddq_mtx); 281 break; 282 } 283 284 /* 285 * Transfer queued items to the local list so that 286 * the mutex can be released. 287 */ 288 while ((entry = list_head(&dyndns_queue.ddq_list)) != NULL) { 289 list_remove(&dyndns_queue.ddq_list, entry); 290 list_insert_tail(&publist, entry); 291 } 292 293 (void) mutex_unlock(&dyndns_queue.ddq_mtx); 294 295 dyndns_process(&publist); 296 } 297 298 (void) mutex_lock(&dyndns_queue.ddq_mtx); 299 dyndns_queue_flush(&dyndns_queue.ddq_list); 300 list_destroy(&dyndns_queue.ddq_list); 301 dyndns_queue.ddq_state = DYNDNS_STATE_INIT; 302 (void) mutex_unlock(&dyndns_queue.ddq_mtx); 303 304 dyndns_queue_flush(&publist); 305 list_destroy(&publist); 306 return (NULL); 307 } 308 309 /* 310 * Remove items from the queue and process them. 311 */ 312 static void 313 dyndns_process(list_t *publist) 314 { 315 dyndns_qentry_t *entry; 316 317 while ((entry = list_head(publist)) != NULL) { 318 (void) mutex_lock(&dyndns_queue.ddq_mtx); 319 if (dyndns_queue.ddq_state != DYNDNS_STATE_PUBLISHING) { 320 (void) mutex_unlock(&dyndns_queue.ddq_mtx); 321 dyndns_queue_flush(publist); 322 return; 323 } 324 (void) mutex_unlock(&dyndns_queue.ddq_mtx); 325 326 list_remove(publist, entry); 327 328 switch (entry->dqe_op) { 329 case DYNDNS_OP_CLEAR: 330 (void) dyndns_clear_rev_zone(entry->dqe_fqdn); 331 break; 332 case DYNDNS_OP_UPDATE: 333 (void) dyndns_update_core(entry->dqe_fqdn); 334 break; 335 default: 336 break; 337 } 338 339 free(entry); 340 } 341 } 342 343 /* 344 * Dynamic DNS update API for kclient. 345 * 346 * Returns 0 upon success. Otherwise, returns -1. 347 */ 348 int 349 dyndns_update(char *fqdn) 350 { 351 int rc; 352 353 if (smb_nic_init() != 0) 354 return (-1); 355 356 dyndns_msgid_init(); 357 rc = dyndns_update_core(fqdn); 358 smb_nic_fini(); 359 return (rc); 360 } 361 362 /* 363 * Initializes the DNS message ID counter using the algorithm 364 * that resolver library uses to initialize the ID field of any res 365 * structure. 366 */ 367 static void 368 dyndns_msgid_init(void) 369 { 370 struct timeval now; 371 372 (void) gettimeofday(&now, NULL); 373 (void) mutex_lock(&dns_msgid_mtx); 374 dns_msgid = (0xffff & (now.tv_sec ^ now.tv_usec ^ getpid())); 375 (void) mutex_unlock(&dns_msgid_mtx); 376 } 377 378 static int 379 dyndns_get_msgid(void) 380 { 381 uint16_t id; 382 383 (void) mutex_lock(&dns_msgid_mtx); 384 id = ++dns_msgid; 385 (void) mutex_unlock(&dns_msgid_mtx); 386 return (id); 387 } 388 389 /* 390 * Log a DNS error message 391 */ 392 static void 393 dyndns_syslog(int severity, int errnum, const char *text) 394 { 395 struct { 396 int errnum; 397 char *errmsg; 398 } errtab[] = { 399 { FORMERR, "message format error" }, 400 { SERVFAIL, "server internal error" }, 401 { NXDOMAIN, "entry should exist but does not exist" }, 402 { NOTIMP, "not supported" }, 403 { REFUSED, "operation refused" }, 404 { YXDOMAIN, "entry should not exist but does exist" }, 405 { YXRRSET, "RRSet should not exist but does exist" }, 406 { NXRRSET, "RRSet should exist but does not exist" }, 407 { NOTAUTH, "server is not authoritative for specified zone" }, 408 { NOTZONE, "name not within specified zone" }, 409 { BADSIG, "bad transaction signature (TSIG)" }, 410 { BADKEY, "bad transaction key (TKEY)" }, 411 { BADTIME, "time not synchronized" }, 412 }; 413 414 char *errmsg = "unknown error"; 415 int i; 416 417 if (errnum == NOERROR) 418 return; 419 420 for (i = 0; i < (sizeof (errtab) / sizeof (errtab[0])); ++i) { 421 if (errtab[i].errnum == errnum) { 422 errmsg = errtab[i].errmsg; 423 break; 424 } 425 } 426 427 syslog(severity, "dyndns: %s: %s: %d", text, errmsg, errnum); 428 } 429 430 /* 431 * display_stat 432 * Display GSS error message from error code. This routine is used to display 433 * the mechanism independent and mechanism specific error messages for GSS 434 * routines. The major status error code is the mechanism independent error 435 * code and the minor status error code is the mechanism specific error code. 436 * Parameters: 437 * maj: GSS major status 438 * min: GSS minor status 439 * Returns: 440 * None 441 */ 442 static void 443 display_stat(OM_uint32 maj, OM_uint32 min) 444 { 445 gss_buffer_desc msg; 446 OM_uint32 msg_ctx = 0; 447 OM_uint32 min2; 448 449 (void) gss_display_status(&min2, maj, GSS_C_GSS_CODE, GSS_C_NULL_OID, 450 &msg_ctx, &msg); 451 syslog(LOG_ERR, "dyndns: GSS major status error: %s", 452 (char *)msg.value); 453 (void) gss_release_buffer(&min2, &msg); 454 455 (void) gss_display_status(&min2, min, GSS_C_MECH_CODE, GSS_C_NULL_OID, 456 &msg_ctx, &msg); 457 syslog(LOG_ERR, "dyndns: GSS minor status error: %s", 458 (char *)msg.value); 459 (void) gss_release_buffer(&min2, &msg); 460 } 461 462 static char * 463 dyndns_put_nshort(char *buf, uint16_t val) 464 { 465 uint16_t nval; 466 467 nval = htons(val); 468 (void) memcpy(buf, &nval, sizeof (uint16_t)); 469 buf += sizeof (uint16_t); 470 return (buf); 471 } 472 473 static char * 474 dyndns_get_nshort(char *buf, uint16_t *val) 475 { 476 uint16_t nval; 477 478 (void) memcpy(&nval, buf, sizeof (uint16_t)); 479 *val = ntohs(nval); 480 buf += sizeof (uint16_t); 481 return (buf); 482 } 483 484 static char * 485 dyndns_put_nlong(char *buf, uint32_t val) 486 { 487 uint32_t lval; 488 489 lval = htonl(val); 490 (void) memcpy(buf, &lval, sizeof (uint32_t)); 491 buf += sizeof (uint32_t); 492 return (buf); 493 } 494 495 static char * 496 dyndns_put_byte(char *buf, char val) 497 { 498 *buf = val; 499 buf++; 500 return (buf); 501 } 502 503 504 505 506 static char * 507 dyndns_put_int(char *buf, int val) 508 { 509 (void) memcpy(buf, &val, sizeof (int)); 510 buf += sizeof (int); 511 return (buf); 512 } 513 514 static char * 515 dyndns_put_v6addr(char *buf, smb_inaddr_t *val) 516 { 517 518 val->a_family = AF_INET6; 519 (void) memcpy(buf, &val->a_ipv6, IN6ADDRSZ); 520 buf += IN6ADDRSZ; 521 return (buf); 522 } 523 /* 524 * dyndns_stuff_str 525 * Converts a domain string by removing periods and replacing with a byte value 526 * of how many characters following period. A byte value is placed in front 527 * to indicate how many characters before first period. A NULL character is 528 * placed at the end. i.e. host.procom.com -> 4host5procom3com0 529 * Buffer space checking is done by caller. 530 * Parameters: 531 * ptr : address of pointer to buffer to store converted string 532 * zone: domain name string 533 * Returns: 534 * ptr: address of pointer to next available buffer space 535 * -1 : error 536 * 0 : success 537 */ 538 static int 539 dyndns_stuff_str(char **ptr, char *zone) 540 { 541 int len; 542 char *lenPtr, *zonePtr; 543 544 for (zonePtr = zone; *zonePtr; ) { 545 lenPtr = *ptr; 546 *ptr = *ptr + 1; 547 len = 0; 548 while (*zonePtr != '.' && *zonePtr != 0) { 549 *ptr = dyndns_put_byte(*ptr, *zonePtr); 550 zonePtr++; 551 len++; 552 } 553 *lenPtr = len; 554 if (*zonePtr == '.') 555 zonePtr++; 556 } 557 *ptr = dyndns_put_byte(*ptr, 0); 558 return (0); 559 } 560 561 /* 562 * dyndns_build_header 563 * Build the header for DNS query and DNS update request message. 564 * Parameters: 565 * ptr : address of pointer to buffer to store header 566 * buf_len : buffer length 567 * msg_id : message id 568 * query_req : use REQ_QUERY for query message or REQ_UPDATE for 569 * update message 570 * quest_zone_cnt : number of question record for query message or 571 * number of zone record for update message 572 * ans_prereq_cnt : number of answer record for query message or 573 * number of prerequisite record for update message 574 * nameser_update_cnt: number of name server for query message or 575 * number of update record for update message 576 * addit_cnt : number of additional record 577 * flags : query flags word 578 * Returns: 579 * ptr: address of pointer to next available buffer space 580 * -1 : error 581 * 0 : success 582 */ 583 static int 584 dyndns_build_header(char **ptr, int buf_len, uint16_t msg_id, int query_req, 585 uint16_t quest_zone_cnt, uint16_t ans_prereq_cnt, 586 uint16_t nameser_update_cnt, uint16_t addit_cnt, int flags) 587 { 588 uint16_t opcode; 589 590 if (buf_len < 12) { 591 syslog(LOG_ERR, "dyndns header section: buffer too small"); 592 return (-1); 593 } 594 595 *ptr = dyndns_put_nshort(*ptr, msg_id); /* mesg ID */ 596 if (query_req == REQ_QUERY) 597 opcode = ns_o_query; /* query msg */ 598 else 599 opcode = ns_o_update << 11; /* update msg */ 600 opcode |= flags; 601 /* mesg opcode */ 602 *ptr = dyndns_put_nshort(*ptr, opcode); 603 /* zone record count */ 604 *ptr = dyndns_put_nshort(*ptr, quest_zone_cnt); 605 /* prerequiste record count */ 606 *ptr = dyndns_put_nshort(*ptr, ans_prereq_cnt); 607 /* update record count */ 608 *ptr = dyndns_put_nshort(*ptr, nameser_update_cnt); 609 /* additional record count */ 610 *ptr = dyndns_put_nshort(*ptr, addit_cnt); 611 612 return (0); 613 } 614 615 /* 616 * dyndns_build_quest_zone 617 * Build the question section for query message or zone section for 618 * update message. 619 * Parameters: 620 * ptr : address of pointer to buffer to store question or zone section 621 * buf_len: buffer length 622 * name : question or zone name 623 * type : type of question or zone 624 * class : class of question or zone 625 * Returns: 626 * ptr: address of pointer to next available buffer space 627 * -1 : error 628 * 0 : success 629 */ 630 static int 631 dyndns_build_quest_zone(char **ptr, int buf_len, char *name, int type, 632 int class) 633 { 634 char *zonePtr; 635 636 if ((strlen(name) + 6) > buf_len) { 637 syslog(LOG_ERR, "dyndns question section: buffer too small"); 638 return (-1); 639 } 640 641 zonePtr = *ptr; 642 (void) dyndns_stuff_str(&zonePtr, name); 643 *ptr = zonePtr; 644 *ptr = dyndns_put_nshort(*ptr, type); 645 *ptr = dyndns_put_nshort(*ptr, class); 646 return (0); 647 } 648 649 /* 650 * dyndns_build_update 651 * Build update section of update message for adding and removing a record. 652 * If the ttl value is 0 then this message is for record deletion. 653 * 654 * Parameters: 655 * ptr : address of pointer to buffer to store update section 656 * buf_len : buffer length 657 * name : resource name of this record 658 * type : type of this record 659 * class : class of this record 660 * ttl : time-to-live, cached time of this entry by others and not 661 * within DNS database, a zero value for record(s) deletion 662 * data : data of this resource record 663 * forw_rev: UPDATE_FORW for forward zone, UPDATE_REV for reverse zone 664 * add_del : UPDATE_ADD for adding entry, UPDATE_DEL for removing zone 665 * del_type: DEL_ONE for deleting one entry, DEL_ALL for deleting all 666 * entries of the same resource name. Only valid for UPDATE_DEL. 667 * Returns: 668 * ptr: address of pointer to next available buffer space 669 * -1 : error 670 * 0 : success 671 */ 672 static int 673 dyndns_build_update(char **ptr, int buf_len, char *name, int type, int class, 674 uint32_t ttl, char *data, int forw_rev, int add_del, int del_type) 675 { 676 char *namePtr; 677 int rec_len, data_len; 678 smb_inaddr_t ipaddr; 679 int isv4 = 1; 680 681 rec_len = strlen(name) + 10; 682 if (inet_pton(AF_INET, data, &ipaddr) == 1) 683 isv4 = 1; 684 else if (inet_pton(AF_INET6, data, &ipaddr) == 1) 685 isv4 = 0; 686 687 if (add_del == UPDATE_ADD) { 688 if (forw_rev == UPDATE_FORW) 689 data_len = isv4 ? 4 : 16; 690 else 691 data_len = strlen(data) + 2; 692 } else { 693 if (del_type == DEL_ALL) 694 data_len = 0; 695 else if (forw_rev == UPDATE_FORW) 696 data_len = isv4 ? 4 : 16; 697 else 698 data_len = strlen(data) + 2; 699 } 700 if (rec_len + data_len > buf_len) { 701 syslog(LOG_ERR, "dyndns update section: buffer too small"); 702 return (-1); 703 } 704 705 namePtr = *ptr; 706 (void) dyndns_stuff_str(&namePtr, name); 707 *ptr = namePtr; 708 if (isv4) 709 *ptr = dyndns_put_nshort(*ptr, type); 710 else 711 *ptr = dyndns_put_nshort(*ptr, ns_t_aaaa); 712 *ptr = dyndns_put_nshort(*ptr, class); 713 *ptr = dyndns_put_nlong(*ptr, ttl); 714 715 if (add_del == UPDATE_DEL && del_type == DEL_ALL) { 716 *ptr = dyndns_put_nshort(*ptr, 0); 717 return (0); 718 } 719 720 if (forw_rev == UPDATE_FORW) { 721 if (isv4) { 722 *ptr = dyndns_put_nshort(*ptr, 4); 723 *ptr = dyndns_put_int(*ptr, ipaddr.a_ipv4); 724 } else { 725 *ptr = dyndns_put_nshort(*ptr, 16); 726 *ptr = dyndns_put_v6addr(*ptr, &ipaddr); 727 } 728 } else { 729 *ptr = dyndns_put_nshort(*ptr, strlen(data)+2); 730 namePtr = *ptr; 731 (void) dyndns_stuff_str(&namePtr, data); /* hostname */ 732 *ptr = namePtr; 733 } 734 return (0); 735 } 736 737 /* 738 * dyndns_build_tkey 739 * Build TKEY section to establish security context for secure dynamic DNS 740 * update. DNS header and question sections need to be build before this 741 * section. The TKEY data are the tokens generated during security context 742 * establishment and the TKEY message is used to transmit those tokens, one 743 * at a time, to the DNS server. 744 * Parameters: 745 * ptr : address of pointer to buffer to store TKEY 746 * buf_len : buffer length 747 * name : key name, must be unique and same as for TSIG record 748 * key_expire: expiration time of this key in second 749 * data : TKEY data 750 * data_size : data size 751 * Returns: 752 * ptr: address of the pointer to the next available buffer space 753 * -1 : error 754 * 0 : success 755 */ 756 static int 757 dyndns_build_tkey(char **ptr, int buf_len, char *name, int key_expire, 758 char *data, int data_size) 759 { 760 char *namePtr; 761 struct timeval tp; 762 763 if (strlen(name)+2 + 45 + data_size > buf_len) { 764 syslog(LOG_ERR, "dyndns TKEY: buffer too small"); 765 return (-1); 766 } 767 768 namePtr = *ptr; 769 (void) dyndns_stuff_str(&namePtr, name); /* unique global name */ 770 *ptr = namePtr; 771 *ptr = dyndns_put_nshort(*ptr, ns_t_tkey); 772 *ptr = dyndns_put_nshort(*ptr, ns_c_any); 773 *ptr = dyndns_put_nlong(*ptr, 0); 774 /* 19 + 14 + data_size + 2 */ 775 *ptr = dyndns_put_nshort(*ptr, 35 + data_size); 776 namePtr = *ptr; 777 (void) dyndns_stuff_str(&namePtr, "gss.microsoft.com"); 778 *ptr = namePtr; 779 (void) gettimeofday(&tp, 0); 780 *ptr = dyndns_put_nlong(*ptr, tp.tv_sec); /* inception */ 781 /* expiration, 86400 */ 782 *ptr = dyndns_put_nlong(*ptr, tp.tv_sec + key_expire); 783 *ptr = dyndns_put_nshort(*ptr, MODE_GSS_API); /* mode: gss-api */ 784 *ptr = dyndns_put_nshort(*ptr, 0); /* error */ 785 *ptr = dyndns_put_nshort(*ptr, data_size); /* key size */ 786 (void) memcpy(*ptr, data, data_size); /* key data */ 787 *ptr += data_size; 788 *ptr = dyndns_put_nshort(*ptr, 0); /* other */ 789 return (0); 790 } 791 792 /* 793 * dyndns_build_tsig 794 * Build TSIG section for secure dynamic DNS update. This routine will be 795 * called twice. First called with TSIG_UNSIGNED, and second with TSIG_SIGNED. 796 * The TSIG data is NULL and ignored for TSIG_UNSIGNED and is the update request 797 * message encrypted for TSIG_SIGNED. The message id must be the same id as the 798 * one in the update request before it is encrypted. 799 * Parameters: 800 * ptr : address of pointer to buffer to store TSIG 801 * buf_len : buffer length 802 * msg_id : message id 803 * name : key name, must be the same as in TKEY record 804 * fudge_time : amount of error time allow in seconds 805 * data : TSIG data if TSIG_SIGNED, otherwise NULL 806 * data_size : size of data, otherwise 0 if data is NULL 807 * data_signed: TSIG_SIGNED to indicate data is signed and encrypted, 808 * otherwise TSIG_UNSIGNED 809 * Returns: 810 * ptr: address of pointer to next available buffer space 811 * -1 : error 812 * 0 : success 813 */ 814 static int 815 dyndns_build_tsig(char **ptr, int buf_len, int msg_id, char *name, 816 int fudge_time, char *data, int data_size, int data_signed) 817 { 818 char *namePtr; 819 struct timeval tp; 820 int signtime, fudge, rec_len; 821 822 if (data_signed == TSIG_UNSIGNED) 823 rec_len = strlen(name)+2 + 37; 824 else 825 rec_len = strlen(name)+2 + 45 + data_size; 826 827 if (rec_len > buf_len) { 828 syslog(LOG_ERR, "dyndns TSIG: buffer too small"); 829 return (-1); 830 } 831 832 namePtr = *ptr; 833 (void) dyndns_stuff_str(&namePtr, name); /* unique global name */ 834 *ptr = namePtr; 835 if (data_signed == TSIG_SIGNED) 836 *ptr = dyndns_put_nshort(*ptr, ns_t_tsig); 837 *ptr = dyndns_put_nshort(*ptr, ns_c_any); 838 *ptr = dyndns_put_nlong(*ptr, 0); 839 if (data_signed == TSIG_SIGNED) { 840 /* 19 + 10 + data_size + 6 */ 841 *ptr = dyndns_put_nshort(*ptr, 35 + data_size); 842 } 843 namePtr = *ptr; 844 (void) dyndns_stuff_str(&namePtr, "gss.microsoft.com"); 845 *ptr = namePtr; 846 (void) gettimeofday(&tp, 0); 847 signtime = tp.tv_sec >> 16; 848 *ptr = dyndns_put_nlong(*ptr, signtime); /* sign time */ 849 fudge = tp.tv_sec << 16; 850 fudge |= fudge_time; 851 *ptr = dyndns_put_nlong(*ptr, fudge); /* fudge time */ 852 if (data_signed == TSIG_SIGNED) { 853 /* signed data size */ 854 *ptr = dyndns_put_nshort(*ptr, data_size); 855 (void) memcpy(*ptr, data, data_size); /* signed data */ 856 *ptr += data_size; 857 *ptr = dyndns_put_nshort(*ptr, msg_id); /* original id */ 858 } 859 *ptr = dyndns_put_nshort(*ptr, 0); /* error */ 860 *ptr = dyndns_put_nshort(*ptr, 0); /* other */ 861 return (0); 862 } 863 864 /* 865 * dyndns_open_init_socket 866 * This routine creates a SOCK_STREAM or SOCK_DGRAM socket and initializes it 867 * by doing bind() and setting linger option to off. 868 * 869 * Parameters: 870 * sock_type: SOCK_STREAM for TCP or SOCK_DGRAM for UDP 871 * dest_addr: destination address in network byte order 872 * port : destination port number 873 * Returns: 874 * descriptor: descriptor referencing the created socket 875 * -1 : error 876 */ 877 878 static int 879 dyndns_open_init_socket(int sock_type, smb_inaddr_t *dest_addr, int port) 880 { 881 int s; 882 struct sockaddr_in my_addr; 883 struct sockaddr_in6 my6_addr; 884 struct sockaddr_in serv_addr; 885 struct sockaddr_in6 serv6_addr; 886 int family; 887 888 family = dest_addr->a_family; 889 890 if ((s = socket(family, sock_type, 0)) == -1) { 891 syslog(LOG_ERR, "dyndns: socket error\n"); 892 return (-1); 893 } 894 if (family == AF_INET) { 895 bzero(&my_addr, sizeof (my_addr)); 896 my_addr.sin_family = family; 897 my_addr.sin_port = htons(0); 898 my_addr.sin_addr.s_addr = htonl(INADDR_ANY); 899 if (bind(s, (struct sockaddr *)&my_addr, 900 sizeof (my_addr)) < 0) { 901 syslog(LOG_ERR, "dyndns: client bind err\n"); 902 (void) close(s); 903 return (-1); 904 } 905 serv_addr.sin_family = family; 906 serv_addr.sin_port = htons(port); 907 serv_addr.sin_addr.s_addr = dest_addr->a_ipv4; 908 if (connect(s, (struct sockaddr *)&serv_addr, 909 sizeof (struct sockaddr_in)) < 0) { 910 syslog(LOG_ERR, "dyndns: client connect (%s)\n", 911 strerror(errno)); 912 (void) close(s); 913 return (-1); 914 } 915 } else { 916 bzero(&my6_addr, sizeof (my6_addr)); 917 my6_addr.sin6_family = family; 918 my6_addr.sin6_port = htons(0); 919 bzero(&my6_addr.sin6_addr.s6_addr, IN6ADDRSZ); 920 if (bind(s, (struct sockaddr *)&my6_addr, 921 sizeof (my6_addr)) < 0) { 922 syslog(LOG_ERR, "dyndns: client bind err\n"); 923 (void) close(s); 924 return (-1); 925 } 926 serv6_addr.sin6_family = family; 927 serv6_addr.sin6_port = htons(port); 928 bcopy(&serv6_addr.sin6_addr.s6_addr, &dest_addr->a_ipv6, 929 IN6ADDRSZ); 930 if (connect(s, (struct sockaddr *)&serv6_addr, 931 sizeof (struct sockaddr_in6)) < 0) { 932 syslog(LOG_ERR, "dyndns: client connect err (%s)\n", 933 strerror(errno)); 934 (void) close(s); 935 return (-1); 936 } 937 } 938 return (s); 939 } 940 /* 941 * dyndns_build_tkey_msg 942 * This routine is used to build the TKEY message to transmit GSS tokens 943 * during GSS security context establishment for secure DNS update. The 944 * TKEY message format uses the DNS query message format. The TKEY section 945 * is the answer section of the query message format. 946 * Microsoft uses a value of 86400 seconds (24 hours) for key expiration time. 947 * Parameters: 948 * buf : buffer to build and store TKEY message 949 * key_name: a unique key name, this same key name must be also be used in 950 * the TSIG message 951 * out_tok : TKEY message data (GSS tokens) 952 * Returns: 953 * id : message id of this TKEY message 954 * message size: the size of the TKEY message 955 * -1 : error 956 */ 957 static int 958 dyndns_build_tkey_msg(char *buf, char *key_name, uint16_t *id, 959 gss_buffer_desc *out_tok) 960 { 961 int queryReq, zoneCount, preqCount, updateCount, additionalCount; 962 int zoneType, zoneClass; 963 char *bufptr; 964 965 queryReq = REQ_QUERY; 966 /* query section of query request */ 967 zoneCount = 1; 968 /* answer section of query request */ 969 preqCount = 1; 970 updateCount = 0; 971 additionalCount = 0; 972 973 (void) memset(buf, 0, MAX_TCP_SIZE); 974 bufptr = buf; 975 *id = dyndns_get_msgid(); 976 977 /* add TCP length info that follows this field */ 978 bufptr = dyndns_put_nshort(bufptr, 979 26 + (strlen(key_name)+2)*2 + 35 + out_tok->length); 980 981 if (dyndns_build_header(&bufptr, BUFLEN_TCP(bufptr, buf), *id, queryReq, 982 zoneCount, preqCount, updateCount, additionalCount, 0) == -1) { 983 return (-1); 984 } 985 986 zoneType = ns_t_tkey; 987 zoneClass = ns_c_in; 988 if (dyndns_build_quest_zone(&bufptr, BUFLEN_TCP(bufptr, buf), key_name, 989 zoneType, zoneClass) == -1) { 990 return (-1); 991 } 992 993 if (dyndns_build_tkey(&bufptr, BUFLEN_TCP(bufptr, buf), key_name, 994 86400, out_tok->value, out_tok->length) == -1) { 995 return (-1); 996 } 997 998 return (bufptr - buf); 999 } 1000 1001 /* 1002 * dyndns_establish_sec_ctx 1003 * This routine is used to establish a security context with the DNS server 1004 * by building TKEY messages and sending them to the DNS server. TKEY messages 1005 * are also received from the DNS server for processing. The security context 1006 * establishment is done with the GSS client on the system producing a token 1007 * and sending the token within the TKEY message to the GSS server on the DNS 1008 * server. The GSS server then processes the token and then send a TKEY reply 1009 * message with a new token to be processed by the GSS client. The GSS client 1010 * processes the new token and then generates a new token to be sent to the 1011 * GSS server. This cycle is continued until the security establishment is 1012 * done. TCP is used to send and receive TKEY messages. 1013 * Parameters: 1014 * cred_handle : handle to credential 1015 * s : socket descriptor to DNS server 1016 * key_name : TKEY key name 1017 * dns_hostname: fully qualified DNS hostname 1018 * oid : contains Kerberos 5 object identifier 1019 * Returns: 1020 * gss_context : handle to security context 1021 */ 1022 static int 1023 dyndns_establish_sec_ctx(gss_ctx_id_t *gss_context, gss_cred_id_t cred_handle, 1024 int s, char *key_name, char *dns_hostname, gss_OID oid) 1025 { 1026 uint16_t id, rid, rsz; 1027 char buf[MAX_TCP_SIZE], buf2[MAX_TCP_SIZE]; 1028 int ret; 1029 char *service_name, *tmpptr; 1030 int service_sz; 1031 OM_uint32 min, maj, time_rec; 1032 gss_buffer_desc service_buf, in_tok, out_tok; 1033 gss_name_t target_name; 1034 gss_buffer_desc *inputptr; 1035 int gss_flags; 1036 OM_uint32 ret_flags; 1037 int buf_sz; 1038 1039 service_sz = strlen(dns_hostname) + 5; 1040 service_name = (char *)malloc(sizeof (char) * service_sz); 1041 if (service_name == NULL) 1042 return (-1); 1043 1044 (void) snprintf(service_name, service_sz, "DNS@%s", dns_hostname); 1045 service_buf.value = service_name; 1046 service_buf.length = strlen(service_name)+1; 1047 if ((maj = gss_import_name(&min, &service_buf, 1048 GSS_C_NT_HOSTBASED_SERVICE, &target_name)) != GSS_S_COMPLETE) { 1049 display_stat(maj, min); 1050 (void) free(service_name); 1051 return (-1); 1052 } 1053 (void) free(service_name); 1054 1055 inputptr = GSS_C_NO_BUFFER; 1056 *gss_context = GSS_C_NO_CONTEXT; 1057 gss_flags = GSS_C_MUTUAL_FLAG | GSS_C_DELEG_FLAG | GSS_C_REPLAY_FLAG | 1058 GSS_C_SEQUENCE_FLAG | GSS_C_CONF_FLAG | GSS_C_INTEG_FLAG; 1059 do { 1060 maj = gss_init_sec_context(&min, cred_handle, gss_context, 1061 target_name, oid, gss_flags, 0, NULL, inputptr, NULL, 1062 &out_tok, &ret_flags, &time_rec); 1063 1064 if (maj != GSS_S_COMPLETE && maj != GSS_S_CONTINUE_NEEDED) { 1065 assert(gss_context); 1066 if (*gss_context != GSS_C_NO_CONTEXT) 1067 (void) gss_delete_sec_context(&min, 1068 gss_context, NULL); 1069 1070 display_stat(maj, min); 1071 (void) gss_release_name(&min, &target_name); 1072 return (-1); 1073 } 1074 1075 if ((maj == GSS_S_COMPLETE) && 1076 !(ret_flags & GSS_C_REPLAY_FLAG)) { 1077 syslog(LOG_ERR, "dyndns: No GSS_C_REPLAY_FLAG"); 1078 if (out_tok.length > 0) 1079 (void) gss_release_buffer(&min, &out_tok); 1080 (void) gss_release_name(&min, &target_name); 1081 return (-1); 1082 } 1083 1084 if ((maj == GSS_S_COMPLETE) && 1085 !(ret_flags & GSS_C_MUTUAL_FLAG)) { 1086 syslog(LOG_ERR, "dyndns: No GSS_C_MUTUAL_FLAG"); 1087 if (out_tok.length > 0) 1088 (void) gss_release_buffer(&min, &out_tok); 1089 (void) gss_release_name(&min, &target_name); 1090 return (-1); 1091 } 1092 1093 if (out_tok.length > 0) { 1094 if ((buf_sz = dyndns_build_tkey_msg(buf, key_name, 1095 &id, &out_tok)) <= 0) { 1096 (void) gss_release_buffer(&min, &out_tok); 1097 (void) gss_release_name(&min, &target_name); 1098 return (-1); 1099 } 1100 1101 (void) gss_release_buffer(&min, &out_tok); 1102 1103 if (send(s, buf, buf_sz, 0) == -1) { 1104 syslog(LOG_ERR, "dyndns: TKEY send error"); 1105 (void) gss_release_name(&min, &target_name); 1106 return (-1); 1107 } 1108 1109 bzero(buf2, MAX_TCP_SIZE); 1110 if (recv(s, buf2, MAX_TCP_SIZE, 0) == -1) { 1111 syslog(LOG_ERR, "dyndns: TKEY recv error"); 1112 (void) gss_release_name(&min, &target_name); 1113 return (-1); 1114 } 1115 1116 ret = buf2[5] & 0xf; /* error field in TCP */ 1117 if (ret != NOERROR) { 1118 dyndns_syslog(LOG_ERR, ret, "TKEY reply"); 1119 (void) gss_release_name(&min, &target_name); 1120 return (-1); 1121 } 1122 1123 tmpptr = &buf2[2]; 1124 (void) dyndns_get_nshort(tmpptr, &rid); 1125 if (id != rid) { 1126 (void) gss_release_name(&min, &target_name); 1127 return (-1); 1128 } 1129 1130 tmpptr = &buf2[59+(strlen(key_name)+2)*2]; 1131 (void) dyndns_get_nshort(tmpptr, &rsz); 1132 in_tok.length = rsz; 1133 1134 /* bsd38 -> 2*7=14 */ 1135 in_tok.value = &buf2[61+(strlen(key_name)+2)*2]; 1136 inputptr = &in_tok; 1137 } 1138 1139 } while (maj != GSS_S_COMPLETE); 1140 1141 (void) gss_release_name(&min, &target_name); 1142 1143 return (0); 1144 } 1145 1146 /* 1147 * dyndns_get_sec_context 1148 * Get security context for secure dynamic DNS update. This routine opens 1149 * a TCP socket to the DNS server and establishes a security context with 1150 * the DNS server using host principal to perform secure dynamic DNS update. 1151 * Parameters: 1152 * hostname: fully qualified hostname 1153 * dns_ip : ip address of hostname in network byte order 1154 * Returns: 1155 * gss_handle: gss credential handle 1156 * gss_context: gss security context 1157 * -1: error 1158 * 0: success 1159 */ 1160 1161 static gss_ctx_id_t 1162 dyndns_get_sec_context(const char *hostname, smb_inaddr_t *dns_ip) 1163 { 1164 int s; 1165 gss_cred_id_t cred_handle; 1166 gss_ctx_id_t gss_context; 1167 gss_OID oid; 1168 char *key_name, dns_hostname[MAXHOSTNAMELEN]; 1169 1170 cred_handle = GSS_C_NO_CREDENTIAL; 1171 oid = GSS_C_NO_OID; 1172 key_name = (char *)hostname; 1173 1174 if (smb_getnameinfo(dns_ip, dns_hostname, 1175 sizeof (dns_hostname), 0)) { 1176 return (NULL); 1177 } 1178 if ((s = dyndns_open_init_socket(SOCK_STREAM, dns_ip, 53)) < 0) { 1179 return (NULL); 1180 } 1181 1182 if (dyndns_establish_sec_ctx(&gss_context, cred_handle, s, key_name, 1183 dns_hostname, oid)) 1184 gss_context = NULL; 1185 1186 (void) close(s); 1187 return (gss_context); 1188 } 1189 1190 /* 1191 * dyndns_build_add_remove_msg 1192 * This routine builds the update request message for adding and removing DNS 1193 * entries which is used for non-secure and secure DNS update. 1194 * This routine builds an UDP message. 1195 * Parameters: 1196 * buf : buffer to build message 1197 * update_zone: the type of zone to update, use UPDATE_FORW for forward 1198 * lookup zone, use UPDATE_REV for reverse lookup zone 1199 * hostname : fully qualified hostname to update DNS with 1200 * ip_addr : IP address of hostname 1201 * life_time : cached time of this entry by others and not within DNS 1202 * database 1203 * update_type: UPDATE_ADD to add entry, UPDATE_DEL to remove entry 1204 * del_type : DEL_ONE for deleting one entry, DEL_ALL for deleting all 1205 * entries of the same resource name. Only valid for UPDATE_DEL. 1206 * addit_cnt : Indicate how many record is in the additional section of 1207 * the DNS message. A value of zero is always used with 1208 * non-secure update message. For secure update message, 1209 * the value will be one because the signed TSIG message 1210 * is added as the additional record of the DNS update message. 1211 * id : DNS message ID. If a positive value then this ID value is 1212 * used, otherwise the next incremented value is used 1213 * level : This is the domain level which we send the request to, level 1214 * zero is the default level, it can go upto 2 in reverse zone 1215 * and virtually to any level in forward zone. 1216 * Returns: 1217 * buf : buffer containing update message 1218 * id : DNS message ID 1219 * int : size of update message 1220 * -1 : error 1221 * 1222 * This function is changed to handle dynamic DNS update retires to higher 1223 * authoritative domains. 1224 */ 1225 static int 1226 dyndns_build_add_remove_msg(char *buf, int update_zone, const char *hostname, 1227 const char *ip_addr, int life_time, int update_type, int del_type, 1228 int addit_cnt, uint16_t *id, int level) 1229 { 1230 int a, b, c, d; 1231 char *bufptr; 1232 int queryReq, zoneCount, preqCount, updateCount, additionalCount; 1233 char *zone, *resource, *data, zone_buf[100], resrc_buf[100]; 1234 int zoneType, zoneClass, type, class, ttl; 1235 char *p; 1236 smb_inaddr_t tmp_addr; 1237 int i, j, k; 1238 int fourcnt; 1239 1240 queryReq = REQ_UPDATE; 1241 zoneCount = 1; 1242 preqCount = 0; 1243 updateCount = 1; 1244 additionalCount = addit_cnt; 1245 1246 (void) memset(buf, 0, NS_PACKETSZ); 1247 bufptr = buf; 1248 1249 if (*id == 0) 1250 *id = dyndns_get_msgid(); 1251 1252 if (dyndns_build_header(&bufptr, BUFLEN_UDP(bufptr, buf), *id, queryReq, 1253 zoneCount, preqCount, updateCount, additionalCount, 0) == -1) { 1254 return (-1); 1255 } 1256 1257 zoneType = ns_t_soa; 1258 zoneClass = ns_c_in; 1259 1260 if (update_zone == UPDATE_FORW) { 1261 p = (char *)hostname; 1262 1263 /* Try higher domains according to the level requested */ 1264 do { 1265 /* domain */ 1266 if ((zone = (char *)strchr(p, '.')) == NULL) 1267 return (-1); 1268 zone += 1; 1269 p = zone; 1270 } while (--level >= 0); 1271 resource = (char *)hostname; 1272 data = (char *)ip_addr; 1273 } else { 1274 if (inet_pton(AF_INET, ip_addr, &tmp_addr) == 1) { 1275 (void) sscanf(ip_addr, "%d.%d.%d.%d", &a, &b, &c, &d); 1276 (void) sprintf(zone_buf, "%d.%d.%d.in-addr.arpa", 1277 c, b, a); 1278 zone = p = zone_buf; 1279 1280 /* Try higher domains based on level requested */ 1281 while (--level >= 0) { 1282 /* domain */ 1283 if ((zone = (char *)strchr(p, '.')) == NULL) { 1284 return (-1); 1285 } 1286 zone += 1; 1287 p = zone; 1288 } 1289 (void) sprintf(resrc_buf, "%d.%d.%d.%d.in-addr.arpa", 1290 d, c, b, a); 1291 } else { 1292 /* 1293 * create reverse nibble ipv6 format 1294 */ 1295 bzero(resrc_buf, 100); 1296 i = 0; 1297 j = 0; 1298 while (ip_addr[i] != 0) 1299 i++; 1300 i--; 1301 while (i >= 0) { 1302 fourcnt = 3; 1303 while ((i >= 0) && (ip_addr[i] != ':')) { 1304 resrc_buf[j++] = ip_addr[i]; 1305 (void) strcat(&resrc_buf[j++], "."); 1306 fourcnt --; 1307 i--; 1308 } 1309 for (k = 0; k <= fourcnt; k++) { 1310 resrc_buf[j++] = '0'; 1311 (void) strcat(&resrc_buf[j++], "."); 1312 } 1313 i--; 1314 } 1315 (void) strcat(resrc_buf, "ip6.arpa"); 1316 (void) strcpy(zone_buf, &resrc_buf[32]); 1317 zone = zone_buf; 1318 } 1319 resource = resrc_buf; /* ip info */ 1320 data = (char *)hostname; 1321 } 1322 if (dyndns_build_quest_zone(&bufptr, BUFLEN_UDP(bufptr, buf), zone, 1323 zoneType, zoneClass) == -1) { 1324 return (-1); 1325 } 1326 1327 if (update_zone == UPDATE_FORW) 1328 type = ns_t_a; 1329 else 1330 type = ns_t_ptr; 1331 1332 if (update_type == UPDATE_ADD) { 1333 class = ns_c_in; 1334 ttl = life_time; 1335 } else { 1336 if (del_type == DEL_ONE) 1337 class = ns_c_none; /* remove one */ 1338 else 1339 class = ns_c_any; /* remove all */ 1340 ttl = 0; 1341 } 1342 if (dyndns_build_update(&bufptr, BUFLEN_UDP(bufptr, buf), 1343 resource, type, class, ttl, data, update_zone, 1344 update_type, del_type) == -1) { 1345 return (-1); 1346 } 1347 1348 return (bufptr - buf); 1349 } 1350 1351 /* 1352 * dyndns_build_unsigned_tsig_msg 1353 * This routine is used to build the unsigned TSIG message for signing. The 1354 * unsigned TSIG message contains the update request message with certain TSIG 1355 * fields included. An error time of 300 seconds is used for fudge time. This 1356 * is the number used by Microsoft clients. 1357 * This routine builds a UDP message. 1358 * Parameters: 1359 * buf : buffer to build message 1360 * update_zone: the type of zone to update, use UPDATE_FORW for forward 1361 * lookup zone, use UPDATE_REV for reverse lookup zone 1362 * hostname : fully qualified hostname to update DNS with 1363 * ip_addr : IP address of hostname 1364 * life_time : cached time of this entry by others and not within DNS 1365 * database 1366 * update_type: UPDATE_ADD to add entry, UPDATE_DEL to remove entry 1367 * del_type : DEL_ONE for deleting one entry, DEL_ALL for deleting all 1368 * entries of the same resource name. Only valid for UPDATE_DEL. 1369 * key_name : same key name used in TKEY message 1370 * id : DNS message ID. If a positive value then this ID value is 1371 * used, otherwise the next incremented value is used 1372 * level : This is the domain level which we send the request to, level 1373 * zero is the default level, it can go upto 2 in reverse zone 1374 * and virtually to any level in forward zone. 1375 * Returns: 1376 * buf : buffer containing update message 1377 * id : DNS message ID 1378 * int : size of update message 1379 * -1 : error 1380 */ 1381 static int 1382 dyndns_build_unsigned_tsig_msg(char *buf, int update_zone, const char *hostname, 1383 const char *ip_addr, int life_time, int update_type, int del_type, 1384 char *key_name, uint16_t *id, int level) 1385 { 1386 char *bufptr; 1387 int buf_sz; 1388 1389 if ((buf_sz = dyndns_build_add_remove_msg(buf, update_zone, hostname, 1390 ip_addr, life_time, update_type, del_type, 0, id, level)) <= 0) { 1391 return (-1); 1392 } 1393 1394 bufptr = buf + buf_sz; 1395 1396 if (dyndns_build_tsig(&bufptr, BUFLEN_UDP(bufptr, buf), 0, 1397 key_name, 300, NULL, 0, TSIG_UNSIGNED) == -1) { 1398 return (-1); 1399 } 1400 1401 return (bufptr - buf); 1402 } 1403 1404 /* 1405 * dyndns_build_signed_tsig_msg 1406 * This routine build the signed TSIG message which contains the update 1407 * request message encrypted. An error time of 300 seconds is used for fudge 1408 * time. This is the number used by Microsoft clients. 1409 * This routine builds a UDP message. 1410 * Parameters: 1411 * buf : buffer to build message 1412 * update_zone: the type of zone to update, use UPDATE_FORW for forward 1413 * lookup zone, use UPDATE_REV for reverse lookup zone 1414 * hostname : fully qualified hostname to update DNS with 1415 * ip_addr : IP address of hostname 1416 * life_time : cached time of this entry by others and not within DNS 1417 * database 1418 * update_type: UPDATE_ADD to add entry, UPDATE_DEL to remove entry 1419 * del_type : DEL_ONE for deleting one entry, DEL_ALL for deleting all 1420 * entries of the same resource name. Only valid for UPDATE_DEL. 1421 * key_name : same key name used in TKEY message 1422 * id : DNS message ID. If a positive value then this ID value is 1423 * used, otherwise the next incremented value is used 1424 * in_mic : the update request message encrypted 1425 * level : This is the domain level which we send the request to, level 1426 * zero is the default level, it can go upto 2 in reverse zone 1427 * and virtually to any level in forward zone. 1428 * 1429 * Returns: 1430 * buf : buffer containing update message 1431 * id : DNS message ID 1432 * int : size of update message 1433 * -1 : error 1434 */ 1435 static int 1436 dyndns_build_signed_tsig_msg(char *buf, int update_zone, const char *hostname, 1437 const char *ip_addr, int life_time, int update_type, int del_type, 1438 char *key_name, uint16_t *id, gss_buffer_desc *in_mic, int level) 1439 { 1440 char *bufptr; 1441 int buf_sz; 1442 1443 if ((buf_sz = dyndns_build_add_remove_msg(buf, update_zone, hostname, 1444 ip_addr, life_time, update_type, del_type, 1, id, level)) <= 0) { 1445 return (-1); 1446 } 1447 1448 bufptr = buf + buf_sz; 1449 1450 if (dyndns_build_tsig(&bufptr, BUFLEN_UDP(bufptr, buf), 1451 *id, key_name, 300, in_mic->value, 1452 in_mic->length, TSIG_SIGNED) == -1) { 1453 return (-1); 1454 } 1455 1456 return (bufptr - buf); 1457 } 1458 1459 /* 1460 * dyndns_udp_send_recv 1461 * This routine sends and receives UDP DNS request and reply messages. 1462 * 1463 * Pre-condition: Caller must call dyndns_open_init_socket() before calling 1464 * this function. 1465 * 1466 * Parameters: 1467 * s : socket descriptor 1468 * buf : buffer containing data to send 1469 * buf_sz : size of data to send 1470 * Returns: 1471 * -1 : error 1472 * rec_buf: reply dat 1473 * 0 : success 1474 */ 1475 1476 static int 1477 dyndns_udp_send_recv(int s, char *buf, int buf_sz, char *rec_buf) 1478 { 1479 int i, retval, addr_len; 1480 struct timeval tv, timeout; 1481 fd_set rfds; 1482 struct sockaddr_in6 from_addr; 1483 1484 timeout.tv_usec = 0; 1485 timeout.tv_sec = DYNDNS_QUERY_TIMEOUT; 1486 1487 for (i = 0; i <= DYNDNS_MAX_QUERY_RETRIES; i++) { 1488 if (send(s, buf, buf_sz, 0) == -1) { 1489 syslog(LOG_ERR, "dyndns: UDP send error (%s)", 1490 strerror(errno)); 1491 return (-1); 1492 } 1493 1494 FD_ZERO(&rfds); 1495 FD_SET(s, &rfds); 1496 1497 tv = timeout; 1498 1499 retval = select(s+1, &rfds, NULL, NULL, &tv); 1500 1501 if (retval == -1) { 1502 return (-1); 1503 } else if (retval > 0) { 1504 bzero(rec_buf, NS_PACKETSZ); 1505 addr_len = sizeof (struct sockaddr_in6); 1506 if (recvfrom(s, rec_buf, NS_PACKETSZ, 0, 1507 (struct sockaddr *)&from_addr, &addr_len) == -1) { 1508 syslog(LOG_ERR, "dyndns: UDP recv error "); 1509 return (-1); 1510 } 1511 break; 1512 } 1513 } 1514 1515 /* did not receive anything */ 1516 if (i == (DYNDNS_MAX_QUERY_RETRIES + 1)) { 1517 syslog(LOG_ERR, "dyndns: max retries for UDP recv reached"); 1518 return (-1); 1519 } 1520 1521 return (0); 1522 } 1523 /* 1524 * dyndns_sec_add_remove_entry 1525 * Perform secure dynamic DNS update after getting security context. 1526 * This routine opens a UDP socket to the DNS sever, gets the security context, 1527 * builds the unsigned TSIG message and signed TSIG message. The signed TSIG 1528 * message containing the encrypted update request message is sent to the DNS 1529 * server. The response is received and check for error. If there is no 1530 * error then credential handle and security context are released and the local 1531 * NSS cached is purged. 1532 * Parameters: 1533 * update_zone : UPDATE_FORW for forward zone, UPDATE_REV for reverse zone 1534 * hostname : fully qualified hostname 1535 * ip_addr : ip address of hostname in string format 1536 * life_time : cached time of this entry by others and not within DNS 1537 * database 1538 * max_retries : maximum retries for sending DNS update request 1539 * recv_timeout: receive timeout 1540 * update_type : UPDATE_ADD for adding entry, UPDATE_DEL for removing entry 1541 * del_type : DEL_ONE for deleting one entry, DEL_ALL for deleting all 1542 * entries of the same resource name. Only valid for UPDATE_DEL 1543 * dns_str : DNS IP address in string format 1544 * Returns: 1545 * -1: error 1546 * 0: success 1547 * 1548 * This function is enhanced to handle the case of NOTAUTH error when DNS server 1549 * is not authoritative for specified zone. In this case we need to resend the 1550 * same request to the higher authoritative domains. 1551 * This is true for both secure and unsecure dynamic DNS updates. 1552 */ 1553 static int 1554 dyndns_sec_add_remove_entry(int update_zone, const char *hostname, 1555 const char *ip_addr, int life_time, int update_type, int del_type, 1556 char *dns_str) 1557 { 1558 int s2; 1559 uint16_t id, rid; 1560 char buf[NS_PACKETSZ], buf2[NS_PACKETSZ]; 1561 int ret; 1562 OM_uint32 min, maj; 1563 gss_buffer_desc in_mic, out_mic; 1564 gss_ctx_id_t gss_context; 1565 smb_inaddr_t dns_ip; 1566 char *key_name; 1567 int buf_sz; 1568 int level = 0; 1569 1570 assert(dns_str); 1571 assert(*dns_str); 1572 1573 if (inet_pton(AF_INET, dns_str, &dns_ip) == 1) 1574 dns_ip.a_family = AF_INET; 1575 else if (inet_pton(AF_INET6, dns_str, &dns_ip) == 1) 1576 dns_ip.a_family = AF_INET6; 1577 1578 sec_retry_higher: 1579 1580 if ((gss_context = dyndns_get_sec_context(hostname, 1581 &dns_ip)) == NULL) { 1582 return (-1); 1583 } 1584 1585 key_name = (char *)hostname; 1586 1587 if ((s2 = dyndns_open_init_socket(SOCK_DGRAM, &dns_ip, 53)) < 0) { 1588 if (gss_context != GSS_C_NO_CONTEXT) 1589 (void) gss_delete_sec_context(&min, &gss_context, NULL); 1590 return (-1); 1591 } 1592 1593 id = 0; 1594 if ((buf_sz = dyndns_build_unsigned_tsig_msg(buf, update_zone, hostname, 1595 ip_addr, life_time, update_type, del_type, 1596 key_name, &id, level)) <= 0) { 1597 (void) close(s2); 1598 if (gss_context != GSS_C_NO_CONTEXT) 1599 (void) gss_delete_sec_context(&min, &gss_context, NULL); 1600 return (-1); 1601 } 1602 1603 in_mic.length = buf_sz; 1604 in_mic.value = buf; 1605 1606 /* sign update message */ 1607 if ((maj = gss_get_mic(&min, gss_context, 0, &in_mic, &out_mic)) != 1608 GSS_S_COMPLETE) { 1609 display_stat(maj, min); 1610 (void) close(s2); 1611 if (gss_context != GSS_C_NO_CONTEXT) 1612 (void) gss_delete_sec_context(&min, &gss_context, NULL); 1613 return (-1); 1614 } 1615 1616 if ((buf_sz = dyndns_build_signed_tsig_msg(buf, update_zone, hostname, 1617 ip_addr, life_time, update_type, del_type, key_name, &id, 1618 &out_mic, level)) <= 0) { 1619 (void) close(s2); 1620 (void) gss_release_buffer(&min, &out_mic); 1621 if (gss_context != GSS_C_NO_CONTEXT) 1622 (void) gss_delete_sec_context(&min, &gss_context, NULL); 1623 return (-1); 1624 } 1625 1626 (void) gss_release_buffer(&min, &out_mic); 1627 1628 if (dyndns_udp_send_recv(s2, buf, buf_sz, buf2)) { 1629 (void) close(s2); 1630 if (gss_context != GSS_C_NO_CONTEXT) 1631 (void) gss_delete_sec_context(&min, &gss_context, NULL); 1632 return (-1); 1633 } 1634 1635 (void) close(s2); 1636 1637 if (gss_context != GSS_C_NO_CONTEXT) 1638 (void) gss_delete_sec_context(&min, &gss_context, NULL); 1639 1640 ret = buf2[3] & 0xf; /* error field in UDP */ 1641 1642 /* 1643 * If it is a NOTAUTH error we should retry with higher domains 1644 * until we get a successful reply or the maximum retries is met. 1645 */ 1646 if (ret == NOTAUTH && level++ < MAX_AUTH_RETRIES) 1647 goto sec_retry_higher; 1648 1649 /* check here for update request is successful */ 1650 if (ret != NOERROR) { 1651 dyndns_syslog(LOG_ERR, ret, "TSIG reply"); 1652 return (-1); 1653 } 1654 1655 (void) dyndns_get_nshort(buf2, &rid); 1656 if (id != rid) 1657 return (-1); 1658 1659 return (0); 1660 } 1661 1662 /* 1663 * dyndns_seach_entry 1664 * Query DNS server for entry. This routine can indicate if an entry exist 1665 * or not during forward or reverse lookup. Also can indicate if the data 1666 * of the entry matched. For example, for forward lookup, the entry is 1667 * searched using the hostname and the data is the IP address. For reverse 1668 * lookup, the entry is searched using the IP address and the data is the 1669 * hostname. 1670 * Parameters: 1671 * update_zone: UPDATE_FORW for forward zone, UPDATE_REV for reverse zone 1672 * hostname : fully qualified hostname 1673 * ip_addr : ip address of hostname in string format 1674 * update_type: UPDATE_ADD for adding entry, UPDATE_DEL for removing entry 1675 * Returns: 1676 * time_out: no use 1677 * is_match: is 1 for found matching entry, otherwise 0 1678 * 1 : an entry exist but not necessarily match 1679 * 0 : an entry does not exist 1680 */ 1681 /*ARGSUSED*/ 1682 1683 static int 1684 dyndns_search_entry(int update_zone, const char *hostname, const char *ip_addr, 1685 int update_type, struct timeval *time_out, int *is_match) 1686 { 1687 smb_inaddr_t ipaddr, dnsip; 1688 char dns_hostname[NI_MAXHOST]; 1689 struct addrinfo hints, *res = NULL; 1690 int salen; 1691 int family; 1692 1693 *is_match = 0; 1694 if (inet_pton(AF_INET, ip_addr, &ipaddr) == 1) { 1695 salen = sizeof (ipaddr.a_ipv4); 1696 family = AF_INET; 1697 } else if (inet_pton(AF_INET6, ip_addr, &ipaddr) == 1) { 1698 salen = sizeof (ipaddr.a_ipv6); 1699 family = AF_INET6; 1700 } 1701 if (update_zone == UPDATE_FORW) { 1702 bzero((char *)&hints, sizeof (hints)); 1703 hints.ai_family = family; 1704 hints.ai_flags = AI_NUMERICHOST; 1705 if (getaddrinfo(hostname, NULL, &hints, &res)) { 1706 return (NULL); 1707 } 1708 if (res) { 1709 /* 1710 * if both ips aren't the same family skip to 1711 * the next record 1712 */ 1713 do { 1714 if ((res->ai_family == AF_INET) && 1715 (family == AF_INET)) { 1716 (void) memcpy(&dnsip, &res->ai_addr[0], 1717 salen); 1718 if (ipaddr.a_ipv4 == 1719 dnsip.a_ipv4) { 1720 *is_match = 1; 1721 break; 1722 } 1723 } else if ((res->ai_family == AF_INET6) && 1724 (family == AF_INET6)) { 1725 (void) memcpy(&dnsip, &res->ai_addr[0], 1726 salen); 1727 /* need compare macro here */ 1728 if (!memcmp(&ipaddr, &dnsip, 1729 IN6ADDRSZ)) { 1730 *is_match = 1; 1731 break; 1732 } 1733 } 1734 } while (res->ai_next); 1735 freeaddrinfo(res); 1736 return (1); 1737 } 1738 } else { 1739 if (smb_getnameinfo(&ipaddr, dns_hostname, NI_MAXHOST, 0)) 1740 return (NULL); 1741 1742 if (strncasecmp(dns_hostname, hostname, 1743 strlen(hostname)) == 0) { 1744 *is_match = 1; 1745 } 1746 return (1); 1747 } 1748 1749 /* entry does not exist */ 1750 return (0); 1751 } 1752 1753 /* 1754 * dyndns_add_remove_entry 1755 * Perform non-secure dynamic DNS update. If it fails and host principal 1756 * keys can be found in the local keytab file, secure update will be performed. 1757 * 1758 * This routine opens a UDP socket to the DNS sever, build the update request 1759 * message, and sends the message to the DNS server. The response is received 1760 * and check for error. If there is no error then the local NSS cached is 1761 * purged. DNS may be used to check to see if an entry already exist before 1762 * adding or to see if an entry does exist before removing it. Adding 1763 * duplicate entries or removing non-existing entries does not cause any 1764 * problems. DNS is not check when doing a delete all. 1765 * Parameters: 1766 * update_zone: UPDATE_FORW for forward zone, UPDATE_REV for reverse zone 1767 * hostname : fully qualified hostname 1768 * ip_addr : ip address of hostname in string format 1769 * life_time : cached time of this entry by others and not within DNS 1770 * database 1771 * update_type: UPDATE_ADD to add entry, UPDATE_DEL to remove entry 1772 * do_check : DNS_CHECK to check first in DNS, DNS_NOCHECK for no DNS 1773 * checking before update 1774 * del_type : DEL_ONE for deleting one entry, DEL_ALL for deleting all 1775 * entries of the same resource name. Only valid for UPDATE_DEL. 1776 * dns_str : DNS IP address in string format 1777 * Returns: 1778 * -1: error 1779 * 0: success 1780 * 1781 * This function is enhanced to handle the case of NOTAUTH error when DNS server 1782 * is not authoritative for specified zone. In this case we need to resend the 1783 * same request to the higher authoritative domains. 1784 * This is true for both secure and unsecure dynamic DNS updates. 1785 */ 1786 static int 1787 dyndns_add_remove_entry(int update_zone, const char *hostname, 1788 const char *ip_addr, int life_time, int update_type, 1789 int do_check, int del_type, char *dns_str) 1790 { 1791 int s; 1792 uint16_t id, rid; 1793 char buf[NS_PACKETSZ], buf2[NS_PACKETSZ]; 1794 int ret; 1795 int is_exist, is_match; 1796 struct timeval timeout; 1797 int buf_sz; 1798 int level = 0; 1799 smb_inaddr_t dns_ip; 1800 1801 assert(dns_str); 1802 assert(*dns_str); 1803 1804 if (do_check == DNS_CHECK && del_type != DEL_ALL) { 1805 is_exist = dyndns_search_entry(update_zone, hostname, ip_addr, 1806 update_type, &timeout, &is_match); 1807 1808 if (update_type == UPDATE_ADD && is_exist && is_match) { 1809 return (0); 1810 } else if (update_type == UPDATE_DEL && !is_exist) { 1811 return (0); 1812 } 1813 } 1814 1815 if (inet_pton(AF_INET, dns_str, &dns_ip) == 1) 1816 dns_ip.a_family = AF_INET; 1817 else if (inet_pton(AF_INET6, dns_str, &dns_ip) == 1) 1818 dns_ip.a_family = AF_INET6; 1819 1820 retry_higher: 1821 if ((s = dyndns_open_init_socket(SOCK_DGRAM, &dns_ip, 53)) < 0) 1822 return (-1); 1823 1824 id = 0; 1825 if ((buf_sz = dyndns_build_add_remove_msg(buf, update_zone, hostname, 1826 ip_addr, life_time, update_type, del_type, 0, &id, level)) <= 0) { 1827 (void) close(s); 1828 return (-1); 1829 } 1830 1831 if (dyndns_udp_send_recv(s, buf, buf_sz, buf2)) { 1832 (void) close(s); 1833 return (-1); 1834 } 1835 1836 (void) close(s); 1837 1838 ret = buf2[3] & 0xf; /* error field in UDP */ 1839 1840 /* 1841 * If it is a NOTAUTH error we should retry with higher domains 1842 * until we get a successful reply 1843 */ 1844 if (ret == NOTAUTH && level++ < MAX_AUTH_RETRIES) 1845 goto retry_higher; 1846 1847 /* check here for update request is successful */ 1848 if (ret == NOERROR) { 1849 (void) dyndns_get_nshort(buf2, &rid); 1850 if (id != rid) 1851 return (-1); 1852 return (0); 1853 } 1854 1855 if (ret == NOTIMP) { 1856 dyndns_syslog(LOG_NOTICE, NOTIMP, "dynamic updates"); 1857 return (-1); 1858 } else if (ret == NOTAUTH) { 1859 dyndns_syslog(LOG_NOTICE, NOTAUTH, "DNS"); 1860 return (-1); 1861 } 1862 1863 if (smb_krb5_find_keytab_entries(hostname, SMBNS_KRB5_KEYTAB)) 1864 ret = dyndns_sec_add_remove_entry(update_zone, hostname, 1865 ip_addr, life_time, update_type, del_type, dns_str); 1866 1867 return (ret); 1868 } 1869 1870 /* 1871 * dyndns_add_entry 1872 * Main routine to add an entry into DNS. The attempt will be made on the 1873 * the servers returned by smb_get_nameserver(). Upon a successful 1874 * attempt on any one of the server, the function will exit with 0. 1875 * Otherwise, -1 is retuned to indicate the update attempt on all the 1876 * nameservers has failed. 1877 * 1878 * Parameters: 1879 * update_zone: the type of zone to update, use UPDATE_FORW for forward 1880 * lookup zone, use UPDATE_REV for reverse lookup zone 1881 * hostname : fully qualified hostname 1882 * ip_addr : ip address of hostname in string format 1883 * life_time : cached time of this entry by others and not within DNS 1884 * database 1885 * Returns: 1886 * -1: error 1887 * 0: success 1888 */ 1889 static int 1890 dyndns_add_entry(int update_zone, const char *hostname, const char *ip_addr, 1891 int life_time) 1892 { 1893 const char *dns_str; 1894 char *which_zone; 1895 smb_inaddr_t ns_list[MAXNS]; 1896 char dns_buf[INET6_ADDRSTRLEN]; 1897 int i, cnt; 1898 int rc = 0; 1899 1900 if (hostname == NULL || ip_addr == NULL) { 1901 return (-1); 1902 } 1903 cnt = smb_get_nameservers(&ns_list[0], MAXNS); 1904 1905 for (i = 0; i < cnt; i++) { 1906 dns_str = smb_inet_ntop(&ns_list[i], dns_buf, 1907 SMB_IPSTRLEN(ns_list[i].a_family)); 1908 if (dns_str == NULL) 1909 continue; 1910 1911 which_zone = (update_zone == UPDATE_FORW) ? 1912 "forward" : "reverse"; 1913 syslog(LOG_DEBUG, "dyndns %s lookup zone update %s (%s)", 1914 which_zone, hostname, ip_addr); 1915 1916 if (dyndns_add_remove_entry(update_zone, hostname, 1917 ip_addr, life_time, 1918 UPDATE_ADD, DNS_NOCHECK, DEL_NONE, dns_buf) != -1) { 1919 rc = 1; 1920 break; 1921 } 1922 } 1923 1924 return (rc ? 0 : -1); 1925 } 1926 1927 /* 1928 * dyndns_remove_entry 1929 * Main routine to remove an entry or all entries of the same resource name 1930 * from DNS. The update attempt will be made on the primary DNS server. If 1931 * there is a failure then another attempt will be made on the secondary DNS 1932 * server. 1933 * Parameters: 1934 * update_zone: the type of zone to update, use UPDATE_FORW for forward 1935 * lookup zone, use UPDATE_REV for reverse lookup zone 1936 * hostname : fully qualified hostname 1937 * ip_addr : ip address of hostname in string format 1938 * del_type : DEL_ONE for deleting one entry, DEL_ALL for deleting all 1939 * entries of the same resource name. Only valid for UPDATE_DEL 1940 * Returns: 1941 * -1: error 1942 * 0: success 1943 */ 1944 static int 1945 dyndns_remove_entry(int update_zone, const char *hostname, const char *ip_addr, 1946 int del_type) 1947 { 1948 const char *dns_str; 1949 smb_inaddr_t ns_list[MAXNS]; 1950 char dns_buf[INET6_ADDRSTRLEN]; 1951 int i, cnt, scnt; 1952 1953 if ((hostname == NULL || ip_addr == NULL)) { 1954 return (-1); 1955 } 1956 cnt = smb_get_nameservers(ns_list, MAXNS); 1957 scnt = 0; 1958 for (i = 0; i < cnt; i++) { 1959 dns_str = smb_inet_ntop(&ns_list[i], dns_buf, 1960 SMB_IPSTRLEN(ns_list[i].a_family)); 1961 if (dns_str == NULL) 1962 continue; 1963 if (update_zone == UPDATE_FORW) { 1964 if (del_type == DEL_ONE) { 1965 syslog(LOG_DEBUG, "Dynamic update " 1966 "on forward lookup " 1967 "zone for %s (%s)...\n", hostname, ip_addr); 1968 } else { 1969 syslog(LOG_DEBUG, "Removing all " 1970 "entries of %s " 1971 "in forward lookup zone...\n", hostname); 1972 } 1973 } else { 1974 if (del_type == DEL_ONE) { 1975 syslog(LOG_DEBUG, "Dynamic update " 1976 "on reverse lookup " 1977 "zone for %s (%s)...\n", hostname, ip_addr); 1978 } else { 1979 syslog(LOG_DEBUG, "Removing all " 1980 "entries of %s " 1981 "in reverse lookup zone...\n", ip_addr); 1982 } 1983 } 1984 if (dyndns_add_remove_entry(update_zone, hostname, ip_addr, 0, 1985 UPDATE_DEL, DNS_NOCHECK, del_type, dns_buf) != -1) { 1986 scnt++; 1987 break; 1988 } 1989 } 1990 if (scnt) 1991 return (0); 1992 return (-1); 1993 } 1994 1995 /* 1996 * dyndns_update_core 1997 * Perform dynamic update on both forward and reverse lookup zone using 1998 * the specified hostname and IP addresses. Before updating DNS, existing 1999 * host entries with the same hostname in the forward lookup zone are removed 2000 * and existing pointer entries with the same IP addresses in the reverse 2001 * lookup zone are removed. After DNS update, host entries for current 2002 * hostname will show current IP addresses and pointer entries for current 2003 * IP addresses will show current hostname. 2004 * Parameters: 2005 * fqhn - fully-qualified hostname 2006 * 2007 * Returns: 2008 * -1: some dynamic DNS updates errors 2009 * 0: successful or DDNS disabled. 2010 */ 2011 int 2012 dyndns_update_core(char *fqdn) 2013 { 2014 int forw_update_ok, error; 2015 char my_ip[INET6_ADDRSTRLEN]; 2016 const char *my_str; 2017 smb_niciter_t ni; 2018 int rc; 2019 char fqhn[MAXHOSTNAMELEN]; 2020 2021 if (fqdn == NULL || *fqdn == '\0') 2022 return (0); 2023 2024 if (!smb_config_getbool(SMB_CI_DYNDNS_ENABLE)) 2025 return (0); 2026 2027 if (smb_gethostname(fqhn, MAXHOSTNAMELEN, 0) != 0) 2028 return (-1); 2029 2030 (void) snprintf(fqhn, MAXHOSTNAMELEN, "%s.%s", fqhn, fqdn); 2031 error = 0; 2032 forw_update_ok = 0; 2033 2034 /* 2035 * Dummy IP is okay since we are removing all using the hostname. 2036 */ 2037 if (dyndns_remove_entry(UPDATE_FORW, fqhn, "1.1.1.1", DEL_ALL) == 0) { 2038 forw_update_ok = 1; 2039 } else { 2040 error++; 2041 } 2042 2043 if (smb_nic_getfirst(&ni) != 0) 2044 return (-1); 2045 2046 do { 2047 if (ni.ni_nic.nic_sysflags & IFF_PRIVATE) 2048 continue; 2049 /* first try ipv4, then ipv6 */ 2050 my_str = smb_inet_ntop(&ni.ni_nic.nic_ip, my_ip, 2051 SMB_IPSTRLEN(ni.ni_nic.nic_ip.a_family)); 2052 if (my_str == NULL) { 2053 error++; 2054 continue; 2055 } 2056 2057 if (forw_update_ok) { 2058 rc = dyndns_add_entry(UPDATE_FORW, fqhn, my_str, 2059 DDNS_TTL); 2060 2061 if (rc == -1) 2062 error++; 2063 } 2064 2065 rc = dyndns_remove_entry(UPDATE_REV, fqhn, my_str, DEL_ALL); 2066 if (rc == 0) { 2067 rc = dyndns_add_entry(UPDATE_REV, fqhn, my_str, 2068 DDNS_TTL); 2069 } 2070 2071 if (rc == -1) 2072 error++; 2073 2074 } while (smb_nic_getnext(&ni) == 0); 2075 2076 return ((error == 0) ? 0 : -1); 2077 } 2078 2079 /* 2080 * dyndns_clear_rev_zone 2081 * Clear the rev zone records. Must be called to clear the OLD if list 2082 * of down records prior to updating the list with new information. 2083 * 2084 * Parameters: 2085 * fqhn - fully-qualified hostname 2086 * Returns: 2087 * -1: some dynamic DNS updates errors 2088 * 0: successful or DDNS disabled. 2089 */ 2090 int 2091 dyndns_clear_rev_zone(char *fqdn) 2092 { 2093 int error; 2094 char my_ip[INET6_ADDRSTRLEN]; 2095 smb_niciter_t ni; 2096 int rc; 2097 char fqhn[MAXHOSTNAMELEN]; 2098 const char *my_str; 2099 2100 if (!smb_config_getbool(SMB_CI_DYNDNS_ENABLE)) 2101 return (0); 2102 2103 if (smb_gethostname(fqhn, MAXHOSTNAMELEN, 0) != 0) 2104 return (-1); 2105 2106 (void) snprintf(fqhn, MAXHOSTNAMELEN, "%s.%s", fqhn, fqdn); 2107 error = 0; 2108 2109 if (smb_nic_getfirst(&ni) != 0) 2110 return (-1); 2111 2112 do { 2113 if (ni.ni_nic.nic_sysflags & IFF_PRIVATE) 2114 continue; 2115 my_str = smb_inet_ntop(&ni.ni_nic.nic_ip, my_ip, 2116 SMB_IPSTRLEN(ni.ni_nic.nic_ip.a_family)); 2117 if (my_str == NULL) { 2118 error++; 2119 continue; 2120 } 2121 2122 rc = dyndns_remove_entry(UPDATE_REV, fqhn, my_ip, DEL_ALL); 2123 if (rc != 0) 2124 error++; 2125 2126 } while (smb_nic_getnext(&ni) == 0); 2127 2128 return ((error == 0) ? 0 : -1); 2129 } 2130