dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / lib / libc / port / nsl / netdir_inet.c
blob90c349f94eef0335f46259d44f0578b1deea8bc5
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
23 * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
24 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
25 * Use is subject to license terms.
26 * Copyright 2012 Milan Jurik. All rights reserved.
30 * This is where we have chosen to combine every useful bit of code for
31 * all the Solaris frontends to lookup hosts, services, and netdir information
32 * for inet family (udp, tcp) transports. gethostbyYY(), getservbyYY(), and
33 * netdir_getbyYY() are all implemented on top of this code. Similarly,
34 * netdir_options, taddr2uaddr, and uaddr2taddr for inet transports also
35 * find a home here.
37 * If the netconfig structure supplied has NO nametoaddr libs (i.e. a "-"
38 * in /etc/netconfig), this code calls the name service switch, and
39 * therefore, /etc/nsswitch.conf is effectively the only place that
40 * dictates hosts/serv lookup policy.
41 * If an administrator chooses to bypass the name service switch by
42 * specifying third party supplied nametoaddr libs in /etc/netconfig, this
43 * implementation does NOT call the name service switch, it merely loops
44 * through the nametoaddr libs. In this case, if this code was called
45 * from gethost/servbyYY() we marshal the inet specific struct into
46 * transport independent netbuf or hostserv, and unmarshal the resulting
47 * nd_addrlist or hostservlist back into hostent and servent, as the case
48 * may be.
50 * Goes without saying that most of the future bugs in gethost/servbyYY
51 * and netdir_getbyYY are lurking somewhere here.
54 #include "mt.h"
55 #include <ctype.h>
56 #include <stdio.h>
57 #include <stdlib.h>
58 #include <string.h>
59 #include <unistd.h>
60 #include <stropts.h>
61 #include <sys/types.h>
62 #include <sys/byteorder.h>
63 #include <sys/ioctl.h>
64 #include <sys/param.h>
65 #include <sys/time.h>
66 #include <errno.h>
67 #include <fcntl.h>
68 #include <thread.h>
69 #include <synch.h>
70 #include <sys/utsname.h>
71 #include <netdb.h>
72 #include <netconfig.h>
73 #include <netdir.h>
74 #include <tiuser.h>
75 #include <sys/socket.h>
76 #include <sys/sockio.h>
77 #include <netinet/in.h>
78 #include <arpa/inet.h>
79 #include <net/if.h>
80 #include <inet/ip.h>
81 #include <inet/ip6_asp.h>
82 #include <sys/dlpi.h>
83 #include <nss_dbdefs.h>
84 #include <nss_netdir.h>
85 #include <syslog.h>
86 #include <nsswitch.h>
87 #include "nss.h"
89 #define MAXIFS 32
90 #define UDPDEV "/dev/udp"
91 #define UDP6DEV "/dev/udp6"
93 #define DOOR_GETHOSTBYNAME_R _switch_gethostbyname_r
94 #define DOOR_GETHOSTBYADDR_R _switch_gethostbyaddr_r
95 #define DOOR_GETIPNODEBYNAME_R _switch_getipnodebyname_r
96 #define DOOR_GETIPNODEBYADDR_R _switch_getipnodebyaddr_r
98 #define DONT_SORT "SORT_ADDRS=NO"
99 #define DONT_SORT2 "SORT_ADDRS=FALSE"
100 #define LINESIZE 100
103 * constant values of addresses for HOST_SELF_BIND, HOST_SELF_CONNECT
104 * and localhost.
106 * The following variables are static to the extent that they should
107 * not be visible outside of this file.
109 static char *localaddr[] = {"\000\000\000\000", NULL};
110 static char *connectaddr[] = {"\177\000\000\001", NULL};
111 static char *localaddr6[] =
112 {"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000", NULL};
113 static char *connectaddr6[] =
114 {"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\001", NULL};
116 /* IPv4 nd_addrlist */
117 static mutex_t nd_addr_lock = DEFAULTMUTEX;
118 static struct sockaddr_in sa_con;
119 static struct netbuf nd_conbuf = {sizeof (sa_con),\
120 sizeof (sa_con), (char *)&sa_con};
121 static struct nd_addrlist nd_conaddrlist = {1, &nd_conbuf};
123 /* IPv6 nd_addrlist */
124 static mutex_t nd6_addr_lock = DEFAULTMUTEX;
125 static struct sockaddr_in6 sa6_con;
126 static struct netbuf nd6_conbuf = {sizeof (sa6_con),\
127 sizeof (sa6_con), (char *)&sa6_con};
128 static struct nd_addrlist nd6_conaddrlist = {1, &nd6_conbuf};
130 #define LOCALHOST "localhost"
132 struct servent *_switch_getservbyname_r(const char *, const char *,
133 struct servent *, char *, int);
134 struct servent *_switch_getservbyport_r(int, const char *, struct servent *,
135 char *, int);
137 static int __herrno2netdir(int h_errnop);
138 static struct ifinfo *get_local_info(void);
139 static int getbroadcastnets(struct netconfig *, struct in_addr **);
140 static int hent2ndaddr(int, char **, int *, struct nd_addrlist **);
141 static int ndaddr2hent(int, const char *, struct nd_addrlist *,
142 struct hostent *, char *, int);
143 static int hsents2ndhostservs(struct hostent *, struct servent *, ushort_t,
144 struct nd_hostservlist **);
145 static int ndaddr2srent(const char *, const char *, ushort_t, struct servent *,
146 char *, int);
147 static int ndhostserv2hent(struct netbuf *, struct nd_hostservlist *,
148 struct hostent *, char *, int);
149 static int ndhostserv2srent(int, const char *, struct nd_hostservlist *,
150 struct servent *, char *, int);
151 static int nd2herrno(int nerr);
152 static void order_haddrlist_inet(char **haddrlist, size_t addrcount);
153 static void order_haddrlist_inet6(char **haddrlist, size_t addrcount);
154 static int dstcmp(const void *, const void *);
155 static int nss_strioctl(int af, int cmd, void *ptr, int ilen);
156 static struct in_addr _inet_makeaddr(in_addr_t, in_addr_t);
157 static boolean_t _read_nsw_file(void);
160 * Begin: PART I
161 * Top Level Interfaces that gethost/serv/netdir funnel through.
164 static int
165 inetdir_free(int ret, struct in_addr *inaddrs, char **baddrlist)
167 free(inaddrs);
168 free(baddrlist);
169 _nderror = ret;
170 return (ret);
174 * gethost/servbyname always call this function; if they call
175 * with nametoaddr libs in nconf, we call netdir_getbyname
176 * implementation: __classic_netdir_getbyname, otherwise nsswitch.
178 * netdir_getbyname calls this only if nametoaddr libs are NOT
179 * specified for inet transports; i.e. it's supposed to follow
180 * the name service switch.
183 _get_hostserv_inetnetdir_byname(struct netconfig *nconf,
184 struct nss_netdirbyname_in *args, union nss_netdirbyname_out *res)
186 int server_port;
187 int *servp = &server_port;
188 char **haddrlist;
189 uint32_t dotnameaddr;
190 char *dotnamelist[2];
191 struct in_addr *inaddrs = NULL;
192 struct in6_addr v6nameaddr;
193 char **baddrlist = NULL;
195 if (nconf == NULL) {
196 _nderror = ND_BADARG;
197 return (ND_BADARG);
201 * 1. gethostbyname()/netdir_getbyname() special cases:
203 switch (args->op_t) {
205 case NSS_HOST:
207 * Worth the performance gain -- assuming a lot of inet apps
208 * actively use "localhost".
210 if (strcmp(args->arg.nss.host.name, LOCALHOST) == 0) {
212 (void) mutex_lock(&nd_addr_lock);
213 IN_SET_LOOPBACK_ADDR(&sa_con);
214 _nderror = ndaddr2hent(AF_INET, args->arg.nss.host.name,
215 &nd_conaddrlist, res->nss.host.hent,
216 args->arg.nss.host.buf,
217 args->arg.nss.host.buflen);
218 (void) mutex_unlock(&nd_addr_lock);
219 if (_nderror != ND_OK)
220 *(res->nss.host.herrno_p) =
221 nd2herrno(_nderror);
222 return (_nderror);
225 * If the caller passed in a dot separated IP notation to
226 * gethostbyname, return that back as the address.
227 * The nd_addr_lock mutex was added to be truely re-entrant.
229 if (inet_aton(args->arg.nss.host.name,
230 (struct in_addr *)&dotnameaddr)) {
231 (void) mutex_lock(&nd_addr_lock);
232 (void) memset(&sa_con, 0, sizeof (sa_con));
233 sa_con.sin_family = AF_INET;
234 sa_con.sin_addr.s_addr = dotnameaddr;
235 _nderror = ndaddr2hent(AF_INET, args->arg.nss.host.name,
236 &nd_conaddrlist, res->nss.host.hent,
237 args->arg.nss.host.buf,
238 args->arg.nss.host.buflen);
239 (void) mutex_unlock(&nd_addr_lock);
240 if (_nderror != ND_OK)
241 *(res->nss.host.herrno_p) =
242 nd2herrno(_nderror);
243 return (_nderror);
245 break;
247 case NSS_HOST6:
249 * Handle case of literal address string.
251 if (strchr(args->arg.nss.host6.name, ':') != NULL &&
252 (inet_pton(AF_INET6, args->arg.nss.host6.name,
253 &v6nameaddr) != 0)) {
254 int ret;
256 (void) mutex_lock(&nd6_addr_lock);
257 (void) memset(&sa6_con, 0, sizeof (sa6_con));
258 sa6_con.sin6_family = AF_INET6;
259 (void) memcpy(&(sa6_con.sin6_addr.s6_addr),
260 &v6nameaddr, sizeof (struct in6_addr));
261 ret = ndaddr2hent(AF_INET6,
262 args->arg.nss.host6.name,
263 &nd6_conaddrlist, res->nss.host.hent,
264 args->arg.nss.host6.buf,
265 args->arg.nss.host6.buflen);
266 (void) mutex_unlock(&nd6_addr_lock);
267 if (ret != ND_OK)
268 *(res->nss.host.herrno_p) = nd2herrno(ret);
269 else
270 res->nss.host.hent->h_aliases = NULL;
271 return (ret);
273 break;
275 case NETDIR_BY:
276 if (args->arg.nd_hs == 0) {
277 _nderror = ND_BADARG;
278 return (ND_BADARG);
281 * If servname is NULL, return 0 as the port number
282 * If servname is rpcbind, return 111 as the port number
283 * If servname is a number, return it back as the port
284 * number.
286 if (args->arg.nd_hs->h_serv == 0) {
287 *servp = htons(0);
288 } else if (strcmp(args->arg.nd_hs->h_serv,
289 "rpcbind") == 0) {
290 *servp = htons(111);
291 } else if (strspn(args->arg.nd_hs->h_serv,
292 "0123456789") ==
293 strlen(args->arg.nd_hs->h_serv)) {
294 *servp = htons(atoi(args->arg.nd_hs->h_serv));
295 } else {
296 /* i.e. need to call a name service on this */
297 servp = NULL;
301 * If the hostname is HOST_SELF_BIND, we return 0.0.0.0
302 * so the binding can be contacted through all
303 * interfaces. If the hostname is HOST_SELF_CONNECT,
304 * we return 127.0.0.1 so the address can be connected
305 * to locally. If the hostname is HOST_ANY, we return
306 * no addresses because IP doesn't know how to specify
307 * a service without a host. And finally if we specify
308 * HOST_BROADCAST then we ask a tli fd to tell us what
309 * the broadcast addresses are for any udp
310 * interfaces on this machine.
312 if (args->arg.nd_hs->h_host == 0) {
313 _nderror = ND_NOHOST;
314 return (ND_NOHOST);
315 } else if ((strcmp(args->arg.nd_hs->h_host,
316 HOST_SELF_BIND) == 0)) {
317 haddrlist = localaddr;
318 } else if ((strcmp(args->arg.nd_hs->h_host,
319 HOST_SELF_CONNECT) == 0)) {
320 haddrlist = connectaddr;
321 } else if ((strcmp(args->arg.nd_hs->h_host,
322 LOCALHOST) == 0)) {
323 haddrlist = connectaddr;
324 } else if ((int)(dotnameaddr =
325 inet_addr(args->arg.nd_hs->h_host)) != -1) {
327 * If the caller passed in a dot separated IP
328 * notation to netdir_getbyname, convert that
329 * back into address.
332 dotnamelist[0] = (char *)&dotnameaddr;
333 dotnamelist[1] = NULL;
334 haddrlist = dotnamelist;
335 } else if ((strcmp(args->arg.nd_hs->h_host,
336 HOST_BROADCAST) == 0)) {
338 * Now that inaddrs and baddrlist are
339 * dynamically allocated, care must be
340 * taken in freeing up the
341 * memory at each 'return()' point.
343 * Early return protection (using
344 * inetdir_free()) is needed only in NETDIR_BY
345 * cases because dynamic allocation is used
346 * when args->op_t == NETDIR_BY.
348 * Early return protection is not needed in
349 * haddrlist==0 conditionals because dynamic
350 * allocation guarantees haddrlist!=0.
352 * Early return protection is not needed in most
353 * servp!=0 conditionals because this is handled
354 * (and returned) first.
356 int i, bnets;
358 bnets = getbroadcastnets(nconf, &inaddrs);
359 if (bnets == 0) {
360 _nderror = ND_NOHOST;
361 return (ND_NOHOST);
363 baddrlist = malloc((bnets+1)*sizeof (char *));
364 if (baddrlist == NULL)
365 return (inetdir_free(ND_NOMEM, inaddrs,
366 baddrlist));
367 for (i = 0; i < bnets; i++)
368 baddrlist[i] = (char *)&inaddrs[i];
369 baddrlist[i] = NULL;
370 haddrlist = baddrlist;
371 } else {
372 /* i.e. need to call a name service on this */
373 haddrlist = 0;
376 if (haddrlist && servp) {
377 int ret;
379 * Convert h_addr_list into nd_addrlist.
380 * malloc's will be done, freed using
381 * netdir_free.
383 ret = hent2ndaddr(AF_INET, haddrlist, servp,
384 res->nd_alist);
385 return (inetdir_free(ret, inaddrs, baddrlist));
387 break;
390 case NETDIR_BY6:
391 if (args->arg.nd_hs == 0) {
392 _nderror = ND_BADARG;
393 return (ND_BADARG);
396 * If servname is NULL, return 0 as the port number.
397 * If servname is rpcbind, return 111 as the port number
398 * If servname is a number, return it back as the port
399 * number.
401 if (args->arg.nd_hs->h_serv == 0) {
402 *servp = htons(0);
403 } else if (strcmp(args->arg.nd_hs->h_serv,
404 "rpcbind") == 0) {
405 *servp = htons(111);
406 } else if (strspn(args->arg.nd_hs->h_serv, "0123456789")
407 == strlen(args->arg.nd_hs->h_serv)) {
408 *servp = htons(atoi(args->arg.nd_hs->h_serv));
409 } else {
410 /* i.e. need to call a name service on this */
411 servp = NULL;
415 * If the hostname is HOST_SELF_BIND, we return ipv6
416 * localaddress so the binding can be contacted through
417 * all interfaces.
418 * If the hostname is HOST_SELF_CONNECT, we return
419 * ipv6 loopback address so the address can be connected
420 * to locally.
421 * If the hostname is HOST_ANY, we return no addresses
422 * because IP doesn't know how to specify a service
423 * without a host.
424 * And finally if we specify HOST_BROADCAST then we
425 * disallow since IPV6 does not have any
426 * broadcast concept.
428 if (args->arg.nd_hs->h_host == 0) {
429 return (ND_NOHOST);
430 } else if ((strcmp(args->arg.nd_hs->h_host,
431 HOST_SELF_BIND) == 0)) {
432 haddrlist = localaddr6;
433 } else if ((strcmp(args->arg.nd_hs->h_host,
434 HOST_SELF_CONNECT) == 0)) {
435 haddrlist = connectaddr6;
436 } else if ((strcmp(args->arg.nd_hs->h_host,
437 LOCALHOST) == 0)) {
438 haddrlist = connectaddr6;
439 } else if (strchr(args->arg.nd_hs->h_host, ':')
440 != NULL) {
443 * If the caller passed in a dot separated IP notation
444 * to netdir_getbyname, convert that back into address.
447 if ((inet_pton(AF_INET6,
448 args->arg.nd_hs->h_host,
449 &v6nameaddr)) != 0) {
450 dotnamelist[0] = (char *)&v6nameaddr;
451 dotnamelist[1] = NULL;
452 haddrlist = dotnamelist;
454 else
455 /* not sure what to return */
456 return (ND_NOHOST);
458 } else if ((strcmp(args->arg.nd_hs->h_host,
459 HOST_BROADCAST) == 0)) {
461 * Don't support broadcast in
462 * IPV6
464 return (ND_NOHOST);
465 } else {
466 /* i.e. need to call a name service on this */
467 haddrlist = 0;
470 if (haddrlist && servp) {
471 int ret;
473 * Convert h_addr_list into nd_addrlist.
474 * malloc's will be done, freed
475 * using netdir_free.
477 ret = hent2ndaddr(AF_INET6, haddrlist,
478 servp, res->nd_alist);
479 return (inetdir_free(ret, inaddrs, baddrlist));
481 break;
487 * 2. Most common scenario. This is the way we ship /etc/netconfig.
488 * Emphasis on improving performance in the "if" part.
490 if (nconf->nc_nlookups == 0) {
491 struct hostent *he = NULL, *tmphe;
492 struct servent *se;
493 int ret;
494 nss_XbyY_buf_t *ndbuf4switch = 0;
496 switch (args->op_t) {
498 case NSS_HOST:
500 he = DOOR_GETHOSTBYNAME_R(args->arg.nss.host.name,
501 res->nss.host.hent, args->arg.nss.host.buf,
502 args->arg.nss.host.buflen,
503 res->nss.host.herrno_p);
504 if (he == NULL)
505 return (_nderror = ND_NOHOST);
506 return (_nderror = ND_OK);
508 case NSS_HOST6:
510 he = DOOR_GETIPNODEBYNAME_R(args->arg.nss.host6.name,
511 res->nss.host.hent, args->arg.nss.host.buf,
512 args->arg.nss.host6.buflen,
513 args->arg.nss.host6.af_family,
514 args->arg.nss.host6.flags,
515 res->nss.host.herrno_p);
517 if (he == NULL)
518 return (_nderror = ND_NOHOST);
519 return (_nderror = ND_OK);
521 case NSS_SERV:
523 se = _switch_getservbyname_r(args->arg.nss.serv.name,
524 args->arg.nss.serv.proto,
525 res->nss.serv, args->arg.nss.serv.buf,
526 args->arg.nss.serv.buflen);
528 _nderror = ND_OK;
529 if (se == 0)
530 _nderror = ND_NOSERV;
531 return (_nderror);
533 case NETDIR_BY:
535 if (servp == 0) {
536 char *proto = (strcmp(nconf->nc_proto,
537 NC_TCP) == 0) ? NC_TCP : NC_UDP;
540 * We go through all this for just one port number,
541 * which is most often constant. How about linking in
542 * an indexed database of well-known ports in the name
543 * of performance ?
545 ndbuf4switch = _nss_XbyY_buf_alloc(
546 sizeof (struct servent), NSS_BUFLEN_SERVICES);
547 if (ndbuf4switch == 0)
548 return (inetdir_free(ND_NOMEM, inaddrs,
549 baddrlist));
550 se = _switch_getservbyname_r(args->arg.nd_hs->h_serv,
551 proto, ndbuf4switch->result,
552 ndbuf4switch->buffer, ndbuf4switch->buflen);
553 if (!se) {
554 NSS_XbyY_FREE(&ndbuf4switch);
555 return (inetdir_free(ND_NOSERV, inaddrs,
556 baddrlist));
558 server_port = se->s_port;
559 NSS_XbyY_FREE(&ndbuf4switch);
562 if (haddrlist == 0) {
563 int h_errnop = 0;
565 ndbuf4switch = _nss_XbyY_buf_alloc(
566 sizeof (struct hostent),
567 NSS_BUFLEN_HOSTS);
568 if (ndbuf4switch == 0) {
569 _nderror = ND_NOMEM;
570 return (ND_NOMEM);
573 * Search the ipnodes (v6) path first,
574 * search will return the v4 addresses
575 * as v4mapped addresses.
577 if ((tmphe = DOOR_GETIPNODEBYNAME_R(
578 args->arg.nd_hs->h_host,
579 ndbuf4switch->result, ndbuf4switch->buffer,
580 ndbuf4switch->buflen, args->arg.nss.host6.af_family,
581 args->arg.nss.host6.flags, &h_errnop)) != NULL)
582 he = __mappedtov4(tmphe, &h_errnop);
584 if (he == NULL) {
585 /* Failover case, try hosts db for v4 address */
586 he = DOOR_GETHOSTBYNAME_R(
587 args->arg.nd_hs->h_host,
588 ndbuf4switch->result, ndbuf4switch->buffer,
589 ndbuf4switch->buflen, &h_errnop);
590 if (he == NULL) {
591 NSS_XbyY_FREE(&ndbuf4switch);
592 _nderror = __herrno2netdir(h_errnop);
593 return (_nderror);
596 * Convert h_addr_list into nd_addrlist.
597 * malloc's will be done, freed using
598 * netdir_free.
600 ret = hent2ndaddr(AF_INET, he->h_addr_list,
601 &server_port, res->nd_alist);
602 } else {
604 * Convert h_addr_list into nd_addrlist.
605 * malloc's will be done, freed using
606 * netdir_free.
608 ret = hent2ndaddr(AF_INET, he->h_addr_list,
609 &server_port, res->nd_alist);
610 freehostent(he);
613 _nderror = ret;
614 NSS_XbyY_FREE(&ndbuf4switch);
615 return (ret);
616 } else {
617 int ret;
619 * Convert h_addr_list into nd_addrlist.
620 * malloc's will be done, freed using netdir_free.
622 ret = hent2ndaddr(AF_INET, haddrlist,
623 &server_port, res->nd_alist);
624 return (inetdir_free(ret, inaddrs, baddrlist));
628 case NETDIR_BY6:
630 if (servp == 0) {
631 char *proto = (strcmp(nconf->nc_proto,
632 NC_TCP) == 0) ? NC_TCP : NC_UDP;
635 * We go through all this for just
636 * one port number,
637 * which is most often constant.
638 * How about linking in
639 * an indexed database of well-known
640 * ports in the name
641 * of performance ?
643 ndbuf4switch = _nss_XbyY_buf_alloc(
644 sizeof (struct servent),
645 NSS_BUFLEN_SERVICES);
646 if (ndbuf4switch == 0)
647 return (inetdir_free(ND_NOMEM, inaddrs,
648 baddrlist));
649 se = _switch_getservbyname_r(
650 args->arg.nd_hs->h_serv,
651 proto, ndbuf4switch->result,
652 ndbuf4switch->buffer, ndbuf4switch->buflen);
653 if (!se) {
654 NSS_XbyY_FREE(&ndbuf4switch);
655 return (inetdir_free(ND_NOSERV, inaddrs,
656 baddrlist));
658 server_port = se->s_port;
659 NSS_XbyY_FREE(&ndbuf4switch);
662 if (haddrlist == 0) {
663 int h_errnop = 0;
665 ndbuf4switch = _nss_XbyY_buf_alloc(
666 sizeof (struct hostent),
667 NSS_BUFLEN_HOSTS);
668 if (ndbuf4switch == 0) {
669 _nderror = ND_NOMEM;
670 return (ND_NOMEM);
672 he = DOOR_GETIPNODEBYNAME_R(
673 args->arg.nd_hs->h_host,
674 ndbuf4switch->result, ndbuf4switch->buffer,
675 ndbuf4switch->buflen,
676 args->arg.nss.host6.af_family,
677 args->arg.nss.host6.flags, &h_errnop);
678 if (he == NULL) {
679 NSS_XbyY_FREE(&ndbuf4switch);
680 _nderror = __herrno2netdir(h_errnop);
681 return (_nderror);
684 * Convert h_addr_list into nd_addrlist.
685 * malloc's will be done,
686 * freed using netdir_free.
688 ret = hent2ndaddr(AF_INET6,
689 ((struct hostent *)
690 (ndbuf4switch->result))->h_addr_list,
691 &server_port, res->nd_alist);
692 _nderror = ret;
693 NSS_XbyY_FREE(&ndbuf4switch);
694 return (ret);
695 } else {
696 int ret;
698 * Convert h_addr_list into nd_addrlist.
699 * malloc's will be done,
700 * freed using netdir_free.
702 ret = hent2ndaddr(AF_INET6, haddrlist,
703 &server_port, res->nd_alist);
704 return (inetdir_free(ret, inaddrs, baddrlist));
707 default:
708 _nderror = ND_BADARG;
709 return (ND_BADARG); /* should never happen */
712 } else {
713 /* haddrlist is no longer used, so clean up */
714 free(inaddrs);
715 free(baddrlist);
719 * 3. We come this far only if nametoaddr libs are specified for
720 * inet transports and we are called by gethost/servbyname only.
722 switch (args->op_t) {
723 struct nd_hostserv service;
724 struct nd_addrlist *addrs;
725 int ret;
727 case NSS_HOST:
729 service.h_host = (char *)args->arg.nss.host.name;
730 service.h_serv = NULL;
731 if ((_nderror = __classic_netdir_getbyname(nconf,
732 &service, &addrs)) != ND_OK) {
733 *(res->nss.host.herrno_p) = nd2herrno(_nderror);
734 return (_nderror);
737 * convert addresses back into sockaddr for gethostbyname.
739 ret = ndaddr2hent(AF_INET, service.h_host, addrs,
740 res->nss.host.hent, args->arg.nss.host.buf,
741 args->arg.nss.host.buflen);
742 if (ret != ND_OK)
743 *(res->nss.host.herrno_p) = nd2herrno(ret);
744 netdir_free((char *)addrs, ND_ADDRLIST);
745 _nderror = ret;
746 return (ret);
748 case NSS_SERV:
750 if (args->arg.nss.serv.proto == NULL) {
752 * A similar HACK showed up in Solaris 2.3.
753 * The caller wild-carded proto -- i.e. will
754 * accept a match using tcp or udp for the port
755 * number. Since we have no hope of getting
756 * directly to a name service switch backend
757 * from here that understands this semantics,
758 * we try calling the netdir interfaces first
759 * with "tcp" and then "udp".
761 args->arg.nss.serv.proto = "tcp";
762 _nderror = _get_hostserv_inetnetdir_byname(nconf, args,
763 res);
764 if (_nderror != ND_OK) {
765 args->arg.nss.serv.proto = "udp";
766 _nderror =
767 _get_hostserv_inetnetdir_byname(nconf,
768 args, res);
770 return (_nderror);
774 * Third-parties should optimize their nametoaddr
775 * libraries for the HOST_SELF case.
777 service.h_host = HOST_SELF;
778 service.h_serv = (char *)args->arg.nss.serv.name;
779 if ((_nderror = __classic_netdir_getbyname(nconf,
780 &service, &addrs)) != ND_OK) {
781 return (_nderror);
784 * convert addresses back into servent for getservbyname.
786 _nderror = ndaddr2srent(service.h_serv,
787 args->arg.nss.serv.proto,
788 /* LINTED pointer cast */
789 ((struct sockaddr_in *)addrs->n_addrs->buf)->sin_port,
790 res->nss.serv,
791 args->arg.nss.serv.buf, args->arg.nss.serv.buflen);
792 netdir_free((char *)addrs, ND_ADDRLIST);
793 return (_nderror);
795 default:
796 _nderror = ND_BADARG;
797 return (ND_BADARG); /* should never happen */
802 * gethostbyaddr/servbyport always call this function; if they call
803 * with nametoaddr libs in nconf, we call netdir_getbyaddr
804 * implementation __classic_netdir_getbyaddr, otherwise nsswitch.
806 * netdir_getbyaddr calls this only if nametoaddr libs are NOT
807 * specified for inet transports; i.e. it's supposed to follow
808 * the name service switch.
811 _get_hostserv_inetnetdir_byaddr(struct netconfig *nconf,
812 struct nss_netdirbyaddr_in *args, union nss_netdirbyaddr_out *res)
814 if (nconf == 0) {
815 _nderror = ND_BADARG;
816 return (_nderror);
820 * 1. gethostbyaddr()/netdir_getbyaddr() special cases:
822 switch (args->op_t) {
824 case NSS_HOST:
826 * Worth the performance gain: assuming a lot of inet apps
827 * actively use "127.0.0.1".
829 /* LINTED pointer cast */
830 if (*(uint32_t *)(args->arg.nss.host.addr) ==
831 htonl(INADDR_LOOPBACK)) {
832 (void) mutex_lock(&nd_addr_lock);
833 IN_SET_LOOPBACK_ADDR(&sa_con);
834 _nderror = ndaddr2hent(AF_INET, LOCALHOST,
835 &nd_conaddrlist, res->nss.host.hent,
836 args->arg.nss.host.buf,
837 args->arg.nss.host.buflen);
838 (void) mutex_unlock(&nd_addr_lock);
839 if (_nderror != ND_OK)
840 *(res->nss.host.herrno_p) =
841 nd2herrno(_nderror);
842 return (_nderror);
844 break;
846 case NETDIR_BY:
847 case NETDIR_BY_NOSRV:
849 struct sockaddr_in *sin;
851 if (args->arg.nd_nbuf == NULL) {
852 _nderror = ND_BADARG;
853 return (_nderror);
857 * Validate the address which was passed
858 * as the request.
860 /* LINTED pointer cast */
861 sin = (struct sockaddr_in *)args->arg.nd_nbuf->buf;
863 if ((args->arg.nd_nbuf->len !=
864 sizeof (struct sockaddr_in)) ||
865 (sin->sin_family != AF_INET)) {
866 _nderror = ND_BADARG;
867 return (_nderror);
870 break;
872 case NETDIR_BY6:
873 case NETDIR_BY_NOSRV6:
875 struct sockaddr_in6 *sin6;
877 if (args->arg.nd_nbuf == NULL) {
878 _nderror = ND_BADARG;
879 return (_nderror);
883 * Validate the address which was passed
884 * as the request.
886 /* LINTED pointer cast */
887 sin6 = (struct sockaddr_in6 *)args->arg.nd_nbuf->buf;
889 if ((args->arg.nd_nbuf->len !=
890 sizeof (struct sockaddr_in6)) ||
891 (sin6->sin6_family != AF_INET6)) {
892 _nderror = ND_BADARG;
893 return (_nderror);
896 break;
901 * 2. Most common scenario. This is the way we ship /etc/netconfig.
902 * Emphasis on improving performance in the "if" part.
904 if (nconf->nc_nlookups == 0) {
905 struct hostent *he = NULL, *tmphe;
906 struct servent *se = NULL;
907 nss_XbyY_buf_t *ndbuf4host = 0;
908 nss_XbyY_buf_t *ndbuf4serv = 0;
909 char *proto =
910 (strcmp(nconf->nc_proto, NC_TCP) == 0) ? NC_TCP : NC_UDP;
911 struct sockaddr_in *sa;
912 struct sockaddr_in6 *sin6;
913 struct in_addr *addr4 = 0;
914 struct in6_addr v4mapbuf;
915 int h_errnop;
917 switch (args->op_t) {
919 case NSS_HOST:
921 he = DOOR_GETHOSTBYADDR_R(args->arg.nss.host.addr,
922 args->arg.nss.host.len, args->arg.nss.host.type,
923 res->nss.host.hent, args->arg.nss.host.buf,
924 args->arg.nss.host.buflen,
925 res->nss.host.herrno_p);
926 if (he == 0)
927 _nderror = ND_NOHOST;
928 else
929 _nderror = ND_OK;
930 return (_nderror);
933 case NSS_HOST6:
934 he = DOOR_GETIPNODEBYADDR_R(args->arg.nss.host.addr,
935 args->arg.nss.host.len, args->arg.nss.host.type,
936 res->nss.host.hent, args->arg.nss.host.buf,
937 args->arg.nss.host.buflen,
938 res->nss.host.herrno_p);
940 if (he == 0)
941 return (ND_NOHOST);
942 return (ND_OK);
945 case NSS_SERV:
947 se = _switch_getservbyport_r(args->arg.nss.serv.port,
948 args->arg.nss.serv.proto,
949 res->nss.serv, args->arg.nss.serv.buf,
950 args->arg.nss.serv.buflen);
952 if (se == 0)
953 _nderror = ND_NOSERV;
954 else
955 _nderror = ND_OK;
956 return (_nderror);
958 case NETDIR_BY:
959 case NETDIR_BY_NOSRV:
961 ndbuf4serv = _nss_XbyY_buf_alloc(sizeof (struct servent),
962 NSS_BUFLEN_SERVICES);
963 if (ndbuf4serv == 0) {
964 _nderror = ND_NOMEM;
965 return (_nderror);
967 /* LINTED pointer cast */
968 sa = (struct sockaddr_in *)(args->arg.nd_nbuf->buf);
969 addr4 = (struct in_addr *)&(sa->sin_addr);
972 * if NETDIR_BY_NOSRV or port == 0 skip the service
973 * lookup.
975 if (args->op_t != NETDIR_BY_NOSRV && sa->sin_port != 0) {
976 se = _switch_getservbyport_r(sa->sin_port, proto,
977 ndbuf4serv->result, ndbuf4serv->buffer,
978 ndbuf4serv->buflen);
979 if (!se) {
980 NSS_XbyY_FREE(&ndbuf4serv);
982 * We can live with this - i.e. the address
983 * does not
984 * belong to a well known service. The caller
985 * traditionally accepts a stringified port
986 * number
987 * as the service name. The state of se is used
988 * ahead to indicate the same.
989 * However, we do not tolerate this nonsense
990 * when we cannot get a host name. See below.
995 ndbuf4host = _nss_XbyY_buf_alloc(sizeof (struct hostent),
996 NSS_BUFLEN_HOSTS);
997 if (ndbuf4host == 0) {
998 if (ndbuf4serv)
999 NSS_XbyY_FREE(&ndbuf4serv);
1000 _nderror = ND_NOMEM;
1001 return (_nderror);
1005 * Since we're going to search the ipnodes (v6) path first,
1006 * we need to treat the address as a v4mapped address.
1009 IN6_INADDR_TO_V4MAPPED(addr4, &v4mapbuf);
1010 if ((tmphe = DOOR_GETIPNODEBYADDR_R((char *)&v4mapbuf,
1011 16, AF_INET6, ndbuf4host->result,
1012 ndbuf4host->buffer,
1013 ndbuf4host->buflen, &h_errnop)) != NULL)
1014 he = __mappedtov4(tmphe, &h_errnop);
1016 if (!he) {
1017 /* Failover case, try hosts db for v4 address */
1018 he = DOOR_GETHOSTBYADDR_R((char *)
1019 &(sa->sin_addr.s_addr), 4,
1020 sa->sin_family, ndbuf4host->result,
1021 ndbuf4host->buffer, ndbuf4host->buflen,
1022 &h_errnop);
1023 if (!he) {
1024 NSS_XbyY_FREE(&ndbuf4host);
1025 if (ndbuf4serv)
1026 NSS_XbyY_FREE(&ndbuf4serv);
1027 _nderror = __herrno2netdir(h_errnop);
1028 return (_nderror);
1031 * Convert host names and service names into hostserv
1032 * pairs. malloc's will be done, freed using
1033 * netdir_free.
1035 h_errnop = hsents2ndhostservs(he, se,
1036 sa->sin_port, res->nd_hslist);
1037 } else {
1039 * Convert host names and service names into hostserv
1040 * pairs. malloc's will be done, freed using
1041 * netdir_free.
1043 h_errnop = hsents2ndhostservs(he, se,
1044 sa->sin_port, res->nd_hslist);
1045 freehostent(he);
1048 NSS_XbyY_FREE(&ndbuf4host);
1049 if (ndbuf4serv)
1050 NSS_XbyY_FREE(&ndbuf4serv);
1051 _nderror = __herrno2netdir(h_errnop);
1052 return (_nderror);
1054 case NETDIR_BY6:
1055 case NETDIR_BY_NOSRV6:
1057 ndbuf4serv = _nss_XbyY_buf_alloc(sizeof (struct servent),
1058 NSS_BUFLEN_SERVICES);
1059 if (ndbuf4serv == 0) {
1060 _nderror = ND_NOMEM;
1061 return (ND_NOMEM);
1063 /* LINTED pointer cast */
1064 sin6 = (struct sockaddr_in6 *)(args->arg.nd_nbuf->buf);
1067 * if NETDIR_BY_NOSRV6 or port == 0 skip the service
1068 * lookup.
1070 if (args->op_t != NETDIR_BY_NOSRV6 && sin6->sin6_port == 0) {
1071 se = _switch_getservbyport_r(sin6->sin6_port, proto,
1072 ndbuf4serv->result, ndbuf4serv->buffer,
1073 ndbuf4serv->buflen);
1074 if (!se) {
1075 NSS_XbyY_FREE(&ndbuf4serv);
1077 * We can live with this - i.e. the address does
1078 * not * belong to a well known service. The
1079 * caller traditionally accepts a stringified
1080 * port number
1081 * as the service name. The state of se is used
1082 * ahead to indicate the same.
1083 * However, we do not tolerate this nonsense
1084 * when we cannot get a host name. See below.
1089 ndbuf4host = _nss_XbyY_buf_alloc(sizeof (struct hostent),
1090 NSS_BUFLEN_HOSTS);
1091 if (ndbuf4host == 0) {
1092 if (ndbuf4serv)
1093 NSS_XbyY_FREE(&ndbuf4serv);
1094 _nderror = ND_NOMEM;
1095 return (_nderror);
1097 he = DOOR_GETIPNODEBYADDR_R((char *)&(sin6->sin6_addr),
1098 16, sin6->sin6_family, ndbuf4host->result,
1099 ndbuf4host->buffer,
1100 ndbuf4host->buflen, &h_errnop);
1101 if (!he) {
1102 NSS_XbyY_FREE(&ndbuf4host);
1103 if (ndbuf4serv)
1104 NSS_XbyY_FREE(&ndbuf4serv);
1105 _nderror = __herrno2netdir(h_errnop);
1106 return (_nderror);
1109 * Convert host names and service names into hostserv
1110 * pairs. malloc's will be done, freed using netdir_free.
1112 h_errnop = hsents2ndhostservs(he, se,
1113 sin6->sin6_port, res->nd_hslist);
1115 NSS_XbyY_FREE(&ndbuf4host);
1116 if (ndbuf4serv)
1117 NSS_XbyY_FREE(&ndbuf4serv);
1118 _nderror = __herrno2netdir(h_errnop);
1119 return (_nderror);
1121 default:
1122 _nderror = ND_BADARG;
1123 return (_nderror); /* should never happen */
1128 * 3. We come this far only if nametoaddr libs are specified for
1129 * inet transports and we are called by gethost/servbyname only.
1131 switch (args->op_t) {
1132 struct netbuf nbuf;
1133 struct nd_hostservlist *addrs;
1134 struct sockaddr_in sa;
1136 case NSS_HOST:
1138 /* LINTED pointer cast */
1139 sa.sin_addr.s_addr = *(uint32_t *)args->arg.nss.host.addr;
1140 sa.sin_family = AF_INET;
1141 /* Hopefully, third-parties get this optimization */
1142 sa.sin_port = 0;
1143 nbuf.buf = (char *)&sa;
1144 nbuf.len = nbuf.maxlen = sizeof (sa);
1145 if ((_nderror = __classic_netdir_getbyaddr(nconf,
1146 &addrs, &nbuf)) != 0) {
1147 *(res->nss.host.herrno_p) = nd2herrno(_nderror);
1148 return (_nderror);
1151 * convert the host-serv pairs into h_aliases and hent.
1153 _nderror = ndhostserv2hent(&nbuf, addrs, res->nss.host.hent,
1154 args->arg.nss.host.buf, args->arg.nss.host.buflen);
1155 if (_nderror != ND_OK)
1156 *(res->nss.host.herrno_p) = nd2herrno(_nderror);
1157 netdir_free((char *)addrs, ND_HOSTSERVLIST);
1158 return (_nderror);
1160 case NSS_SERV:
1162 if (args->arg.nss.serv.proto == NULL) {
1164 * A similar HACK showed up in Solaris 2.3.
1165 * The caller wild-carded proto -- i.e. will
1166 * accept a match on tcp or udp for the port
1167 * number. Since we have no hope of getting
1168 * directly to a name service switch backend
1169 * from here that understands this semantics,
1170 * we try calling the netdir interfaces first
1171 * with "tcp" and then "udp".
1173 args->arg.nss.serv.proto = "tcp";
1174 _nderror = _get_hostserv_inetnetdir_byaddr(nconf, args,
1175 res);
1176 if (_nderror != ND_OK) {
1177 args->arg.nss.serv.proto = "udp";
1178 _nderror =
1179 _get_hostserv_inetnetdir_byaddr(nconf,
1180 args, res);
1182 return (_nderror);
1186 * Third-party nametoaddr_libs should be optimized for
1187 * this case. It also gives a special semantics twist to
1188 * netdir_getbyaddr. Only for the INADDR_ANY case, it gives
1189 * higher priority to service lookups (over host lookups).
1190 * If service lookup fails, the backend returns ND_NOSERV to
1191 * facilitate lookup in the "next" naming service.
1192 * BugId: 1075403.
1194 sa.sin_addr.s_addr = INADDR_ANY;
1195 sa.sin_family = AF_INET;
1196 sa.sin_port = (ushort_t)args->arg.nss.serv.port;
1197 sa.sin_zero[0] = '\0';
1198 nbuf.buf = (char *)&sa;
1199 nbuf.len = nbuf.maxlen = sizeof (sa);
1200 if ((_nderror = __classic_netdir_getbyaddr(nconf,
1201 &addrs, &nbuf)) != ND_OK) {
1202 return (_nderror);
1205 * convert the host-serv pairs into s_aliases and servent.
1207 _nderror = ndhostserv2srent(args->arg.nss.serv.port,
1208 args->arg.nss.serv.proto, addrs, res->nss.serv,
1209 args->arg.nss.serv.buf, args->arg.nss.serv.buflen);
1210 netdir_free((char *)addrs, ND_HOSTSERVLIST);
1211 return (_nderror);
1213 default:
1214 _nderror = ND_BADARG;
1215 return (_nderror); /* should never happen */
1220 * Part II: Name Service Switch interfacing routines.
1223 static DEFINE_NSS_DB_ROOT(db_root_hosts);
1224 static DEFINE_NSS_DB_ROOT(db_root_ipnodes);
1225 static DEFINE_NSS_DB_ROOT(db_root_services);
1229 * There is a copy of __nss2herrno() in nsswitch/files/gethostent.c.
1230 * It is there because /etc/lib/nss_files.so.1 cannot call
1231 * routines in libnsl. Care should be taken to keep the two copies
1232 * in sync (except that case NSS_NISSERVDNS_TRYAGAIN is not needed in
1233 * nsswitch/files).
1236 __nss2herrno(nss_status_t nsstat)
1238 switch (nsstat) {
1239 case NSS_SUCCESS:
1240 /* no macro-defined success code for h_errno */
1241 return (0);
1242 case NSS_NOTFOUND:
1243 return (HOST_NOT_FOUND);
1244 case NSS_TRYAGAIN:
1245 return (TRY_AGAIN);
1246 case NSS_UNAVAIL:
1247 return (NO_RECOVERY);
1248 case NSS_NISSERVDNS_TRYAGAIN:
1249 return (TRY_AGAIN);
1251 /* anything else */
1252 return (NO_RECOVERY);
1255 nss_status_t
1256 _herrno2nss(int h_errno)
1258 switch (h_errno) {
1259 case 0:
1260 return (NSS_SUCCESS);
1261 case TRY_AGAIN:
1262 return (NSS_TRYAGAIN);
1263 case NO_RECOVERY:
1264 case NETDB_INTERNAL:
1265 return (NSS_UNAVAIL);
1266 case HOST_NOT_FOUND:
1267 case NO_DATA:
1268 default:
1269 return (NSS_NOTFOUND);
1273 static int
1274 __herrno2netdir(int h_errnop)
1276 switch (h_errnop) {
1277 case 0:
1278 return (ND_OK);
1279 case HOST_NOT_FOUND:
1280 return (ND_NOHOST);
1281 case TRY_AGAIN:
1282 return (ND_TRY_AGAIN);
1283 case NO_RECOVERY:
1284 case NETDB_INTERNAL:
1285 return (ND_NO_RECOVERY);
1286 case NO_DATA:
1287 return (ND_NO_DATA);
1288 default:
1289 return (ND_NOHOST);
1294 * The _switch_getXXbyYY_r() routines should be static. They used to
1295 * be exported in SunOS 5.3, and in fact publicised as work-around
1296 * interfaces for getting CNAME/aliases, and therefore, we preserve
1297 * their signatures here. Just in case.
1300 struct hostent *
1301 _switch_gethostbyname_r(const char *name, struct hostent *result, char *buffer,
1302 int buflen, int *h_errnop)
1304 nss_XbyY_args_t arg;
1305 nss_status_t res;
1307 NSS_XbyY_INIT(&arg, result, buffer, buflen, str2hostent);
1308 arg.key.name = name;
1309 arg.stayopen = 0;
1310 res = nss_search(&db_root_hosts, _nss_initf_hosts,
1311 NSS_DBOP_HOSTS_BYNAME, &arg);
1312 arg.status = res;
1313 if (res != NSS_SUCCESS)
1314 *h_errnop = arg.h_errno ? arg.h_errno : __nss2herrno(res);
1315 if (arg.returnval != NULL)
1316 order_haddrlist_af(result->h_addrtype, result->h_addr_list);
1317 return ((struct hostent *)NSS_XbyY_FINI(&arg));
1320 struct hostent *
1321 _switch_getipnodebyname_r(const char *name, struct hostent *result,
1322 char *buffer, int buflen, int af_family, int flags, int *h_errnop)
1324 nss_XbyY_args_t arg;
1325 nss_status_t res;
1327 NSS_XbyY_INIT(&arg, result, buffer, buflen, str2hostent6);
1328 arg.key.ipnode.name = name;
1329 arg.key.ipnode.af_family = af_family;
1330 arg.key.ipnode.flags = flags;
1331 arg.stayopen = 0;
1332 res = nss_search(&db_root_ipnodes, _nss_initf_ipnodes,
1333 NSS_DBOP_IPNODES_BYNAME, &arg);
1334 arg.status = res;
1335 if (res != NSS_SUCCESS)
1336 *h_errnop = arg.h_errno ? arg.h_errno : __nss2herrno(res);
1337 if (arg.returnval != NULL)
1338 order_haddrlist_af(result->h_addrtype, result->h_addr_list);
1339 return ((struct hostent *)NSS_XbyY_FINI(&arg));
1342 struct hostent *
1343 _switch_gethostbyaddr_r(const char *addr, int len, int type,
1344 struct hostent *result, char *buffer, int buflen, int *h_errnop)
1346 nss_XbyY_args_t arg;
1347 nss_status_t res;
1349 NSS_XbyY_INIT(&arg, result, buffer, buflen, str2hostent);
1350 arg.key.hostaddr.addr = addr;
1351 arg.key.hostaddr.len = len;
1352 arg.key.hostaddr.type = type;
1353 arg.stayopen = 0;
1354 res = nss_search(&db_root_hosts, _nss_initf_hosts,
1355 NSS_DBOP_HOSTS_BYADDR, &arg);
1356 arg.status = res;
1357 if (res != NSS_SUCCESS)
1358 *h_errnop = arg.h_errno ? arg.h_errno : __nss2herrno(res);
1359 return (struct hostent *)NSS_XbyY_FINI(&arg);
1362 struct hostent *
1363 _switch_getipnodebyaddr_r(const char *addr, int len, int type,
1364 struct hostent *result, char *buffer, int buflen, int *h_errnop)
1366 nss_XbyY_args_t arg;
1367 nss_status_t res;
1369 NSS_XbyY_INIT(&arg, result, buffer, buflen, str2hostent6);
1370 arg.key.hostaddr.addr = addr;
1371 arg.key.hostaddr.len = len;
1372 arg.key.hostaddr.type = type;
1373 arg.stayopen = 0;
1374 res = nss_search(&db_root_ipnodes, _nss_initf_ipnodes,
1375 NSS_DBOP_IPNODES_BYADDR, &arg);
1376 arg.status = res;
1377 if (res != NSS_SUCCESS)
1378 *h_errnop = arg.h_errno ? arg.h_errno : __nss2herrno(res);
1379 return (struct hostent *)NSS_XbyY_FINI(&arg);
1382 static void
1383 _nss_initf_services(nss_db_params_t *p)
1385 p->name = NSS_DBNAM_SERVICES;
1386 p->default_config = NSS_DEFCONF_SERVICES;
1389 struct servent *
1390 _switch_getservbyname_r(const char *name, const char *proto,
1391 struct servent *result, char *buffer, int buflen)
1393 nss_XbyY_args_t arg;
1394 nss_status_t res;
1396 NSS_XbyY_INIT(&arg, result, buffer, buflen, str2servent);
1397 arg.key.serv.serv.name = name;
1398 arg.key.serv.proto = proto;
1399 arg.stayopen = 0;
1400 res = nss_search(&db_root_services, _nss_initf_services,
1401 NSS_DBOP_SERVICES_BYNAME, &arg);
1402 arg.status = res;
1403 return ((struct servent *)NSS_XbyY_FINI(&arg));
1406 struct servent *
1407 _switch_getservbyport_r(int port, const char *proto, struct servent *result,
1408 char *buffer, int buflen)
1410 nss_XbyY_args_t arg;
1411 nss_status_t res;
1413 NSS_XbyY_INIT(&arg, result, buffer, buflen, str2servent);
1414 arg.key.serv.serv.port = port;
1415 arg.key.serv.proto = proto;
1416 arg.stayopen = 0;
1417 res = nss_search(&db_root_services, _nss_initf_services,
1418 NSS_DBOP_SERVICES_BYPORT, &arg);
1419 arg.status = res;
1420 return ((struct servent *)NSS_XbyY_FINI(&arg));
1425 * Return values: 0 = success, 1 = parse error, 2 = erange ...
1426 * The structure pointer passed in is a structure in the caller's space
1427 * wherein the field pointers would be set to areas in the buffer if
1428 * need be. instring and buffer should be separate areas.
1430 * Defined here because we need it and we (libnsl) cannot have a dependency
1431 * on libsocket (however, libsocket always depends on libnsl).
1434 str2servent(const char *instr, int lenstr, void *ent, char *buffer, int buflen)
1436 struct servent *serv = (struct servent *)ent;
1437 const char *p, *fieldstart, *limit, *namestart;
1438 ssize_t fieldlen, namelen = 0;
1439 char numbuf[12];
1440 char *numend;
1442 if ((instr >= buffer && (buffer + buflen) > instr) ||
1443 (buffer >= instr && (instr + lenstr) > buffer)) {
1444 return (NSS_STR_PARSE_PARSE);
1447 p = instr;
1448 limit = p + lenstr;
1450 while (p < limit && isspace(*p)) {
1451 p++;
1453 namestart = p;
1454 while (p < limit && !isspace(*p)) {
1455 p++; /* Skip over the canonical name */
1457 namelen = p - namestart;
1459 if (buflen <= namelen) { /* not enough buffer */
1460 return (NSS_STR_PARSE_ERANGE);
1462 (void) memcpy(buffer, namestart, namelen);
1463 buffer[namelen] = '\0';
1464 serv->s_name = buffer;
1466 while (p < limit && isspace(*p)) {
1467 p++;
1470 fieldstart = p;
1471 do {
1472 if (p > limit || isspace(*p)) {
1473 /* Syntax error -- no port/proto */
1474 return (NSS_STR_PARSE_PARSE);
1476 } while (*p++ != '/');
1477 fieldlen = p - fieldstart - 1;
1478 if (fieldlen == 0 || fieldlen >= sizeof (numbuf)) {
1479 /* Syntax error -- supposed number is empty or too long */
1480 return (NSS_STR_PARSE_PARSE);
1482 (void) memcpy(numbuf, fieldstart, fieldlen);
1483 numbuf[fieldlen] = '\0';
1484 serv->s_port = htons((int)strtol(numbuf, &numend, 10));
1485 if (*numend != '\0') {
1486 /* Syntax error -- port number isn't a number */
1487 return (NSS_STR_PARSE_PARSE);
1490 fieldstart = p;
1491 while (p < limit && !isspace(*p)) {
1492 p++; /* Scan the protocol name */
1494 fieldlen = p - fieldstart + 1; /* Include '\0' this time */
1495 if (fieldlen > buflen - namelen - 1) {
1496 return (NSS_STR_PARSE_ERANGE);
1498 serv->s_proto = buffer + namelen + 1;
1499 (void) memcpy(serv->s_proto, fieldstart, fieldlen - 1);
1500 serv->s_proto[fieldlen - 1] = '\0';
1502 while (p < limit && isspace(*p)) {
1503 p++;
1506 * Although nss_files_XY_all calls us with # stripped,
1507 * we should be able to deal with it here in order to
1508 * be more useful.
1510 if (p >= limit || *p == '#') { /* no aliases, no problem */
1511 char **ptr;
1513 ptr = (char **)ROUND_UP(buffer + namelen + 1 + fieldlen,
1514 sizeof (char *));
1515 if ((char *)ptr >= buffer + buflen) {
1516 /* hope they don't try to peek in */
1517 serv->s_aliases = 0;
1518 return (NSS_STR_PARSE_ERANGE);
1519 } else {
1520 *ptr = 0;
1521 serv->s_aliases = ptr;
1522 return (NSS_STR_PARSE_SUCCESS);
1525 serv->s_aliases = _nss_netdb_aliases(p, (int)(lenstr - (p - instr)),
1526 buffer + namelen + 1 + fieldlen,
1527 (int)(buflen - namelen - 1 - fieldlen));
1528 return (NSS_STR_PARSE_SUCCESS);
1532 * Part III: All `n sundry routines that are useful only in this
1533 * module. In the interest of keeping this source file shorter,
1534 * we would create them a new module only if the linker allowed
1535 * "library-static" functions.
1537 * Routines to order addresses based on local interfaces and netmasks,
1538 * to get and check reserved ports, and to get broadcast nets.
1541 union __v4v6addr {
1542 struct in6_addr in6;
1543 struct in_addr in4;
1546 struct __ifaddr {
1547 sa_family_t af;
1548 union __v4v6addr addr;
1549 union __v4v6addr mask;
1552 struct ifinfo {
1553 int count;
1554 struct __ifaddr *addresses;
1557 typedef enum {ADDR_ONLINK = 0, ADDR_OFFLINK} addr_class_t;
1558 #define ADDR_NUMCLASSES 2
1560 typedef enum {IF_ADDR, IF_MASK} __ifaddr_type;
1561 static int __inet_ifassign(sa_family_t, struct __ifaddr *, __ifaddr_type,
1562 void *);
1563 int __inet_address_is_local_af(void *, sa_family_t, void *);
1565 #define ifaf(index) (localinfo->addresses[index].af)
1566 #define ifaddr4(index) (localinfo->addresses[index].addr.in4)
1567 #define ifaddr6(index) (localinfo->addresses[index].addr.in6)
1568 #define ifmask4(index) (localinfo->addresses[index].mask.in4)
1569 #define ifmask6(index) (localinfo->addresses[index].mask.in6)
1570 #define ifinfosize(n) (sizeof (struct ifinfo) + (n)*sizeof (struct __ifaddr))
1572 #define lifraddrp(lifr) ((lifr.lifr_addr.ss_family == AF_INET6) ? \
1573 (void *)&((struct sockaddr_in6 *)&lifr.lifr_addr)->sin6_addr : \
1574 (void *)&((struct sockaddr_in *)&lifr.lifr_addr)->sin_addr)
1576 #define ifassign(lifr, index, type) \
1577 __inet_ifassign(lifr.lifr_addr.ss_family, \
1578 &localinfo->addresses[index], type, \
1579 lifraddrp(lifr))
1582 * The number of nanoseconds the order_haddrlist_inet() function waits
1583 * to retreive IP interface information. The default is five minutes.
1585 #define IFINFOTIMEOUT ((hrtime_t)300 * NANOSEC)
1588 * Sort the addresses in haddrlist. Since the sorting algorithms are
1589 * address-family specific, the work is done in the address-family
1590 * specific order_haddrlist_<family> functions.
1592 * Do not sort addresses if SORT_ADDRS variable is set to NO or FALSE
1593 * in the configuration file /etc/default/nss. This is useful in case
1594 * the order of addresses returned by the nameserver needs to be
1595 * maintained. (DNS round robin feature is one example)
1597 void
1598 order_haddrlist_af(sa_family_t af, char **haddrlist)
1600 size_t addrcount;
1601 char **addrptr;
1602 static boolean_t checksortcfg = B_TRUE;
1603 static boolean_t nosort = B_FALSE;
1604 static mutex_t checksortcfg_lock = DEFAULTMUTEX;
1606 if (haddrlist == NULL)
1607 return;
1610 * Check if SORT_ADDRS is set to NO or FALSE in the configuration
1611 * file. We do not have to sort addresses in that case.
1613 (void) mutex_lock(&checksortcfg_lock);
1614 if (checksortcfg == B_TRUE) {
1615 checksortcfg = B_FALSE;
1616 nosort = _read_nsw_file();
1618 (void) mutex_unlock(&checksortcfg_lock);
1620 if (nosort)
1621 return;
1623 /* Count the addresses to sort */
1624 addrcount = 0;
1625 for (addrptr = haddrlist; *addrptr != NULL; addrptr++)
1626 addrcount++;
1629 * If there's only one address or no addresses to sort, then
1630 * there's nothing for us to do.
1632 if (addrcount <= 1)
1633 return;
1635 /* Call the address-family specific sorting functions. */
1636 switch (af) {
1637 case AF_INET:
1638 order_haddrlist_inet(haddrlist, addrcount);
1639 break;
1640 case AF_INET6:
1641 order_haddrlist_inet6(haddrlist, addrcount);
1642 break;
1643 default:
1644 break;
1649 * Move any local (on-link) addresses toward the beginning of haddrlist.
1650 * The order within these two classes is preserved.
1652 * The interface list is retrieved no more often than every
1653 * IFINFOTIMEOUT nanoseconds. Access to the interface list is
1654 * protected by an RW lock.
1656 * If this function encounters an error, haddrlist is unaltered.
1658 static void
1659 order_haddrlist_inet(char **haddrlist, size_t addrcount)
1661 static struct ifinfo *localinfo = NULL;
1662 static hrtime_t then = 0; /* the last time localinfo was updated */
1663 hrtime_t now;
1664 static rwlock_t localinfo_lock = DEFAULTRWLOCK;
1665 uint8_t *sortbuf;
1666 size_t sortbuf_size;
1667 struct in_addr **inaddrlist = (struct in_addr **)haddrlist;
1668 struct in_addr **sorted;
1669 struct in_addr **classnext[ADDR_NUMCLASSES];
1670 uint_t classcount[ADDR_NUMCLASSES];
1671 addr_class_t *sortclass;
1672 int i;
1673 int rc;
1677 * The classes in the sortclass array correspond to the class
1678 * of the address in the haddrlist list of the same index.
1679 * The classes are:
1681 * ADDR_ONLINK on-link address
1682 * ADDR_OFFLINK off-link address
1684 sortbuf_size = addrcount *
1685 (sizeof (struct in_addr *) + sizeof (addr_class_t));
1686 if ((sortbuf = malloc(sortbuf_size)) == NULL)
1687 return;
1688 /* LINTED pointer cast */
1689 sorted = (struct in_addr **)sortbuf;
1690 /* LINTED pointer cast */
1691 sortclass = (addr_class_t *)(sortbuf +
1692 (addrcount * sizeof (struct in_addr *)));
1695 * Get a read lock, and check if the interface information
1696 * is too old.
1698 (void) rw_rdlock(&localinfo_lock);
1699 now = gethrtime();
1700 if (localinfo == NULL || ((now - then) > IFINFOTIMEOUT)) {
1701 /* Need to update I/F info. Upgrade to write lock. */
1702 (void) rw_unlock(&localinfo_lock);
1703 (void) rw_wrlock(&localinfo_lock);
1705 * Another thread might have updated "then" between
1706 * the rw_unlock() and rw_wrlock() calls above, so
1707 * re-check the timeout.
1709 if (localinfo == NULL || ((now - then) > IFINFOTIMEOUT)) {
1710 free(localinfo);
1711 if ((localinfo = get_local_info()) == NULL) {
1712 (void) rw_unlock(&localinfo_lock);
1713 free(sortbuf);
1714 return;
1716 then = now;
1718 /* Downgrade to read lock */
1719 (void) rw_unlock(&localinfo_lock);
1720 (void) rw_rdlock(&localinfo_lock);
1722 * Another thread may have updated the I/F info,
1723 * so verify that the 'localinfo' pointer still
1724 * is non-NULL.
1726 if (localinfo == NULL) {
1727 (void) rw_unlock(&localinfo_lock);
1728 free(sortbuf);
1729 return;
1734 * Classify the addresses. We also maintain the classcount
1735 * array to keep track of the number of addresses in each
1736 * class.
1738 (void) memset(classcount, 0, sizeof (classcount));
1739 for (i = 0; i < addrcount; i++) {
1740 if (__inet_address_is_local_af(localinfo, AF_INET,
1741 inaddrlist[i]))
1742 sortclass[i] = ADDR_ONLINK;
1743 else
1744 sortclass[i] = ADDR_OFFLINK;
1745 classcount[sortclass[i]]++;
1748 /* Don't need the interface list anymore in this call */
1749 (void) rw_unlock(&localinfo_lock);
1752 * Each element in the classnext array points to the next
1753 * element for that class in the sorted address list. 'rc' is
1754 * the running count of elements as we sum the class
1755 * sub-totals.
1757 for (rc = 0, i = 0; i < ADDR_NUMCLASSES; i++) {
1758 classnext[i] = &sorted[rc];
1759 rc += classcount[i];
1762 /* Now for the actual rearrangement of the addresses */
1763 for (i = 0; i < addrcount; i++) {
1764 *(classnext[sortclass[i]]) = inaddrlist[i];
1765 classnext[sortclass[i]]++;
1768 /* Copy the sorted list to inaddrlist */
1769 (void) memcpy(inaddrlist, sorted,
1770 addrcount * sizeof (struct in_addr *));
1771 free(sortbuf);
1775 * This function implements the IPv6 Default Address Selection's
1776 * destination address ordering mechanism. The algorithm is described
1777 * in getaddrinfo(3SOCKET).
1779 static void
1780 order_haddrlist_inet6(char **haddrlist, size_t addrcount)
1782 struct dstinforeq *dinfo, *dinfoptr;
1783 struct in6_addr **in6addrlist = (struct in6_addr **)haddrlist;
1784 struct in6_addr **in6addr;
1786 if ((dinfo = calloc(addrcount, sizeof (struct dstinforeq))) == NULL)
1787 return;
1789 /* Initialize the dstinfo array we'll use for SIOCGDSTINFO */
1790 dinfoptr = dinfo;
1791 for (in6addr = in6addrlist; *in6addr != NULL; in6addr++) {
1792 dinfoptr->dir_daddr = **in6addr;
1793 dinfoptr++;
1796 if (nss_strioctl(AF_INET6, SIOCGDSTINFO, dinfo,
1797 addrcount * sizeof (struct dstinforeq)) < 0) {
1798 free(dinfo);
1799 return;
1802 /* Sort the dinfo array */
1803 qsort(dinfo, addrcount, sizeof (struct dstinforeq), dstcmp);
1805 /* Copy the addresses back into in6addrlist */
1806 dinfoptr = dinfo;
1807 for (in6addr = in6addrlist; *in6addr != NULL; in6addr++) {
1808 **in6addr = dinfoptr->dir_daddr;
1809 dinfoptr++;
1812 free(dinfo);
1816 * Determine number of leading bits that are common between two addresses.
1817 * Only consider bits which fall within the prefix length plen.
1819 static uint_t
1820 ip_addr_commonbits_v6(const in6_addr_t *a1, const in6_addr_t *a2)
1822 uint_t bits;
1823 uint_t i;
1824 uint32_t diff; /* Bits that differ */
1826 for (i = 0; i < 4; i++) {
1827 if (a1->_S6_un._S6_u32[i] != a2->_S6_un._S6_u32[i])
1828 break;
1830 bits = i * 32;
1832 if (bits == IPV6_ABITS)
1833 return (IPV6_ABITS);
1836 * Find number of leading common bits in the word which might
1837 * have some common bits by searching for the first one from the left
1838 * in the xor of the two addresses.
1840 diff = ntohl(a1->_S6_un._S6_u32[i] ^ a2->_S6_un._S6_u32[i]);
1841 if (diff & 0xffff0000ul)
1842 diff >>= 16;
1843 else
1844 bits += 16;
1845 if (diff & 0xff00)
1846 diff >>= 8;
1847 else
1848 bits += 8;
1849 if (diff & 0xf0)
1850 diff >>= 4;
1851 else
1852 bits += 4;
1853 if (diff & 0xc)
1854 diff >>= 2;
1855 else
1856 bits += 2;
1857 if (!(diff & 2))
1858 bits++;
1861 * We don't need to shift and check for the last bit. The
1862 * check for IPV6_ABITS above would have caught that.
1865 return (bits);
1870 * The following group of functions named rule_*() are individual
1871 * sorting rules for the AF_INET6 address sorting algorithm. The
1872 * functions compare two addresses (described by two dstinforeq
1873 * structures), and determines if one is "greater" than the other, or
1874 * if the two are equal according to that rule.
1876 typedef int (*rulef_t)(const struct dstinforeq *, const struct dstinforeq *);
1879 * These values of these constants are no accident. Since qsort()
1880 * implements the AF_INET6 address sorting, the comparison function
1881 * must return an integer less than, equal to, or greater than zero to
1882 * indicate if the first address is considered "less than", "equal
1883 * to", or "greater than" the second one. Since we want the best
1884 * addresses first on the list, "less than" is considered preferrable.
1886 #define RULE_PREFER_DA -1
1887 #define RULE_PREFER_DB 1
1888 #define RULE_EQUAL 0
1890 /* Prefer the addresses that is reachable. */
1891 static int
1892 rule_reachable(const struct dstinforeq *da, const struct dstinforeq *db)
1894 if (da->dir_dreachable == db->dir_dreachable)
1895 return (RULE_EQUAL);
1896 if (da->dir_dreachable)
1897 return (RULE_PREFER_DA);
1898 return (RULE_PREFER_DB);
1901 /* Prefer the address whose scope matches that of its source address. */
1902 static int
1903 rule_matchscope(const struct dstinforeq *da, const struct dstinforeq *db)
1905 boolean_t da_scope_match, db_scope_match;
1907 da_scope_match = da->dir_dscope == da->dir_sscope;
1908 db_scope_match = db->dir_dscope == db->dir_sscope;
1910 if (da_scope_match == db_scope_match)
1911 return (RULE_EQUAL);
1912 if (da_scope_match)
1913 return (RULE_PREFER_DA);
1914 return (RULE_PREFER_DB);
1917 /* Avoid the address with the link local source address. */
1918 static int
1919 rule_avoidlinklocal(const struct dstinforeq *da, const struct dstinforeq *db)
1921 if (da->dir_sscope == IP6_SCOPE_LINKLOCAL &&
1922 da->dir_dscope != IP6_SCOPE_LINKLOCAL &&
1923 db->dir_sscope != IP6_SCOPE_LINKLOCAL)
1924 return (RULE_PREFER_DB);
1925 if (db->dir_sscope == IP6_SCOPE_LINKLOCAL &&
1926 db->dir_dscope != IP6_SCOPE_LINKLOCAL &&
1927 da->dir_sscope != IP6_SCOPE_LINKLOCAL)
1928 return (RULE_PREFER_DA);
1929 return (RULE_EQUAL);
1932 /* Prefer the address whose source address isn't deprecated. */
1933 static int
1934 rule_deprecated(const struct dstinforeq *da, const struct dstinforeq *db)
1936 if (da->dir_sdeprecated == db->dir_sdeprecated)
1937 return (RULE_EQUAL);
1938 if (db->dir_sdeprecated)
1939 return (RULE_PREFER_DA);
1940 return (RULE_PREFER_DB);
1943 /* Prefer the address whose label matches that of its source address. */
1944 static int
1945 rule_label(const struct dstinforeq *da, const struct dstinforeq *db)
1947 if (da->dir_labelmatch == db->dir_labelmatch)
1948 return (RULE_EQUAL);
1949 if (da->dir_labelmatch)
1950 return (RULE_PREFER_DA);
1951 return (RULE_PREFER_DB);
1954 /* Prefer the address with the higher precedence. */
1955 static int
1956 rule_precedence(const struct dstinforeq *da, const struct dstinforeq *db)
1958 if (da->dir_precedence == db->dir_precedence)
1959 return (RULE_EQUAL);
1960 if (da->dir_precedence > db->dir_precedence)
1961 return (RULE_PREFER_DA);
1962 return (RULE_PREFER_DB);
1965 /* Prefer the address whose output interface isn't an IP tunnel */
1966 static int
1967 rule_native(const struct dstinforeq *da, const struct dstinforeq *db)
1969 boolean_t isatun, isbtun;
1971 /* Get the common case out of the way early */
1972 if (da->dir_dmactype == db->dir_dmactype)
1973 return (RULE_EQUAL);
1975 isatun = da->dir_dmactype == DL_IPV4 || da->dir_dmactype == DL_IPV6;
1976 isbtun = db->dir_dmactype == DL_IPV4 || db->dir_dmactype == DL_IPV6;
1978 if (isatun == isbtun)
1979 return (RULE_EQUAL);
1980 if (isbtun)
1981 return (RULE_PREFER_DA);
1982 return (RULE_PREFER_DB);
1985 /* Prefer the address with the smaller scope. */
1986 static int
1987 rule_scope(const struct dstinforeq *da, const struct dstinforeq *db)
1989 if (da->dir_dscope == db->dir_dscope)
1990 return (RULE_EQUAL);
1991 if (da->dir_dscope < db->dir_dscope)
1992 return (RULE_PREFER_DA);
1993 return (RULE_PREFER_DB);
1997 * Prefer the address that has the most leading bits in common with its
1998 * source address.
2000 static int
2001 rule_prefix(const struct dstinforeq *da, const struct dstinforeq *db)
2003 uint_t da_commonbits, db_commonbits;
2004 boolean_t da_isipv4, db_isipv4;
2006 da_isipv4 = IN6_IS_ADDR_V4MAPPED(&da->dir_daddr);
2007 db_isipv4 = IN6_IS_ADDR_V4MAPPED(&db->dir_daddr);
2010 * At this point, the order doesn't matter if the two addresses
2011 * aren't of the same address family.
2013 if (da_isipv4 != db_isipv4)
2014 return (RULE_EQUAL);
2016 da_commonbits = ip_addr_commonbits_v6(&da->dir_daddr, &da->dir_saddr);
2017 db_commonbits = ip_addr_commonbits_v6(&db->dir_daddr, &db->dir_saddr);
2019 if (da_commonbits > db_commonbits)
2020 return (RULE_PREFER_DA);
2021 if (da_commonbits < db_commonbits)
2022 return (RULE_PREFER_DB);
2023 return (RULE_EQUAL);
2027 * This is the function passed to qsort() that does the AF_INET6
2028 * address comparisons. It compares two addresses using a list of
2029 * rules. The rules are applied in order until one prefers one
2030 * address over the other.
2032 static int
2033 dstcmp(const void *da, const void *db)
2035 int index, result;
2036 rulef_t rules[] = {
2037 rule_reachable,
2038 rule_matchscope,
2039 rule_avoidlinklocal,
2040 rule_deprecated,
2041 rule_label,
2042 rule_precedence,
2043 rule_native,
2044 rule_scope,
2045 rule_prefix,
2046 NULL
2049 result = 0;
2050 for (index = 0; rules[index] != NULL; index++) {
2051 result = (rules[index])(da, db);
2052 if (result != RULE_EQUAL)
2053 break;
2056 return (result);
2060 * Given haddrlist and a port number, mallocs and populates a new
2061 * nd_addrlist. The new nd_addrlist maintains the order of the addresses
2062 * in haddrlist, which have already been sorted by order_haddrlist_inet()
2063 * or order_haddrlist_inet6(). For IPv6 this function filters out
2064 * IPv4-mapped IPv6 addresses.
2067 hent2ndaddr(int af, char **haddrlist, int *servp, struct nd_addrlist **nd_alist)
2069 struct nd_addrlist *result;
2070 int num;
2071 struct netbuf *na;
2072 struct sockaddr_in *sinbuf, *sin;
2073 struct sockaddr_in6 *sin6buf, *sin6;
2074 struct in_addr **inaddr, **inaddrlist;
2075 struct in6_addr **in6addr, **in6addrlist;
2077 /* Address count */
2078 num = 0;
2079 if (af == AF_INET6) {
2080 in6addrlist = (struct in6_addr **)haddrlist;
2083 * Exclude IPv4-mapped IPv6 addresses from the count, as
2084 * these are not included in the nd_addrlist we return.
2086 for (in6addr = in6addrlist; *in6addr != NULL; in6addr++)
2087 if (!IN6_IS_ADDR_V4MAPPED(*in6addr))
2088 num++;
2089 } else {
2090 inaddrlist = (struct in_addr **)haddrlist;
2092 for (inaddr = inaddrlist; *inaddr != NULL; inaddr++)
2093 num++;
2095 if (num == 0)
2096 return (ND_NOHOST);
2098 result = malloc(sizeof (struct nd_addrlist));
2099 if (result == 0)
2100 return (ND_NOMEM);
2102 result->n_cnt = num;
2103 result->n_addrs = calloc(num, sizeof (struct netbuf));
2104 if (result->n_addrs == 0) {
2105 free(result);
2106 return (ND_NOMEM);
2109 na = result->n_addrs;
2110 if (af == AF_INET) {
2111 sinbuf = calloc(num, sizeof (struct sockaddr_in));
2112 if (sinbuf == NULL) {
2113 free(result->n_addrs);
2114 free(result);
2115 return (ND_NOMEM);
2118 sin = sinbuf;
2119 for (inaddr = inaddrlist; *inaddr != NULL; inaddr++) {
2120 na->len = na->maxlen = sizeof (struct sockaddr_in);
2121 na->buf = (char *)sin;
2122 sin->sin_family = AF_INET;
2123 sin->sin_addr = **inaddr;
2124 sin->sin_port = *servp;
2125 na++;
2126 sin++;
2128 } else if (af == AF_INET6) {
2129 sin6buf = calloc(num, sizeof (struct sockaddr_in6));
2130 if (sin6buf == NULL) {
2131 free(result->n_addrs);
2132 free(result);
2133 return (ND_NOMEM);
2136 sin6 = sin6buf;
2137 for (in6addr = in6addrlist; *in6addr != NULL; in6addr++) {
2138 if (IN6_IS_ADDR_V4MAPPED(*in6addr))
2139 continue;
2141 na->len = na->maxlen = sizeof (struct sockaddr_in6);
2142 na->buf = (char *)sin6;
2143 sin6->sin6_family = AF_INET6;
2144 sin6->sin6_addr = **in6addr;
2145 sin6->sin6_port = *servp;
2146 na++;
2147 sin6++;
2150 *(nd_alist) = result;
2151 return (ND_OK);
2155 * Given a hostent and a servent, mallocs and populates
2156 * a new nd_hostservlist with host and service names.
2158 * We could be passed in a NULL servent, in which case stringify port.
2161 hsents2ndhostservs(struct hostent *he, struct servent *se,
2162 ushort_t port, struct nd_hostservlist **hslist)
2164 struct nd_hostservlist *result;
2165 struct nd_hostserv *hs;
2166 int hosts, servs, i, j;
2167 char **hn, **sn;
2169 if ((result = malloc(sizeof (struct nd_hostservlist))) == 0)
2170 return (ND_NOMEM);
2173 * We initialize the counters to 1 rather than zero because
2174 * we have to count the "official" name as well as the aliases.
2176 for (hn = he->h_aliases, hosts = 1; hn && *hn; hn++, hosts++) {};
2177 if (se) {
2178 for (sn = se->s_aliases, servs = 1; sn && *sn; sn++, servs++) {
2180 } else
2181 servs = 1;
2183 if ((hs = calloc(hosts * servs, sizeof (struct nd_hostserv))) == 0) {
2184 free(result);
2185 return (ND_NOMEM);
2188 result->h_cnt = servs * hosts;
2189 result->h_hostservs = hs;
2191 for (i = 0, hn = he->h_aliases; i < hosts; i++) {
2192 sn = se ? se->s_aliases : NULL;
2194 for (j = 0; j < servs; j++) {
2195 if (i == 0)
2196 hs->h_host = strdup(he->h_name);
2197 else
2198 hs->h_host = strdup(*hn);
2199 if (j == 0) {
2200 if (se)
2201 hs->h_serv = strdup(se->s_name);
2202 else {
2203 /* Convert to a number string */
2204 char stmp[16];
2206 (void) sprintf(stmp, "%d", port);
2207 hs->h_serv = strdup(stmp);
2209 } else
2210 hs->h_serv = strdup(*sn++);
2212 if ((hs->h_host == 0) || (hs->h_serv == 0)) {
2213 free(result->h_hostservs);
2214 free(result);
2215 return (ND_NOMEM);
2217 hs++;
2219 if (i)
2220 hn++;
2222 *(hslist) = result;
2223 return (ND_OK);
2227 * Process results from nd_addrlist ( returned by netdir_getbyname)
2228 * into a hostent using buf.
2229 * *** ASSUMES that nd_addrlist->n_addrs->buf contains IP addresses in
2230 * sockaddr_in's ***
2233 ndaddr2hent(int af, const char *nam, struct nd_addrlist *addrs,
2234 struct hostent *result, char *buffer, int buflen)
2236 int i, count;
2237 struct in_addr *addrp;
2238 struct in6_addr *addr6p;
2239 char **addrvec;
2240 struct netbuf *na;
2241 size_t len;
2243 result->h_name = buffer;
2244 result->h_addrtype = af;
2245 result->h_length = (af == AF_INET) ? sizeof (*addrp):
2246 sizeof (*addr6p);
2249 * Build addrlist at start of buffer (after name); store the
2250 * addresses themselves at the end of the buffer.
2252 len = strlen(nam) + 1;
2253 addrvec = (char **)ROUND_UP(buffer + len, sizeof (*addrvec));
2254 result->h_addr_list = addrvec;
2256 if (af == AF_INET) {
2257 addrp = (struct in_addr *)ROUND_DOWN(buffer + buflen,
2258 sizeof (*addrp));
2260 count = addrs->n_cnt;
2261 if ((char *)(&addrvec[count + 1]) > (char *)(&addrp[-count]))
2262 return (ND_NOMEM);
2264 (void) memcpy(buffer, nam, len);
2266 for (na = addrs->n_addrs, i = 0; i < count; na++, i++) {
2267 --addrp;
2268 (void) memcpy(addrp,
2269 /* LINTED pointer cast */
2270 &((struct sockaddr_in *)na->buf)->sin_addr,
2271 sizeof (*addrp));
2272 *addrvec++ = (char *)addrp;
2274 } else {
2275 addr6p = (struct in6_addr *)ROUND_DOWN(buffer + buflen,
2276 sizeof (*addr6p));
2278 count = addrs->n_cnt;
2279 if ((char *)(&addrvec[count + 1]) > (char *)(&addr6p[-count]))
2280 return (ND_NOMEM);
2282 (void) memcpy(buffer, nam, len);
2284 for (na = addrs->n_addrs, i = 0; i < count; na++, i++) {
2285 --addr6p;
2286 (void) memcpy(addr6p,
2287 /* LINTED pointer cast */
2288 &((struct sockaddr_in6 *)na->buf)->sin6_addr,
2289 sizeof (*addr6p));
2290 *addrvec++ = (char *)addr6p;
2293 *addrvec = 0;
2294 result->h_aliases = addrvec;
2296 return (ND_OK);
2300 * Process results from nd_addrlist ( returned by netdir_getbyname)
2301 * into a servent using buf.
2304 ndaddr2srent(const char *name, const char *proto, ushort_t port,
2305 struct servent *result, char *buffer, int buflen)
2307 size_t i;
2308 char *bufend = (buffer + buflen);
2310 result->s_port = (int)port;
2312 result->s_aliases =
2313 (char **)ROUND_UP(buffer, sizeof (char *));
2314 result->s_aliases[0] = NULL;
2315 buffer = (char *)&result->s_aliases[1];
2316 result->s_name = buffer;
2317 i = strlen(name) + 1;
2318 if ((buffer + i) > bufend)
2319 return (ND_NOMEM);
2320 (void) memcpy(buffer, name, i);
2321 buffer += i;
2323 result->s_proto = buffer;
2324 i = strlen(proto) + 1;
2325 if ((buffer + i) > bufend)
2326 return (ND_NOMEM);
2327 (void) memcpy(buffer, proto, i);
2328 buffer += i;
2330 return (ND_OK);
2334 * Process results from nd_hostservlist ( returned by netdir_getbyaddr)
2335 * into a hostent using buf.
2336 * *** ASSUMES that nd_buf->buf is a sockaddr_in ***
2339 ndhostserv2hent(struct netbuf *nbuf, struct nd_hostservlist *addrs,
2340 struct hostent *result, char *buffer, int buflen)
2342 int i, count;
2343 char *aliasp;
2344 char **aliasvec;
2345 struct sockaddr_in *sa;
2346 struct nd_hostserv *hs;
2347 const char *la;
2348 size_t length;
2350 /* First, give the lonely address a specious home in h_addr_list. */
2351 aliasp = (char *)ROUND_UP(buffer, sizeof (sa->sin_addr));
2352 /* LINTED pointer cast */
2353 sa = (struct sockaddr_in *)nbuf->buf;
2354 (void) memcpy(aliasp, &(sa->sin_addr), sizeof (sa->sin_addr));
2355 aliasvec = (char **)ROUND_UP(aliasp + sizeof (sa->sin_addr),
2356 sizeof (*aliasvec));
2357 result->h_addr_list = aliasvec;
2358 *aliasvec++ = aliasp;
2359 *aliasvec++ = 0;
2362 * Build h_aliases at start of buffer (after addr and h_addr_list);
2363 * store the alias strings at the end of the buffer (before h_name).
2366 aliasp = buffer + buflen;
2368 result->h_aliases = aliasvec;
2370 hs = addrs->h_hostservs;
2371 if (!hs)
2372 return (ND_NOHOST);
2374 length = strlen(hs->h_host) + 1;
2375 aliasp -= length;
2376 if ((char *)(&aliasvec[1]) > aliasp)
2377 return (ND_NOMEM);
2378 (void) memcpy(aliasp, hs->h_host, length);
2380 result->h_name = aliasp;
2381 result->h_addrtype = AF_INET;
2382 result->h_length = sizeof (sa->sin_addr);
2385 * Assumption: the netdir nametoaddr_libs
2386 * sort the vector of (host, serv) pairs in such a way that
2387 * all pairs with the same host name are contiguous.
2389 la = hs->h_host;
2390 count = addrs->h_cnt;
2391 for (i = 0; i < count; i++, hs++)
2392 if (strcmp(la, hs->h_host) != 0) {
2393 size_t len = strlen(hs->h_host) + 1;
2395 aliasp -= len;
2396 if ((char *)(&aliasvec[2]) > aliasp)
2397 return (ND_NOMEM);
2398 (void) memcpy(aliasp, hs->h_host, len);
2399 *aliasvec++ = aliasp;
2400 la = hs->h_host;
2402 *aliasvec = 0;
2404 return (ND_OK);
2408 * Process results from nd_hostservlist ( returned by netdir_getbyaddr)
2409 * into a servent using buf.
2412 ndhostserv2srent(int port, const char *proto, struct nd_hostservlist *addrs,
2413 struct servent *result, char *buffer, int buflen)
2415 int i, count;
2416 char *aliasp;
2417 char **aliasvec;
2418 struct nd_hostserv *hs;
2419 const char *host_cname;
2420 size_t leni, lenj;
2422 result->s_port = port;
2424 * Build s_aliases at start of buffer;
2425 * store proto and aliases at the end of the buffer (before h_name).
2428 aliasp = buffer + buflen;
2429 aliasvec = (char **)ROUND_UP(buffer, sizeof (char *));
2431 result->s_aliases = aliasvec;
2433 hs = addrs->h_hostservs;
2434 if (!hs)
2435 return (ND_NOHOST);
2436 host_cname = hs->h_host;
2438 leni = strlen(proto) + 1;
2439 lenj = strlen(hs->h_serv) + 1;
2440 if ((char *)(&aliasvec[2]) > (aliasp - leni - lenj))
2441 return (ND_NOMEM);
2443 aliasp -= leni;
2444 (void) memcpy(aliasp, proto, leni);
2445 result->s_proto = aliasp;
2447 aliasp -= lenj;
2448 (void) memcpy(aliasp, hs->h_serv, lenj);
2449 result->s_name = aliasp;
2452 * Assumption: the netdir nametoaddr_libs
2453 * do a host aliases first and serv aliases next
2454 * enumeration for creating the list of hostserv
2455 * structures.
2457 count = addrs->h_cnt;
2458 for (i = 0;
2459 i < count && hs->h_serv && strcmp(hs->h_host, host_cname) == 0;
2460 i++, hs++) {
2461 size_t len = strlen(hs->h_serv) + 1;
2463 aliasp -= len;
2464 if ((char *)(&aliasvec[2]) > aliasp)
2465 return (ND_NOMEM);
2466 (void) memcpy(aliasp, hs->h_serv, len);
2467 *aliasvec++ = aliasp;
2469 *aliasvec = NULL;
2471 return (ND_OK);
2475 static int
2476 nd2herrno(int nerr)
2478 switch (nerr) {
2479 case ND_OK:
2480 return (0);
2481 case ND_TRY_AGAIN:
2482 return (TRY_AGAIN);
2483 case ND_NO_RECOVERY:
2484 case ND_BADARG:
2485 case ND_NOMEM:
2486 return (NO_RECOVERY);
2487 case ND_NO_DATA:
2488 return (NO_DATA);
2489 case ND_NOHOST:
2490 case ND_NOSERV:
2491 return (HOST_NOT_FOUND);
2492 default:
2493 return (NO_RECOVERY);
2498 * This is a utility function so that various parts of libnsl can
2499 * easily send ioctls down to ip.
2503 nss_ioctl(int af, int cmd, void *arg)
2505 int fd;
2506 char *devpath;
2507 int retv;
2509 switch (af) {
2510 case AF_INET6:
2511 devpath = UDP6DEV;
2512 break;
2513 case AF_INET:
2514 case AF_UNSPEC:
2515 default:
2516 devpath = UDPDEV;
2518 if ((fd = open(devpath, O_RDONLY)) < 0) {
2519 return (-1);
2521 while ((retv = ioctl(fd, cmd, arg)) == -1) {
2522 if (errno != EINTR)
2523 break;
2525 (void) close(fd);
2526 return (retv);
2529 static int
2530 nss_strioctl(int af, int cmd, void *ptr, int ilen)
2532 struct strioctl str;
2534 str.ic_cmd = cmd;
2535 str.ic_timout = 0;
2536 str.ic_len = ilen;
2537 str.ic_dp = ptr;
2539 return (nss_ioctl(af, I_STR, &str));
2542 static struct ifinfo *
2543 get_local_info(void)
2545 int numifs;
2546 int n;
2547 char *buf = NULL;
2548 size_t needed;
2549 struct lifconf lifc;
2550 struct lifreq lifreq, *lifr;
2551 struct lifnum lifn;
2552 struct ifinfo *localinfo;
2554 lifn.lifn_family = AF_UNSPEC;
2555 lifn.lifn_flags = 0;
2557 getifnum:
2558 if (nss_ioctl(AF_UNSPEC, SIOCGLIFNUM, &lifn) == -1) {
2559 numifs = MAXIFS;
2560 } else {
2561 numifs = lifn.lifn_count;
2565 * Add a small fudge factor in case interfaces get plumbed between
2566 * the call to SIOCGLIFNUM and SIOCGLIFCONF.
2568 needed = (numifs + 4) * sizeof (lifreq);
2569 if (buf == NULL)
2570 buf = malloc(needed);
2571 else
2572 buf = realloc(buf, needed);
2573 if (buf == NULL) {
2574 (void) syslog(LOG_ERR, "n2a get_local_info: malloc failed: %m");
2575 _nderror = ND_NOMEM;
2576 return (NULL);
2578 lifc.lifc_family = AF_UNSPEC;
2579 lifc.lifc_flags = 0;
2580 lifc.lifc_len = needed;
2581 lifc.lifc_buf = buf;
2582 if (nss_ioctl(AF_UNSPEC, SIOCGLIFCONF, &lifc) == -1) {
2584 * IP returns EINVAL if the buffer was too small to fit
2585 * all of the entries. If that's the case, go back and
2586 * try again.
2588 if (errno == EINVAL)
2589 goto getifnum;
2591 (void) syslog(LOG_ERR, "n2a get_local_info: "
2592 "ioctl (get interface configuration): %m");
2593 free(buf);
2594 _nderror = ND_SYSTEM;
2595 return (NULL);
2597 /* LINTED pointer cast */
2598 lifr = (struct lifreq *)buf;
2599 numifs = lifc.lifc_len/sizeof (lifreq);
2600 localinfo = malloc(ifinfosize(numifs));
2601 if (localinfo == NULL) {
2602 (void) syslog(LOG_ERR, "n2a get_local_info: malloc failed: %m");
2603 free(buf);
2604 _nderror = ND_SYSTEM;
2605 return (NULL);
2608 /* LINTED pointer cast */
2609 localinfo->addresses = (struct __ifaddr *)
2610 ((char *)localinfo + sizeof (struct ifinfo));
2612 for (localinfo->count = 0, n = numifs; n > 0; n--, lifr++) {
2613 int af;
2615 lifreq = *lifr;
2616 af = lifreq.lifr_addr.ss_family;
2618 /* Squirrel away the address */
2619 if (ifassign(lifreq, localinfo->count, IF_ADDR) == 0)
2620 continue;
2622 if (nss_ioctl(af, SIOCGLIFFLAGS, &lifreq) < 0) {
2623 (void) syslog(LOG_ERR,
2624 "n2a get_local_info: "
2625 "ioctl (get interface flags): %m");
2626 continue;
2628 if (!(lifreq.lifr_flags & IFF_UP))
2629 continue;
2631 if (nss_ioctl(af, SIOCGLIFNETMASK, &lifreq) < 0) {
2632 (void) syslog(LOG_ERR,
2633 "n2a get_local_info: "
2634 "ioctl (get interface netmask): %m");
2635 continue;
2638 if (ifassign(lifreq, localinfo->count, IF_MASK) == 0)
2639 continue;
2641 localinfo->count++;
2644 free(buf);
2645 return (localinfo);
2648 static int
2649 __inet_ifassign(sa_family_t af, struct __ifaddr *ifa, __ifaddr_type type,
2650 void *addr) {
2651 switch (type) {
2652 case IF_ADDR:
2653 ifa->af = af;
2654 if (af == AF_INET6) {
2655 ifa->addr.in6 = *(struct in6_addr *)addr;
2656 } else {
2657 ifa->addr.in4 = *(struct in_addr *)addr;
2659 break;
2660 case IF_MASK:
2661 if (ifa->af == af) {
2662 if (af == AF_INET6) {
2663 ifa->mask.in6 = *(struct in6_addr *)addr;
2664 } else {
2665 ifa->mask.in4 = *(struct in_addr *)addr;
2667 } else {
2668 return (0);
2670 break;
2671 default:
2672 return (0);
2675 return (1);
2679 * Some higher-level routines for determining if an address is
2680 * on a local network.
2682 * __inet_get_local_interfaces() - get an opaque handle with
2683 * with a list of local interfaces
2684 * __inet_address_is_local() - return 1 if an address is
2685 * on a local network; 0 otherwise
2686 * __inet_free_local_interfaces() - free handle that was
2687 * returned by __inet_get_local_interfaces()
2689 * A typical calling sequence is:
2691 * p = __inet_get_local_interfaces();
2692 * if (__inet_address_is_local(p, inaddr)) {
2693 * ...
2695 * __inet_free_local_interfaces(p);
2699 * Return an opaque pointer to a list of configured interfaces.
2701 void *
2702 __inet_get_local_interfaces(void)
2704 return (get_local_info());
2708 * Free memory allocated by inet_local_interfaces().
2710 void
2711 __inet_free_local_interfaces(void *p)
2713 free(p);
2717 * Determine if an address is on a local network.
2719 * Might have made sense to use SIOCTONLINK, except that it doesn't
2720 * handle matching on IPv4 network addresses.
2723 __inet_address_is_local_af(void *p, sa_family_t af, void *addr) {
2725 struct ifinfo *localinfo = (struct ifinfo *)p;
2726 int i, a;
2727 struct in_addr v4addr;
2729 if (localinfo == 0)
2730 return (0);
2732 if (af == AF_INET6 && IN6_IS_ADDR_V4MAPPED((struct in6_addr *)addr)) {
2733 IN6_V4MAPPED_TO_INADDR((struct in6_addr *)addr, &v4addr);
2734 af = AF_INET;
2735 addr = (void *)&v4addr;
2738 for (i = 0; i < localinfo->count; i++) {
2739 if (ifaf(i) == af) {
2740 if (af == AF_INET6) {
2741 struct in6_addr *a6 = (struct in6_addr *)addr;
2742 for (a = 0; a < sizeof (a6->s6_addr); a++) {
2743 if ((a6->s6_addr[a] &
2744 ifmask6(i).s6_addr[a]) !=
2745 (ifaddr6(i).s6_addr[a] &
2746 ifmask6(i).s6_addr[a]))
2747 break;
2749 if (a >= sizeof (a6->s6_addr))
2750 return (1);
2751 } else {
2752 if ((((struct in_addr *)addr)->s_addr &
2753 ifmask4(i).s_addr) ==
2754 (ifaddr4(i).s_addr &
2755 ifmask4(i).s_addr))
2756 return (1);
2761 return (0);
2765 __inet_address_is_local(void *p, struct in_addr addr)
2767 return (__inet_address_is_local_af(p, AF_INET, &addr));
2771 __inet_uaddr_is_local(void *p, struct netconfig *nc, char *uaddr)
2773 struct netbuf *taddr;
2774 sa_family_t af;
2775 int ret;
2777 taddr = uaddr2taddr(nc, uaddr);
2778 if (taddr == 0)
2779 return (0);
2781 /* LINTED pointer cast */
2782 af = ((struct sockaddr *)taddr->buf)->sa_family;
2784 ret = __inet_address_is_local_af(p, af, (af == AF_INET6) ?
2785 /* LINTED pointer cast */
2786 (void *)&((struct sockaddr_in6 *)taddr->buf)->sin6_addr :
2787 /* LINTED pointer cast */
2788 (void *)&((struct sockaddr_in *)taddr->buf)->sin_addr);
2790 netdir_free(taddr, ND_ADDR);
2791 return (ret);
2796 __inet_address_count(void *p)
2798 struct ifinfo *lp = (struct ifinfo *)p;
2800 if (lp != 0) {
2801 return (lp->count);
2802 } else {
2803 return (0);
2807 uint32_t
2808 __inet_get_addr(void *p, int n)
2810 struct ifinfo *localinfo = (struct ifinfo *)p;
2812 if (localinfo == 0 || n >= localinfo->count || ifaf(n) != AF_INET)
2813 return (0);
2815 return (ifaddr4(n).s_addr);
2818 uint32_t
2819 __inet_get_network(void *p, int n)
2821 struct ifinfo *localinfo = (struct ifinfo *)p;
2823 if (localinfo == 0 || n >= localinfo->count || ifaf(n) != AF_INET)
2824 return (0);
2826 return (ifaddr4(n).s_addr & ifmask4(n).s_addr);
2829 char *
2830 __inet_get_uaddr(void *p, struct netconfig *nc, int n)
2832 struct ifinfo *localinfo = (struct ifinfo *)p;
2833 char *uaddr;
2834 struct sockaddr_in sin4;
2835 struct sockaddr_in6 sin6;
2836 struct netbuf nb;
2838 if (localinfo == 0 || nc == 0 || n >= localinfo->count)
2839 return (0);
2841 if (ifaf(n) == AF_INET6) {
2842 if (strcmp(NC_INET6, nc->nc_protofmly) != 0)
2843 return (0);
2844 (void) memset(&sin6, 0, sizeof (sin6));
2845 sin6.sin6_family = AF_INET6;
2846 sin6.sin6_addr = ifaddr6(n);
2847 nb.buf = (char *)&sin6;
2848 nb.len = sizeof (sin6);
2849 } else {
2850 if (strcmp(NC_INET, nc->nc_protofmly) != 0)
2851 return (0);
2852 (void) memset(&sin4, 0, sizeof (sin4));
2853 sin4.sin_family = AF_INET;
2854 sin4.sin_addr = ifaddr4(n);
2855 nb.buf = (char *)&sin4;
2856 nb.len = sizeof (sin4);
2859 nb.maxlen = nb.len;
2861 uaddr = taddr2uaddr(nc, &nb);
2862 return (uaddr);
2865 char *
2866 __inet_get_networka(void *p, int n)
2868 struct ifinfo *localinfo = (struct ifinfo *)p;
2870 if (localinfo == 0 || n >= localinfo->count)
2871 return (0);
2873 if (ifaf(n) == AF_INET6) {
2874 char buf[INET6_ADDRSTRLEN];
2875 struct in6_addr in6;
2876 int i;
2878 for (i = 0; i < sizeof (in6.s6_addr); i++) {
2879 in6.s6_addr[i] = ifaddr6(n).s6_addr[i] &
2880 ifmask6(n).s6_addr[i];
2882 return (strdup(inet_ntop(AF_INET6, &in6, buf, sizeof (buf))));
2883 } else {
2884 struct in_addr in4;
2886 in4.s_addr = ifaddr4(n).s_addr & ifmask4(n).s_addr;
2887 return (strdup(inet_ntoa(in4)));
2891 static int
2892 in_list(struct in_addr *addrs, int n, struct in_addr a)
2894 int i;
2896 for (i = 0; i < n; i++) {
2897 if (addrs[i].s_addr == a.s_addr)
2898 return (1);
2900 return (0);
2903 static int
2904 getbroadcastnets(struct netconfig *tp, struct in_addr **addrs)
2906 struct ifconf ifc;
2907 struct ifreq ifreq, *ifr;
2908 struct sockaddr_in *sin;
2909 struct in_addr a;
2910 int fd;
2911 int n, i, numifs;
2912 char *buf;
2913 int use_loopback = 0;
2915 _nderror = ND_SYSTEM;
2916 fd = open(tp->nc_device, O_RDONLY);
2917 if (fd < 0) {
2918 (void) syslog(LOG_ERR,
2919 "broadcast: open to get interface configuration: %m");
2920 return (0);
2922 if (ioctl(fd, SIOCGIFNUM, (char *)&numifs) < 0)
2923 numifs = MAXIFS;
2924 buf = malloc(numifs * sizeof (struct ifreq));
2925 if (buf == NULL) {
2926 (void) syslog(LOG_ERR, "broadcast: malloc failed: %m");
2927 (void) close(fd);
2928 return (0);
2930 *addrs = malloc(numifs * sizeof (struct in_addr));
2931 if (*addrs == NULL) {
2932 (void) syslog(LOG_ERR, "broadcast: malloc failed: %m");
2933 free(buf);
2934 (void) close(fd);
2935 return (0);
2937 ifc.ifc_len = numifs * (int)sizeof (struct ifreq);
2938 ifc.ifc_buf = buf;
2940 * Ideally, this ioctl should also tell me, how many bytes were
2941 * finally allocated, but it doesnt.
2943 if (ioctl(fd, SIOCGIFCONF, (char *)&ifc) < 0) {
2944 (void) syslog(LOG_ERR,
2945 "broadcast: ioctl (get interface configuration): %m");
2946 free(buf);
2947 free(*addrs);
2948 (void) close(fd);
2949 return (0);
2952 retry:
2953 /* LINTED pointer cast */
2954 ifr = (struct ifreq *)buf;
2955 for (i = 0, n = ifc.ifc_len / (int)sizeof (struct ifreq);
2956 n > 0; n--, ifr++) {
2957 ifreq = *ifr;
2958 if (ioctl(fd, SIOCGIFFLAGS, (char *)&ifreq) < 0) {
2959 (void) syslog(LOG_ERR, "broadcast: "
2960 "ioctl (get interface flags): %m");
2961 continue;
2963 if (!(ifreq.ifr_flags & IFF_UP) ||
2964 (ifr->ifr_addr.sa_family != AF_INET))
2965 continue;
2966 if (ifreq.ifr_flags & IFF_BROADCAST) {
2967 /* LINTED pointer cast */
2968 sin = (struct sockaddr_in *)&ifr->ifr_addr;
2969 if (ioctl(fd, SIOCGIFBRDADDR, (char *)&ifreq) < 0) {
2970 /* May not work with other implementation */
2971 a = _inet_makeaddr(
2972 inet_netof(sin->sin_addr),
2973 INADDR_ANY);
2974 if (!in_list(*addrs, i, a))
2975 (*addrs)[i++] = a;
2976 } else {
2977 /* LINTED pointer cast */
2978 a = ((struct sockaddr_in *)
2979 &ifreq.ifr_addr)->sin_addr;
2980 if (!in_list(*addrs, i, a))
2981 (*addrs)[i++] = a;
2983 continue;
2985 if (use_loopback && (ifreq.ifr_flags & IFF_LOOPBACK)) {
2986 /* LINTED pointer cast */
2987 sin = (struct sockaddr_in *)&ifr->ifr_addr;
2988 a = sin->sin_addr;
2989 if (!in_list(*addrs, i, a))
2990 (*addrs)[i++] = a;
2991 continue;
2993 if (ifreq.ifr_flags & IFF_POINTOPOINT) {
2994 if (ioctl(fd, SIOCGIFDSTADDR, (char *)&ifreq) < 0)
2995 continue;
2996 /* LINTED pointer cast */
2997 a = ((struct sockaddr_in *)
2998 &ifreq.ifr_addr)->sin_addr;
2999 if (!in_list(*addrs, i, a))
3000 (*addrs)[i++] = a;
3001 continue;
3004 if (i == 0 && !use_loopback) {
3005 use_loopback = 1;
3006 goto retry;
3008 free(buf);
3009 (void) close(fd);
3010 if (i)
3011 _nderror = ND_OK;
3012 else
3013 free(*addrs);
3014 return (i);
3018 * This is lifted straight from libsocket/inet/inet_mkaddr.c.
3019 * Copied here to avoid our dependency on libsocket. More importantly,
3020 * to make sure partially static apps that use libnsl, but not
3021 * libsocket, don't get screwed up.
3022 * If you understand the above paragraph, try to get rid of
3023 * this copy of inet_makeaddr; if you don;t, leave it alone.
3025 * Formulate an Internet address from network + host. Used in
3026 * building addresses stored in the ifnet structure.
3028 static struct in_addr
3029 _inet_makeaddr(in_addr_t net, in_addr_t host)
3031 in_addr_t addr;
3032 struct in_addr inaddr;
3034 if (net < 128)
3035 addr = (net << IN_CLASSA_NSHIFT) | (host & IN_CLASSA_HOST);
3036 else if (net < 65536)
3037 addr = (net << IN_CLASSB_NSHIFT) | (host & IN_CLASSB_HOST);
3038 else if (net < 16777216L)
3039 addr = (net << IN_CLASSC_NSHIFT) | (host & IN_CLASSC_HOST);
3040 else
3041 addr = net | host;
3042 inaddr.s_addr = htonl(addr);
3043 return (inaddr);
3047 * Routine to read the default configuration file and check if SORT_ADDRS
3048 * is set to NO or FALSE. This routine is called by order_haddrlist_af()
3049 * to determine if the addresses need to be sorted.
3051 static boolean_t
3052 _read_nsw_file(void)
3054 char defval[LINESIZE];
3055 FILE *defl;
3056 boolean_t nosort = B_FALSE;
3059 do {
3060 defl = fopen(__NSW_DEFAULT_FILE, "rF");
3061 } while ((defl == NULL) && (errno == EINTR));
3063 if (defl == NULL)
3064 return (B_FALSE);
3066 while (fgets(defval, sizeof (defval), defl) != NULL) {
3067 if ((strncmp(DONT_SORT, defval, sizeof (DONT_SORT) - 1) == 0) ||
3068 (strncmp(DONT_SORT2, defval,
3069 sizeof (DONT_SORT2) - 1) == 0)) {
3070 nosort = B_TRUE;
3071 break;
3074 (void) fclose(defl);
3075 return (nosort);