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 2006 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 /* 29 * dns_common.c 30 */ 31 32 #include "dns_common.h" 33 34 #pragma weak dn_expand 35 #pragma weak res_ninit 36 #pragma weak res_nsearch 37 #pragma weak res_nclose 38 #pragma weak ns_get16 39 #pragma weak ns_get32 40 #pragma weak __ns_get16 41 #pragma weak __ns_get32 42 43 #define DNS_ALIASES 0 44 #define DNS_ADDRLIST 1 45 #define DNS_MAPDLIST 2 46 47 static int 48 dns_netdb_aliases(from_list, to_list, aliaspp, type, count, af_type) 49 char **from_list, **to_list, **aliaspp; 50 int type, *count, af_type; 51 { 52 char *fstr; 53 int cnt = 0; 54 size_t len; 55 56 *count = 0; 57 if ((char *)to_list >= *aliaspp) 58 return (NSS_STR_PARSE_ERANGE); 59 60 for (fstr = from_list[cnt]; fstr != NULL; fstr = from_list[cnt]) { 61 if (type == DNS_ALIASES) 62 len = strlen(fstr) + 1; 63 else 64 len = (af_type == AF_INET) ? sizeof (struct in_addr) 65 : sizeof (struct in6_addr); 66 *aliaspp -= len; 67 to_list[cnt] = *aliaspp; 68 if (*aliaspp <= (char *)&to_list[cnt+1]) 69 return (NSS_STR_PARSE_ERANGE); 70 if (type == DNS_MAPDLIST) { 71 /* LINTED: E_BAD_PTR_CAST_ALIGN */ 72 struct in6_addr *addr6p = (struct in6_addr *)*aliaspp; 73 74 (void) memset(addr6p, '\0', sizeof (struct in6_addr)); 75 (void) memcpy(&addr6p->s6_addr[12], fstr, 76 sizeof (struct in_addr)); 77 addr6p->s6_addr[10] = 0xffU; 78 addr6p->s6_addr[11] = 0xffU; 79 ++cnt; 80 } else { 81 (void) memcpy (*aliaspp, fstr, len); 82 ++cnt; 83 } 84 } 85 to_list[cnt] = NULL; 86 87 *count = cnt; 88 if (cnt == 0) 89 return (NSS_STR_PARSE_PARSE); 90 91 return (NSS_STR_PARSE_SUCCESS); 92 } 93 94 95 int 96 ent2result(he, argp, af_type) 97 struct hostent *he; 98 nss_XbyY_args_t *argp; 99 int af_type; 100 { 101 char *buffer, *limit; 102 int buflen = argp->buf.buflen; 103 int ret, count; 104 size_t len; 105 struct hostent *host; 106 struct in_addr *addrp; 107 struct in6_addr *addrp6; 108 109 limit = argp->buf.buffer + buflen; 110 host = (struct hostent *)argp->buf.result; 111 buffer = argp->buf.buffer; 112 113 /* h_addrtype and h_length */ 114 host->h_addrtype = af_type; 115 host->h_length = (af_type == AF_INET) ? sizeof (struct in_addr) 116 : sizeof (struct in6_addr); 117 118 /* h_name */ 119 len = strlen(he->h_name) + 1; 120 host->h_name = buffer; 121 if (host->h_name + len >= limit) 122 return (NSS_STR_PARSE_ERANGE); 123 (void) memcpy(host->h_name, he->h_name, len); 124 buffer += len; 125 126 /* h_addr_list */ 127 if (af_type == AF_INET) { 128 addrp = (struct in_addr *)ROUND_DOWN(limit, sizeof (*addrp)); 129 host->h_addr_list = (char **) 130 ROUND_UP(buffer, sizeof (char **)); 131 ret = dns_netdb_aliases(he->h_addr_list, host->h_addr_list, 132 (char **)&addrp, DNS_ADDRLIST, &count, af_type); 133 if (ret != NSS_STR_PARSE_SUCCESS) 134 return (ret); 135 /* h_aliases */ 136 host->h_aliases = host->h_addr_list + count + 1; 137 ret = dns_netdb_aliases(he->h_aliases, host->h_aliases, 138 (char **)&addrp, DNS_ALIASES, &count, af_type); 139 } else { 140 addrp6 = (struct in6_addr *) 141 ROUND_DOWN(limit, sizeof (*addrp6)); 142 host->h_addr_list = (char **) 143 ROUND_UP(buffer, sizeof (char **)); 144 if (he->h_addrtype == AF_INET && af_type == AF_INET6) { 145 ret = dns_netdb_aliases(he->h_addr_list, 146 host->h_addr_list, (char **)&addrp6, 147 DNS_MAPDLIST, &count, af_type); 148 } else { 149 ret = dns_netdb_aliases(he->h_addr_list, 150 host->h_addr_list, (char **)&addrp6, 151 DNS_ADDRLIST, &count, af_type); 152 } 153 if (ret != NSS_STR_PARSE_SUCCESS) 154 return (ret); 155 /* h_aliases */ 156 host->h_aliases = host->h_addr_list + count + 1; 157 ret = dns_netdb_aliases(he->h_aliases, host->h_aliases, 158 (char **)&addrp6, DNS_ALIASES, &count, af_type); 159 } 160 if (ret == NSS_STR_PARSE_PARSE) 161 ret = NSS_STR_PARSE_SUCCESS; 162 163 return (ret); 164 } 165 166 /* 167 * Convert the hostent structure into string in the following 168 * format: 169 * 170 * IP-address official-host-name nicknames ... 171 * 172 * If more than one IP-addresses matches the official-host-name, 173 * the above line will be followed by: 174 * IP-address-1 official-host-name 175 * IP-address-2 official-host-name 176 * ... 177 * 178 * This is so that the str2hostent function in libnsl 179 * can convert the string back to the original hostent 180 * data. 181 */ 182 int 183 ent2str( 184 struct hostent *hp, 185 nss_XbyY_args_t *ap, 186 int af_type) 187 { 188 char **p; 189 char obuf[INET6_ADDRSTRLEN]; 190 void *addr; 191 struct in_addr in4; 192 int af; 193 int n; 194 const char *res; 195 char **q; 196 int l = ap->buf.buflen; 197 char *s = ap->buf.buffer; 198 199 /* 200 * for "hosts" lookup, we only want address type of 201 * AF_INET. For "ipnodes", we can have both AF_INET 202 * and AF_INET6. 203 */ 204 if (af_type == AF_INET && hp->h_addrtype != AF_INET) 205 return (NSS_STR_PARSE_PARSE); 206 207 for (p = hp->h_addr_list; *p != 0; p++) { 208 209 if (p != hp->h_addr_list) { 210 *s = '\n'; 211 s++; 212 l--; 213 } 214 215 if (hp->h_addrtype == AF_INET6) { 216 /* LINTED: E_BAD_PTR_CAST_ALIGN */ 217 if (IN6_IS_ADDR_V4MAPPED((struct in6_addr *)*p)) { 218 /* LINTED: E_BAD_PTR_CAST_ALIGN */ 219 IN6_V4MAPPED_TO_INADDR((struct in6_addr *)*p, 220 &in4); 221 af = AF_INET; 222 addr = &in4; 223 } else { 224 af = AF_INET6; 225 addr = *p; 226 } 227 } else { 228 af = AF_INET; 229 addr = *p; 230 } 231 res = inet_ntop(af, addr, obuf, sizeof (obuf)); 232 if (res == NULL) 233 return (NSS_STR_PARSE_PARSE); 234 235 if ((n = snprintf(s, l, "%s %s", res, hp->h_name)) >= l) 236 return (NSS_STR_PARSE_ERANGE); 237 l -= n; 238 s += n; 239 if (p == hp->h_addr_list) { 240 for (q = hp->h_aliases; q && *q; q++) { 241 if ((n = snprintf(s, l, " %s", *q)) >= l) 242 return (NSS_STR_PARSE_ERANGE); 243 l -= n; 244 s += n; 245 } 246 } 247 } 248 249 ap->returnlen = s - ap->buf.buffer; 250 return (NSS_STR_PARSE_SUCCESS); 251 } 252 253 nss_backend_t * 254 _nss_dns_constr(dns_backend_op_t ops[], int n_ops) 255 { 256 dns_backend_ptr_t be; 257 258 if ((be = (dns_backend_ptr_t)malloc(sizeof (*be))) == 0) 259 return (0); 260 261 be->ops = ops; 262 be->n_ops = n_ops; 263 return ((nss_backend_t *)be); 264 } 265 266 267 /* 268 * nss_dns_gethost_withttl(void *buffer, size_t bufsize, int ipnode) 269 * nss2 get hosts/ipnodes with ttl backend DNS search engine. 270 * 271 * This API is given a pointer to a packed buffer, and the buffer size 272 * It's job is to perform the appropriate res_nsearch, extract the 273 * results and build a unmarshalled hosts/ipnodes result buffer. 274 * Additionally in the extended results a nssuint_t ttl is placed. 275 * This ttl is the lessor of the ttl's extracted from the result. 276 * 277 * ***Currently the first version of this API only performs simple 278 * single res_nsearch lookups for with T_A or T_AAAA results. 279 * Other searches are deferred to the generic API w/t ttls. 280 * 281 * This function is not a generic res_* operation. It only performs 282 * a single T_A or T_AAAA lookups*** 283 * 284 * RETURNS: NSS_SUCCESS or NSS_ERROR 285 * If an NSS_ERROR result is returned, nscd is expected 286 * to resubmit the gethosts request using the old style 287 * nsswitch lookup format. 288 */ 289 290 struct tsd_priv { 291 struct __res_state *statp; /* dns state block */ 292 union msg { 293 uchar_t buf[NS_MAXMSG]; /* max legal DNS answer size */ 294 HEADER h; 295 } resbuf; 296 char aliases[NS_MAXMSG]; /* set of aliases */ 297 }; 298 299 static void ghttlcleanup(void *ptr) 300 { 301 struct tsd_priv *priv = (struct tsd_priv *)ptr; 302 303 if (priv) { 304 if (priv->statp != NULL) { 305 res_nclose(priv->statp); 306 free((void *)priv->statp); 307 } 308 free(ptr); 309 } 310 } 311 312 nss_status_t 313 _nss_dns_gethost_withttl(void *buffer, size_t bufsize, int ipnode) 314 { 315 /* nss buffer variables */ 316 nss_pheader_t *pbuf = (nss_pheader_t *)buffer; 317 nss_XbyY_args_t arg; 318 char *dbname; 319 int dbop; 320 nss_status_t sret; 321 size_t bsize, blen; 322 char *bptr; 323 /* resolver query variables */ 324 static mutex_t keylock; 325 static thread_key_t key; 326 static int once_per_keyname = 0; 327 struct tsd_priv *tsd = NULL; 328 const char *name; 329 int qtype; 330 /* answer parsing variables */ 331 HEADER *hp; 332 uchar_t *cp; /* current location in message */ 333 uchar_t *bom; /* start of message */ 334 uchar_t *eom; /* end of message */ 335 uchar_t *eor; /* end of record */ 336 int ancount, qdcount; 337 int type, class; 338 nssuint_t nttl, ttl, *pttl; /* The purpose of this API */ 339 int n, ret; 340 const char *np; 341 /* temporary buffers */ 342 char nbuf[INET6_ADDRSTRLEN]; /* address parser */ 343 char host[MAXHOSTNAMELEN]; /* result host name */ 344 char ans[MAXHOSTNAMELEN]; /* record name */ 345 char aname[MAXHOSTNAMELEN]; /* alias result (C_NAME) */ 346 /* misc variables */ 347 int af; 348 char *ap, *apc; 349 int hlen, alen, iplen, len; 350 351 if (!once_per_keyname) { 352 (void) mutex_lock(&keylock); 353 if (!once_per_keyname) { 354 (void) thr_keycreate(&key, ghttlcleanup); 355 once_per_keyname++; 356 } 357 (void) mutex_unlock(&keylock); 358 } 359 (void) thr_getspecific(key, (void **)&tsd); 360 if (tsd == NULL) { 361 tsd = (struct tsd_priv *)calloc(1, sizeof (struct tsd_priv)); 362 (void) thr_setspecific(key, (void *)tsd); 363 (void) thr_getspecific(key, (void **)&tsd); 364 tsd->statp = (struct __res_state *) 365 calloc(1, sizeof (struct __res_state)); 366 if (tsd->statp == NULL) 367 return (NSS_ERROR); 368 if (res_ninit(tsd->statp) == -1) { 369 free(tsd->statp); 370 return (NSS_ERROR); 371 } 372 } 373 ap = apc = (char *)tsd->aliases; 374 alen = 0; 375 ttl = (nssuint_t)0xFFFFFFF; /* start w/max, find smaller */ 376 377 /* save space for ttl otherwise, why bother... */ 378 bsize = pbuf->data_len - sizeof (nssuint_t); 379 bptr = (char *)buffer + pbuf->data_off; 380 blen = 0; 381 sret = nss_packed_getkey(buffer, bufsize, &dbname, &dbop, &arg); 382 if (sret != NSS_SUCCESS) { 383 return (NSS_ERROR); 384 } 385 386 if (ipnode) { 387 /* initially only handle the simple cases */ 388 if (arg.key.ipnode.flags != 0) 389 return (NSS_ERROR); 390 name = arg.key.ipnode.name; 391 if (arg.key.ipnode.af_family == AF_INET6) 392 qtype = T_AAAA; 393 else 394 qtype = T_A; 395 } else { 396 name = arg.key.name; 397 qtype = T_A; 398 } 399 ret = res_nsearch(tsd->statp, name, C_IN, qtype, 400 tsd->resbuf.buf, NS_MAXMSG); 401 if (ret == -1) { 402 if (tsd->statp->res_h_errno == HOST_NOT_FOUND) { 403 pbuf->p_herrno = HOST_NOT_FOUND; 404 pbuf->p_status = NSS_NOTFOUND; 405 pbuf->data_len = 0; 406 return (NSS_NOTFOUND); 407 } 408 /* else lookup error - handle in general code */ 409 return (NSS_ERROR); 410 } 411 412 cp = tsd->resbuf.buf; 413 hp = (HEADER *)&tsd->resbuf.h; 414 bom = cp; 415 eom = cp + ret; 416 417 ancount = ntohs(hp->ancount); 418 qdcount = ntohs(hp->qdcount); 419 cp += HFIXEDSZ; 420 if (qdcount != 1) 421 return (NSS_ERROR); 422 n = dn_expand(bom, eom, cp, host, MAXHOSTNAMELEN); 423 if (n < 0) { 424 return (NSS_ERROR); 425 } else 426 hlen = strlen(host); 427 cp += n + QFIXEDSZ; 428 if (cp > eom) 429 return (NSS_ERROR); 430 while (ancount-- > 0 && cp < eom && blen < bsize) { 431 n = dn_expand(bom, eom, cp, ans, MAXHOSTNAMELEN); 432 if (n > 0) { 433 if (strncasecmp(host, ans, hlen) != 0) 434 return (NSS_ERROR); /* spoof? */ 435 } 436 cp += n; 437 /* bounds check */ 438 type = ns_get16(cp); /* type */ 439 cp += INT16SZ; 440 class = ns_get16(cp); /* class */ 441 cp += INT16SZ; 442 nttl = (nssuint_t)ns_get32(cp); /* ttl in sec */ 443 if (nttl < ttl) 444 ttl = nttl; 445 cp += INT32SZ; 446 n = ns_get16(cp); /* len */ 447 cp += INT16SZ; 448 if (class != C_IN) { 449 cp += n; 450 continue; 451 } 452 eor = cp + n; 453 if (type == T_CNAME) { 454 /* add an alias to the alias list */ 455 n = dn_expand(bom, eor, cp, aname, MAXHOSTNAMELEN); 456 if (n > 0) { 457 len = strlen(aname); 458 if (len > 0) { 459 /* 460 * Just error out if there is an 461 * attempted buffer overflow exploit 462 * generic code will do a syslog 463 */ 464 if (alen + len + 2 > NS_MAXMSG) 465 return (NSS_ERROR); 466 *apc++ = ' '; 467 alen++; 468 (void) strlcpy(apc, aname, len + 1); 469 alen += len; 470 apc += len; 471 } 472 } 473 cp += n; 474 continue; 475 } 476 if (type != qtype) { 477 cp += n; 478 continue; 479 } 480 /* check data size */ 481 if ((type == T_A && n != INADDRSZ) || 482 (type == T_AAAA && n != IN6ADDRSZ)) { 483 cp += n; 484 continue; 485 } 486 af = (type == T_A ? AF_INET : AF_INET6); 487 np = inet_ntop(af, (void *)cp, nbuf, INET6_ADDRSTRLEN); 488 if (np == NULL) 489 return (NSS_ERROR); 490 cp += n; 491 /* append IP host aliases to results */ 492 iplen = strlen(np); 493 /* ip <SP> hostname [<SP>][aliases] */ 494 len = iplen + 2 + hlen + alen; 495 if (alen > 0) 496 len++; 497 if (blen + len > bsize) 498 return (NSS_ERROR); 499 (void) strlcpy(bptr, np, bsize - blen); 500 blen += iplen; 501 bptr += iplen; 502 *bptr++ = ' '; 503 blen++; 504 (void) strlcpy(bptr, host, bsize - blen); 505 blen += hlen; 506 bptr += hlen; 507 if (alen > 0) { 508 *bptr++ = ' '; 509 blen++; 510 (void) strlcpy(bptr, ap, bsize - blen); 511 blen += alen; 512 bptr += alen; 513 } 514 *bptr++ = '\n'; 515 blen++; 516 } 517 /* Presumably the buffer is now filled. */ 518 len = ROUND_UP(blen, sizeof (nssuint_t)); 519 /* still room? */ 520 if (len + sizeof (nssuint_t) > pbuf->data_len) { 521 /* sigh, no, what happened? */ 522 return (NSS_ERROR); 523 } 524 pbuf->ext_off = pbuf->data_off + len; 525 pbuf->ext_len = sizeof (nssuint_t); 526 pbuf->data_len = blen; 527 pttl = (nssuint_t *)((void *)((char *)pbuf + pbuf->ext_off)); 528 *pttl = ttl; 529 return (NSS_SUCCESS); 530 } 531