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