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 nss_status_t 291 _nss_dns_gethost_withttl(void *buffer, size_t bufsize, int ipnode) 292 { 293 /* nss buffer variables */ 294 nss_pheader_t *pbuf = (nss_pheader_t *)buffer; 295 nss_XbyY_args_t arg; 296 char *dbname; 297 int dbop; 298 nss_status_t sret; 299 size_t bsize, blen; 300 char *bptr; 301 /* resolver query variables */ 302 struct __res_state stat, *statp; /* dns state block */ 303 union msg { 304 uchar_t buf[NS_MAXMSG]; /* max legal DNS answer size */ 305 HEADER h; 306 } resbuf; 307 char aliases[NS_MAXMSG]; /* set of aliases */ 308 const char *name; 309 int qtype; 310 /* answer parsing variables */ 311 HEADER *hp; 312 uchar_t *cp; /* current location in message */ 313 uchar_t *bom; /* start of message */ 314 uchar_t *eom; /* end of message */ 315 uchar_t *eor; /* end of record */ 316 int ancount, qdcount; 317 int type, class; 318 nssuint_t nttl, ttl, *pttl; /* The purpose of this API */ 319 int n, ret; 320 const char *np; 321 /* temporary buffers */ 322 char nbuf[INET6_ADDRSTRLEN]; /* address parser */ 323 char host[MAXHOSTNAMELEN]; /* result host name */ 324 char ans[MAXHOSTNAMELEN]; /* record name */ 325 char aname[MAXHOSTNAMELEN]; /* alias result (C_NAME) */ 326 /* misc variables */ 327 int af; 328 char *ap, *apc; 329 int hlen, alen, iplen, len; 330 331 statp = &stat; 332 (void) memset(statp, '\0', sizeof (struct __res_state)); 333 if (res_ninit(statp) == -1) 334 return (NSS_ERROR); 335 336 ap = apc = (char *)aliases; 337 alen = 0; 338 ttl = (nssuint_t)0xFFFFFFF; /* start w/max, find smaller */ 339 340 /* save space for ttl otherwise, why bother... */ 341 bsize = pbuf->data_len - sizeof (nssuint_t); 342 bptr = (char *)buffer + pbuf->data_off; 343 blen = 0; 344 sret = nss_packed_getkey(buffer, bufsize, &dbname, &dbop, &arg); 345 if (sret != NSS_SUCCESS) { 346 res_nclose(statp); 347 return (NSS_ERROR); 348 } 349 350 if (ipnode) { 351 /* initially only handle the simple cases */ 352 if (arg.key.ipnode.flags != 0) { 353 res_nclose(statp); 354 return (NSS_ERROR); 355 } 356 name = arg.key.ipnode.name; 357 if (arg.key.ipnode.af_family == AF_INET6) 358 qtype = T_AAAA; 359 else 360 qtype = T_A; 361 } else { 362 name = arg.key.name; 363 qtype = T_A; 364 } 365 ret = res_nsearch(statp, name, C_IN, qtype, resbuf.buf, NS_MAXMSG); 366 if (ret == -1) { 367 if (statp->res_h_errno == HOST_NOT_FOUND) { 368 pbuf->p_herrno = HOST_NOT_FOUND; 369 pbuf->p_status = NSS_NOTFOUND; 370 pbuf->data_len = 0; 371 res_nclose(statp); 372 return (NSS_NOTFOUND); 373 } 374 /* else lookup error - handle in general code */ 375 res_nclose(statp); 376 return (NSS_ERROR); 377 } 378 379 cp = resbuf.buf; 380 hp = (HEADER *)&resbuf.h; 381 bom = cp; 382 eom = cp + ret; 383 384 ancount = ntohs(hp->ancount); 385 qdcount = ntohs(hp->qdcount); 386 cp += HFIXEDSZ; 387 if (qdcount != 1) { 388 res_nclose(statp); 389 return (NSS_ERROR); 390 } 391 n = dn_expand(bom, eom, cp, host, MAXHOSTNAMELEN); 392 if (n < 0) { 393 res_nclose(statp); 394 return (NSS_ERROR); 395 } else 396 hlen = strlen(host); 397 cp += n + QFIXEDSZ; 398 if (cp > eom) { 399 res_nclose(statp); 400 return (NSS_ERROR); 401 } 402 while (ancount-- > 0 && cp < eom && blen < bsize) { 403 n = dn_expand(bom, eom, cp, ans, MAXHOSTNAMELEN); 404 if (n > 0) { 405 if (strncasecmp(host, ans, hlen) != 0) { 406 res_nclose(statp); 407 return (NSS_ERROR); /* spoof? */ 408 } 409 } 410 cp += n; 411 /* bounds check */ 412 type = ns_get16(cp); /* type */ 413 cp += INT16SZ; 414 class = ns_get16(cp); /* class */ 415 cp += INT16SZ; 416 nttl = (nssuint_t)ns_get32(cp); /* ttl in sec */ 417 if (nttl < ttl) 418 ttl = nttl; 419 cp += INT32SZ; 420 n = ns_get16(cp); /* len */ 421 cp += INT16SZ; 422 if (class != C_IN) { 423 cp += n; 424 continue; 425 } 426 eor = cp + n; 427 if (type == T_CNAME) { 428 /* add an alias to the alias list */ 429 n = dn_expand(bom, eor, cp, aname, MAXHOSTNAMELEN); 430 if (n > 0) { 431 len = strlen(aname); 432 if (len > 0) { 433 /* 434 * Just error out if there is an 435 * attempted buffer overflow exploit 436 * generic code will do a syslog 437 */ 438 if (alen + len + 2 > NS_MAXMSG) 439 return (NSS_ERROR); 440 *apc++ = ' '; 441 alen++; 442 (void) strlcpy(apc, aname, len + 1); 443 alen += len; 444 apc += len; 445 } 446 } 447 cp += n; 448 continue; 449 } 450 if (type != qtype) { 451 cp += n; 452 continue; 453 } 454 /* check data size */ 455 if ((type == T_A && n != INADDRSZ) || 456 (type == T_AAAA && n != IN6ADDRSZ)) { 457 cp += n; 458 continue; 459 } 460 af = (type == T_A ? AF_INET : AF_INET6); 461 np = inet_ntop(af, (void *)cp, nbuf, INET6_ADDRSTRLEN); 462 if (np == NULL) { 463 res_nclose(statp); 464 return (NSS_ERROR); 465 } 466 cp += n; 467 /* append IP host aliases to results */ 468 iplen = strlen(np); 469 /* ip <SP> hostname [<SP>][aliases] */ 470 len = iplen + 2 + hlen + alen; 471 if (alen > 0) 472 len++; 473 if (blen + len > bsize) 474 return (NSS_ERROR); 475 (void) strlcpy(bptr, np, bsize - blen); 476 blen += iplen; 477 bptr += iplen; 478 *bptr++ = ' '; 479 blen++; 480 (void) strlcpy(bptr, host, bsize - blen); 481 blen += hlen; 482 bptr += hlen; 483 if (alen > 0) { 484 *bptr++ = ' '; 485 blen++; 486 (void) strlcpy(bptr, ap, bsize - blen); 487 blen += alen; 488 bptr += alen; 489 } 490 *bptr++ = '\n'; 491 blen++; 492 } 493 /* Presumably the buffer is now filled. */ 494 len = ROUND_UP(blen, sizeof (nssuint_t)); 495 /* still room? */ 496 if (len + sizeof (nssuint_t) > pbuf->data_len) { 497 /* sigh, no, what happened? */ 498 return (NSS_ERROR); 499 } 500 pbuf->ext_off = pbuf->data_off + len; 501 pbuf->ext_len = sizeof (nssuint_t); 502 pbuf->data_len = blen; 503 pttl = (nssuint_t *)((void *)((char *)pbuf + pbuf->ext_off)); 504 *pttl = ttl; 505 res_nclose(statp); 506 return (NSS_SUCCESS); 507 } 508