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 2010 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 * 26 * Copyright 2016 Joyent, Inc. 27 * 28 * This file defines and implements the re-entrant getipnodebyname(), 29 * getipnodebyaddr(), and freehostent() routines for IPv6. These routines 30 * follow use the netdir_getbyYY() (see netdir_inet.c). 31 * 32 * lib/libnsl/nss/getipnodeby.c 33 */ 34 35 #include "mt.h" 36 #include <stdlib.h> 37 #include <unistd.h> 38 #include <stropts.h> 39 #include <ctype.h> 40 #include <string.h> 41 #include <strings.h> 42 #include <netdb.h> 43 #include <stdio.h> 44 #include <arpa/inet.h> 45 #include <nss_dbdefs.h> 46 #include <netinet/in.h> 47 #include <sys/socket.h> 48 #include <sys/sockio.h> 49 #include <nss_netdir.h> 50 #include <net/if.h> 51 #include <netinet/in.h> 52 #include <netdir.h> 53 #include <thread.h> 54 #include <synch.h> 55 #include <fcntl.h> 56 #include <sys/time.h> 57 #include "nss.h" 58 59 #define IPV6_LITERAL_CHAR ':' 60 61 /* 62 * The number of nanoseconds getipnodebyname() waits before getting 63 * fresh interface count information with SIOCGLIFNUM. The default is 64 * five minutes. 65 */ 66 #define IFNUM_TIMEOUT ((hrtime_t)300 * NANOSEC) 67 68 /* 69 * Bits in the bitfield returned by getipnodebyname_processflags(). 70 * 71 * IPNODE_WANTIPV6 The user wants IPv6 addresses returned. 72 * IPNODE_WANTIPV4 The user wants IPv4 addresses returned. 73 * IPNODE_IPV4IFNOIPV6 The user only wants IPv4 addresses returned if no IPv6 74 * addresses are returned. 75 * IPNODE_LOOKUPIPNODES getipnodebyname() needs to lookup the name in ipnodes. 76 * IPNODE_LOOKUPHOSTS getipnodebyname() needs to lookup the name in hosts. 77 * IPNODE_ISLITERAL The name supplied is a literal address string. 78 * IPNODE_UNMAP The user doesn't want v4 mapped addresses if no IPv6 79 * interfaces are plumbed on the system. 80 */ 81 #define IPNODE_WANTIPV6 0x00000001u 82 #define IPNODE_WANTIPV4 0x00000002u 83 #define IPNODE_IPV4IFNOIPV6 0x00000004u 84 #define IPNODE_LOOKUPIPNODES 0x00000008u 85 #define IPNODE_LOOKUPHOSTS 0x00000010u 86 #define IPNODE_LITERAL 0x00000020u 87 #define IPNODE_UNMAP 0x00000040u 88 #define IPNODE_IPV4 (IPNODE_WANTIPV4 | IPNODE_IPV4IFNOIPV6) 89 90 /* 91 * The private flag between libsocket and libnsl. See 92 * lib/libsocket/inet/getaddrinfo.c for more information. 93 */ 94 #define AI_ADDRINFO 0x8000 95 96 /* 97 * The default set of bits corresponding to a getipnodebyname() flags 98 * argument of AI_DEFAULT. 99 */ 100 #define IPNODE_DEFAULT (IPNODE_WANTIPV6 | IPNODE_IPV4 | \ 101 IPNODE_LOOKUPIPNODES | IPNODE_LOOKUPHOSTS) 102 103 extern struct netconfig *__rpc_getconfip(char *); 104 105 static struct hostent *__mapv4tov6(struct hostent *, struct hostent *, 106 nss_XbyY_buf_t *, int); 107 struct hostent *__mappedtov4(struct hostent *, int *); 108 static struct hostent *__filter_addresses(int, struct hostent *); 109 static int __find_mapped(struct hostent *, int); 110 static nss_XbyY_buf_t *__IPv6_alloc(int); 111 static void __IPv6_cleanup(nss_XbyY_buf_t *); 112 static int __ai_addrconfig(int, boolean_t); 113 114 115 #ifdef PIC 116 struct hostent * 117 _uncached_getipnodebyname(const char *nam, struct hostent *result, 118 char *buffer, int buflen, int af_family, int flags, int *h_errnop) 119 { 120 return (_switch_getipnodebyname_r(nam, result, buffer, buflen, 121 af_family, flags, h_errnop)); 122 } 123 124 struct hostent * 125 _uncached_getipnodebyaddr(const char *addr, int length, int type, 126 struct hostent *result, char *buffer, int buflen, int *h_errnop) 127 { 128 if (type == AF_INET) 129 return (_switch_gethostbyaddr_r(addr, length, type, 130 result, buffer, buflen, h_errnop)); 131 else if (type == AF_INET6) 132 return (_switch_getipnodebyaddr_r(addr, length, type, 133 result, buffer, buflen, h_errnop)); 134 return (NULL); 135 } 136 #endif 137 138 /* 139 * Given a name, an address family, and a set of flags, return a 140 * bitfield that getipnodebyname() will use. 141 */ 142 static uint_t 143 getipnodebyname_processflags(const char *name, int af, int flags) 144 { 145 uint_t ipnode_bits = IPNODE_DEFAULT; 146 boolean_t ipv6configured = B_FALSE; 147 boolean_t ipv4configured = B_FALSE; 148 149 /* 150 * If AI_ADDRCONFIG is specified, we need to determine the number of 151 * addresses of each address family configured on the system as 152 * appropriate. 153 * 154 * When trying to determine which addresses should be used for 155 * addrconfig, we first ignore loopback devices. This generally makes 156 * sense as policy, as most of these queries will be trying to go 157 * off-box and one should not have an IPv6 loopback address suggest that 158 * we can now send IPv6 traffic off the box or the equivalent with IPv4. 159 * However, it's possible that no non-loopback interfaces are up on the 160 * box. In those cases, we then check which interfaces are up and 161 * consider loopback devices. While this isn't to the letter of RFC 3493 162 * (which itself is a bit vague in this case, as is SUS), it matches 163 * expected user behavior in these situations. 164 */ 165 if (flags & AI_ADDRCONFIG) { 166 boolean_t hv4, hv6; 167 168 hv4 = __ai_addrconfig(AF_INET, B_FALSE) > 0; 169 hv6 = __ai_addrconfig(AF_INET6, B_FALSE) > 0; 170 171 if (hv4 == B_FALSE && hv6 == B_FALSE) { 172 hv4 = __ai_addrconfig(AF_INET, B_TRUE) > 0; 173 hv6 = __ai_addrconfig(AF_INET6, B_TRUE) > 0; 174 } 175 176 ipv6configured = (af == AF_INET6 && hv6); 177 ipv4configured = (af == AF_INET || (flags & AI_V4MAPPED)) && 178 hv4; 179 } 180 181 /* 182 * Determine what kinds of addresses the user is interested 183 * in getting back. 184 */ 185 switch (af) { 186 case AF_INET6: 187 if ((flags & AI_ADDRCONFIG) && !ipv6configured) 188 ipnode_bits &= ~IPNODE_WANTIPV6; 189 190 if (flags & AI_V4MAPPED) { 191 if ((flags & AI_ADDRCONFIG) && !ipv4configured) { 192 ipnode_bits &= ~IPNODE_IPV4; 193 } else if (flags & AI_ALL) { 194 ipnode_bits &= ~IPNODE_IPV4IFNOIPV6; 195 } 196 if ((flags & AI_ADDRCONFIG) && !ipv6configured && 197 (flags & AI_ADDRINFO)) { 198 ipnode_bits |= IPNODE_UNMAP; 199 } 200 } else { 201 ipnode_bits &= ~IPNODE_IPV4; 202 } 203 break; 204 case AF_INET: 205 if ((flags & AI_ADDRCONFIG) && !ipv4configured) 206 ipnode_bits &= ~IPNODE_IPV4; 207 ipnode_bits &= ~IPNODE_WANTIPV6; 208 ipnode_bits &= ~IPNODE_IPV4IFNOIPV6; 209 break; 210 default: 211 ipnode_bits = 0; 212 break; 213 } 214 215 /* 216 * If we're not looking for IPv4 addresses, don't bother looking 217 * in hosts. 218 */ 219 if (!(ipnode_bits & IPNODE_WANTIPV4)) 220 ipnode_bits &= ~IPNODE_LOOKUPHOSTS; 221 222 /* 223 * Determine if name is a literal IP address. This will 224 * further narrow down what type of lookup we're going to do. 225 */ 226 if (strchr(name, IPV6_LITERAL_CHAR) != NULL) { 227 /* Literal IPv6 address */ 228 ipnode_bits |= IPNODE_LITERAL; 229 /* 230 * In s9 we accepted the literal without filtering independent 231 * of what family was passed in hints. We continue to do 232 * this. 233 */ 234 ipnode_bits |= (IPNODE_WANTIPV6 | IPNODE_WANTIPV4); 235 ipnode_bits &= ~IPNODE_LOOKUPHOSTS; 236 } else if (inet_addr(name) != 0xffffffffU) { 237 /* Literal IPv4 address */ 238 ipnode_bits |= (IPNODE_LITERAL | IPNODE_WANTIPV4); 239 ipnode_bits &= ~IPNODE_WANTIPV6; 240 ipnode_bits &= ~IPNODE_LOOKUPIPNODES; 241 } 242 return (ipnode_bits); 243 } 244 245 struct hostent * 246 getipnodebyname(const char *name, int af, int flags, int *error_num) 247 { 248 struct hostent *hp = NULL; 249 nss_XbyY_buf_t *buf4 = NULL; 250 nss_XbyY_buf_t *buf6 = NULL; 251 struct netconfig *nconf; 252 struct nss_netdirbyname_in nssin; 253 union nss_netdirbyname_out nssout; 254 int ret; 255 uint_t ipnode_bits; 256 257 if ((nconf = __rpc_getconfip("udp")) == NULL && 258 (nconf = __rpc_getconfip("tcp")) == NULL) { 259 *error_num = NO_RECOVERY; 260 return (NULL); 261 } 262 263 ipnode_bits = getipnodebyname_processflags(name, af, flags); 264 265 /* Make sure we have something to look up. */ 266 if (!(ipnode_bits & (IPNODE_WANTIPV6 | IPNODE_WANTIPV4))) { 267 *error_num = HOST_NOT_FOUND; 268 goto cleanup; 269 } 270 271 /* 272 * Perform the requested lookups. We always look through 273 * ipnodes first for both IPv4 and IPv6 addresses. Depending 274 * on what was returned and what was needed, we either filter 275 * out the garbage, or ask for more using hosts. 276 */ 277 if (ipnode_bits & IPNODE_LOOKUPIPNODES) { 278 if ((buf6 = __IPv6_alloc(NSS_BUFLEN_IPNODES)) == NULL) { 279 *error_num = NO_RECOVERY; 280 goto cleanup; 281 } 282 nssin.op_t = NSS_HOST6; 283 nssin.arg.nss.host6.name = name; 284 nssin.arg.nss.host6.buf = buf6->buffer; 285 nssin.arg.nss.host6.buflen = buf6->buflen; 286 nssin.arg.nss.host6.af_family = af; 287 nssin.arg.nss.host6.flags = flags; 288 nssout.nss.host.hent = buf6->result; 289 nssout.nss.host.herrno_p = error_num; 290 ret = _get_hostserv_inetnetdir_byname(nconf, &nssin, &nssout); 291 if (ret != ND_OK) { 292 __IPv6_cleanup(buf6); 293 buf6 = NULL; 294 } else if (ipnode_bits & IPNODE_WANTIPV4) { 295 /* 296 * buf6 may have all that we need if we either 297 * only wanted IPv4 addresses if there were no 298 * IPv6 addresses returned, or if there are 299 * IPv4-mapped addresses in buf6. If either 300 * of these are true, then there's no need to 301 * look in hosts. 302 */ 303 if (ipnode_bits & IPNODE_IPV4IFNOIPV6 || 304 __find_mapped(buf6->result, 0) != 0) { 305 ipnode_bits &= ~IPNODE_LOOKUPHOSTS; 306 } else if (!(ipnode_bits & IPNODE_WANTIPV6)) { 307 /* 308 * If all we're looking for are IPv4 309 * addresses and there are none in 310 * buf6 then buf6 is now useless. 311 */ 312 __IPv6_cleanup(buf6); 313 buf6 = NULL; 314 } 315 } 316 } 317 if (ipnode_bits & IPNODE_LOOKUPHOSTS) { 318 if ((buf4 = __IPv6_alloc(NSS_BUFLEN_HOSTS)) == NULL) { 319 *error_num = NO_RECOVERY; 320 goto cleanup; 321 } 322 nssin.op_t = NSS_HOST; 323 nssin.arg.nss.host.name = name; 324 nssin.arg.nss.host.buf = buf4->buffer; 325 nssin.arg.nss.host.buflen = buf4->buflen; 326 nssout.nss.host.hent = buf4->result; 327 nssout.nss.host.herrno_p = error_num; 328 ret = _get_hostserv_inetnetdir_byname(nconf, &nssin, &nssout); 329 if (ret != ND_OK) { 330 __IPv6_cleanup(buf4); 331 buf4 = NULL; 332 } 333 } 334 335 if (buf6 == NULL && buf4 == NULL) { 336 *error_num = HOST_NOT_FOUND; 337 goto cleanup; 338 } 339 340 /* Extract the appropriate addresses from the returned buffer(s). */ 341 switch (af) { 342 case AF_INET6: { 343 if (buf4 != NULL) { 344 nss_XbyY_buf_t *mergebuf; 345 346 /* 347 * The IPv4 results we have need to be 348 * converted to IPv4-mapped addresses, 349 * conditionally merged with the IPv6 350 * results, and the end result needs to be 351 * re-ordered. 352 */ 353 mergebuf = __IPv6_alloc(NSS_BUFLEN_IPNODES); 354 if (mergebuf == NULL) { 355 *error_num = NO_RECOVERY; 356 goto cleanup; 357 } 358 hp = __mapv4tov6(buf4->result, 359 ((buf6 != NULL) ? buf6->result : NULL), 360 mergebuf, 1); 361 if (hp != NULL) 362 order_haddrlist_af(AF_INET6, hp->h_addr_list); 363 else 364 *error_num = NO_RECOVERY; 365 free(mergebuf); 366 } 367 368 if (buf4 == NULL && buf6 != NULL) { 369 hp = buf6->result; 370 371 /* 372 * We have what we need in buf6, but we may need 373 * to filter out some addresses depending on what 374 * is being asked for. 375 */ 376 if (!(ipnode_bits & IPNODE_WANTIPV4)) 377 hp = __filter_addresses(AF_INET, buf6->result); 378 else if (!(ipnode_bits & IPNODE_WANTIPV6)) 379 hp = __filter_addresses(AF_INET6, buf6->result); 380 381 /* 382 * We've been asked to unmap v4 addresses. This 383 * situation implies IPNODE_WANTIPV4 and 384 * !IPNODE_WANTIPV6. 385 */ 386 if (hp != NULL && (ipnode_bits & IPNODE_UNMAP)) { 387 /* 388 * Just set hp to a new value, cleanup: will 389 * free the old one 390 */ 391 hp = __mappedtov4(hp, error_num); 392 } else if (hp == NULL) 393 *error_num = NO_ADDRESS; 394 } 395 396 break; 397 } 398 399 case AF_INET: 400 /* We could have results in buf6 or buf4, not both */ 401 if (buf6 != NULL) { 402 /* 403 * Extract the IPv4-mapped addresses from buf6 404 * into hp. 405 */ 406 hp = __mappedtov4(buf6->result, error_num); 407 } else { 408 /* We have what we need in buf4. */ 409 hp = buf4->result; 410 if (ipnode_bits & IPNODE_LITERAL) { 411 /* 412 * There is a special case here for literal 413 * IPv4 address strings. The hosts 414 * front-end sets h_aliases to a one 415 * element array containing a single NULL 416 * pointer (in ndaddr2hent()), while 417 * getipnodebyname() requires h_aliases to 418 * be a NULL pointer itself. We're not 419 * going to change the front-end since it 420 * needs to remain backward compatible for 421 * gethostbyname() and friends. Just set 422 * h_aliases to NULL here instead. 423 */ 424 hp->h_aliases = NULL; 425 } 426 } 427 428 break; 429 430 default: 431 break; 432 } 433 434 cleanup: 435 /* 436 * Free the memory we allocated, but make sure we don't free 437 * the memory we're returning to the caller. 438 */ 439 if (buf6 != NULL) { 440 if (buf6->result == hp) 441 buf6->result = NULL; 442 __IPv6_cleanup(buf6); 443 } 444 if (buf4 != NULL) { 445 if (buf4->result == hp) 446 buf4->result = NULL; 447 __IPv6_cleanup(buf4); 448 } 449 (void) freenetconfigent(nconf); 450 451 return (hp); 452 } 453 454 /* 455 * This is the IPv6 interface for "gethostbyaddr". 456 */ 457 struct hostent * 458 getipnodebyaddr(const void *src, size_t len, int type, int *error_num) 459 { 460 struct in6_addr *addr6 = 0; 461 struct in_addr *addr4 = 0; 462 nss_XbyY_buf_t *buf = 0; 463 nss_XbyY_buf_t *res = 0; 464 struct netconfig *nconf; 465 struct hostent *hp = 0; 466 struct nss_netdirbyaddr_in nssin; 467 union nss_netdirbyaddr_out nssout; 468 int neterr; 469 char tmpbuf[64]; 470 471 if (type == AF_INET6) { 472 if ((addr6 = (struct in6_addr *)src) == NULL) { 473 *error_num = HOST_NOT_FOUND; 474 return (NULL); 475 } 476 } else if (type == AF_INET) { 477 if ((addr4 = (struct in_addr *)src) == NULL) { 478 *error_num = HOST_NOT_FOUND; 479 return (NULL); 480 } 481 } else { 482 *error_num = HOST_NOT_FOUND; 483 return (NULL); 484 } 485 /* 486 * Specific case: query for "::" 487 */ 488 if (type == AF_INET6 && IN6_IS_ADDR_UNSPECIFIED(addr6)) { 489 *error_num = HOST_NOT_FOUND; 490 return (NULL); 491 } 492 /* 493 * Step 1: IPv4-mapped address or IPv4 Compat 494 */ 495 if ((type == AF_INET6 && len == 16) && 496 ((IN6_IS_ADDR_V4MAPPED(addr6)) || 497 (IN6_IS_ADDR_V4COMPAT(addr6)))) { 498 if ((buf = __IPv6_alloc(NSS_BUFLEN_IPNODES)) == 0) { 499 *error_num = NO_RECOVERY; 500 return (NULL); 501 } 502 if ((nconf = __rpc_getconfip("udp")) == NULL && 503 (nconf = __rpc_getconfip("tcp")) == NULL) { 504 *error_num = NO_RECOVERY; 505 __IPv6_cleanup(buf); 506 return (NULL); 507 } 508 nssin.op_t = NSS_HOST6; 509 if (IN6_IS_ADDR_V4COMPAT(addr6)) { 510 (void) memcpy(tmpbuf, addr6, sizeof (*addr6)); 511 tmpbuf[10] = 0xffU; 512 tmpbuf[11] = 0xffU; 513 nssin.arg.nss.host.addr = (const char *)tmpbuf; 514 } else { 515 nssin.arg.nss.host.addr = (const char *)addr6; 516 } 517 nssin.arg.nss.host.len = sizeof (struct in6_addr); 518 nssin.arg.nss.host.type = AF_INET6; 519 nssin.arg.nss.host.buf = buf->buffer; 520 nssin.arg.nss.host.buflen = buf->buflen; 521 522 nssout.nss.host.hent = buf->result; 523 nssout.nss.host.herrno_p = error_num; 524 /* 525 * We pass in nconf and let the implementation of the 526 * long-named func decide whether to use the switch based on 527 * nc_nlookups. 528 */ 529 neterr = 530 _get_hostserv_inetnetdir_byaddr(nconf, &nssin, &nssout); 531 532 (void) freenetconfigent(nconf); 533 if (neterr != ND_OK) { 534 /* Failover case, try hosts db for v4 address */ 535 if (!gethostbyaddr_r(((char *)addr6) + 12, 536 sizeof (in_addr_t), AF_INET, buf->result, 537 buf->buffer, buf->buflen, error_num)) { 538 __IPv6_cleanup(buf); 539 return (NULL); 540 } 541 /* Found one, now format it into mapped/compat addr */ 542 if ((res = __IPv6_alloc(NSS_BUFLEN_IPNODES)) == 0) { 543 __IPv6_cleanup(buf); 544 *error_num = NO_RECOVERY; 545 return (NULL); 546 } 547 /* Convert IPv4 to mapped/compat address w/name */ 548 hp = res->result; 549 (void) __mapv4tov6(buf->result, 0, res, 550 IN6_IS_ADDR_V4MAPPED(addr6)); 551 __IPv6_cleanup(buf); 552 free(res); 553 return (hp); 554 } 555 /* 556 * At this point, we'll have a v4mapped hostent. If that's 557 * what was passed in, just return. If the request was a compat, 558 * twiggle the two bytes to make the mapped address a compat. 559 */ 560 hp = buf->result; 561 if (IN6_IS_ADDR_V4COMPAT(addr6)) { 562 /* LINTED pointer cast */ 563 addr6 = (struct in6_addr *)hp->h_addr_list[0]; 564 addr6->s6_addr[10] = 0; 565 addr6->s6_addr[11] = 0; 566 } 567 free(buf); 568 return (hp); 569 } 570 /* 571 * Step 2: AF_INET, v4 lookup. Since we're going to search the 572 * ipnodes (v6) path first, we need to treat this as a v4mapped 573 * address. nscd(8) caches v4 from ipnodes as mapped v6's. The 574 * switch backend knows to lookup v4's (not v4mapped) from the 575 * name services. 576 */ 577 if (type == AF_INET) { 578 struct in6_addr v4mapbuf; 579 addr6 = &v4mapbuf; 580 581 IN6_INADDR_TO_V4MAPPED(addr4, addr6); 582 if ((nconf = __rpc_getconfip("udp")) == NULL && 583 (nconf = __rpc_getconfip("tcp")) == NULL) { 584 *error_num = NO_RECOVERY; 585 return (NULL); 586 } 587 if ((buf = __IPv6_alloc(NSS_BUFLEN_IPNODES)) == 0) { 588 *error_num = NO_RECOVERY; 589 freenetconfigent(nconf); 590 return (NULL); 591 } 592 nssin.op_t = NSS_HOST6; 593 nssin.arg.nss.host.addr = (const char *)addr6; 594 nssin.arg.nss.host.len = sizeof (struct in6_addr); 595 nssin.arg.nss.host.type = AF_INET6; 596 nssin.arg.nss.host.buf = buf->buffer; 597 nssin.arg.nss.host.buflen = buf->buflen; 598 599 nssout.nss.host.hent = buf->result; 600 nssout.nss.host.herrno_p = error_num; 601 /* 602 * We pass in nconf and let the implementation of the 603 * long-named func decide whether to use the switch based on 604 * nc_nlookups. 605 */ 606 neterr = 607 _get_hostserv_inetnetdir_byaddr(nconf, &nssin, &nssout); 608 609 (void) freenetconfigent(nconf); 610 if (neterr != ND_OK) { 611 /* Failover case, try hosts db for v4 address */ 612 hp = buf->result; 613 if (!gethostbyaddr_r(src, len, type, buf->result, 614 buf->buffer, buf->buflen, error_num)) { 615 __IPv6_cleanup(buf); 616 return (NULL); 617 } 618 free(buf); 619 return (hp); 620 } 621 if ((hp = __mappedtov4(buf->result, error_num)) == NULL) { 622 __IPv6_cleanup(buf); 623 return (NULL); 624 } 625 __IPv6_cleanup(buf); 626 return (hp); 627 } 628 /* 629 * Step 3: AF_INET6, plain vanilla v6 getipnodebyaddr() call. 630 */ 631 if (type == AF_INET6) { 632 if ((nconf = __rpc_getconfip("udp")) == NULL && 633 (nconf = __rpc_getconfip("tcp")) == NULL) { 634 *error_num = NO_RECOVERY; 635 return (NULL); 636 } 637 if ((buf = __IPv6_alloc(NSS_BUFLEN_IPNODES)) == 0) { 638 *error_num = NO_RECOVERY; 639 freenetconfigent(nconf); 640 return (NULL); 641 } 642 nssin.op_t = NSS_HOST6; 643 nssin.arg.nss.host.addr = (const char *)addr6; 644 nssin.arg.nss.host.len = len; 645 nssin.arg.nss.host.type = type; 646 nssin.arg.nss.host.buf = buf->buffer; 647 nssin.arg.nss.host.buflen = buf->buflen; 648 649 nssout.nss.host.hent = buf->result; 650 nssout.nss.host.herrno_p = error_num; 651 /* 652 * We pass in nconf and let the implementation of the 653 * long-named func decide whether to use the switch based on 654 * nc_nlookups. 655 */ 656 neterr = 657 _get_hostserv_inetnetdir_byaddr(nconf, &nssin, &nssout); 658 659 (void) freenetconfigent(nconf); 660 if (neterr != ND_OK) { 661 __IPv6_cleanup(buf); 662 return (NULL); 663 } 664 free(buf); 665 return (nssout.nss.host.hent); 666 } 667 /* 668 * If we got here, unknown type. 669 */ 670 *error_num = HOST_NOT_FOUND; 671 return (NULL); 672 } 673 674 void 675 freehostent(struct hostent *hent) 676 { 677 free(hent); 678 } 679 680 static int 681 __ai_addrconfig(int af, boolean_t loopback) 682 { 683 struct lifnum lifn; 684 struct lifconf lifc; 685 struct lifreq *lifp, *buf = NULL; 686 size_t bufsize; 687 hrtime_t now, *then; 688 static hrtime_t then4, then6; /* the last time we updated ifnum# */ 689 static int ifnum4 = -1, ifnum6 = -1, iflb4 = 0, iflb6 = 0; 690 int *num, *lb; 691 int nlifr, count = 0; 692 693 694 switch (af) { 695 case AF_INET: 696 num = &ifnum4; 697 then = &then4; 698 lb = &iflb4; 699 break; 700 case AF_INET6: 701 num = &ifnum6; 702 then = &then6; 703 lb = &iflb6; 704 break; 705 default: 706 return (0); 707 } 708 709 /* 710 * We don't need to check this every time someone does a name 711 * lookup. Do it every IFNUM_TIMEOUT for each address family. 712 * 713 * There's no need to protect all of this with a lock. The 714 * worst that can happen is that we update the interface count 715 * twice instead of once. That's no big deal. 716 */ 717 now = gethrtime(); 718 if (*num == -1 || ((now - *then) >= IFNUM_TIMEOUT)) { 719 lifn.lifn_family = af; 720 /* 721 * We want to determine if this machine knows anything 722 * at all about the address family; the status of the 723 * interface is less important. Hence, set 724 * 'lifn_flags' to zero. 725 */ 726 lifn.lifn_flags = 0; 727 again: 728 if (nss_ioctl(af, SIOCGLIFNUM, &lifn) < 0) 729 goto fail; 730 731 if (lifn.lifn_count == 0) { 732 *lb = 0; 733 *num = 0; 734 *then = now; 735 return (*num); 736 } 737 738 /* 739 * Pad the interface count to detect when additional 740 * interfaces have been configured between SIOCGLIFNUM 741 * and SIOCGLIFCONF. 742 */ 743 lifn.lifn_count += 4; 744 745 bufsize = lifn.lifn_count * sizeof (struct lifreq); 746 if ((buf = realloc(buf, bufsize)) == NULL) 747 goto fail; 748 749 lifc.lifc_family = af; 750 lifc.lifc_flags = 0; 751 lifc.lifc_len = bufsize; 752 lifc.lifc_buf = (caddr_t)buf; 753 if (nss_ioctl(af, SIOCGLIFCONF, &lifc) < 0) 754 goto fail; 755 756 nlifr = lifc.lifc_len / sizeof (struct lifreq); 757 if (nlifr >= lifn.lifn_count) 758 goto again; 759 /* 760 * Do not include any loopback addresses, 127.0.0.1 for AF_INET 761 * and ::1 for AF_INET6, while counting the number of available 762 * IPv4 or IPv6 addresses. (RFC 3493 requires this, whenever 763 * AI_ADDRCONFIG flag is set) However, if the loopback flag is 764 * set to true we'll include it in the output. 765 */ 766 for (lifp = buf; lifp < buf + nlifr; lifp++) { 767 switch (af) { 768 case AF_INET: { 769 struct sockaddr_in *in; 770 771 in = (struct sockaddr_in *)&lifp->lifr_addr; 772 if (ntohl(in->sin_addr.s_addr) == 773 INADDR_LOOPBACK) { 774 count++; 775 } 776 break; 777 } 778 case AF_INET6: { 779 struct sockaddr_in6 *in6; 780 781 in6 = (struct sockaddr_in6 *)&lifp->lifr_addr; 782 if (IN6_IS_ADDR_LOOPBACK(&in6->sin6_addr)) 783 count++; 784 break; 785 } 786 } 787 } 788 *num = nlifr - count; 789 *lb = count; 790 *then = now; 791 free(buf); 792 } 793 if (loopback == B_TRUE) 794 return (*num + *lb); 795 else 796 return (*num); 797 fail: 798 free(buf); 799 /* 800 * If the process is running without the NET_ACCESS basic privilege, 801 * pretend we still have inet/inet6 interfaces. 802 */ 803 if (errno == EACCES) 804 return (1); 805 return (-1); 806 } 807 808 /* 809 * This routine will either convert an IPv4 address to a mapped or compat 810 * IPv6 (if he6 == NULL) or merge IPv6 (he6) addresses with mapped 811 * v4 (he4) addresses. In either case, the results are returned in res. 812 * Caller must provide all buffers. 813 * Inputs: 814 * he4 pointer to IPv4 buffer 815 * he6 pointer to IPv6 buffer (NULL if not merging v4/v6 816 * res pointer to results buffer 817 * mapped mapped == 1, map IPv4 : mapped == 0, compat IPv4 818 * mapped flag is ignored if he6 != NULL 819 * 820 * The results are packed into the res->buffer as follows: 821 * <--------------- buffer + buflen --------------------------------------> 822 * |-----------------|-----------------|----------------|----------------| 823 * | pointers vector | pointers vector | aliases grow | addresses grow | 824 * | for addresses | for aliases | | | 825 * | this way -> | this way -> | <- this way |<- this way | 826 * |-----------------|-----------------|----------------|----------------| 827 * | grows in PASS 1 | grows in PASS2 | grows in PASS2 | grows in PASS 1| 828 */ 829 static struct hostent * 830 __mapv4tov6(struct hostent *he4, struct hostent *he6, nss_XbyY_buf_t *res, 831 int mapped) 832 { 833 char *buffer, *limit; 834 int buflen = res->buflen; 835 struct in6_addr *addr6p; 836 char *buff_locp; 837 struct hostent *host; 838 int count = 0, len, i; 839 char *h_namep; 840 841 if (he4 == NULL || res == NULL) { 842 return (NULL); 843 } 844 limit = res->buffer + buflen; 845 host = (struct hostent *)res->result; 846 buffer = res->buffer; 847 848 buff_locp = (char *)ROUND_DOWN(limit, sizeof (struct in6_addr)); 849 host->h_addr_list = (char **)ROUND_UP(buffer, sizeof (char **)); 850 if ((char *)host->h_addr_list >= limit || 851 buff_locp <= (char *)host->h_addr_list) { 852 return (NULL); 853 } 854 if (he6 == NULL) { 855 /* 856 * If he6==NULL, map the v4 address into the v6 address format. 857 * This is used for getipnodebyaddr() (single address, mapped or 858 * compatible) or for v4 mapped for getipnodebyname(), which 859 * could be multiple addresses. This could also be a literal 860 * address string, which is why there is a inet_addr() call. 861 */ 862 for (i = 0; he4->h_addr_list[i] != NULL; i++) { 863 buff_locp -= sizeof (struct in6_addr); 864 if (buff_locp <= 865 (char *)&(host->h_addr_list[count + 1])) { 866 /* 867 * Has to be room for the pointer to the address we're 868 * about to add, as well as the final NULL ptr. 869 */ 870 return (NULL); 871 } 872 /* LINTED pointer cast */ 873 addr6p = (struct in6_addr *)buff_locp; 874 host->h_addr_list[count] = (char *)addr6p; 875 bzero(addr6p->s6_addr, sizeof (struct in6_addr)); 876 if (mapped) { 877 addr6p->s6_addr[10] = 0xff; 878 addr6p->s6_addr[11] = 0xff; 879 } 880 bcopy((char *)he4->h_addr_list[i], 881 &addr6p->s6_addr[12], sizeof (struct in_addr)); 882 ++count; 883 } 884 /* 885 * Set last array element to NULL and add cname as first alias 886 */ 887 host->h_addr_list[count] = NULL; 888 host->h_aliases = host->h_addr_list + count + 1; 889 count = 0; 890 if ((int)(inet_addr(he4->h_name)) != -1) { 891 /* 892 * Literal address string, since we're mapping, we need the IPv6 893 * V4 mapped literal address string for h_name. 894 */ 895 char tmpstr[128]; 896 (void) inet_ntop(AF_INET6, host->h_addr_list[0], tmpstr, 897 sizeof (tmpstr)); 898 buff_locp -= (len = strlen(tmpstr) + 1); 899 h_namep = tmpstr; 900 if (buff_locp <= (char *)(host->h_aliases)) 901 return (NULL); 902 bcopy(h_namep, buff_locp, len); 903 host->h_name = buff_locp; 904 host->h_aliases = NULL; /* no aliases for literal */ 905 host->h_length = sizeof (struct in6_addr); 906 host->h_addrtype = AF_INET6; 907 return (host); /* we're done, return result */ 908 } 909 /* 910 * Not a literal address string, so just copy h_name. 911 */ 912 buff_locp -= (len = strlen(he4->h_name) + 1); 913 h_namep = he4->h_name; 914 if (buff_locp <= (char *)(host->h_aliases)) 915 return (NULL); 916 bcopy(h_namep, buff_locp, len); 917 host->h_name = buff_locp; 918 /* 919 * Pass 2 (IPv4 aliases): 920 */ 921 for (i = 0; he4->h_aliases[i] != NULL; i++) { 922 buff_locp -= (len = strlen(he4->h_aliases[i]) + 1); 923 if (buff_locp <= 924 (char *)&(host->h_aliases[count + 1])) { 925 /* 926 * Has to be room for the pointer to the address we're 927 * about to add, as well as the final NULL ptr. 928 */ 929 return (NULL); 930 } 931 host->h_aliases[count] = buff_locp; 932 bcopy((char *)he4->h_aliases[i], buff_locp, len); 933 ++count; 934 } 935 host->h_aliases[count] = NULL; 936 host->h_length = sizeof (struct in6_addr); 937 host->h_addrtype = AF_INET6; 938 return (host); 939 } else { 940 /* 941 * Merge IPv4 mapped addresses with IPv6 addresses. The 942 * IPv6 address will go in first, followed by the v4 mapped. 943 * 944 * Pass 1 (IPv6 addresses): 945 */ 946 for (i = 0; he6->h_addr_list[i] != NULL; i++) { 947 buff_locp -= sizeof (struct in6_addr); 948 if (buff_locp <= 949 (char *)&(host->h_addr_list[count + 1])) { 950 /* 951 * Has to be room for the pointer to the address we're 952 * about to add, as well as the final NULL ptr. 953 */ 954 return (NULL); 955 } 956 host->h_addr_list[count] = buff_locp; 957 bcopy((char *)he6->h_addr_list[i], buff_locp, 958 sizeof (struct in6_addr)); 959 ++count; 960 } 961 /* 962 * Pass 1 (IPv4 mapped addresses): 963 */ 964 for (i = 0; he4->h_addr_list[i] != NULL; i++) { 965 buff_locp -= sizeof (struct in6_addr); 966 if (buff_locp <= 967 (char *)&(host->h_addr_list[count + 1])) { 968 /* 969 * Has to be room for the pointer to the address we're 970 * about to add, as well as the final NULL ptr. 971 */ 972 return (NULL); 973 } 974 /* LINTED pointer cast */ 975 addr6p = (struct in6_addr *)buff_locp; 976 host->h_addr_list[count] = (char *)addr6p; 977 bzero(addr6p->s6_addr, sizeof (struct in6_addr)); 978 addr6p->s6_addr[10] = 0xff; 979 addr6p->s6_addr[11] = 0xff; 980 bcopy(he4->h_addr_list[i], &addr6p->s6_addr[12], 981 sizeof (struct in_addr)); 982 ++count; 983 } 984 /* 985 * Pass 2 (IPv6 aliases, host name first). We start h_aliases 986 * one after where h_addr_list array ended. This is where cname 987 * is put, followed by all aliases. Reset count to 0, for index 988 * in the h_aliases array. 989 */ 990 host->h_addr_list[count] = NULL; 991 host->h_aliases = host->h_addr_list + count + 1; 992 count = 0; 993 buff_locp -= (len = strlen(he6->h_name) + 1); 994 if (buff_locp <= (char *)(host->h_aliases)) 995 return (NULL); 996 bcopy(he6->h_name, buff_locp, len); 997 host->h_name = buff_locp; 998 for (i = 0; he6->h_aliases[i] != NULL; i++) { 999 buff_locp -= (len = strlen(he6->h_aliases[i]) + 1); 1000 if (buff_locp <= 1001 (char *)&(host->h_aliases[count + 1])) { 1002 /* 1003 * Has to be room for the pointer to the address we're 1004 * about to add, as well as the final NULL ptr. 1005 */ 1006 return (NULL); 1007 } 1008 host->h_aliases[count] = buff_locp; 1009 bcopy((char *)he6->h_aliases[i], buff_locp, len); 1010 ++count; 1011 } 1012 /* 1013 * Pass 2 (IPv4 aliases): 1014 */ 1015 for (i = 0; he4->h_aliases[i] != NULL; i++) { 1016 buff_locp -= (len = strlen(he4->h_aliases[i]) + 1); 1017 if (buff_locp <= 1018 (char *)&(host->h_aliases[count + 1])) { 1019 /* 1020 * Has to be room for the pointer to the address we're 1021 * about to add, as well as the final NULL ptr. 1022 */ 1023 return (NULL); 1024 } 1025 host->h_aliases[count] = buff_locp; 1026 bcopy((char *)he4->h_aliases[i], buff_locp, len); 1027 ++count; 1028 } 1029 host->h_aliases[count] = NULL; 1030 host->h_length = sizeof (struct in6_addr); 1031 host->h_addrtype = AF_INET6; 1032 return (host); 1033 } 1034 } 1035 1036 /* 1037 * This routine will convert a mapped v4 hostent (AF_INET6) to a 1038 * AF_INET hostent. If no mapped addrs found, then a NULL is returned. 1039 * If mapped addrs found, then a new buffer is alloc'd and all the v4 mapped 1040 * addresses are extracted and copied to it. On sucess, a pointer to a new 1041 * hostent is returned. 1042 * There are two possible errors in which case a NULL is returned. 1043 * One of two error codes are returned: 1044 * 1045 * NO_RECOVERY - a malloc failed or the like for which there's no recovery. 1046 * NO_ADDRESS - after filtering all the v4, there was nothing left! 1047 * 1048 * Inputs: 1049 * he pointer to hostent with mapped v4 addresses 1050 * filter_error pointer to return error code 1051 * Return: 1052 * pointer to a malloc'd hostent with v4 addresses. 1053 * 1054 * The results are packed into the res->buffer as follows: 1055 * <--------------- buffer + buflen --------------------------------------> 1056 * |-----------------|-----------------|----------------|----------------| 1057 * | pointers vector | pointers vector | aliases grow | addresses grow | 1058 * | for addresses | for aliases | | | 1059 * | this way -> | this way -> | <- this way |<- this way | 1060 * |-----------------|-----------------|----------------|----------------| 1061 * | grows in PASS 1 | grows in PASS2 | grows in PASS2 | grows in PASS 1| 1062 */ 1063 struct hostent * 1064 __mappedtov4(struct hostent *he, int *extract_error) 1065 { 1066 char *buffer, *limit; 1067 nss_XbyY_buf_t *res; 1068 int buflen = NSS_BUFLEN_HOSTS; 1069 struct in_addr *addr4p; 1070 char *buff_locp; 1071 struct hostent *host; 1072 int count = 0, len, i; 1073 char *h_namep; 1074 1075 if (he == NULL) { 1076 *extract_error = NO_ADDRESS; 1077 return (NULL); 1078 } 1079 if ((__find_mapped(he, 0)) == 0) { 1080 *extract_error = NO_ADDRESS; 1081 return (NULL); 1082 } 1083 if ((res = __IPv6_alloc(NSS_BUFLEN_HOSTS)) == 0) { 1084 *extract_error = NO_RECOVERY; 1085 return (NULL); 1086 } 1087 limit = res->buffer + buflen; 1088 host = (struct hostent *)res->result; 1089 buffer = res->buffer; 1090 1091 buff_locp = (char *)ROUND_DOWN(limit, sizeof (struct in_addr)); 1092 host->h_addr_list = (char **)ROUND_UP(buffer, sizeof (char **)); 1093 if ((char *)host->h_addr_list >= limit || 1094 buff_locp <= (char *)host->h_addr_list) 1095 goto cleanup; 1096 /* 1097 * "Unmap" the v4 mapped address(es) into a v4 hostent format. 1098 * This is used for getipnodebyaddr() (single address) or for 1099 * v4 mapped for getipnodebyname(), which could be multiple 1100 * addresses. This could also be a literal address string, 1101 * which is why there is a inet_addr() call. 1102 */ 1103 for (i = 0; he->h_addr_list[i] != NULL; i++) { 1104 /* LINTED pointer cast */ 1105 if (!IN6_IS_ADDR_V4MAPPED((struct in6_addr *) 1106 he->h_addr_list[i])) 1107 continue; 1108 buff_locp -= sizeof (struct in6_addr); 1109 /* 1110 * Has to be room for the pointer to the address we're 1111 * about to add, as well as the final NULL ptr. 1112 */ 1113 if (buff_locp <= 1114 (char *)&(host->h_addr_list[count + 1])) 1115 goto cleanup; 1116 /* LINTED pointer cast */ 1117 addr4p = (struct in_addr *)buff_locp; 1118 host->h_addr_list[count] = (char *)addr4p; 1119 bzero((char *)&addr4p->s_addr, 1120 sizeof (struct in_addr)); 1121 /* LINTED pointer cast */ 1122 IN6_V4MAPPED_TO_INADDR( 1123 (struct in6_addr *)he->h_addr_list[i], addr4p); 1124 ++count; 1125 } 1126 /* 1127 * Set last array element to NULL and add cname as first alias 1128 */ 1129 host->h_addr_list[count] = NULL; 1130 host->h_aliases = host->h_addr_list + count + 1; 1131 count = 0; 1132 /* Copy official host name */ 1133 buff_locp -= (len = strlen(he->h_name) + 1); 1134 h_namep = he->h_name; 1135 if (buff_locp <= (char *)(host->h_aliases)) 1136 goto cleanup; 1137 bcopy(h_namep, buff_locp, len); 1138 host->h_name = buff_locp; 1139 /* 1140 * Pass 2 (IPv4 aliases): 1141 */ 1142 if (he->h_aliases != NULL) { 1143 for (i = 0; he->h_aliases[i] != NULL; i++) { 1144 buff_locp -= (len = strlen(he->h_aliases[i]) + 1); 1145 /* 1146 * Has to be room for the pointer to the address we're 1147 * about to add, as well as the final NULL ptr. 1148 */ 1149 if (buff_locp <= 1150 (char *)&(host->h_aliases[count + 1])) 1151 goto cleanup; 1152 host->h_aliases[count] = buff_locp; 1153 bcopy((char *)he->h_aliases[i], buff_locp, len); 1154 ++count; 1155 } 1156 } 1157 host->h_aliases[count] = NULL; 1158 host->h_length = sizeof (struct in_addr); 1159 host->h_addrtype = AF_INET; 1160 free(res); 1161 return (host); 1162 cleanup: 1163 *extract_error = NO_RECOVERY; 1164 (void) __IPv6_cleanup(res); 1165 return (NULL); 1166 } 1167 1168 /* 1169 * This routine takes as input a pointer to a hostent and filters out 1170 * the type of addresses specified by the af argument. AF_INET 1171 * indicates that the caller wishes to filter out IPv4-mapped 1172 * addresses, and AF_INET6 indicates that the caller wishes to filter 1173 * out IPv6 addresses which aren't IPv4-mapped. If filtering would 1174 * result in all addresses being filtered out, a NULL pointer is returned. 1175 * Otherwise, the he pointer passed in is returned, even if no addresses 1176 * were filtered out. 1177 */ 1178 static struct hostent * 1179 __filter_addresses(int af, struct hostent *he) 1180 { 1181 struct in6_addr **in6addrlist, **in6addr; 1182 boolean_t isipv4mapped; 1183 int i = 0; 1184 1185 if (he == NULL) 1186 return (NULL); 1187 1188 in6addrlist = (struct in6_addr **)he->h_addr_list; 1189 for (in6addr = in6addrlist; *in6addr != NULL; in6addr++) { 1190 isipv4mapped = IN6_IS_ADDR_V4MAPPED(*in6addr); 1191 1192 if ((af == AF_INET && !isipv4mapped) || 1193 (af == AF_INET6 && isipv4mapped)) { 1194 if (in6addrlist[i] != *in6addr) 1195 in6addrlist[i] = *in6addr; 1196 i++; 1197 } 1198 } 1199 1200 if (i == 0) { 1201 /* We filtered everything out. */ 1202 return (NULL); 1203 } else { 1204 /* NULL terminate the list and return the hostent */ 1205 in6addrlist[i] = NULL; 1206 return (he); 1207 } 1208 } 1209 1210 /* 1211 * This routine searches a hostent for v4 mapped IPv6 addresses. 1212 * he hostent structure to seach 1213 * find_both flag indicating if only want mapped or both map'd and v6 1214 * return values: 1215 * 0 = No mapped addresses 1216 * 1 = Mapped v4 address found (returns on first one found) 1217 * 2 = Both v6 and v4 mapped are present 1218 * 1219 * If hostent passed in with no addresses, zero will be returned. 1220 */ 1221 1222 static int 1223 __find_mapped(struct hostent *he, int find_both) 1224 { 1225 int i; 1226 int mapd_found = 0; 1227 int v6_found = 0; 1228 1229 for (i = 0; he->h_addr_list[i] != NULL; i++) { 1230 /* LINTED pointer cast */ 1231 if (IN6_IS_ADDR_V4MAPPED( 1232 (struct in6_addr *)he->h_addr_list[i])) { 1233 if (find_both) 1234 mapd_found = 1; 1235 else 1236 return (1); 1237 } else { 1238 v6_found = 1; 1239 } 1240 /* save some iterations once both found */ 1241 if (mapd_found && v6_found) 1242 return (2); 1243 } 1244 return (mapd_found); 1245 } 1246 1247 /* 1248 * This routine was added specifically for the IPv6 getipnodeby*() APIs. This 1249 * separates the result pointer (ptr to hostent+data buf) from the 1250 * nss_XbyY_buf_t ptr (required for nsswitch API). The returned hostent ptr 1251 * can be passed to freehostent() and freed independently. 1252 * 1253 * bufp->result bufp->buffer 1254 * | | 1255 * V V 1256 * ------------------------------------------------...-- 1257 * |struct hostent |addresses aliases | 1258 * ------------------------------------------------...-- 1259 * | |<--------bufp->buflen-------------->| 1260 */ 1261 1262 #define ALIGN(x) ((((long)(x)) + sizeof (long) - 1) & ~(sizeof (long) - 1)) 1263 1264 static nss_XbyY_buf_t * 1265 __IPv6_alloc(int bufsz) 1266 { 1267 nss_XbyY_buf_t *bufp; 1268 1269 if ((bufp = malloc(sizeof (nss_XbyY_buf_t))) == NULL) 1270 return (NULL); 1271 1272 if ((bufp->result = malloc(ALIGN(sizeof (struct hostent)) + bufsz)) == 1273 NULL) { 1274 free(bufp); 1275 return (NULL); 1276 } 1277 bufp->buffer = (char *)(bufp->result) + sizeof (struct hostent); 1278 bufp->buflen = bufsz; 1279 return (bufp); 1280 } 1281 1282 /* 1283 * This routine is use only for error return cleanup. This will free the 1284 * hostent pointer, so don't use for successful returns. 1285 */ 1286 static void 1287 __IPv6_cleanup(nss_XbyY_buf_t *bufp) 1288 { 1289 if (bufp == NULL) 1290 return; 1291 if (bufp->result != NULL) 1292 free(bufp->result); 1293 free(bufp); 1294 } 1295