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