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