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 /* 23 * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. 24 * Copyright 2014 Nexenta Systems, Inc. All rights reserved. 25 */ 26 27 /* 28 * DNS query helper functions for addisc.c 29 */ 30 31 #include <stdio.h> 32 #include <string.h> 33 #include <strings.h> 34 #include <unistd.h> 35 #include <assert.h> 36 #include <stdlib.h> 37 #include <net/if.h> 38 #include <sys/types.h> 39 #include <sys/socket.h> 40 #include <sys/sockio.h> 41 #include <netinet/in.h> 42 #include <arpa/inet.h> 43 #include <arpa/nameser.h> 44 #include <resolv.h> 45 #include <netdb.h> 46 #include <ctype.h> 47 #include <errno.h> 48 #include <ldap.h> 49 #include <sasl/sasl.h> 50 #include <sys/u8_textprep.h> 51 #include <syslog.h> 52 #include <uuid/uuid.h> 53 #include <ads/dsgetdc.h> 54 #include "adutils_impl.h" 55 #include "addisc_impl.h" 56 57 static void save_addr(ad_disc_cds_t *, sa_family_t, uchar_t *, size_t); 58 static struct addrinfo *make_addrinfo(sa_family_t, uchar_t *, size_t); 59 60 static void do_getaddrinfo(ad_disc_cds_t *); 61 static ad_disc_cds_t *srv_parse(uchar_t *, int, int *, int *); 62 static void add_preferred(ad_disc_cds_t *, ad_disc_ds_t *, int *, int); 63 static void get_addresses(ad_disc_cds_t *, int); 64 65 /* 66 * Simplified version of srv_query() for domain auto-discovery. 67 */ 68 int 69 srv_getdom(res_state state, const char *svc_name, char **rrname) 70 { 71 union { 72 HEADER hdr; 73 uchar_t buf[NS_MAXMSG]; 74 } msg; 75 int len, qdcount, ancount; 76 uchar_t *ptr, *eom; 77 char namebuf[NS_MAXDNAME]; 78 79 /* query necessary resource records */ 80 81 *rrname = NULL; 82 if (DBG(DNS, 1)) { 83 logger(LOG_DEBUG, "Looking for SRV RRs '%s.*'", svc_name); 84 } 85 len = res_nsearch(state, svc_name, C_IN, T_SRV, 86 msg.buf, sizeof (msg.buf)); 87 if (len < 0) { 88 if (DBG(DNS, 0)) { 89 logger(LOG_DEBUG, 90 "DNS search for '%s' failed (%s)", 91 svc_name, hstrerror(state->res_h_errno)); 92 } 93 return (-1); 94 } 95 96 if (len > sizeof (msg.buf)) { 97 logger(LOG_WARNING, 98 "DNS query %ib message doesn't fit into %ib buffer", 99 len, sizeof (msg.buf)); 100 len = sizeof (msg.buf); 101 } 102 103 /* parse the reply header */ 104 105 ptr = msg.buf + sizeof (msg.hdr); 106 eom = msg.buf + len; 107 qdcount = ntohs(msg.hdr.qdcount); 108 ancount = ntohs(msg.hdr.ancount); 109 110 /* skip the question section */ 111 112 len = ns_skiprr(ptr, eom, ns_s_qd, qdcount); 113 if (len < 0) { 114 logger(LOG_ERR, "DNS query invalid message format"); 115 return (-1); 116 } 117 ptr += len; 118 119 /* parse the answer section */ 120 if (ancount < 1) { 121 logger(LOG_ERR, "DNS query - no answers"); 122 return (-1); 123 } 124 125 len = dn_expand(msg.buf, eom, ptr, namebuf, sizeof (namebuf)); 126 if (len < 0) { 127 logger(LOG_ERR, "DNS query invalid message format"); 128 return (-1); 129 } 130 *rrname = strdup(namebuf); 131 if (*rrname == NULL) { 132 logger(LOG_ERR, "Out of memory"); 133 return (-1); 134 } 135 136 return (0); 137 } 138 139 140 /* 141 * Compare SRC RRs; used with qsort(). Sort order: 142 * "Earliest" (lowest number) priority first, 143 * then weight highest to lowest. 144 */ 145 static int 146 srvcmp(ad_disc_ds_t *s1, ad_disc_ds_t *s2) 147 { 148 if (s1->priority < s2->priority) 149 return (-1); 150 else if (s1->priority > s2->priority) 151 return (1); 152 153 if (s1->weight < s2->weight) 154 return (1); 155 else if (s1->weight > s2->weight) 156 return (-1); 157 158 return (0); 159 } 160 161 /* 162 * Query or search the SRV RRs for a given name. 163 * 164 * If dname == NULL then search (as in res_nsearch(3RESOLV), honoring any 165 * search list/option), else query (as in res_nquery(3RESOLV)). 166 * 167 * The output TTL will be the one of the SRV RR with the lowest TTL. 168 */ 169 ad_disc_cds_t * 170 srv_query(res_state state, const char *svc_name, const char *dname, 171 ad_disc_ds_t *prefer) 172 { 173 ad_disc_cds_t *cds_res = NULL; 174 uchar_t *msg = NULL; 175 int len, scnt, maxcnt; 176 177 msg = malloc(NS_MAXMSG); 178 if (msg == NULL) { 179 logger(LOG_ERR, "Out of memory"); 180 return (NULL); 181 } 182 183 /* query necessary resource records */ 184 185 /* Search, querydomain or query */ 186 if (dname == NULL) { 187 dname = "*"; 188 if (DBG(DNS, 1)) { 189 logger(LOG_DEBUG, "Looking for SRV RRs '%s.*'", 190 svc_name); 191 } 192 len = res_nsearch(state, svc_name, C_IN, T_SRV, 193 msg, NS_MAXMSG); 194 if (len < 0) { 195 if (DBG(DNS, 0)) { 196 logger(LOG_DEBUG, 197 "DNS search for '%s' failed (%s)", 198 svc_name, hstrerror(state->res_h_errno)); 199 } 200 goto errout; 201 } 202 } else { /* dname != NULL */ 203 if (DBG(DNS, 1)) { 204 logger(LOG_DEBUG, "Looking for SRV RRs '%s.%s' ", 205 svc_name, dname); 206 } 207 208 len = res_nquerydomain(state, svc_name, dname, C_IN, T_SRV, 209 msg, NS_MAXMSG); 210 211 if (len < 0) { 212 if (DBG(DNS, 0)) { 213 logger(LOG_DEBUG, "DNS: %s.%s: %s", 214 svc_name, dname, 215 hstrerror(state->res_h_errno)); 216 } 217 goto errout; 218 } 219 } 220 221 if (len > NS_MAXMSG) { 222 logger(LOG_WARNING, 223 "DNS query %ib message doesn't fit into %ib buffer", 224 len, NS_MAXMSG); 225 len = NS_MAXMSG; 226 } 227 228 229 /* parse the reply header */ 230 231 cds_res = srv_parse(msg, len, &scnt, &maxcnt); 232 if (cds_res == NULL) 233 goto errout; 234 235 if (prefer != NULL) 236 add_preferred(cds_res, prefer, &scnt, maxcnt); 237 238 get_addresses(cds_res, scnt); 239 240 /* sort list of candidates */ 241 if (scnt > 1) 242 qsort(cds_res, scnt, sizeof (*cds_res), 243 (int (*)(const void *, const void *))srvcmp); 244 245 free(msg); 246 return (cds_res); 247 248 errout: 249 free(msg); 250 return (NULL); 251 } 252 253 static ad_disc_cds_t * 254 srv_parse(uchar_t *msg, int len, int *scnt, int *maxcnt) 255 { 256 ad_disc_cds_t *cds; 257 ad_disc_cds_t *cds_res = NULL; 258 HEADER *hdr; 259 int i, qdcount, ancount, nscount, arcount; 260 uchar_t *ptr, *eom; 261 uchar_t *end; 262 uint16_t type; 263 /* LINTED E_FUNC_SET_NOT_USED */ 264 uint16_t class; 265 uint32_t rttl; 266 uint16_t size; 267 char namebuf[NS_MAXDNAME]; 268 269 eom = msg + len; 270 hdr = (void *)msg; 271 ptr = msg + sizeof (HEADER); 272 273 qdcount = ntohs(hdr->qdcount); 274 ancount = ntohs(hdr->ancount); 275 nscount = ntohs(hdr->nscount); 276 arcount = ntohs(hdr->arcount); 277 278 /* skip the question section */ 279 280 len = ns_skiprr(ptr, eom, ns_s_qd, qdcount); 281 if (len < 0) { 282 logger(LOG_ERR, "DNS query invalid message format"); 283 return (NULL); 284 } 285 ptr += len; 286 287 /* 288 * Walk through the answer section, building the result array. 289 * The array size is +2 because we (possibly) add the preferred 290 * DC if it was not there, and an empty one (null termination). 291 */ 292 293 *maxcnt = ancount + 2; 294 cds_res = calloc(*maxcnt, sizeof (*cds_res)); 295 if (cds_res == NULL) { 296 logger(LOG_ERR, "Out of memory"); 297 return (NULL); 298 } 299 300 cds = cds_res; 301 for (i = 0; i < ancount; i++) { 302 303 len = dn_expand(msg, eom, ptr, namebuf, 304 sizeof (namebuf)); 305 if (len < 0) { 306 logger(LOG_ERR, "DNS query invalid message format"); 307 goto err; 308 } 309 ptr += len; 310 NS_GET16(type, ptr); 311 NS_GET16(class, ptr); 312 NS_GET32(rttl, ptr); 313 NS_GET16(size, ptr); 314 if ((end = ptr + size) > eom) { 315 logger(LOG_ERR, "DNS query invalid message format"); 316 goto err; 317 } 318 319 if (type != T_SRV) { 320 ptr = end; 321 continue; 322 } 323 324 NS_GET16(cds->cds_ds.priority, ptr); 325 NS_GET16(cds->cds_ds.weight, ptr); 326 NS_GET16(cds->cds_ds.port, ptr); 327 len = dn_expand(msg, eom, ptr, cds->cds_ds.host, 328 sizeof (cds->cds_ds.host)); 329 if (len < 0) { 330 logger(LOG_ERR, "DNS query invalid SRV record"); 331 goto err; 332 } 333 334 cds->cds_ds.ttl = rttl; 335 336 if (DBG(DNS, 2)) { 337 logger(LOG_DEBUG, " %s", namebuf); 338 logger(LOG_DEBUG, 339 " ttl=%d pri=%d weight=%d %s:%d", 340 rttl, cds->cds_ds.priority, cds->cds_ds.weight, 341 cds->cds_ds.host, cds->cds_ds.port); 342 } 343 cds++; 344 345 /* move ptr to the end of current record */ 346 ptr = end; 347 } 348 *scnt = (cds - cds_res); 349 350 /* skip the nameservers section (if any) */ 351 352 len = ns_skiprr(ptr, eom, ns_s_ns, nscount); 353 if (len < 0) { 354 logger(LOG_ERR, "DNS query invalid message format"); 355 goto err; 356 } 357 ptr += len; 358 359 /* walk through the additional records */ 360 for (i = 0; i < arcount; i++) { 361 sa_family_t af; 362 363 len = dn_expand(msg, eom, ptr, namebuf, 364 sizeof (namebuf)); 365 if (len < 0) { 366 logger(LOG_ERR, "DNS query invalid message format"); 367 goto err; 368 } 369 ptr += len; 370 NS_GET16(type, ptr); 371 NS_GET16(class, ptr); 372 NS_GET32(rttl, ptr); 373 NS_GET16(size, ptr); 374 if ((end = ptr + size) > eom) { 375 logger(LOG_ERR, "DNS query invalid message format"); 376 goto err; 377 } 378 switch (type) { 379 case ns_t_a: 380 af = AF_INET; 381 break; 382 case ns_t_aaaa: 383 af = AF_INET6; 384 break; 385 default: 386 continue; 387 } 388 389 if (DBG(DNS, 2)) { 390 char abuf[INET6_ADDRSTRLEN]; 391 const char *ap; 392 393 ap = inet_ntop(af, ptr, abuf, sizeof (abuf)); 394 logger(LOG_DEBUG, " %s %s %s", 395 (af == AF_INET) ? "A " : "AAAA", 396 (ap) ? ap : "?", namebuf); 397 } 398 399 /* Find the server, add to its address list. */ 400 for (cds = cds_res; cds->cds_ds.host[0] != '\0'; cds++) 401 if (0 == strcmp(namebuf, cds->cds_ds.host)) 402 save_addr(cds, af, ptr, size); 403 404 /* move ptr to the end of current record */ 405 ptr = end; 406 } 407 408 return (cds_res); 409 410 err: 411 free(cds_res); 412 return (NULL); 413 } 414 415 /* 416 * Save this address on the server, if not already there. 417 */ 418 static void 419 save_addr(ad_disc_cds_t *cds, sa_family_t af, uchar_t *addr, size_t alen) 420 { 421 struct addrinfo *ai, *new_ai, *last_ai; 422 423 new_ai = make_addrinfo(af, addr, alen); 424 if (new_ai == NULL) 425 return; 426 427 last_ai = NULL; 428 for (ai = cds->cds_ai; ai != NULL; ai = ai->ai_next) { 429 last_ai = ai; 430 431 if (new_ai->ai_family == ai->ai_family && 432 new_ai->ai_addrlen == ai->ai_addrlen && 433 0 == memcmp(new_ai->ai_addr, ai->ai_addr, 434 ai->ai_addrlen)) { 435 /* it's already there */ 436 freeaddrinfo(new_ai); 437 return; 438 } 439 } 440 441 /* Not found. Append. */ 442 if (last_ai != NULL) { 443 last_ai->ai_next = new_ai; 444 } else { 445 cds->cds_ai = new_ai; 446 } 447 } 448 449 static struct addrinfo * 450 make_addrinfo(sa_family_t af, uchar_t *addr, size_t alen) 451 { 452 struct addrinfo *ai; 453 struct sockaddr *sa; 454 struct sockaddr_in *sin; 455 struct sockaddr_in6 *sin6; 456 int slen; 457 458 ai = calloc(1, sizeof (*ai)); 459 sa = calloc(1, sizeof (struct sockaddr_in6)); 460 461 if (ai == NULL || sa == NULL) { 462 logger(LOG_ERR, "Out of memory"); 463 goto errout; 464 } 465 466 switch (af) { 467 case AF_INET: 468 sin = (void *)sa; 469 if (alen < sizeof (in_addr_t)) { 470 logger(LOG_ERR, "bad IPv4 addr len"); 471 goto errout; 472 } 473 alen = sizeof (in_addr_t); 474 sin->sin_family = af; 475 (void) memcpy(&sin->sin_addr, addr, alen); 476 slen = sizeof (*sin); 477 break; 478 479 case AF_INET6: 480 sin6 = (void *)sa; 481 if (alen < sizeof (in6_addr_t)) { 482 logger(LOG_ERR, "bad IPv6 addr len"); 483 goto errout; 484 } 485 alen = sizeof (in6_addr_t); 486 sin6->sin6_family = af; 487 (void) memcpy(&sin6->sin6_addr, addr, alen); 488 slen = sizeof (*sin6); 489 break; 490 491 default: 492 goto errout; 493 } 494 495 ai->ai_family = af; 496 ai->ai_addrlen = slen; 497 ai->ai_addr = sa; 498 sa->sa_family = af; 499 return (ai); 500 501 errout: 502 free(ai); 503 free(sa); 504 return (NULL); 505 } 506 507 /* 508 * Set a preferred candidate, which may already be in the list, 509 * in which case we just bump its priority, or else add it. 510 */ 511 static void 512 add_preferred(ad_disc_cds_t *cds, ad_disc_ds_t *prefer, int *nds, int maxds) 513 { 514 ad_disc_ds_t *ds; 515 int i; 516 517 assert(*nds < maxds); 518 for (i = 0; i < *nds; i++) { 519 ds = &cds[i].cds_ds; 520 521 if (strcasecmp(ds->host, prefer->host) == 0) { 522 /* Force this element to be sorted first. */ 523 ds->priority = 0; 524 ds->weight = 200; 525 return; 526 } 527 } 528 529 /* 530 * The preferred DC was not found in this DNS response, 531 * so add it. Again arrange for it to be sorted first. 532 * Address info. is added later. 533 */ 534 ds = &cds[i].cds_ds; 535 (void) memcpy(ds, prefer, sizeof (*ds)); 536 ds->priority = 0; 537 ds->weight = 200; 538 *nds = i + 1; 539 } 540 541 /* 542 * Do another pass over the array to check for missing addresses and 543 * try resolving the names. Normally, the DNS response from AD will 544 * have supplied additional address records for all the SRV records. 545 */ 546 static void 547 get_addresses(ad_disc_cds_t *cds, int cnt) 548 { 549 int i; 550 551 for (i = 0; i < cnt; i++) { 552 if (cds[i].cds_ai == NULL) { 553 do_getaddrinfo(&cds[i]); 554 } 555 } 556 } 557 558 static void 559 do_getaddrinfo(ad_disc_cds_t *cds) 560 { 561 struct addrinfo hints; 562 struct addrinfo *ai; 563 ad_disc_ds_t *ds; 564 time_t t0, t1; 565 int err; 566 567 (void) memset(&hints, 0, sizeof (hints)); 568 hints.ai_protocol = IPPROTO_TCP; 569 hints.ai_socktype = SOCK_STREAM; 570 ds = &cds->cds_ds; 571 572 /* 573 * This getaddrinfo call may take a LONG time, i.e. if our 574 * DNS servers are misconfigured or not responding. 575 * We need something like getaddrinfo_a(), with a timeout. 576 * For now, just log when this happens so we'll know 577 * if these calls are taking a long time. 578 */ 579 if (DBG(DNS, 2)) 580 logger(LOG_DEBUG, "getaddrinfo %s ...", ds->host); 581 t0 = time(NULL); 582 err = getaddrinfo(cds->cds_ds.host, NULL, &hints, &ai); 583 t1 = time(NULL); 584 if (DBG(DNS, 2)) 585 logger(LOG_DEBUG, "getaddrinfo %s rc=%d", ds->host, err); 586 if (t1 > (t0 + 5)) { 587 logger(LOG_WARNING, "Lookup host (%s) took %u sec. " 588 "(Check DNS settings)", ds->host, (int)(t1 - t0)); 589 } 590 if (err != 0) { 591 logger(LOG_ERR, "No address for host: %s (%s)", 592 ds->host, gai_strerror(err)); 593 /* Make this sort at the end. */ 594 ds->priority = 1 << 16; 595 return; 596 } 597 598 cds->cds_ai = ai; 599 } 600 601 void 602 srv_free(ad_disc_cds_t *cds_vec) 603 { 604 ad_disc_cds_t *cds; 605 606 for (cds = cds_vec; cds->cds_ds.host[0] != '\0'; cds++) { 607 if (cds->cds_ai != NULL) { 608 freeaddrinfo(cds->cds_ai); 609 } 610 } 611 free(cds_vec); 612 } 613