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 2008 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 /*
28 * This is where we have chosen to combine every useful bit of code for
29 * all the Solaris frontends to lookup hosts, services, and netdir information
30 * for inet family (udp, tcp) transports. gethostbyYY(), getservbyYY(), and
31 * netdir_getbyYY() are all implemented on top of this code. Similarly,
32 * netdir_options, taddr2uaddr, and uaddr2taddr for inet transports also
33 * find a home here.
34 *
35 * If the netconfig structure supplied has NO nametoaddr libs (i.e. a "-"
36 * in /etc/netconfig), this code calls the name service switch, and
37 * therefore, /etc/nsswitch.conf is effectively the only place that
38 * dictates hosts/serv lookup policy.
39 * If an administrator chooses to bypass the name service switch by
40 * specifying third party supplied nametoaddr libs in /etc/netconfig, this
41 * implementation does NOT call the name service switch, it merely loops
42 * through the nametoaddr libs. In this case, if this code was called
43 * from gethost/servbyYY() we marshal the inet specific struct into
44 * transport independent netbuf or hostserv, and unmarshal the resulting
45 * nd_addrlist or hostservlist back into hostent and servent, as the case
46 * may be.
47 *
48 * Goes without saying that most of the future bugs in gethost/servbyYY
49 * and netdir_getbyYY are lurking somewhere here.
50 */
51
52 #include "mt.h"
53 #include <ctype.h>
54 #include <stdio.h>
55 #include <stdlib.h>
56 #include <string.h>
57 #include <unistd.h>
58 #include <stropts.h>
59 #include <sys/types.h>
60 #include <sys/byteorder.h>
61 #include <sys/ioctl.h>
62 #include <sys/param.h>
63 #include <sys/time.h>
64 #include <errno.h>
65 #include <fcntl.h>
66 #include <thread.h>
67 #include <synch.h>
68 #include <sys/utsname.h>
69 #include <netdb.h>
70 #include <netconfig.h>
71 #include <netdir.h>
72 #include <tiuser.h>
73 #include <sys/socket.h>
74 #include <sys/sockio.h>
75 #include <netinet/in.h>
76 #include <arpa/inet.h>
77 #include <net/if.h>
78 #include <inet/ip.h>
79 #include <inet/ip6_asp.h>
80 #include <sys/dlpi.h>
81 #include <nss_dbdefs.h>
82 #include <nss_netdir.h>
83 #include <syslog.h>
84 #include <nsswitch.h>
85 #include "nss.h"
86
87 #define MAXIFS 32
88 #define UDPDEV "/dev/udp"
89 #define UDP6DEV "/dev/udp6"
90
91 #define DOOR_GETHOSTBYNAME_R _switch_gethostbyname_r
92 #define DOOR_GETHOSTBYADDR_R _switch_gethostbyaddr_r
93 #define DOOR_GETIPNODEBYNAME_R _switch_getipnodebyname_r
94 #define DOOR_GETIPNODEBYADDR_R _switch_getipnodebyaddr_r
95
96 #define DONT_SORT "SORT_ADDRS=NO"
97 #define DONT_SORT2 "SORT_ADDRS=FALSE"
98 #define LINESIZE 100
99
100 /*
101 * constant values of addresses for HOST_SELF_BIND, HOST_SELF_CONNECT
102 * and localhost.
103 *
104 * The following variables are static to the extent that they should
105 * not be visible outside of this file.
106 */
107 static char *localaddr[] = {"\000\000\000\000", NULL};
108 static char *connectaddr[] = {"\177\000\000\001", NULL};
109 static char *localaddr6[] =
110 {"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000", NULL};
111 static char *connectaddr6[] =
112 {"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\001", NULL};
113
114 /* IPv4 nd_addrlist */
115 static mutex_t nd_addr_lock = DEFAULTMUTEX;
116 static struct sockaddr_in sa_con;
117 static struct netbuf nd_conbuf = {sizeof (sa_con),\
118 sizeof (sa_con), (char *)&sa_con};
119 static struct nd_addrlist nd_conaddrlist = {1, &nd_conbuf};
120
121 /* IPv6 nd_addrlist */
122 static mutex_t nd6_addr_lock = DEFAULTMUTEX;
123 static struct sockaddr_in6 sa6_con;
124 static struct netbuf nd6_conbuf = {sizeof (sa6_con),\
125 sizeof (sa6_con), (char *)&sa6_con};
126 static struct nd_addrlist nd6_conaddrlist = {1, &nd6_conbuf};
127
128 #define LOCALHOST "localhost"
129
130 struct servent *_switch_getservbyname_r(const char *, const char *,
131 struct servent *, char *, int);
132 struct servent *_switch_getservbyport_r(int, const char *, struct servent *,
133 char *, int);
134
135 static int __herrno2netdir(int h_errnop);
136 static struct ifinfo *get_local_info(void);
137 static int getbroadcastnets(struct netconfig *, struct in_addr **);
138 static int hent2ndaddr(int, char **, int *, struct nd_addrlist **);
139 static int ndaddr2hent(int, const char *, struct nd_addrlist *,
140 struct hostent *, char *, int);
141 static int hsents2ndhostservs(struct hostent *, struct servent *, ushort_t,
142 struct nd_hostservlist **);
143 static int ndaddr2srent(const char *, const char *, ushort_t, struct servent *,
144 char *, int);
145 static int ndhostserv2hent(struct netbuf *, struct nd_hostservlist *,
146 struct hostent *, char *, int);
147 static int ndhostserv2srent(int, const char *, struct nd_hostservlist *,
148 struct servent *, char *, int);
149 static int nd2herrno(int nerr);
150 static void order_haddrlist_inet(char **haddrlist, size_t addrcount);
151 static void order_haddrlist_inet6(char **haddrlist, size_t addrcount);
152 static int dstcmp(const void *, const void *);
153 static int nss_strioctl(int af, int cmd, void *ptr, int ilen);
154 static struct in_addr _inet_makeaddr(in_addr_t, in_addr_t);
155 static boolean_t _read_nsw_file(void);
156
157 /*
158 * Begin: PART I
159 * Top Level Interfaces that gethost/serv/netdir funnel through.
160 */
161
162 /*
163 * gethost/servbyname always call this function; if they call
164 * with nametoaddr libs in nconf, we call netdir_getbyname
165 * implementation: __classic_netdir_getbyname, otherwise nsswitch.
166 *
167 * netdir_getbyname calls this only if nametoaddr libs are NOT
168 * specified for inet transports; i.e. it's supposed to follow
169 * the name service switch.
170 */
171 int
_get_hostserv_inetnetdir_byname(struct netconfig * nconf,struct nss_netdirbyname_in * args,union nss_netdirbyname_out * res)172 _get_hostserv_inetnetdir_byname(struct netconfig *nconf,
173 struct nss_netdirbyname_in *args, union nss_netdirbyname_out *res)
174 {
175 int server_port;
176 int *servp = &server_port;
177 char **haddrlist;
178 uint32_t dotnameaddr;
179 char *dotnamelist[2];
180 struct in_addr *inaddrs = NULL;
181 struct in6_addr v6nameaddr;
182 char **baddrlist = NULL;
183
184
185 if (nconf == NULL) {
186 _nderror = ND_BADARG;
187 return (ND_BADARG);
188 }
189
190 /*
191 * 1. gethostbyname()/netdir_getbyname() special cases:
192 */
193 switch (args->op_t) {
194
195 case NSS_HOST:
196 /*
197 * Worth the performance gain -- assuming a lot of inet apps
198 * actively use "localhost".
199 */
200 if (strcmp(args->arg.nss.host.name, LOCALHOST) == 0) {
201
202 (void) mutex_lock(&nd_addr_lock);
203 IN_SET_LOOPBACK_ADDR(&sa_con);
204 _nderror = ndaddr2hent(AF_INET, args->arg.nss.host.name,
205 &nd_conaddrlist, res->nss.host.hent,
206 args->arg.nss.host.buf,
207 args->arg.nss.host.buflen);
208 (void) mutex_unlock(&nd_addr_lock);
209 if (_nderror != ND_OK)
210 *(res->nss.host.herrno_p) =
211 nd2herrno(_nderror);
212 return (_nderror);
213 }
214 /*
215 * If the caller passed in a dot separated IP notation to
216 * gethostbyname, return that back as the address.
217 * The nd_addr_lock mutex was added to be truely re-entrant.
218 */
219 if (inet_aton(args->arg.nss.host.name,
220 (struct in_addr *)&dotnameaddr)) {
221 (void) mutex_lock(&nd_addr_lock);
222 (void) memset(&sa_con, 0, sizeof (sa_con));
223 sa_con.sin_family = AF_INET;
224 sa_con.sin_addr.s_addr = dotnameaddr;
225 _nderror = ndaddr2hent(AF_INET, args->arg.nss.host.name,
226 &nd_conaddrlist, res->nss.host.hent,
227 args->arg.nss.host.buf,
228 args->arg.nss.host.buflen);
229 (void) mutex_unlock(&nd_addr_lock);
230 if (_nderror != ND_OK)
231 *(res->nss.host.herrno_p) =
232 nd2herrno(_nderror);
233 return (_nderror);
234 }
235 break;
236
237 case NSS_HOST6:
238 /*
239 * Handle case of literal address string.
240 */
241 if (strchr(args->arg.nss.host6.name, ':') != NULL &&
242 (inet_pton(AF_INET6, args->arg.nss.host6.name,
243 &v6nameaddr) != 0)) {
244 int ret;
245
246 (void) mutex_lock(&nd6_addr_lock);
247 (void) memset(&sa6_con, 0, sizeof (sa6_con));
248 sa6_con.sin6_family = AF_INET6;
249 (void) memcpy(&(sa6_con.sin6_addr.s6_addr),
250 &v6nameaddr, sizeof (struct in6_addr));
251 ret = ndaddr2hent(AF_INET6,
252 args->arg.nss.host6.name,
253 &nd6_conaddrlist, res->nss.host.hent,
254 args->arg.nss.host6.buf,
255 args->arg.nss.host6.buflen);
256 (void) mutex_unlock(&nd6_addr_lock);
257 if (ret != ND_OK)
258 *(res->nss.host.herrno_p) = nd2herrno(ret);
259 else
260 res->nss.host.hent->h_aliases = NULL;
261 return (ret);
262 }
263 break;
264
265 case NETDIR_BY:
266 if (args->arg.nd_hs == 0) {
267 _nderror = ND_BADARG;
268 return (ND_BADARG);
269 }
270 /*
271 * If servname is NULL, return 0 as the port number
272 * If servname is rpcbind, return 111 as the port number
273 * If servname is a number, return it back as the port
274 * number.
275 */
276 if (args->arg.nd_hs->h_serv == 0) {
277 *servp = htons(0);
278 } else if (strcmp(args->arg.nd_hs->h_serv,
279 "rpcbind") == 0) {
280 *servp = htons(111);
281 } else if (strspn(args->arg.nd_hs->h_serv,
282 "0123456789") ==
283 strlen(args->arg.nd_hs->h_serv)) {
284 *servp = htons(atoi(args->arg.nd_hs->h_serv));
285 } else {
286 /* i.e. need to call a name service on this */
287 servp = NULL;
288 }
289
290 /*
291 * If the hostname is HOST_SELF_BIND, we return 0.0.0.0
292 * so the binding can be contacted through all
293 * interfaces. If the hostname is HOST_SELF_CONNECT,
294 * we return 127.0.0.1 so the address can be connected
295 * to locally. If the hostname is HOST_ANY, we return
296 * no addresses because IP doesn't know how to specify
297 * a service without a host. And finally if we specify
298 * HOST_BROADCAST then we ask a tli fd to tell us what
299 * the broadcast addresses are for any udp
300 * interfaces on this machine.
301 */
302 if (args->arg.nd_hs->h_host == 0) {
303 _nderror = ND_NOHOST;
304 return (ND_NOHOST);
305 } else if ((strcmp(args->arg.nd_hs->h_host,
306 HOST_SELF_BIND) == 0)) {
307 haddrlist = localaddr;
308 } else if ((strcmp(args->arg.nd_hs->h_host,
309 HOST_SELF_CONNECT) == 0)) {
310 haddrlist = connectaddr;
311 } else if ((strcmp(args->arg.nd_hs->h_host,
312 LOCALHOST) == 0)) {
313 haddrlist = connectaddr;
314 } else if ((int)(dotnameaddr =
315 inet_addr(args->arg.nd_hs->h_host)) != -1) {
316 /*
317 * If the caller passed in a dot separated IP
318 * notation to netdir_getbyname, convert that
319 * back into address.
320 */
321
322 dotnamelist[0] = (char *)&dotnameaddr;
323 dotnamelist[1] = NULL;
324 haddrlist = dotnamelist;
325 } else if ((strcmp(args->arg.nd_hs->h_host,
326 HOST_BROADCAST) == 0)) {
327 /*
328 * Now that inaddrs and baddrlist are
329 * dynamically allocated, care must be
330 * taken in freeing up the
331 * memory at each 'return()' point.
332 *
333 * Early return protection (using
334 * FREE_return()) is needed only in NETDIR_BY
335 * cases because dynamic allocation is used
336 * when args->op_t == NETDIR_BY.
337 *
338 * Early return protection is not needed in
339 * haddrlist==0 conditionals because dynamic
340 * allocation guarantees haddrlist!=0.
341 *
342 * Early return protection is not needed in most
343 * servp!=0 conditionals because this is handled
344 * (and returned) first.
345 */
346 #define FREE_return(ret) \
347 { \
348 if (inaddrs) \
349 free(inaddrs); \
350 if (baddrlist) \
351 free(baddrlist); \
352 _nderror = ret; \
353 return (ret); \
354 }
355 int i, bnets;
356
357 bnets = getbroadcastnets(nconf, &inaddrs);
358 if (bnets == 0) {
359 _nderror = ND_NOHOST;
360 return (ND_NOHOST);
361 }
362 baddrlist = malloc((bnets+1)*sizeof (char *));
363 if (baddrlist == NULL)
364 FREE_return(ND_NOMEM);
365 for (i = 0; i < bnets; i++)
366 baddrlist[i] = (char *)&inaddrs[i];
367 baddrlist[i] = NULL;
368 haddrlist = baddrlist;
369 } else {
370 /* i.e. need to call a name service on this */
371 haddrlist = 0;
372 }
373
374 if (haddrlist && servp) {
375 int ret;
376 /*
377 * Convert h_addr_list into nd_addrlist.
378 * malloc's will be done, freed using
379 * netdir_free.
380 */
381 ret = hent2ndaddr(AF_INET, haddrlist, servp,
382 res->nd_alist);
383 FREE_return(ret);
384 }
385 break;
386
387
388 case NETDIR_BY6:
389 if (args->arg.nd_hs == 0) {
390 _nderror = ND_BADARG;
391 return (ND_BADARG);
392 }
393 /*
394 * If servname is NULL, return 0 as the port number.
395 * If servname is rpcbind, return 111 as the port number
396 * If servname is a number, return it back as the port
397 * number.
398 */
399 if (args->arg.nd_hs->h_serv == 0) {
400 *servp = htons(0);
401 } else if (strcmp(args->arg.nd_hs->h_serv,
402 "rpcbind") == 0) {
403 *servp = htons(111);
404 } else if (strspn(args->arg.nd_hs->h_serv, "0123456789")
405 == strlen(args->arg.nd_hs->h_serv)) {
406 *servp = htons(atoi(args->arg.nd_hs->h_serv));
407 } else {
408 /* i.e. need to call a name service on this */
409 servp = NULL;
410 }
411
412 /*
413 * If the hostname is HOST_SELF_BIND, we return ipv6
414 * localaddress so the binding can be contacted through
415 * all interfaces.
416 * If the hostname is HOST_SELF_CONNECT, we return
417 * ipv6 loopback address so the address can be connected
418 * to locally.
419 * If the hostname is HOST_ANY, we return no addresses
420 * because IP doesn't know how to specify a service
421 * without a host.
422 * And finally if we specify HOST_BROADCAST then we
423 * disallow since IPV6 does not have any
424 * broadcast concept.
425 */
426 if (args->arg.nd_hs->h_host == 0) {
427 return (ND_NOHOST);
428 } else if ((strcmp(args->arg.nd_hs->h_host,
429 HOST_SELF_BIND) == 0)) {
430 haddrlist = localaddr6;
431 } else if ((strcmp(args->arg.nd_hs->h_host,
432 HOST_SELF_CONNECT) == 0)) {
433 haddrlist = connectaddr6;
434 } else if ((strcmp(args->arg.nd_hs->h_host,
435 LOCALHOST) == 0)) {
436 haddrlist = connectaddr6;
437 } else if (strchr(args->arg.nd_hs->h_host, ':')
438 != NULL) {
439
440 /*
441 * If the caller passed in a dot separated IP notation
442 * to netdir_getbyname, convert that back into address.
443 */
444
445 if ((inet_pton(AF_INET6,
446 args->arg.nd_hs->h_host,
447 &v6nameaddr)) != 0) {
448 dotnamelist[0] = (char *)&v6nameaddr;
449 dotnamelist[1] = NULL;
450 haddrlist = dotnamelist;
451 }
452 else
453 /* not sure what to return */
454 return (ND_NOHOST);
455
456 } else if ((strcmp(args->arg.nd_hs->h_host,
457 HOST_BROADCAST) == 0)) {
458 /*
459 * Don't support broadcast in
460 * IPV6
461 */
462 return (ND_NOHOST);
463 } else {
464 /* i.e. need to call a name service on this */
465 haddrlist = 0;
466 }
467
468 if (haddrlist && servp) {
469 int ret;
470 /*
471 * Convert h_addr_list into nd_addrlist.
472 * malloc's will be done, freed
473 * using netdir_free.
474 */
475 ret = hent2ndaddr(AF_INET6, haddrlist,
476 servp, res->nd_alist);
477 FREE_return(ret);
478 }
479 break;
480
481
482 }
483
484 /*
485 * 2. Most common scenario. This is the way we ship /etc/netconfig.
486 * Emphasis on improving performance in the "if" part.
487 */
488 if (nconf->nc_nlookups == 0) {
489 struct hostent *he = NULL, *tmphe;
490 struct servent *se;
491 int ret;
492 nss_XbyY_buf_t *ndbuf4switch = 0;
493
494 switch (args->op_t) {
495
496 case NSS_HOST:
497
498 he = DOOR_GETHOSTBYNAME_R(args->arg.nss.host.name,
499 res->nss.host.hent, args->arg.nss.host.buf,
500 args->arg.nss.host.buflen,
501 res->nss.host.herrno_p);
502 if (he == NULL)
503 return (_nderror = ND_NOHOST);
504 return (_nderror = ND_OK);
505
506 case NSS_HOST6:
507
508 he = DOOR_GETIPNODEBYNAME_R(args->arg.nss.host6.name,
509 res->nss.host.hent, args->arg.nss.host.buf,
510 args->arg.nss.host6.buflen,
511 args->arg.nss.host6.af_family,
512 args->arg.nss.host6.flags,
513 res->nss.host.herrno_p);
514
515 if (he == NULL)
516 return (_nderror = ND_NOHOST);
517 return (_nderror = ND_OK);
518
519 case NSS_SERV:
520
521 se = _switch_getservbyname_r(args->arg.nss.serv.name,
522 args->arg.nss.serv.proto,
523 res->nss.serv, args->arg.nss.serv.buf,
524 args->arg.nss.serv.buflen);
525
526 _nderror = ND_OK;
527 if (se == 0)
528 _nderror = ND_NOSERV;
529 return (_nderror);
530
531 case NETDIR_BY:
532
533 if (servp == 0) {
534 char *proto = (strcmp(nconf->nc_proto,
535 NC_TCP) == 0) ? NC_TCP : NC_UDP;
536
537 /*
538 * We go through all this for just one port number,
539 * which is most often constant. How about linking in
540 * an indexed database of well-known ports in the name
541 * of performance ?
542 */
543 ndbuf4switch = _nss_XbyY_buf_alloc(
544 sizeof (struct servent), NSS_BUFLEN_SERVICES);
545 if (ndbuf4switch == 0)
546 FREE_return(ND_NOMEM);
547 se = _switch_getservbyname_r(args->arg.nd_hs->h_serv,
548 proto, ndbuf4switch->result,
549 ndbuf4switch->buffer, ndbuf4switch->buflen);
550 if (!se) {
551 NSS_XbyY_FREE(&ndbuf4switch);
552 FREE_return(ND_NOSERV);
553 }
554 server_port = se->s_port;
555 NSS_XbyY_FREE(&ndbuf4switch);
556 }
557
558 if (haddrlist == 0) {
559 int h_errnop = 0;
560
561 ndbuf4switch = _nss_XbyY_buf_alloc(
562 sizeof (struct hostent),
563 NSS_BUFLEN_HOSTS);
564 if (ndbuf4switch == 0) {
565 _nderror = ND_NOMEM;
566 return (ND_NOMEM);
567 }
568 /*
569 * Search the ipnodes (v6) path first,
570 * search will return the v4 addresses
571 * as v4mapped addresses.
572 */
573 if ((tmphe = DOOR_GETIPNODEBYNAME_R(
574 args->arg.nd_hs->h_host,
575 ndbuf4switch->result, ndbuf4switch->buffer,
576 ndbuf4switch->buflen, args->arg.nss.host6.af_family,
577 args->arg.nss.host6.flags, &h_errnop)) != NULL)
578 he = __mappedtov4(tmphe, &h_errnop);
579
580 if (he == NULL) {
581 /* Failover case, try hosts db for v4 address */
582 he = DOOR_GETHOSTBYNAME_R(
583 args->arg.nd_hs->h_host,
584 ndbuf4switch->result, ndbuf4switch->buffer,
585 ndbuf4switch->buflen, &h_errnop);
586 if (he == NULL) {
587 NSS_XbyY_FREE(&ndbuf4switch);
588 _nderror = h_errnop ?
589 __herrno2netdir(h_errnop) :
590 ND_NOHOST;
591 return (_nderror);
592 }
593 /*
594 * Convert h_addr_list into nd_addrlist.
595 * malloc's will be done, freed using
596 * netdir_free.
597 */
598 ret = hent2ndaddr(AF_INET, he->h_addr_list,
599 &server_port, res->nd_alist);
600 } else {
601 /*
602 * Convert h_addr_list into nd_addrlist.
603 * malloc's will be done, freed using
604 * netdir_free.
605 */
606 ret = hent2ndaddr(AF_INET, he->h_addr_list,
607 &server_port, res->nd_alist);
608 freehostent(he);
609 }
610
611 _nderror = ret;
612 NSS_XbyY_FREE(&ndbuf4switch);
613 return (ret);
614 } else {
615 int ret;
616 /*
617 * Convert h_addr_list into nd_addrlist.
618 * malloc's will be done, freed using netdir_free.
619 */
620 ret = hent2ndaddr(AF_INET, haddrlist,
621 &server_port, res->nd_alist);
622 FREE_return(ret);
623 }
624
625
626 case NETDIR_BY6:
627
628 if (servp == 0) {
629 char *proto = (strcmp(nconf->nc_proto,
630 NC_TCP) == 0) ? NC_TCP : NC_UDP;
631
632 /*
633 * We go through all this for just
634 * one port number,
635 * which is most often constant.
636 * How about linking in
637 * an indexed database of well-known
638 * ports in the name
639 * of performance ?
640 */
641 ndbuf4switch = _nss_XbyY_buf_alloc(
642 sizeof (struct servent),
643 NSS_BUFLEN_SERVICES);
644 if (ndbuf4switch == 0)
645 FREE_return(ND_NOMEM);
646 se = _switch_getservbyname_r(
647 args->arg.nd_hs->h_serv,
648 proto, ndbuf4switch->result,
649 ndbuf4switch->buffer, ndbuf4switch->buflen);
650 if (!se) {
651 NSS_XbyY_FREE(&ndbuf4switch);
652 FREE_return(ND_NOSERV);
653 }
654 server_port = se->s_port;
655 NSS_XbyY_FREE(&ndbuf4switch);
656 }
657
658 if (haddrlist == 0) {
659 int h_errnop = 0;
660
661 ndbuf4switch = _nss_XbyY_buf_alloc(
662 sizeof (struct hostent),
663 NSS_BUFLEN_HOSTS);
664 if (ndbuf4switch == 0) {
665 _nderror = ND_NOMEM;
666 return (ND_NOMEM);
667 }
668 he = DOOR_GETIPNODEBYNAME_R(
669 args->arg.nd_hs->h_host,
670 ndbuf4switch->result, ndbuf4switch->buffer,
671 ndbuf4switch->buflen,
672 args->arg.nss.host6.af_family,
673 args->arg.nss.host6.flags, &h_errnop);
674 if (he == NULL) {
675 NSS_XbyY_FREE(&ndbuf4switch);
676 _nderror = h_errnop ?
677 __herrno2netdir(h_errnop) :
678 ND_NOHOST;
679 return (_nderror);
680 }
681 /*
682 * Convert h_addr_list into nd_addrlist.
683 * malloc's will be done,
684 * freed using netdir_free.
685 */
686 ret = hent2ndaddr(AF_INET6,
687 ((struct hostent *)
688 (ndbuf4switch->result))->h_addr_list,
689 &server_port, res->nd_alist);
690 _nderror = ret;
691 NSS_XbyY_FREE(&ndbuf4switch);
692 return (ret);
693 } else {
694 int ret;
695 /*
696 * Convert h_addr_list into nd_addrlist.
697 * malloc's will be done,
698 * freed using netdir_free.
699 */
700 ret = hent2ndaddr(AF_INET6, haddrlist,
701 &server_port, res->nd_alist);
702 FREE_return(ret);
703 }
704
705 default:
706 _nderror = ND_BADARG;
707 return (ND_BADARG); /* should never happen */
708 }
709
710 } else {
711 /* haddrlist is no longer used, so clean up */
712 if (inaddrs)
713 free(inaddrs);
714 if (baddrlist)
715 free(baddrlist);
716 }
717
718 /*
719 * 3. We come this far only if nametoaddr libs are specified for
720 * inet transports and we are called by gethost/servbyname only.
721 */
722 switch (args->op_t) {
723 struct nd_hostserv service;
724 struct nd_addrlist *addrs;
725 int ret;
726
727 case NSS_HOST:
728
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);
735 }
736 /*
737 * convert addresses back into sockaddr for gethostbyname.
738 */
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);
747
748 case NSS_SERV:
749
750 if (args->arg.nss.serv.proto == NULL) {
751 /*
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".
760 */
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);
769 }
770 return (_nderror);
771 }
772
773 /*
774 * Third-parties should optimize their nametoaddr
775 * libraries for the HOST_SELF case.
776 */
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);
782 }
783 /*
784 * convert addresses back into servent for getservbyname.
785 */
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);
794
795 default:
796 _nderror = ND_BADARG;
797 return (ND_BADARG); /* should never happen */
798 }
799 }
800
801 /*
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.
805 *
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.
809 */
810 int
_get_hostserv_inetnetdir_byaddr(struct netconfig * nconf,struct nss_netdirbyaddr_in * args,union nss_netdirbyaddr_out * res)811 _get_hostserv_inetnetdir_byaddr(struct netconfig *nconf,
812 struct nss_netdirbyaddr_in *args, union nss_netdirbyaddr_out *res)
813 {
814 if (nconf == 0) {
815 _nderror = ND_BADARG;
816 return (_nderror);
817 }
818
819 /*
820 * 1. gethostbyaddr()/netdir_getbyaddr() special cases:
821 */
822 switch (args->op_t) {
823
824 case NSS_HOST:
825 /*
826 * Worth the performance gain: assuming a lot of inet apps
827 * actively use "127.0.0.1".
828 */
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);
843 }
844 break;
845
846 case NETDIR_BY:
847 case NETDIR_BY_NOSRV:
848 {
849 struct sockaddr_in *sin;
850
851 if (args->arg.nd_nbuf == NULL) {
852 _nderror = ND_BADARG;
853 return (_nderror);
854 }
855
856 /*
857 * Validate the address which was passed
858 * as the request.
859 */
860 /* LINTED pointer cast */
861 sin = (struct sockaddr_in *)args->arg.nd_nbuf->buf;
862
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);
868 }
869 }
870 break;
871
872 case NETDIR_BY6:
873 case NETDIR_BY_NOSRV6:
874 {
875 struct sockaddr_in6 *sin6;
876
877 if (args->arg.nd_nbuf == NULL) {
878 _nderror = ND_BADARG;
879 return (_nderror);
880 }
881
882 /*
883 * Validate the address which was passed
884 * as the request.
885 */
886 /* LINTED pointer cast */
887 sin6 = (struct sockaddr_in6 *)args->arg.nd_nbuf->buf;
888
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);
894 }
895 }
896 break;
897
898 }
899
900 /*
901 * 2. Most common scenario. This is the way we ship /etc/netconfig.
902 * Emphasis on improving performance in the "if" part.
903 */
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;
916
917 switch (args->op_t) {
918
919 case NSS_HOST:
920
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);
931
932
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);
939
940 if (he == 0)
941 return (ND_NOHOST);
942 return (ND_OK);
943
944
945 case NSS_SERV:
946
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);
951
952 if (se == 0)
953 _nderror = ND_NOSERV;
954 else
955 _nderror = ND_OK;
956 return (_nderror);
957
958 case NETDIR_BY:
959 case NETDIR_BY_NOSRV:
960
961 ndbuf4serv = _nss_XbyY_buf_alloc(sizeof (struct servent),
962 NSS_BUFLEN_SERVICES);
963 if (ndbuf4serv == 0) {
964 _nderror = ND_NOMEM;
965 return (_nderror);
966 }
967 /* LINTED pointer cast */
968 sa = (struct sockaddr_in *)(args->arg.nd_nbuf->buf);
969 addr4 = (struct in_addr *)&(sa->sin_addr);
970
971 /*
972 * if NETDIR_BY_NOSRV or port == 0 skip the service
973 * lookup.
974 */
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);
981 /*
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.
991 */
992 }
993 }
994
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);
1002 }
1003
1004 /*
1005 * Since we're going to search the ipnodes (v6) path first,
1006 * we need to treat the address as a v4mapped address.
1007 */
1008
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);
1015
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);
1029 }
1030 /*
1031 * Convert host names and service names into hostserv
1032 * pairs. malloc's will be done, freed using
1033 * netdir_free.
1034 */
1035 h_errnop = hsents2ndhostservs(he, se,
1036 sa->sin_port, res->nd_hslist);
1037 } else {
1038 /*
1039 * Convert host names and service names into hostserv
1040 * pairs. malloc's will be done, freed using
1041 * netdir_free.
1042 */
1043 h_errnop = hsents2ndhostservs(he, se,
1044 sa->sin_port, res->nd_hslist);
1045 freehostent(he);
1046 }
1047
1048 NSS_XbyY_FREE(&ndbuf4host);
1049 if (ndbuf4serv)
1050 NSS_XbyY_FREE(&ndbuf4serv);
1051 _nderror = __herrno2netdir(h_errnop);
1052 return (_nderror);
1053
1054 case NETDIR_BY6:
1055 case NETDIR_BY_NOSRV6:
1056
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);
1062 }
1063 /* LINTED pointer cast */
1064 sin6 = (struct sockaddr_in6 *)(args->arg.nd_nbuf->buf);
1065
1066 /*
1067 * if NETDIR_BY_NOSRV6 or port == 0 skip the service
1068 * lookup.
1069 */
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);
1076 /*
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.
1085 */
1086 }
1087 }
1088
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);
1096 }
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);
1107 }
1108 /*
1109 * Convert host names and service names into hostserv
1110 * pairs. malloc's will be done, freed using netdir_free.
1111 */
1112 h_errnop = hsents2ndhostservs(he, se,
1113 sin6->sin6_port, res->nd_hslist);
1114
1115 NSS_XbyY_FREE(&ndbuf4host);
1116 if (ndbuf4serv)
1117 NSS_XbyY_FREE(&ndbuf4serv);
1118 _nderror = __herrno2netdir(h_errnop);
1119 return (_nderror);
1120
1121 default:
1122 _nderror = ND_BADARG;
1123 return (_nderror); /* should never happen */
1124 }
1125
1126 }
1127 /*
1128 * 3. We come this far only if nametoaddr libs are specified for
1129 * inet transports and we are called by gethost/servbyname only.
1130 */
1131 switch (args->op_t) {
1132 struct netbuf nbuf;
1133 struct nd_hostservlist *addrs;
1134 struct sockaddr_in sa;
1135
1136 case NSS_HOST:
1137
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);
1149 }
1150 /*
1151 * convert the host-serv pairs into h_aliases and hent.
1152 */
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);
1159
1160 case NSS_SERV:
1161
1162 if (args->arg.nss.serv.proto == NULL) {
1163 /*
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".
1172 */
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);
1181 }
1182 return (_nderror);
1183 }
1184
1185 /*
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.
1193 */
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);
1203 }
1204 /*
1205 * convert the host-serv pairs into s_aliases and servent.
1206 */
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);
1212
1213 default:
1214 _nderror = ND_BADARG;
1215 return (_nderror); /* should never happen */
1216 }
1217 }
1218
1219 /*
1220 * Part II: Name Service Switch interfacing routines.
1221 */
1222
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);
1226
1227
1228 /*
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).
1234 */
1235 int
__nss2herrno(nss_status_t nsstat)1236 __nss2herrno(nss_status_t nsstat)
1237 {
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);
1250 }
1251 /* anything else */
1252 return (NO_RECOVERY);
1253 }
1254
1255 nss_status_t
_herrno2nss(int h_errno)1256 _herrno2nss(int h_errno)
1257 {
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);
1270 }
1271 }
1272
1273 static int
__herrno2netdir(int h_errnop)1274 __herrno2netdir(int h_errnop)
1275 {
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);
1290 }
1291 }
1292
1293 /*
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.
1298 */
1299
1300 struct hostent *
_switch_gethostbyname_r(const char * name,struct hostent * result,char * buffer,int buflen,int * h_errnop)1301 _switch_gethostbyname_r(const char *name, struct hostent *result, char *buffer,
1302 int buflen, int *h_errnop)
1303 {
1304 nss_XbyY_args_t arg;
1305 nss_status_t res;
1306
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 *h_errnop = arg.h_errno;
1314 if (arg.returnval != NULL)
1315 order_haddrlist_af(result->h_addrtype, result->h_addr_list);
1316 return ((struct hostent *)NSS_XbyY_FINI(&arg));
1317 }
1318
1319 struct hostent *
_switch_getipnodebyname_r(const char * name,struct hostent * result,char * buffer,int buflen,int af_family,int flags,int * h_errnop)1320 _switch_getipnodebyname_r(const char *name, struct hostent *result,
1321 char *buffer, int buflen, int af_family, int flags, int *h_errnop)
1322 {
1323 nss_XbyY_args_t arg;
1324 nss_status_t res;
1325
1326 NSS_XbyY_INIT(&arg, result, buffer, buflen, str2hostent6);
1327 arg.key.ipnode.name = name;
1328 arg.key.ipnode.af_family = af_family;
1329 arg.key.ipnode.flags = flags;
1330 arg.stayopen = 0;
1331 res = nss_search(&db_root_ipnodes, _nss_initf_ipnodes,
1332 NSS_DBOP_IPNODES_BYNAME, &arg);
1333 arg.status = res;
1334 *h_errnop = arg.h_errno;
1335 if (arg.returnval != NULL)
1336 order_haddrlist_af(result->h_addrtype, result->h_addr_list);
1337 return ((struct hostent *)NSS_XbyY_FINI(&arg));
1338 }
1339
1340 struct hostent *
_switch_gethostbyaddr_r(const char * addr,int len,int type,struct hostent * result,char * buffer,int buflen,int * h_errnop)1341 _switch_gethostbyaddr_r(const char *addr, int len, int type,
1342 struct hostent *result, char *buffer, int buflen, int *h_errnop)
1343 {
1344 nss_XbyY_args_t arg;
1345 nss_status_t res;
1346
1347 NSS_XbyY_INIT(&arg, result, buffer, buflen, str2hostent);
1348 arg.key.hostaddr.addr = addr;
1349 arg.key.hostaddr.len = len;
1350 arg.key.hostaddr.type = type;
1351 arg.stayopen = 0;
1352 res = nss_search(&db_root_hosts, _nss_initf_hosts,
1353 NSS_DBOP_HOSTS_BYADDR, &arg);
1354 arg.status = res;
1355 *h_errnop = arg.h_errno;
1356 return (struct hostent *)NSS_XbyY_FINI(&arg);
1357 }
1358
1359 struct hostent *
_switch_getipnodebyaddr_r(const char * addr,int len,int type,struct hostent * result,char * buffer,int buflen,int * h_errnop)1360 _switch_getipnodebyaddr_r(const char *addr, int len, int type,
1361 struct hostent *result, char *buffer, int buflen, int *h_errnop)
1362 {
1363 nss_XbyY_args_t arg;
1364 nss_status_t res;
1365
1366 NSS_XbyY_INIT(&arg, result, buffer, buflen, str2hostent6);
1367 arg.key.hostaddr.addr = addr;
1368 arg.key.hostaddr.len = len;
1369 arg.key.hostaddr.type = type;
1370 arg.stayopen = 0;
1371 res = nss_search(&db_root_ipnodes, _nss_initf_ipnodes,
1372 NSS_DBOP_IPNODES_BYADDR, &arg);
1373 arg.status = res;
1374 *h_errnop = arg.h_errno;
1375 return (struct hostent *)NSS_XbyY_FINI(&arg);
1376 }
1377
1378 static void
_nss_initf_services(nss_db_params_t * p)1379 _nss_initf_services(nss_db_params_t *p)
1380 {
1381 p->name = NSS_DBNAM_SERVICES;
1382 p->default_config = NSS_DEFCONF_SERVICES;
1383 }
1384
1385 struct servent *
_switch_getservbyname_r(const char * name,const char * proto,struct servent * result,char * buffer,int buflen)1386 _switch_getservbyname_r(const char *name, const char *proto,
1387 struct servent *result, char *buffer, int buflen)
1388 {
1389 nss_XbyY_args_t arg;
1390 nss_status_t res;
1391
1392 NSS_XbyY_INIT(&arg, result, buffer, buflen, str2servent);
1393 arg.key.serv.serv.name = name;
1394 arg.key.serv.proto = proto;
1395 arg.stayopen = 0;
1396 res = nss_search(&db_root_services, _nss_initf_services,
1397 NSS_DBOP_SERVICES_BYNAME, &arg);
1398 arg.status = res;
1399 return ((struct servent *)NSS_XbyY_FINI(&arg));
1400 }
1401
1402 struct servent *
_switch_getservbyport_r(int port,const char * proto,struct servent * result,char * buffer,int buflen)1403 _switch_getservbyport_r(int port, const char *proto, struct servent *result,
1404 char *buffer, int buflen)
1405 {
1406 nss_XbyY_args_t arg;
1407 nss_status_t res;
1408
1409 NSS_XbyY_INIT(&arg, result, buffer, buflen, str2servent);
1410 arg.key.serv.serv.port = port;
1411 arg.key.serv.proto = proto;
1412 arg.stayopen = 0;
1413 res = nss_search(&db_root_services, _nss_initf_services,
1414 NSS_DBOP_SERVICES_BYPORT, &arg);
1415 arg.status = res;
1416 return ((struct servent *)NSS_XbyY_FINI(&arg));
1417 }
1418
1419
1420 /*
1421 * Return values: 0 = success, 1 = parse error, 2 = erange ...
1422 * The structure pointer passed in is a structure in the caller's space
1423 * wherein the field pointers would be set to areas in the buffer if
1424 * need be. instring and buffer should be separate areas.
1425 *
1426 * Defined here because we need it and we (libnsl) cannot have a dependency
1427 * on libsocket (however, libsocket always depends on libnsl).
1428 */
1429 int
str2servent(const char * instr,int lenstr,void * ent,char * buffer,int buflen)1430 str2servent(const char *instr, int lenstr, void *ent, char *buffer, int buflen)
1431 {
1432 struct servent *serv = (struct servent *)ent;
1433 const char *p, *fieldstart, *limit, *namestart;
1434 ssize_t fieldlen, namelen = 0;
1435 char numbuf[12];
1436 char *numend;
1437
1438 if ((instr >= buffer && (buffer + buflen) > instr) ||
1439 (buffer >= instr && (instr + lenstr) > buffer)) {
1440 return (NSS_STR_PARSE_PARSE);
1441 }
1442
1443 p = instr;
1444 limit = p + lenstr;
1445
1446 while (p < limit && isspace(*p)) {
1447 p++;
1448 }
1449 namestart = p;
1450 while (p < limit && !isspace(*p)) {
1451 p++; /* Skip over the canonical name */
1452 }
1453 namelen = p - namestart;
1454
1455 if (buflen <= namelen) { /* not enough buffer */
1456 return (NSS_STR_PARSE_ERANGE);
1457 }
1458 (void) memcpy(buffer, namestart, namelen);
1459 buffer[namelen] = '\0';
1460 serv->s_name = buffer;
1461
1462 while (p < limit && isspace(*p)) {
1463 p++;
1464 }
1465
1466 fieldstart = p;
1467 do {
1468 if (p > limit || isspace(*p)) {
1469 /* Syntax error -- no port/proto */
1470 return (NSS_STR_PARSE_PARSE);
1471 }
1472 } while (*p++ != '/');
1473 fieldlen = p - fieldstart - 1;
1474 if (fieldlen == 0 || fieldlen >= sizeof (numbuf)) {
1475 /* Syntax error -- supposed number is empty or too long */
1476 return (NSS_STR_PARSE_PARSE);
1477 }
1478 (void) memcpy(numbuf, fieldstart, fieldlen);
1479 numbuf[fieldlen] = '\0';
1480 serv->s_port = htons((int)strtol(numbuf, &numend, 10));
1481 if (*numend != '\0') {
1482 /* Syntax error -- port number isn't a number */
1483 return (NSS_STR_PARSE_PARSE);
1484 }
1485
1486 fieldstart = p;
1487 while (p < limit && !isspace(*p)) {
1488 p++; /* Scan the protocol name */
1489 }
1490 fieldlen = p - fieldstart + 1; /* Include '\0' this time */
1491 if (fieldlen > buflen - namelen - 1) {
1492 return (NSS_STR_PARSE_ERANGE);
1493 }
1494 serv->s_proto = buffer + namelen + 1;
1495 (void) memcpy(serv->s_proto, fieldstart, fieldlen - 1);
1496 serv->s_proto[fieldlen - 1] = '\0';
1497
1498 while (p < limit && isspace(*p)) {
1499 p++;
1500 }
1501 /*
1502 * Although nss_files_XY_all calls us with # stripped,
1503 * we should be able to deal with it here in order to
1504 * be more useful.
1505 */
1506 if (p >= limit || *p == '#') { /* no aliases, no problem */
1507 char **ptr;
1508
1509 ptr = (char **)ROUND_UP(buffer + namelen + 1 + fieldlen,
1510 sizeof (char *));
1511 if ((char *)ptr >= buffer + buflen) {
1512 /* hope they don't try to peek in */
1513 serv->s_aliases = 0;
1514 return (NSS_STR_PARSE_ERANGE);
1515 } else {
1516 *ptr = 0;
1517 serv->s_aliases = ptr;
1518 return (NSS_STR_PARSE_SUCCESS);
1519 }
1520 }
1521 serv->s_aliases = _nss_netdb_aliases(p, (int)(lenstr - (p - instr)),
1522 buffer + namelen + 1 + fieldlen,
1523 (int)(buflen - namelen - 1 - fieldlen));
1524 return (NSS_STR_PARSE_SUCCESS);
1525 }
1526
1527 /*
1528 * Part III: All `n sundry routines that are useful only in this
1529 * module. In the interest of keeping this source file shorter,
1530 * we would create them a new module only if the linker allowed
1531 * "library-static" functions.
1532 *
1533 * Routines to order addresses based on local interfaces and netmasks,
1534 * to get and check reserved ports, and to get broadcast nets.
1535 */
1536
1537 union __v4v6addr {
1538 struct in6_addr in6;
1539 struct in_addr in4;
1540 };
1541
1542 struct __ifaddr {
1543 sa_family_t af;
1544 union __v4v6addr addr;
1545 union __v4v6addr mask;
1546 };
1547
1548 struct ifinfo {
1549 int count;
1550 struct __ifaddr *addresses;
1551 };
1552
1553 typedef enum {ADDR_ONLINK = 0, ADDR_OFFLINK} addr_class_t;
1554 #define ADDR_NUMCLASSES 2
1555
1556 typedef enum {IF_ADDR, IF_MASK} __ifaddr_type;
1557 static int __inet_ifassign(sa_family_t, struct __ifaddr *, __ifaddr_type,
1558 void *);
1559 int __inet_address_is_local_af(void *, sa_family_t, void *);
1560
1561 #define ifaf(index) (localinfo->addresses[index].af)
1562 #define ifaddr4(index) (localinfo->addresses[index].addr.in4)
1563 #define ifaddr6(index) (localinfo->addresses[index].addr.in6)
1564 #define ifmask4(index) (localinfo->addresses[index].mask.in4)
1565 #define ifmask6(index) (localinfo->addresses[index].mask.in6)
1566 #define ifinfosize(n) (sizeof (struct ifinfo) + (n)*sizeof (struct __ifaddr))
1567
1568 #define lifraddrp(lifr) ((lifr.lifr_addr.ss_family == AF_INET6) ? \
1569 (void *)&((struct sockaddr_in6 *)&lifr.lifr_addr)->sin6_addr : \
1570 (void *)&((struct sockaddr_in *)&lifr.lifr_addr)->sin_addr)
1571
1572 #define ifassign(lifr, index, type) \
1573 __inet_ifassign(lifr.lifr_addr.ss_family, \
1574 &localinfo->addresses[index], type, \
1575 lifraddrp(lifr))
1576
1577 /*
1578 * The number of nanoseconds the order_haddrlist_inet() function waits
1579 * to retreive IP interface information. The default is five minutes.
1580 */
1581 #define IFINFOTIMEOUT ((hrtime_t)300 * NANOSEC)
1582
1583 /*
1584 * Sort the addresses in haddrlist. Since the sorting algorithms are
1585 * address-family specific, the work is done in the address-family
1586 * specific order_haddrlist_<family> functions.
1587 *
1588 * Do not sort addresses if SORT_ADDRS variable is set to NO or FALSE
1589 * in the configuration file /etc/default/nss. This is useful in case
1590 * the order of addresses returned by the nameserver needs to be
1591 * maintained. (DNS round robin feature is one example)
1592 */
1593 void
order_haddrlist_af(sa_family_t af,char ** haddrlist)1594 order_haddrlist_af(sa_family_t af, char **haddrlist)
1595 {
1596 size_t addrcount;
1597 char **addrptr;
1598 static boolean_t checksortcfg = B_TRUE;
1599 static boolean_t nosort = B_FALSE;
1600 static mutex_t checksortcfg_lock = DEFAULTMUTEX;
1601
1602 if (haddrlist == NULL)
1603 return;
1604
1605 /*
1606 * Check if SORT_ADDRS is set to NO or FALSE in the configuration
1607 * file. We do not have to sort addresses in that case.
1608 */
1609 (void) mutex_lock(&checksortcfg_lock);
1610 if (checksortcfg == B_TRUE) {
1611 checksortcfg = B_FALSE;
1612 nosort = _read_nsw_file();
1613 }
1614 (void) mutex_unlock(&checksortcfg_lock);
1615
1616 if (nosort)
1617 return;
1618
1619 /* Count the addresses to sort */
1620 addrcount = 0;
1621 for (addrptr = haddrlist; *addrptr != NULL; addrptr++)
1622 addrcount++;
1623
1624 /*
1625 * If there's only one address or no addresses to sort, then
1626 * there's nothing for us to do.
1627 */
1628 if (addrcount <= 1)
1629 return;
1630
1631 /* Call the address-family specific sorting functions. */
1632 switch (af) {
1633 case AF_INET:
1634 order_haddrlist_inet(haddrlist, addrcount);
1635 break;
1636 case AF_INET6:
1637 order_haddrlist_inet6(haddrlist, addrcount);
1638 break;
1639 default:
1640 break;
1641 }
1642 }
1643
1644 /*
1645 * Move any local (on-link) addresses toward the beginning of haddrlist.
1646 * The order within these two classes is preserved.
1647 *
1648 * The interface list is retrieved no more often than every
1649 * IFINFOTIMEOUT nanoseconds. Access to the interface list is
1650 * protected by an RW lock.
1651 *
1652 * If this function encounters an error, haddrlist is unaltered.
1653 */
1654 static void
order_haddrlist_inet(char ** haddrlist,size_t addrcount)1655 order_haddrlist_inet(char **haddrlist, size_t addrcount)
1656 {
1657 static struct ifinfo *localinfo = NULL;
1658 static hrtime_t then = 0; /* the last time localinfo was updated */
1659 hrtime_t now;
1660 static rwlock_t localinfo_lock = DEFAULTRWLOCK;
1661 uint8_t *sortbuf;
1662 size_t sortbuf_size;
1663 struct in_addr **inaddrlist = (struct in_addr **)haddrlist;
1664 struct in_addr **sorted;
1665 struct in_addr **classnext[ADDR_NUMCLASSES];
1666 uint_t classcount[ADDR_NUMCLASSES];
1667 addr_class_t *sortclass;
1668 int i;
1669 int rc;
1670
1671
1672 /*
1673 * The classes in the sortclass array correspond to the class
1674 * of the address in the haddrlist list of the same index.
1675 * The classes are:
1676 *
1677 * ADDR_ONLINK on-link address
1678 * ADDR_OFFLINK off-link address
1679 */
1680 sortbuf_size = addrcount *
1681 (sizeof (struct in_addr *) + sizeof (addr_class_t));
1682 if ((sortbuf = malloc(sortbuf_size)) == NULL)
1683 return;
1684 /* LINTED pointer cast */
1685 sorted = (struct in_addr **)sortbuf;
1686 /* LINTED pointer cast */
1687 sortclass = (addr_class_t *)(sortbuf +
1688 (addrcount * sizeof (struct in_addr *)));
1689
1690 /*
1691 * Get a read lock, and check if the interface information
1692 * is too old.
1693 */
1694 (void) rw_rdlock(&localinfo_lock);
1695 now = gethrtime();
1696 if (localinfo == NULL || ((now - then) > IFINFOTIMEOUT)) {
1697 /* Need to update I/F info. Upgrade to write lock. */
1698 (void) rw_unlock(&localinfo_lock);
1699 (void) rw_wrlock(&localinfo_lock);
1700 /*
1701 * Another thread might have updated "then" between
1702 * the rw_unlock() and rw_wrlock() calls above, so
1703 * re-check the timeout.
1704 */
1705 if (localinfo == NULL || ((now - then) > IFINFOTIMEOUT)) {
1706 if (localinfo != NULL)
1707 free(localinfo);
1708 if ((localinfo = get_local_info()) == NULL) {
1709 (void) rw_unlock(&localinfo_lock);
1710 free(sortbuf);
1711 return;
1712 }
1713 then = now;
1714 }
1715 /* Downgrade to read lock */
1716 (void) rw_unlock(&localinfo_lock);
1717 (void) rw_rdlock(&localinfo_lock);
1718 /*
1719 * Another thread may have updated the I/F info,
1720 * so verify that the 'localinfo' pointer still
1721 * is non-NULL.
1722 */
1723 if (localinfo == NULL) {
1724 (void) rw_unlock(&localinfo_lock);
1725 free(sortbuf);
1726 return;
1727 }
1728 }
1729
1730 /*
1731 * Classify the addresses. We also maintain the classcount
1732 * array to keep track of the number of addresses in each
1733 * class.
1734 */
1735 (void) memset(classcount, 0, sizeof (classcount));
1736 for (i = 0; i < addrcount; i++) {
1737 if (__inet_address_is_local_af(localinfo, AF_INET,
1738 inaddrlist[i]))
1739 sortclass[i] = ADDR_ONLINK;
1740 else
1741 sortclass[i] = ADDR_OFFLINK;
1742 classcount[sortclass[i]]++;
1743 }
1744
1745 /* Don't need the interface list anymore in this call */
1746 (void) rw_unlock(&localinfo_lock);
1747
1748 /*
1749 * Each element in the classnext array points to the next
1750 * element for that class in the sorted address list. 'rc' is
1751 * the running count of elements as we sum the class
1752 * sub-totals.
1753 */
1754 for (rc = 0, i = 0; i < ADDR_NUMCLASSES; i++) {
1755 classnext[i] = &sorted[rc];
1756 rc += classcount[i];
1757 }
1758
1759 /* Now for the actual rearrangement of the addresses */
1760 for (i = 0; i < addrcount; i++) {
1761 *(classnext[sortclass[i]]) = inaddrlist[i];
1762 classnext[sortclass[i]]++;
1763 }
1764
1765 /* Copy the sorted list to inaddrlist */
1766 (void) memcpy(inaddrlist, sorted,
1767 addrcount * sizeof (struct in_addr *));
1768 free(sortbuf);
1769 }
1770
1771 /*
1772 * This function implements the IPv6 Default Address Selection's
1773 * destination address ordering mechanism. The algorithm is described
1774 * in getaddrinfo(3SOCKET).
1775 */
1776 static void
order_haddrlist_inet6(char ** haddrlist,size_t addrcount)1777 order_haddrlist_inet6(char **haddrlist, size_t addrcount)
1778 {
1779 struct dstinforeq *dinfo, *dinfoptr;
1780 struct in6_addr **in6addrlist = (struct in6_addr **)haddrlist;
1781 struct in6_addr **in6addr;
1782
1783 if ((dinfo = calloc(addrcount, sizeof (struct dstinforeq))) == NULL)
1784 return;
1785
1786 /* Initialize the dstinfo array we'll use for SIOCGDSTINFO */
1787 dinfoptr = dinfo;
1788 for (in6addr = in6addrlist; *in6addr != NULL; in6addr++) {
1789 dinfoptr->dir_daddr = **in6addr;
1790 dinfoptr++;
1791 }
1792
1793 if (nss_strioctl(AF_INET6, SIOCGDSTINFO, dinfo,
1794 addrcount * sizeof (struct dstinforeq)) < 0) {
1795 free(dinfo);
1796 return;
1797 }
1798
1799 /* Sort the dinfo array */
1800 qsort(dinfo, addrcount, sizeof (struct dstinforeq), dstcmp);
1801
1802 /* Copy the addresses back into in6addrlist */
1803 dinfoptr = dinfo;
1804 for (in6addr = in6addrlist; *in6addr != NULL; in6addr++) {
1805 **in6addr = dinfoptr->dir_daddr;
1806 dinfoptr++;
1807 }
1808
1809 free(dinfo);
1810 }
1811
1812 /*
1813 * Determine number of leading bits that are common between two addresses.
1814 * Only consider bits which fall within the prefix length plen.
1815 */
1816 static uint_t
ip_addr_commonbits_v6(const in6_addr_t * a1,const in6_addr_t * a2)1817 ip_addr_commonbits_v6(const in6_addr_t *a1, const in6_addr_t *a2)
1818 {
1819 uint_t bits;
1820 uint_t i;
1821 uint32_t diff; /* Bits that differ */
1822
1823 for (i = 0; i < 4; i++) {
1824 if (a1->_S6_un._S6_u32[i] != a2->_S6_un._S6_u32[i])
1825 break;
1826 }
1827 bits = i * 32;
1828
1829 if (bits == IPV6_ABITS)
1830 return (IPV6_ABITS);
1831
1832 /*
1833 * Find number of leading common bits in the word which might
1834 * have some common bits by searching for the first one from the left
1835 * in the xor of the two addresses.
1836 */
1837 diff = ntohl(a1->_S6_un._S6_u32[i] ^ a2->_S6_un._S6_u32[i]);
1838 if (diff & 0xffff0000ul)
1839 diff >>= 16;
1840 else
1841 bits += 16;
1842 if (diff & 0xff00)
1843 diff >>= 8;
1844 else
1845 bits += 8;
1846 if (diff & 0xf0)
1847 diff >>= 4;
1848 else
1849 bits += 4;
1850 if (diff & 0xc)
1851 diff >>= 2;
1852 else
1853 bits += 2;
1854 if (!(diff & 2))
1855 bits++;
1856
1857 /*
1858 * We don't need to shift and check for the last bit. The
1859 * check for IPV6_ABITS above would have caught that.
1860 */
1861
1862 return (bits);
1863 }
1864
1865
1866 /*
1867 * The following group of functions named rule_*() are individual
1868 * sorting rules for the AF_INET6 address sorting algorithm. The
1869 * functions compare two addresses (described by two dstinforeq
1870 * structures), and determines if one is "greater" than the other, or
1871 * if the two are equal according to that rule.
1872 */
1873 typedef int (*rulef_t)(const struct dstinforeq *, const struct dstinforeq *);
1874
1875 /*
1876 * These values of these constants are no accident. Since qsort()
1877 * implements the AF_INET6 address sorting, the comparison function
1878 * must return an integer less than, equal to, or greater than zero to
1879 * indicate if the first address is considered "less than", "equal
1880 * to", or "greater than" the second one. Since we want the best
1881 * addresses first on the list, "less than" is considered preferrable.
1882 */
1883 #define RULE_PREFER_DA -1
1884 #define RULE_PREFER_DB 1
1885 #define RULE_EQUAL 0
1886
1887 /* Prefer the addresses that is reachable. */
1888 static int
rule_reachable(const struct dstinforeq * da,const struct dstinforeq * db)1889 rule_reachable(const struct dstinforeq *da, const struct dstinforeq *db)
1890 {
1891 if (da->dir_dreachable == db->dir_dreachable)
1892 return (RULE_EQUAL);
1893 if (da->dir_dreachable)
1894 return (RULE_PREFER_DA);
1895 return (RULE_PREFER_DB);
1896 }
1897
1898 /* Prefer the address whose scope matches that of its source address. */
1899 static int
rule_matchscope(const struct dstinforeq * da,const struct dstinforeq * db)1900 rule_matchscope(const struct dstinforeq *da, const struct dstinforeq *db)
1901 {
1902 boolean_t da_scope_match, db_scope_match;
1903
1904 da_scope_match = da->dir_dscope == da->dir_sscope;
1905 db_scope_match = db->dir_dscope == db->dir_sscope;
1906
1907 if (da_scope_match == db_scope_match)
1908 return (RULE_EQUAL);
1909 if (da_scope_match)
1910 return (RULE_PREFER_DA);
1911 return (RULE_PREFER_DB);
1912 }
1913
1914 /* Avoid the address with the link local source address. */
1915 static int
rule_avoidlinklocal(const struct dstinforeq * da,const struct dstinforeq * db)1916 rule_avoidlinklocal(const struct dstinforeq *da, const struct dstinforeq *db)
1917 {
1918 if (da->dir_sscope == IP6_SCOPE_LINKLOCAL &&
1919 da->dir_dscope != IP6_SCOPE_LINKLOCAL &&
1920 db->dir_sscope != IP6_SCOPE_LINKLOCAL)
1921 return (RULE_PREFER_DB);
1922 if (db->dir_sscope == IP6_SCOPE_LINKLOCAL &&
1923 db->dir_dscope != IP6_SCOPE_LINKLOCAL &&
1924 da->dir_sscope != IP6_SCOPE_LINKLOCAL)
1925 return (RULE_PREFER_DA);
1926 return (RULE_EQUAL);
1927 }
1928
1929 /* Prefer the address whose source address isn't deprecated. */
1930 static int
rule_deprecated(const struct dstinforeq * da,const struct dstinforeq * db)1931 rule_deprecated(const struct dstinforeq *da, const struct dstinforeq *db)
1932 {
1933 if (da->dir_sdeprecated == db->dir_sdeprecated)
1934 return (RULE_EQUAL);
1935 if (db->dir_sdeprecated)
1936 return (RULE_PREFER_DA);
1937 return (RULE_PREFER_DB);
1938 }
1939
1940 /* Prefer the address whose label matches that of its source address. */
1941 static int
rule_label(const struct dstinforeq * da,const struct dstinforeq * db)1942 rule_label(const struct dstinforeq *da, const struct dstinforeq *db)
1943 {
1944 if (da->dir_labelmatch == db->dir_labelmatch)
1945 return (RULE_EQUAL);
1946 if (da->dir_labelmatch)
1947 return (RULE_PREFER_DA);
1948 return (RULE_PREFER_DB);
1949 }
1950
1951 /* Prefer the address with the higher precedence. */
1952 static int
rule_precedence(const struct dstinforeq * da,const struct dstinforeq * db)1953 rule_precedence(const struct dstinforeq *da, const struct dstinforeq *db)
1954 {
1955 if (da->dir_precedence == db->dir_precedence)
1956 return (RULE_EQUAL);
1957 if (da->dir_precedence > db->dir_precedence)
1958 return (RULE_PREFER_DA);
1959 return (RULE_PREFER_DB);
1960 }
1961
1962 /* Prefer the address whose output interface isn't an IP tunnel */
1963 static int
rule_native(const struct dstinforeq * da,const struct dstinforeq * db)1964 rule_native(const struct dstinforeq *da, const struct dstinforeq *db)
1965 {
1966 boolean_t isatun, isbtun;
1967
1968 /* Get the common case out of the way early */
1969 if (da->dir_dmactype == db->dir_dmactype)
1970 return (RULE_EQUAL);
1971
1972 isatun = da->dir_dmactype == DL_IPV4 || da->dir_dmactype == DL_IPV6;
1973 isbtun = db->dir_dmactype == DL_IPV4 || db->dir_dmactype == DL_IPV6;
1974
1975 if (isatun == isbtun)
1976 return (RULE_EQUAL);
1977 if (isbtun)
1978 return (RULE_PREFER_DA);
1979 return (RULE_PREFER_DB);
1980 }
1981
1982 /* Prefer the address with the smaller scope. */
1983 static int
rule_scope(const struct dstinforeq * da,const struct dstinforeq * db)1984 rule_scope(const struct dstinforeq *da, const struct dstinforeq *db)
1985 {
1986 if (da->dir_dscope == db->dir_dscope)
1987 return (RULE_EQUAL);
1988 if (da->dir_dscope < db->dir_dscope)
1989 return (RULE_PREFER_DA);
1990 return (RULE_PREFER_DB);
1991 }
1992
1993 /*
1994 * Prefer the address that has the most leading bits in common with its
1995 * source address.
1996 */
1997 static int
rule_prefix(const struct dstinforeq * da,const struct dstinforeq * db)1998 rule_prefix(const struct dstinforeq *da, const struct dstinforeq *db)
1999 {
2000 uint_t da_commonbits, db_commonbits;
2001 boolean_t da_isipv4, db_isipv4;
2002
2003 da_isipv4 = IN6_IS_ADDR_V4MAPPED(&da->dir_daddr);
2004 db_isipv4 = IN6_IS_ADDR_V4MAPPED(&db->dir_daddr);
2005
2006 /*
2007 * At this point, the order doesn't matter if the two addresses
2008 * aren't of the same address family.
2009 */
2010 if (da_isipv4 != db_isipv4)
2011 return (RULE_EQUAL);
2012
2013 da_commonbits = ip_addr_commonbits_v6(&da->dir_daddr, &da->dir_saddr);
2014 db_commonbits = ip_addr_commonbits_v6(&db->dir_daddr, &db->dir_saddr);
2015
2016 if (da_commonbits > db_commonbits)
2017 return (RULE_PREFER_DA);
2018 if (da_commonbits < db_commonbits)
2019 return (RULE_PREFER_DB);
2020 return (RULE_EQUAL);
2021 }
2022
2023 /*
2024 * This is the function passed to qsort() that does the AF_INET6
2025 * address comparisons. It compares two addresses using a list of
2026 * rules. The rules are applied in order until one prefers one
2027 * address over the other.
2028 */
2029 static int
dstcmp(const void * da,const void * db)2030 dstcmp(const void *da, const void *db)
2031 {
2032 int index, result;
2033 rulef_t rules[] = {
2034 rule_reachable,
2035 rule_matchscope,
2036 rule_avoidlinklocal,
2037 rule_deprecated,
2038 rule_label,
2039 rule_precedence,
2040 rule_native,
2041 rule_scope,
2042 rule_prefix,
2043 NULL
2044 };
2045
2046 result = 0;
2047 for (index = 0; rules[index] != NULL; index++) {
2048 result = (rules[index])(da, db);
2049 if (result != RULE_EQUAL)
2050 break;
2051 }
2052
2053 return (result);
2054 }
2055
2056 /*
2057 * Given haddrlist and a port number, mallocs and populates a new
2058 * nd_addrlist. The new nd_addrlist maintains the order of the addresses
2059 * in haddrlist, which have already been sorted by order_haddrlist_inet()
2060 * or order_haddrlist_inet6(). For IPv6 this function filters out
2061 * IPv4-mapped IPv6 addresses.
2062 */
2063 int
hent2ndaddr(int af,char ** haddrlist,int * servp,struct nd_addrlist ** nd_alist)2064 hent2ndaddr(int af, char **haddrlist, int *servp, struct nd_addrlist **nd_alist)
2065 {
2066 struct nd_addrlist *result;
2067 int num;
2068 struct netbuf *na;
2069 struct sockaddr_in *sinbuf, *sin;
2070 struct sockaddr_in6 *sin6buf, *sin6;
2071 struct in_addr **inaddr, **inaddrlist;
2072 struct in6_addr **in6addr, **in6addrlist;
2073
2074 /* Address count */
2075 num = 0;
2076 if (af == AF_INET6) {
2077 in6addrlist = (struct in6_addr **)haddrlist;
2078
2079 /*
2080 * Exclude IPv4-mapped IPv6 addresses from the count, as
2081 * these are not included in the nd_addrlist we return.
2082 */
2083 for (in6addr = in6addrlist; *in6addr != NULL; in6addr++)
2084 if (!IN6_IS_ADDR_V4MAPPED(*in6addr))
2085 num++;
2086 } else {
2087 inaddrlist = (struct in_addr **)haddrlist;
2088
2089 for (inaddr = inaddrlist; *inaddr != NULL; inaddr++)
2090 num++;
2091 }
2092 if (num == 0)
2093 return (ND_NOHOST);
2094
2095 result = malloc(sizeof (struct nd_addrlist));
2096 if (result == 0)
2097 return (ND_NOMEM);
2098
2099 result->n_cnt = num;
2100 result->n_addrs = calloc(num, sizeof (struct netbuf));
2101 if (result->n_addrs == 0) {
2102 free(result);
2103 return (ND_NOMEM);
2104 }
2105
2106 na = result->n_addrs;
2107 if (af == AF_INET) {
2108 sinbuf = calloc(num, sizeof (struct sockaddr_in));
2109 if (sinbuf == NULL) {
2110 free(result->n_addrs);
2111 free(result);
2112 return (ND_NOMEM);
2113 }
2114
2115 sin = sinbuf;
2116 for (inaddr = inaddrlist; *inaddr != NULL; inaddr++) {
2117 na->len = na->maxlen = sizeof (struct sockaddr_in);
2118 na->buf = (char *)sin;
2119 sin->sin_family = AF_INET;
2120 sin->sin_addr = **inaddr;
2121 sin->sin_port = *servp;
2122 na++;
2123 sin++;
2124 }
2125 } else if (af == AF_INET6) {
2126 sin6buf = calloc(num, sizeof (struct sockaddr_in6));
2127 if (sin6buf == NULL) {
2128 free(result->n_addrs);
2129 free(result);
2130 return (ND_NOMEM);
2131 }
2132
2133 sin6 = sin6buf;
2134 for (in6addr = in6addrlist; *in6addr != NULL; in6addr++) {
2135 if (IN6_IS_ADDR_V4MAPPED(*in6addr))
2136 continue;
2137
2138 na->len = na->maxlen = sizeof (struct sockaddr_in6);
2139 na->buf = (char *)sin6;
2140 sin6->sin6_family = AF_INET6;
2141 sin6->sin6_addr = **in6addr;
2142 sin6->sin6_port = *servp;
2143 na++;
2144 sin6++;
2145 }
2146 }
2147 *(nd_alist) = result;
2148 return (ND_OK);
2149 }
2150
2151 /*
2152 * Given a hostent and a servent, mallocs and populates
2153 * a new nd_hostservlist with host and service names.
2154 *
2155 * We could be passed in a NULL servent, in which case stringify port.
2156 */
2157 int
hsents2ndhostservs(struct hostent * he,struct servent * se,ushort_t port,struct nd_hostservlist ** hslist)2158 hsents2ndhostservs(struct hostent *he, struct servent *se,
2159 ushort_t port, struct nd_hostservlist **hslist)
2160 {
2161 struct nd_hostservlist *result;
2162 struct nd_hostserv *hs;
2163 int hosts, servs, i, j;
2164 char **hn, **sn;
2165
2166 if ((result = malloc(sizeof (struct nd_hostservlist))) == 0)
2167 return (ND_NOMEM);
2168
2169 /*
2170 * We initialize the counters to 1 rather than zero because
2171 * we have to count the "official" name as well as the aliases.
2172 */
2173 for (hn = he->h_aliases, hosts = 1; hn && *hn; hn++, hosts++) {};
2174 if (se) {
2175 for (sn = se->s_aliases, servs = 1; sn && *sn; sn++, servs++) {
2176 };
2177 } else
2178 servs = 1;
2179
2180 if ((hs = calloc(hosts * servs, sizeof (struct nd_hostserv))) == 0) {
2181 free(result);
2182 return (ND_NOMEM);
2183 }
2184
2185 result->h_cnt = servs * hosts;
2186 result->h_hostservs = hs;
2187
2188 for (i = 0, hn = he->h_aliases; i < hosts; i++) {
2189 sn = se ? se->s_aliases : NULL;
2190
2191 for (j = 0; j < servs; j++) {
2192 if (i == 0)
2193 hs->h_host = strdup(he->h_name);
2194 else
2195 hs->h_host = strdup(*hn);
2196 if (j == 0) {
2197 if (se)
2198 hs->h_serv = strdup(se->s_name);
2199 else {
2200 /* Convert to a number string */
2201 char stmp[16];
2202
2203 (void) sprintf(stmp, "%d", port);
2204 hs->h_serv = strdup(stmp);
2205 }
2206 } else
2207 hs->h_serv = strdup(*sn++);
2208
2209 if ((hs->h_host == 0) || (hs->h_serv == 0)) {
2210 free(result->h_hostservs);
2211 free(result);
2212 return (ND_NOMEM);
2213 }
2214 hs++;
2215 }
2216 if (i)
2217 hn++;
2218 }
2219 *(hslist) = result;
2220 return (ND_OK);
2221 }
2222
2223 /*
2224 * Process results from nd_addrlist ( returned by netdir_getbyname)
2225 * into a hostent using buf.
2226 * *** ASSUMES that nd_addrlist->n_addrs->buf contains IP addresses in
2227 * sockaddr_in's ***
2228 */
2229 int
ndaddr2hent(int af,const char * nam,struct nd_addrlist * addrs,struct hostent * result,char * buffer,int buflen)2230 ndaddr2hent(int af, const char *nam, struct nd_addrlist *addrs,
2231 struct hostent *result, char *buffer, int buflen)
2232 {
2233 int i, count;
2234 struct in_addr *addrp;
2235 struct in6_addr *addr6p;
2236 char **addrvec;
2237 struct netbuf *na;
2238 size_t len;
2239
2240 result->h_name = buffer;
2241 result->h_addrtype = af;
2242 result->h_length = (af == AF_INET) ? sizeof (*addrp):
2243 sizeof (*addr6p);
2244
2245 /*
2246 * Build addrlist at start of buffer (after name); store the
2247 * addresses themselves at the end of the buffer.
2248 */
2249 len = strlen(nam) + 1;
2250 addrvec = (char **)ROUND_UP(buffer + len, sizeof (*addrvec));
2251 result->h_addr_list = addrvec;
2252
2253 if (af == AF_INET) {
2254 addrp = (struct in_addr *)ROUND_DOWN(buffer + buflen,
2255 sizeof (*addrp));
2256
2257 count = addrs->n_cnt;
2258 if ((char *)(&addrvec[count + 1]) > (char *)(&addrp[-count]))
2259 return (ND_NOMEM);
2260
2261 (void) memcpy(buffer, nam, len);
2262
2263 for (na = addrs->n_addrs, i = 0; i < count; na++, i++) {
2264 --addrp;
2265 (void) memcpy(addrp,
2266 /* LINTED pointer cast */
2267 &((struct sockaddr_in *)na->buf)->sin_addr,
2268 sizeof (*addrp));
2269 *addrvec++ = (char *)addrp;
2270 }
2271 } else {
2272 addr6p = (struct in6_addr *)ROUND_DOWN(buffer + buflen,
2273 sizeof (*addr6p));
2274
2275 count = addrs->n_cnt;
2276 if ((char *)(&addrvec[count + 1]) > (char *)(&addr6p[-count]))
2277 return (ND_NOMEM);
2278
2279 (void) memcpy(buffer, nam, len);
2280
2281 for (na = addrs->n_addrs, i = 0; i < count; na++, i++) {
2282 --addr6p;
2283 (void) memcpy(addr6p,
2284 /* LINTED pointer cast */
2285 &((struct sockaddr_in6 *)na->buf)->sin6_addr,
2286 sizeof (*addr6p));
2287 *addrvec++ = (char *)addr6p;
2288 }
2289 }
2290 *addrvec = 0;
2291 result->h_aliases = addrvec;
2292
2293 return (ND_OK);
2294 }
2295
2296 /*
2297 * Process results from nd_addrlist ( returned by netdir_getbyname)
2298 * into a servent using buf.
2299 */
2300 int
ndaddr2srent(const char * name,const char * proto,ushort_t port,struct servent * result,char * buffer,int buflen)2301 ndaddr2srent(const char *name, const char *proto, ushort_t port,
2302 struct servent *result, char *buffer, int buflen)
2303 {
2304 size_t i;
2305 char *bufend = (buffer + buflen);
2306
2307 result->s_port = (int)port;
2308
2309 result->s_aliases =
2310 (char **)ROUND_UP(buffer, sizeof (char *));
2311 result->s_aliases[0] = NULL;
2312 buffer = (char *)&result->s_aliases[1];
2313 result->s_name = buffer;
2314 i = strlen(name) + 1;
2315 if ((buffer + i) > bufend)
2316 return (ND_NOMEM);
2317 (void) memcpy(buffer, name, i);
2318 buffer += i;
2319
2320 result->s_proto = buffer;
2321 i = strlen(proto) + 1;
2322 if ((buffer + i) > bufend)
2323 return (ND_NOMEM);
2324 (void) memcpy(buffer, proto, i);
2325 buffer += i;
2326
2327 return (ND_OK);
2328 }
2329
2330 /*
2331 * Process results from nd_hostservlist ( returned by netdir_getbyaddr)
2332 * into a hostent using buf.
2333 * *** ASSUMES that nd_buf->buf is a sockaddr_in ***
2334 */
2335 int
ndhostserv2hent(struct netbuf * nbuf,struct nd_hostservlist * addrs,struct hostent * result,char * buffer,int buflen)2336 ndhostserv2hent(struct netbuf *nbuf, struct nd_hostservlist *addrs,
2337 struct hostent *result, char *buffer, int buflen)
2338 {
2339 int i, count;
2340 char *aliasp;
2341 char **aliasvec;
2342 struct sockaddr_in *sa;
2343 struct nd_hostserv *hs;
2344 const char *la;
2345 size_t length;
2346
2347 /* First, give the lonely address a specious home in h_addr_list. */
2348 aliasp = (char *)ROUND_UP(buffer, sizeof (sa->sin_addr));
2349 /* LINTED pointer cast */
2350 sa = (struct sockaddr_in *)nbuf->buf;
2351 (void) memcpy(aliasp, &(sa->sin_addr), sizeof (sa->sin_addr));
2352 aliasvec = (char **)ROUND_UP(aliasp + sizeof (sa->sin_addr),
2353 sizeof (*aliasvec));
2354 result->h_addr_list = aliasvec;
2355 *aliasvec++ = aliasp;
2356 *aliasvec++ = 0;
2357
2358 /*
2359 * Build h_aliases at start of buffer (after addr and h_addr_list);
2360 * store the alias strings at the end of the buffer (before h_name).
2361 */
2362
2363 aliasp = buffer + buflen;
2364
2365 result->h_aliases = aliasvec;
2366
2367 hs = addrs->h_hostservs;
2368 if (!hs)
2369 return (ND_NOHOST);
2370
2371 length = strlen(hs->h_host) + 1;
2372 aliasp -= length;
2373 if ((char *)(&aliasvec[1]) > aliasp)
2374 return (ND_NOMEM);
2375 (void) memcpy(aliasp, hs->h_host, length);
2376
2377 result->h_name = aliasp;
2378 result->h_addrtype = AF_INET;
2379 result->h_length = sizeof (sa->sin_addr);
2380
2381 /*
2382 * Assumption: the netdir nametoaddr_libs
2383 * sort the vector of (host, serv) pairs in such a way that
2384 * all pairs with the same host name are contiguous.
2385 */
2386 la = hs->h_host;
2387 count = addrs->h_cnt;
2388 for (i = 0; i < count; i++, hs++)
2389 if (strcmp(la, hs->h_host) != 0) {
2390 size_t len = strlen(hs->h_host) + 1;
2391
2392 aliasp -= len;
2393 if ((char *)(&aliasvec[2]) > aliasp)
2394 return (ND_NOMEM);
2395 (void) memcpy(aliasp, hs->h_host, len);
2396 *aliasvec++ = aliasp;
2397 la = hs->h_host;
2398 }
2399 *aliasvec = 0;
2400
2401 return (ND_OK);
2402 }
2403
2404 /*
2405 * Process results from nd_hostservlist ( returned by netdir_getbyaddr)
2406 * into a servent using buf.
2407 */
2408 int
ndhostserv2srent(int port,const char * proto,struct nd_hostservlist * addrs,struct servent * result,char * buffer,int buflen)2409 ndhostserv2srent(int port, const char *proto, struct nd_hostservlist *addrs,
2410 struct servent *result, char *buffer, int buflen)
2411 {
2412 int i, count;
2413 char *aliasp;
2414 char **aliasvec;
2415 struct nd_hostserv *hs;
2416 const char *host_cname;
2417 size_t leni, lenj;
2418
2419 result->s_port = port;
2420 /*
2421 * Build s_aliases at start of buffer;
2422 * store proto and aliases at the end of the buffer (before h_name).
2423 */
2424
2425 aliasp = buffer + buflen;
2426 aliasvec = (char **)ROUND_UP(buffer, sizeof (char *));
2427
2428 result->s_aliases = aliasvec;
2429
2430 hs = addrs->h_hostservs;
2431 if (!hs)
2432 return (ND_NOHOST);
2433 host_cname = hs->h_host;
2434
2435 leni = strlen(proto) + 1;
2436 lenj = strlen(hs->h_serv) + 1;
2437 if ((char *)(&aliasvec[2]) > (aliasp - leni - lenj))
2438 return (ND_NOMEM);
2439
2440 aliasp -= leni;
2441 (void) memcpy(aliasp, proto, leni);
2442 result->s_proto = aliasp;
2443
2444 aliasp -= lenj;
2445 (void) memcpy(aliasp, hs->h_serv, lenj);
2446 result->s_name = aliasp;
2447
2448 /*
2449 * Assumption: the netdir nametoaddr_libs
2450 * do a host aliases first and serv aliases next
2451 * enumeration for creating the list of hostserv
2452 * structures.
2453 */
2454 count = addrs->h_cnt;
2455 for (i = 0;
2456 i < count && hs->h_serv && strcmp(hs->h_host, host_cname) == 0;
2457 i++, hs++) {
2458 size_t len = strlen(hs->h_serv) + 1;
2459
2460 aliasp -= len;
2461 if ((char *)(&aliasvec[2]) > aliasp)
2462 return (ND_NOMEM);
2463 (void) memcpy(aliasp, hs->h_serv, len);
2464 *aliasvec++ = aliasp;
2465 }
2466 *aliasvec = NULL;
2467
2468 return (ND_OK);
2469 }
2470
2471
2472 static int
nd2herrno(int nerr)2473 nd2herrno(int nerr)
2474 {
2475 switch (nerr) {
2476 case ND_OK:
2477 return (0);
2478 case ND_TRY_AGAIN:
2479 return (TRY_AGAIN);
2480 case ND_NO_RECOVERY:
2481 case ND_BADARG:
2482 case ND_NOMEM:
2483 return (NO_RECOVERY);
2484 case ND_NO_DATA:
2485 return (NO_DATA);
2486 case ND_NOHOST:
2487 case ND_NOSERV:
2488 return (HOST_NOT_FOUND);
2489 default:
2490 return (NO_RECOVERY);
2491 }
2492 }
2493
2494 /*
2495 * This is a utility function so that various parts of libnsl can
2496 * easily send ioctls down to ip.
2497 *
2498 */
2499 int
nss_ioctl(int af,int cmd,void * arg)2500 nss_ioctl(int af, int cmd, void *arg)
2501 {
2502 int fd;
2503 char *devpath;
2504 int retv;
2505
2506 switch (af) {
2507 case AF_INET6:
2508 devpath = UDP6DEV;
2509 break;
2510 case AF_INET:
2511 case AF_UNSPEC:
2512 default:
2513 devpath = UDPDEV;
2514 }
2515 if ((fd = open(devpath, O_RDONLY)) < 0) {
2516 return (-1);
2517 }
2518 while ((retv = ioctl(fd, cmd, arg)) == -1) {
2519 if (errno != EINTR)
2520 break;
2521 }
2522 (void) close(fd);
2523 return (retv);
2524 }
2525
2526 static int
nss_strioctl(int af,int cmd,void * ptr,int ilen)2527 nss_strioctl(int af, int cmd, void *ptr, int ilen)
2528 {
2529 struct strioctl str;
2530
2531 str.ic_cmd = cmd;
2532 str.ic_timout = 0;
2533 str.ic_len = ilen;
2534 str.ic_dp = ptr;
2535
2536 return (nss_ioctl(af, I_STR, &str));
2537 }
2538
2539 static struct ifinfo *
get_local_info(void)2540 get_local_info(void)
2541 {
2542 int numifs;
2543 int n;
2544 char *buf = NULL;
2545 size_t needed;
2546 struct lifconf lifc;
2547 struct lifreq lifreq, *lifr;
2548 struct lifnum lifn;
2549 struct ifinfo *localinfo;
2550
2551 lifn.lifn_family = AF_UNSPEC;
2552 lifn.lifn_flags = 0;
2553
2554 getifnum:
2555 if (nss_ioctl(AF_UNSPEC, SIOCGLIFNUM, &lifn) == -1) {
2556 numifs = MAXIFS;
2557 } else {
2558 numifs = lifn.lifn_count;
2559 }
2560
2561 /*
2562 * Add a small fudge factor in case interfaces get plumbed between
2563 * the call to SIOCGLIFNUM and SIOCGLIFCONF.
2564 */
2565 needed = (numifs + 4) * sizeof (lifreq);
2566 if (buf == NULL)
2567 buf = malloc(needed);
2568 else
2569 buf = realloc(buf, needed);
2570 if (buf == NULL) {
2571 (void) syslog(LOG_ERR, "n2a get_local_info: malloc failed: %m");
2572 _nderror = ND_NOMEM;
2573 return (NULL);
2574 }
2575 lifc.lifc_family = AF_UNSPEC;
2576 lifc.lifc_flags = 0;
2577 lifc.lifc_len = needed;
2578 lifc.lifc_buf = buf;
2579 if (nss_ioctl(AF_UNSPEC, SIOCGLIFCONF, &lifc) == -1) {
2580 /*
2581 * IP returns EINVAL if the buffer was too small to fit
2582 * all of the entries. If that's the case, go back and
2583 * try again.
2584 */
2585 if (errno == EINVAL)
2586 goto getifnum;
2587
2588 (void) syslog(LOG_ERR, "n2a get_local_info: "
2589 "ioctl (get interface configuration): %m");
2590 free(buf);
2591 _nderror = ND_SYSTEM;
2592 return (NULL);
2593 }
2594 /* LINTED pointer cast */
2595 lifr = (struct lifreq *)buf;
2596 numifs = lifc.lifc_len/sizeof (lifreq);
2597 localinfo = malloc(ifinfosize(numifs));
2598 if (localinfo == NULL) {
2599 (void) syslog(LOG_ERR, "n2a get_local_info: malloc failed: %m");
2600 free(buf);
2601 _nderror = ND_SYSTEM;
2602 return (NULL);
2603 }
2604
2605 /* LINTED pointer cast */
2606 localinfo->addresses = (struct __ifaddr *)
2607 ((char *)localinfo + sizeof (struct ifinfo));
2608
2609 for (localinfo->count = 0, n = numifs; n > 0; n--, lifr++) {
2610 int af;
2611
2612 lifreq = *lifr;
2613 af = lifreq.lifr_addr.ss_family;
2614
2615 /* Squirrel away the address */
2616 if (ifassign(lifreq, localinfo->count, IF_ADDR) == 0)
2617 continue;
2618
2619 if (nss_ioctl(af, SIOCGLIFFLAGS, &lifreq) < 0) {
2620 (void) syslog(LOG_ERR,
2621 "n2a get_local_info: "
2622 "ioctl (get interface flags): %m");
2623 continue;
2624 }
2625 if (!(lifreq.lifr_flags & IFF_UP))
2626 continue;
2627
2628 if (nss_ioctl(af, SIOCGLIFNETMASK, &lifreq) < 0) {
2629 (void) syslog(LOG_ERR,
2630 "n2a get_local_info: "
2631 "ioctl (get interface netmask): %m");
2632 continue;
2633 }
2634
2635 if (ifassign(lifreq, localinfo->count, IF_MASK) == 0)
2636 continue;
2637
2638 localinfo->count++;
2639 }
2640
2641 free(buf);
2642 return (localinfo);
2643 }
2644
2645 static int
__inet_ifassign(sa_family_t af,struct __ifaddr * ifa,__ifaddr_type type,void * addr)2646 __inet_ifassign(sa_family_t af, struct __ifaddr *ifa, __ifaddr_type type,
2647 void *addr) {
2648 switch (type) {
2649 case IF_ADDR:
2650 ifa->af = af;
2651 if (af == AF_INET6) {
2652 ifa->addr.in6 = *(struct in6_addr *)addr;
2653 } else {
2654 ifa->addr.in4 = *(struct in_addr *)addr;
2655 }
2656 break;
2657 case IF_MASK:
2658 if (ifa->af == af) {
2659 if (af == AF_INET6) {
2660 ifa->mask.in6 = *(struct in6_addr *)addr;
2661 } else {
2662 ifa->mask.in4 = *(struct in_addr *)addr;
2663 }
2664 } else {
2665 return (0);
2666 }
2667 break;
2668 default:
2669 return (0);
2670 }
2671
2672 return (1);
2673 }
2674
2675 /*
2676 * Some higher-level routines for determining if an address is
2677 * on a local network.
2678 *
2679 * __inet_get_local_interfaces() - get an opaque handle with
2680 * with a list of local interfaces
2681 * __inet_address_is_local() - return 1 if an address is
2682 * on a local network; 0 otherwise
2683 * __inet_free_local_interfaces() - free handle that was
2684 * returned by __inet_get_local_interfaces()
2685 *
2686 * A typical calling sequence is:
2687 *
2688 * p = __inet_get_local_interfaces();
2689 * if (__inet_address_is_local(p, inaddr)) {
2690 * ...
2691 * }
2692 * __inet_free_local_interfaces(p);
2693 */
2694
2695 /*
2696 * Return an opaque pointer to a list of configured interfaces.
2697 */
2698 void *
__inet_get_local_interfaces(void)2699 __inet_get_local_interfaces(void)
2700 {
2701 return (get_local_info());
2702 }
2703
2704 /*
2705 * Free memory allocated by inet_local_interfaces().
2706 */
2707 void
__inet_free_local_interfaces(void * p)2708 __inet_free_local_interfaces(void *p)
2709 {
2710 free(p);
2711 }
2712
2713 /*
2714 * Determine if an address is on a local network.
2715 *
2716 * Might have made sense to use SIOCTONLINK, except that it doesn't
2717 * handle matching on IPv4 network addresses.
2718 */
2719 int
__inet_address_is_local_af(void * p,sa_family_t af,void * addr)2720 __inet_address_is_local_af(void *p, sa_family_t af, void *addr) {
2721
2722 struct ifinfo *localinfo = (struct ifinfo *)p;
2723 int i, a;
2724 struct in_addr v4addr;
2725
2726 if (localinfo == 0)
2727 return (0);
2728
2729 if (af == AF_INET6 && IN6_IS_ADDR_V4MAPPED((struct in6_addr *)addr)) {
2730 IN6_V4MAPPED_TO_INADDR((struct in6_addr *)addr, &v4addr);
2731 af = AF_INET;
2732 addr = (void *)&v4addr;
2733 }
2734
2735 for (i = 0; i < localinfo->count; i++) {
2736 if (ifaf(i) == af) {
2737 if (af == AF_INET6) {
2738 struct in6_addr *a6 = (struct in6_addr *)addr;
2739 for (a = 0; a < sizeof (a6->s6_addr); a++) {
2740 if ((a6->s6_addr[a] &
2741 ifmask6(i).s6_addr[a]) !=
2742 (ifaddr6(i).s6_addr[a] &
2743 ifmask6(i).s6_addr[a]))
2744 break;
2745 }
2746 if (a >= sizeof (a6->s6_addr))
2747 return (1);
2748 } else {
2749 if ((((struct in_addr *)addr)->s_addr &
2750 ifmask4(i).s_addr) ==
2751 (ifaddr4(i).s_addr &
2752 ifmask4(i).s_addr))
2753 return (1);
2754 }
2755 }
2756 }
2757
2758 return (0);
2759 }
2760
2761 int
__inet_address_is_local(void * p,struct in_addr addr)2762 __inet_address_is_local(void *p, struct in_addr addr)
2763 {
2764 return (__inet_address_is_local_af(p, AF_INET, &addr));
2765 }
2766
2767 int
__inet_uaddr_is_local(void * p,struct netconfig * nc,char * uaddr)2768 __inet_uaddr_is_local(void *p, struct netconfig *nc, char *uaddr)
2769 {
2770 struct netbuf *taddr;
2771 sa_family_t af;
2772 int ret;
2773
2774 taddr = uaddr2taddr(nc, uaddr);
2775 if (taddr == 0)
2776 return (0);
2777
2778 /* LINTED pointer cast */
2779 af = ((struct sockaddr *)taddr->buf)->sa_family;
2780
2781 ret = __inet_address_is_local_af(p, af, (af == AF_INET6) ?
2782 /* LINTED pointer cast */
2783 (void *)&((struct sockaddr_in6 *)taddr->buf)->sin6_addr :
2784 /* LINTED pointer cast */
2785 (void *)&((struct sockaddr_in *)taddr->buf)->sin_addr);
2786
2787 netdir_free(taddr, ND_ADDR);
2788 return (ret);
2789 }
2790
2791
2792 int
__inet_address_count(void * p)2793 __inet_address_count(void *p)
2794 {
2795 struct ifinfo *lp = (struct ifinfo *)p;
2796
2797 if (lp != 0) {
2798 return (lp->count);
2799 } else {
2800 return (0);
2801 }
2802 }
2803
2804 uint32_t
__inet_get_addr(void * p,int n)2805 __inet_get_addr(void *p, int n)
2806 {
2807 struct ifinfo *localinfo = (struct ifinfo *)p;
2808
2809 if (localinfo == 0 || n >= localinfo->count || ifaf(n) != AF_INET)
2810 return (0);
2811
2812 return (ifaddr4(n).s_addr);
2813 }
2814
2815 uint32_t
__inet_get_network(void * p,int n)2816 __inet_get_network(void *p, int n)
2817 {
2818 struct ifinfo *localinfo = (struct ifinfo *)p;
2819
2820 if (localinfo == 0 || n >= localinfo->count || ifaf(n) != AF_INET)
2821 return (0);
2822
2823 return (ifaddr4(n).s_addr & ifmask4(n).s_addr);
2824 }
2825
2826 char *
__inet_get_uaddr(void * p,struct netconfig * nc,int n)2827 __inet_get_uaddr(void *p, struct netconfig *nc, int n)
2828 {
2829 struct ifinfo *localinfo = (struct ifinfo *)p;
2830 char *uaddr;
2831 struct sockaddr_in sin4;
2832 struct sockaddr_in6 sin6;
2833 struct netbuf nb;
2834
2835 if (localinfo == 0 || nc == 0 || n >= localinfo->count)
2836 return (0);
2837
2838 if (ifaf(n) == AF_INET6) {
2839 if (strcmp(NC_INET6, nc->nc_protofmly) != 0)
2840 return (0);
2841 (void) memset(&sin6, 0, sizeof (sin6));
2842 sin6.sin6_family = AF_INET6;
2843 sin6.sin6_addr = ifaddr6(n);
2844 nb.buf = (char *)&sin6;
2845 nb.len = sizeof (sin6);
2846 } else {
2847 if (strcmp(NC_INET, nc->nc_protofmly) != 0)
2848 return (0);
2849 (void) memset(&sin4, 0, sizeof (sin4));
2850 sin4.sin_family = AF_INET;
2851 sin4.sin_addr = ifaddr4(n);
2852 nb.buf = (char *)&sin4;
2853 nb.len = sizeof (sin4);
2854 }
2855
2856 nb.maxlen = nb.len;
2857
2858 uaddr = taddr2uaddr(nc, &nb);
2859 return (uaddr);
2860 }
2861
2862 char *
__inet_get_networka(void * p,int n)2863 __inet_get_networka(void *p, int n)
2864 {
2865 struct ifinfo *localinfo = (struct ifinfo *)p;
2866
2867 if (localinfo == 0 || n >= localinfo->count)
2868 return (0);
2869
2870 if (ifaf(n) == AF_INET6) {
2871 char buf[INET6_ADDRSTRLEN];
2872 struct in6_addr in6;
2873 int i;
2874
2875 for (i = 0; i < sizeof (in6.s6_addr); i++) {
2876 in6.s6_addr[i] = ifaddr6(n).s6_addr[i] &
2877 ifmask6(n).s6_addr[i];
2878 }
2879 return (strdup(inet_ntop(AF_INET6, &in6, buf, sizeof (buf))));
2880 } else {
2881 struct in_addr in4;
2882
2883 in4.s_addr = ifaddr4(n).s_addr & ifmask4(n).s_addr;
2884 return (strdup(inet_ntoa(in4)));
2885 }
2886 }
2887
2888 static int
in_list(struct in_addr * addrs,int n,struct in_addr a)2889 in_list(struct in_addr *addrs, int n, struct in_addr a)
2890 {
2891 int i;
2892
2893 for (i = 0; i < n; i++) {
2894 if (addrs[i].s_addr == a.s_addr)
2895 return (1);
2896 }
2897 return (0);
2898 }
2899
2900 static int
getbroadcastnets(struct netconfig * tp,struct in_addr ** addrs)2901 getbroadcastnets(struct netconfig *tp, struct in_addr **addrs)
2902 {
2903 struct ifconf ifc;
2904 struct ifreq ifreq, *ifr;
2905 struct sockaddr_in *sin;
2906 struct in_addr a;
2907 int fd;
2908 int n, i, numifs;
2909 char *buf;
2910 int use_loopback = 0;
2911
2912 _nderror = ND_SYSTEM;
2913 fd = open(tp->nc_device, O_RDONLY);
2914 if (fd < 0) {
2915 (void) syslog(LOG_ERR,
2916 "broadcast: open to get interface configuration: %m");
2917 return (0);
2918 }
2919 if (ioctl(fd, SIOCGIFNUM, (char *)&numifs) < 0)
2920 numifs = MAXIFS;
2921 buf = malloc(numifs * sizeof (struct ifreq));
2922 if (buf == NULL) {
2923 (void) syslog(LOG_ERR, "broadcast: malloc failed: %m");
2924 (void) close(fd);
2925 return (0);
2926 }
2927 *addrs = malloc(numifs * sizeof (struct in_addr));
2928 if (*addrs == NULL) {
2929 (void) syslog(LOG_ERR, "broadcast: malloc failed: %m");
2930 free(buf);
2931 (void) close(fd);
2932 return (0);
2933 }
2934 ifc.ifc_len = numifs * (int)sizeof (struct ifreq);
2935 ifc.ifc_buf = buf;
2936 /*
2937 * Ideally, this ioctl should also tell me, how many bytes were
2938 * finally allocated, but it doesnt.
2939 */
2940 if (ioctl(fd, SIOCGIFCONF, (char *)&ifc) < 0) {
2941 (void) syslog(LOG_ERR,
2942 "broadcast: ioctl (get interface configuration): %m");
2943 free(buf);
2944 free(*addrs);
2945 (void) close(fd);
2946 return (0);
2947 }
2948
2949 retry:
2950 /* LINTED pointer cast */
2951 ifr = (struct ifreq *)buf;
2952 for (i = 0, n = ifc.ifc_len / (int)sizeof (struct ifreq);
2953 n > 0; n--, ifr++) {
2954 ifreq = *ifr;
2955 if (ioctl(fd, SIOCGIFFLAGS, (char *)&ifreq) < 0) {
2956 (void) syslog(LOG_ERR, "broadcast: "
2957 "ioctl (get interface flags): %m");
2958 continue;
2959 }
2960 if (!(ifreq.ifr_flags & IFF_UP) ||
2961 (ifr->ifr_addr.sa_family != AF_INET))
2962 continue;
2963 if (ifreq.ifr_flags & IFF_BROADCAST) {
2964 /* LINTED pointer cast */
2965 sin = (struct sockaddr_in *)&ifr->ifr_addr;
2966 if (ioctl(fd, SIOCGIFBRDADDR, (char *)&ifreq) < 0) {
2967 /* May not work with other implementation */
2968 a = _inet_makeaddr(
2969 inet_netof(sin->sin_addr),
2970 INADDR_ANY);
2971 if (!in_list(*addrs, i, a))
2972 (*addrs)[i++] = a;
2973 } else {
2974 /* LINTED pointer cast */
2975 a = ((struct sockaddr_in *)
2976 &ifreq.ifr_addr)->sin_addr;
2977 if (!in_list(*addrs, i, a))
2978 (*addrs)[i++] = a;
2979 }
2980 continue;
2981 }
2982 if (use_loopback && (ifreq.ifr_flags & IFF_LOOPBACK)) {
2983 /* LINTED pointer cast */
2984 sin = (struct sockaddr_in *)&ifr->ifr_addr;
2985 a = sin->sin_addr;
2986 if (!in_list(*addrs, i, a))
2987 (*addrs)[i++] = a;
2988 continue;
2989 }
2990 if (ifreq.ifr_flags & IFF_POINTOPOINT) {
2991 if (ioctl(fd, SIOCGIFDSTADDR, (char *)&ifreq) < 0)
2992 continue;
2993 /* LINTED pointer cast */
2994 a = ((struct sockaddr_in *)
2995 &ifreq.ifr_addr)->sin_addr;
2996 if (!in_list(*addrs, i, a))
2997 (*addrs)[i++] = a;
2998 continue;
2999 }
3000 }
3001 if (i == 0 && !use_loopback) {
3002 use_loopback = 1;
3003 goto retry;
3004 }
3005 free(buf);
3006 (void) close(fd);
3007 if (i)
3008 _nderror = ND_OK;
3009 else
3010 free(*addrs);
3011 return (i);
3012 }
3013
3014 /*
3015 * This is lifted straight from libsocket/inet/inet_mkaddr.c.
3016 * Copied here to avoid our dependency on libsocket. More importantly,
3017 * to make sure partially static apps that use libnsl, but not
3018 * libsocket, don't get screwed up.
3019 * If you understand the above paragraph, try to get rid of
3020 * this copy of inet_makeaddr; if you don;t, leave it alone.
3021 *
3022 * Formulate an Internet address from network + host. Used in
3023 * building addresses stored in the ifnet structure.
3024 */
3025 static struct in_addr
_inet_makeaddr(in_addr_t net,in_addr_t host)3026 _inet_makeaddr(in_addr_t net, in_addr_t host)
3027 {
3028 in_addr_t addr;
3029 struct in_addr inaddr;
3030
3031 if (net < 128)
3032 addr = (net << IN_CLASSA_NSHIFT) | (host & IN_CLASSA_HOST);
3033 else if (net < 65536)
3034 addr = (net << IN_CLASSB_NSHIFT) | (host & IN_CLASSB_HOST);
3035 else if (net < 16777216L)
3036 addr = (net << IN_CLASSC_NSHIFT) | (host & IN_CLASSC_HOST);
3037 else
3038 addr = net | host;
3039 inaddr.s_addr = htonl(addr);
3040 return (inaddr);
3041 }
3042
3043 /*
3044 * Routine to read the default configuration file and check if SORT_ADDRS
3045 * is set to NO or FALSE. This routine is called by order_haddrlist_af()
3046 * to determine if the addresses need to be sorted.
3047 */
3048 static boolean_t
_read_nsw_file(void)3049 _read_nsw_file(void)
3050 {
3051 char defval[LINESIZE];
3052 FILE *defl;
3053 boolean_t nosort = B_FALSE;
3054
3055
3056 do {
3057 defl = fopen(__NSW_DEFAULT_FILE, "rF");
3058 } while ((defl == NULL) && (errno == EINTR));
3059
3060 if (defl == NULL)
3061 return (B_FALSE);
3062
3063 while (fgets(defval, sizeof (defval), defl) != NULL) {
3064 if ((strncmp(DONT_SORT, defval, sizeof (DONT_SORT) - 1) == 0) ||
3065 (strncmp(DONT_SORT2, defval,
3066 sizeof (DONT_SORT2) - 1) == 0)) {
3067 nosort = B_TRUE;
3068 break;
3069 }
3070 }
3071 (void) fclose(defl);
3072 return (nosort);
3073 }
3074