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