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