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