xref: /illumos-gate/usr/src/lib/libnsl/nss/netdir_inet_sundry.c (revision 45ede40b2394db7967e59f19288fae9b62efd4aa)
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 2009 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  * Copyright (c) 2016 by Delphix. All rights reserved.
26  *
27  * lib/libnsl/nss/netdir_inet_sundry.c
28  *
29  * This file contains inet-specific implementations of netdir_options,
30  * uaddr2taddr, and taddr2uaddr. These implementations
31  * used to be in both tcpip.so and switch.so (identical copies).
32  * Since we got rid of those, and also it's a good idea to build-in
33  * inet-specific implementations in one place, we decided to put
34  * them in this file with a not-so glorious name. These are INET-SPECIFIC
35  * only, and will not be used for non-inet transports or by third-parties
36  * that decide to provide their own nametoaddr libs for inet transports
37  * (they are on their own for these as well => they get flexibility).
38  *
39  * Copied mostly from erstwhile lib/nametoaddr/tcpip/tcpip.c.
40  */
41 
42 #include "mt.h"
43 #include <stdlib.h>
44 #include <stdio.h>
45 #include <string.h>
46 #include <strings.h>
47 #include <unistd.h>
48 #include <sys/types.h>
49 #include <sys/stat.h>
50 #include <fcntl.h>
51 #include <errno.h>
52 #include <thread.h>
53 #include <netconfig.h>
54 #include <netdir.h>
55 #include <nss_netdir.h>
56 #include <tiuser.h>
57 #include <sys/socket.h>
58 #include <net/if.h>
59 #include <sys/sockio.h>
60 #include <sys/fcntl.h>
61 #include <netinet/in.h>
62 #include <netinet/tcp.h>
63 #include <netinet/udp.h>
64 #include <arpa/inet.h>
65 #include <rpc/types.h>
66 #include <rpc/rpc_com.h>
67 #include <syslog.h>
68 #include <values.h>
69 #include <limits.h>
70 #include <nss_dbdefs.h>
71 #include "nss.h"
72 
73 #define	MAXIFS 32
74 
75 /*
76  * Extracted from socketvar.h
77  */
78 #define	SOV_DEFAULT	1	/* Select based on so_default_version */
79 #define	SOV_SOCKBSD	3	/* Socket with no streams operations */
80 
81 extern int _so_socket(int, int, int, char *, int);
82 extern int _so_connect(int, struct sockaddr *, socklen_t, int);
83 extern int _so_getsockname(int, struct sockaddr *, socklen_t *, int);
84 
85 
86 static char *inet_netdir_mergeaddr(struct netconfig *, char *, char *);
87 static int bindresvport(struct netconfig *, int, struct netbuf *);
88 static int checkresvport(struct netbuf *);
89 static struct netbuf *ip_uaddr2taddr(char *);
90 static struct netbuf *ipv6_uaddr2taddr(char *);
91 
92 
93 extern char *inet_ntoa_r(struct in_addr, char *);
94 
95 int
96 __inet_netdir_options(struct netconfig *tp, int opts, int fd, char *par)
97 {
98 	struct nd_mergearg *ma;
99 
100 	switch (opts) {
101 	case ND_SET_BROADCAST:
102 		/* Every one is allowed to broadcast without asking */
103 		return (ND_OK);
104 	case ND_SET_RESERVEDPORT:	/* bind to a resered port */
105 		/* LINTED pointer cast */
106 		return (bindresvport(tp, fd, (struct netbuf *)par));
107 	case ND_CHECK_RESERVEDPORT:	/* check if reserved prot */
108 		/* LINTED pointer cast */
109 		return (checkresvport((struct netbuf *)par));
110 	case ND_MERGEADDR:	/* Merge two addresses */
111 		/* LINTED pointer cast */
112 		ma = (struct nd_mergearg *)(par);
113 		ma->m_uaddr = inet_netdir_mergeaddr(tp, ma->c_uaddr,
114 		    ma->s_uaddr);
115 		return (_nderror);
116 	default:
117 		return (ND_NOCTRL);
118 	}
119 }
120 
121 
122 /*
123  * This routine will convert a TCP/IP internal format address
124  * into a "universal" format address. In our case it prints out the
125  * decimal dot equivalent. h1.h2.h3.h4.p1.p2 where h1-h4 are the host
126  * address and p1-p2 are the port number.
127  */
128 char *
129 __inet_taddr2uaddr(struct netconfig *tp, struct netbuf *addr)
130 {
131 	struct sockaddr_in	*sa;	/* our internal format */
132 	struct sockaddr_in6	*sa6;	/* our internal format */
133 	char			tmp[RPC_INET6_MAXUADDRSIZE];
134 	unsigned short		myport;
135 
136 	if (addr == NULL || tp == NULL || addr->buf == NULL) {
137 		_nderror = ND_BADARG;
138 		return (NULL);
139 	}
140 	if (strcmp(tp->nc_protofmly, NC_INET) == 0) {
141 		/* LINTED pointer cast */
142 		sa = (struct sockaddr_in *)(addr->buf);
143 		myport = ntohs(sa->sin_port);
144 		(void) inet_ntoa_r(sa->sin_addr, tmp);
145 	} else {
146 		/* LINTED pointer cast */
147 		sa6 = (struct sockaddr_in6 *)(addr->buf);
148 		myport = ntohs(sa6->sin6_port);
149 		if (inet_ntop(AF_INET6, sa6->sin6_addr.s6_addr, tmp,
150 		    sizeof (tmp)) == NULL) {
151 			_nderror = ND_BADARG;
152 			return (NULL);
153 		}
154 	}
155 
156 	(void) sprintf(tmp + strlen(tmp), ".%d.%d", myport >> 8, myport & 255);
157 	return (strdup(tmp));	/* Doesn't return static data ! */
158 }
159 
160 /*
161  * This internal routine will convert one of those "universal" addresses
162  * to the internal format used by the Sun TLI TCP/IP provider.
163  */
164 struct netbuf *
165 __inet_uaddr2taddr(struct netconfig *tp, char *addr)
166 {
167 	if (!addr || !tp) {
168 		_nderror = ND_BADARG;
169 		return (NULL);
170 	}
171 	if (strcmp(tp->nc_protofmly, NC_INET) == 0)
172 		return (ip_uaddr2taddr(addr));
173 	else
174 		return (ipv6_uaddr2taddr(addr));
175 }
176 
177 static struct netbuf *
178 ip_uaddr2taddr(char *addr)
179 {
180 
181 	struct sockaddr_in	*sa;
182 	uint32_t		inaddr;
183 	unsigned short		inport;
184 	int			h1, h2, h3, h4, p1, p2;
185 	struct netbuf		*result;
186 
187 	result = malloc(sizeof (struct netbuf));
188 	if (!result) {
189 		_nderror = ND_NOMEM;
190 		return (NULL);
191 	}
192 
193 	sa = calloc(1, sizeof (*sa));
194 
195 	if (!sa) {
196 		free(result);
197 		_nderror = ND_NOMEM;
198 		return (NULL);
199 	}
200 
201 	result->buf = (char *)(sa);
202 	result->maxlen = sizeof (struct sockaddr_in);
203 	result->len = sizeof (struct sockaddr_in);
204 
205 	/* XXX there is probably a better way to do this. */
206 	if (sscanf(addr, "%d.%d.%d.%d.%d.%d", &h1, &h2, &h3, &h4,
207 	    &p1, &p2) != 6) {
208 		free(result);
209 		_nderror = ND_NO_RECOVERY;
210 		return (NULL);
211 	}
212 
213 	/* convert the host address first */
214 	inaddr = (h1 << 24) + (h2 << 16) + (h3 << 8) + h4;
215 	sa->sin_addr.s_addr = htonl(inaddr);
216 
217 	/* convert the port */
218 	inport = (p1 << 8) + p2;
219 	sa->sin_port = htons(inport);
220 
221 	sa->sin_family = AF_INET;
222 
223 	return (result);
224 }
225 
226 static struct netbuf *
227 ipv6_uaddr2taddr(char	*addr)
228 {
229 	struct sockaddr_in6	*sa;
230 	unsigned short		inport;
231 	int	 p1, p2;
232 	struct netbuf		*result;
233 	char tmpaddr[RPC_INET6_MAXUADDRSIZE];
234 	char *dot;
235 
236 	result = malloc(sizeof (struct netbuf));
237 	if (!result) {
238 		_nderror = ND_NOMEM;
239 		return (NULL);
240 	}
241 
242 	sa = calloc(1, sizeof (struct sockaddr_in6));
243 	if (!sa) {
244 		free(result);
245 		_nderror = ND_NOMEM;
246 		return (NULL);
247 	}
248 	result->buf = (char *)(sa);
249 	result->maxlen = sizeof (struct sockaddr_in6);
250 	result->len = sizeof (struct sockaddr_in6);
251 
252 	/* retrieve the ipv6 address and port info */
253 
254 	if (strlen(addr) > sizeof (tmpaddr) - 1) {
255 		free(result);
256 		_nderror = ND_NOMEM;
257 		return (NULL);
258 	}
259 
260 	(void) strcpy(tmpaddr, addr);
261 
262 	if ((dot = strrchr(tmpaddr, '.')) != 0) {
263 		*dot = '\0';
264 		p2 = atoi(dot+1);
265 		if ((dot = strrchr(tmpaddr, '.')) != 0) {
266 			*dot = '\0';
267 			p1 = atoi(dot+1);
268 		}
269 	}
270 
271 	if (dot == 0) {
272 		free(result);
273 		_nderror = ND_NOMEM;
274 		return (NULL);
275 	}
276 
277 	if (inet_pton(AF_INET6, tmpaddr, sa->sin6_addr.s6_addr) == 0) {
278 		free(result);
279 		_nderror = ND_NOMEM;
280 		return (NULL);
281 	}
282 
283 	/* convert the port */
284 	inport = (p1 << 8) + p2;
285 	sa->sin6_port = htons(inport);
286 
287 	sa->sin6_family = AF_INET6;
288 
289 	return (result);
290 }
291 
292 /*
293  * Interface caching routines.  The cache is refreshed every
294  * IF_CACHE_REFRESH_TIME seconds.  A read-write lock is used to
295  * protect the cache.
296  */
297 #define	IF_CACHE_REFRESH_TIME 10
298 
299 static int if_cache_refresh_time = IF_CACHE_REFRESH_TIME;
300 static rwlock_t iflock = DEFAULTRWLOCK;
301 static time_t last_updated = 0;		/* protected by iflock */
302 
303 /*
304  * Changing the data type of if_flags from uint_t to uint64_t to accomodate
305  * extra flags. Refer <net/if.h> for the extra flags.
306  */
307 typedef struct if_info_s {
308 	struct in_addr if_netmask;	/* netmask in network order */
309 	struct in_addr if_address;	/* address in network order */
310 	uint64_t if_flags;		/* interface flags */
311 } if_info_t;
312 
313 static if_info_t *if_info = NULL;	/* if cache, protected by iflock */
314 static int n_ifs = 0;			/* number of cached interfaces */
315 static int numifs_last = 0;		/* number of interfaces last seen */
316 
317 /*
318  * Builds the interface cache.  Write lock on iflock is needed
319  * for calling this routine.  It sets _nderror for error returns.
320  * Returns TRUE if successful, FALSE otherwise.
321  * Changing the structures ifreq and ifconf to lifreq and lifconf to
322  * have larger flag field. This is to accomodate the extra flags associated
323  * with the interface. Also introducing lifn which will contain the number
324  * of IPV4 interfaces present.
325  */
326 static bool_t
327 get_if_info(void)
328 {
329 	size_t		needed;
330 	struct lifreq	*buf = NULL;
331 	int		numifs;
332 	struct lifconf  lifc;
333 	struct lifreq   *lifr;
334 	struct lifnum   lifn;
335 
336 	lifn.lifn_family = AF_INET;
337 	lifn.lifn_flags = 0;
338 getifnum:
339 	if (nss_ioctl(AF_INET, SIOCGLIFNUM, &lifn) == -1) {
340 		numifs = MAXIFS;
341 	} else {
342 		numifs = lifn.lifn_count;
343 	}
344 	/*
345 	 * Add a small fudge factor in case interfaces are plumbed
346 	 * between the SIOCGLIFNUM and SIOCGLIFCONF.
347 	 */
348 	needed = (numifs + 4) * sizeof (struct lifreq);
349 	if (buf == NULL)
350 		buf = malloc(needed);
351 	else
352 		buf = realloc(buf, needed);
353 	if (buf == NULL) {
354 		_nderror = ND_NOMEM;
355 		return (FALSE);
356 	}
357 
358 	lifc.lifc_family = AF_INET;
359 	lifc.lifc_flags = 0;
360 	lifc.lifc_len = needed;
361 	lifc.lifc_buf = (char *)buf;
362 	if (nss_ioctl(AF_INET, SIOCGLIFCONF, &lifc) == -1) {
363 		/*
364 		 * IP returns EINVAL if the buffer was too small to fit
365 		 * all of the entries.  If that's the case, go back and
366 		 * try again.
367 		 */
368 		if (errno == EINVAL)
369 			goto getifnum;
370 
371 		free(buf);
372 		free(if_info);
373 		if_info = NULL;
374 		_nderror = ND_SYSTEM;
375 		return (FALSE);
376 	}
377 	numifs = lifc.lifc_len / (int)sizeof (struct lifreq);
378 
379 	if (if_info == NULL || numifs > numifs_last) {
380 		if (if_info == NULL)
381 			if_info = malloc(numifs * sizeof (if_info_t));
382 		else
383 			if_info = realloc(if_info, numifs * sizeof (if_info_t));
384 		if (if_info == NULL) {
385 			free(buf);
386 			_nderror = ND_NOMEM;
387 			return (FALSE);
388 		}
389 		numifs_last = numifs;
390 	}
391 
392 	n_ifs = 0;
393 	for (lifr = buf; lifr < (buf + numifs); lifr++) {
394 		if (lifr->lifr_addr.ss_family != AF_INET)
395 			continue;
396 
397 		if_info[n_ifs].if_address =
398 		    ((struct sockaddr_in *)&lifr->lifr_addr)->sin_addr;
399 
400 		if (nss_ioctl(AF_INET, SIOCGLIFFLAGS, lifr) < 0)
401 			continue;
402 
403 		if ((lifr->lifr_flags & IFF_UP) == 0)
404 			continue;
405 		if_info[n_ifs].if_flags = lifr->lifr_flags;
406 
407 		if (nss_ioctl(AF_INET, SIOCGLIFNETMASK, lifr) < 0)
408 			continue;
409 
410 		if_info[n_ifs].if_netmask =
411 		    ((struct sockaddr_in *)&lifr->lifr_addr)->sin_addr;
412 		n_ifs++;
413 	}
414 	free(buf);
415 	return (TRUE);
416 }
417 
418 
419 /*
420  * Update the interface cache based on last update time.
421  */
422 static bool_t
423 update_if_cache(void)
424 {
425 	time_t	curtime;
426 
427 	(void) rw_wrlock(&iflock);
428 	/*
429 	 * Check if some other thread has beaten this one to it.
430 	 */
431 	(void) time(&curtime);
432 	if ((curtime - last_updated) >= if_cache_refresh_time) {
433 		if (!get_if_info()) {
434 			(void) rw_unlock(&iflock);
435 			return (FALSE);
436 		}
437 		(void) time(&last_updated);
438 	}
439 	(void) rw_unlock(&iflock);
440 	return (TRUE);
441 }
442 
443 
444 /*
445  * Given an IP address, check if this matches any of the interface
446  * addresses.  If an error occurs, return FALSE so that the caller
447  * will not assume that this address belongs to this machine.
448  */
449 static bool_t
450 is_my_address(struct in_addr addr)
451 {
452 	time_t		curtime;
453 	if_info_t	*ifn;
454 
455 	(void) time(&curtime);
456 	if ((curtime - last_updated) >= if_cache_refresh_time) {
457 		/*
458 		 * Cache needs to be refreshed.
459 		 */
460 		if (!update_if_cache())
461 			return (FALSE);
462 	}
463 	(void) rw_rdlock(&iflock);
464 	for (ifn = if_info; ifn < (if_info + n_ifs); ifn++) {
465 		if (addr.s_addr == ifn->if_address.s_addr) {
466 			(void) rw_unlock(&iflock);
467 			return (TRUE);
468 		}
469 	}
470 	(void) rw_unlock(&iflock);
471 	return (FALSE);
472 }
473 
474 
475 /*
476  * Given a host name, check if it is this host.
477  */
478 bool_t
479 __inet_netdir_is_my_host(const char *host)
480 {
481 	int		error;
482 	char		buf[NSS_BUFLEN_HOSTS];
483 	struct hostent	res, *h;
484 	char		**c;
485 	struct in_addr	in;
486 
487 	h = gethostbyname_r(host, (void *)&res, buf, sizeof (buf), &error);
488 	if (h == NULL)
489 		return (FALSE);
490 	if (h->h_addrtype != AF_INET)
491 		return (FALSE);
492 	for (c = h->h_addr_list; *c != NULL; c++) {
493 		(void) memcpy(&in.s_addr, *c, sizeof (in.s_addr));
494 		if (is_my_address(in))
495 			return (TRUE);
496 	}
497 	return (FALSE);
498 }
499 
500 
501 /*
502  * Given an IP address, find the interface address that has the best
503  * prefix match.  Return the address in network order.
504  */
505 static uint32_t
506 get_best_match(struct in_addr addr)
507 {
508 	if_info_t *bestmatch, *ifn;
509 	int bestcount, count, limit;
510 	uint32_t mask, netmask, clnt_addr, if_addr;
511 	bool_t found, subnet_match;
512 	int subnet_count;
513 
514 	bestmatch = NULL;				/* no match yet */
515 	bestcount = BITSPERBYTE * sizeof (uint32_t);	/* worst match */
516 	clnt_addr = ntohl(addr.s_addr);			/* host order */
517 
518 	subnet_match = FALSE;		/* subnet match not found yet */
519 	subnet_count = bestcount;	/* worst subnet match */
520 
521 	for (ifn = if_info; ifn < (if_info + n_ifs); ifn++) {
522 		netmask = ntohl(ifn->if_netmask.s_addr);  /* host order */
523 		if_addr = ntohl(ifn->if_address.s_addr);  /* host order */
524 
525 		/*
526 		 * set initial count to first bit set in netmask, with
527 		 * zero being the number of the least significant bit.
528 		 */
529 		count = 0;
530 		for (mask = netmask; mask && ((mask & 1) == 0); mask >>= 1)
531 			count++;
532 
533 		/*
534 		 * Set limit so that we don't try to match prefixes shorter
535 		 * than the inherent netmask for the class (A, B, C, etc).
536 		 */
537 		if (IN_CLASSC(if_addr))
538 			limit = IN_CLASSC_NSHIFT;
539 		else if (IN_CLASSB(if_addr))
540 			limit = IN_CLASSB_NSHIFT;
541 		else if (IN_CLASSA(if_addr))
542 			limit = IN_CLASSA_NSHIFT;
543 		else
544 			limit = 0;
545 
546 		/*
547 		 * We assume that the netmask consists of a contiguous
548 		 * sequence of 1-bits starting with the most significant bit.
549 		 * Prefix comparison starts at the subnet mask level.
550 		 * The prefix mask used for comparison is progressively
551 		 * reduced until it equals the inherent mask for the
552 		 * interface address class.  The algorithm finds an
553 		 * interface in the following order of preference:
554 		 *
555 		 * (1) the longest subnet match
556 		 * (2) the best partial subnet match
557 		 * (3) the first non-loopback && non-PPP interface
558 		 * (4) the first non-loopback interface (PPP is OK)
559 		 */
560 		found = FALSE;
561 		while (netmask && count < subnet_count) {
562 			if ((netmask & clnt_addr) == (netmask & if_addr)) {
563 				bestcount = count;
564 				bestmatch = ifn;
565 				found = TRUE;
566 				break;
567 			}
568 			netmask <<= 1;
569 			count++;
570 			if (count >= bestcount || count > limit || subnet_match)
571 				break;
572 		}
573 		/*
574 		 * If a subnet level match occurred, note this for
575 		 * comparison with future subnet matches.
576 		 */
577 		if (found && (netmask == ntohl(ifn->if_netmask.s_addr))) {
578 			subnet_match = TRUE;
579 			subnet_count = count;
580 		}
581 	}
582 
583 	/*
584 	 * If we don't have a match, select the first interface that
585 	 * is not a loopback interface (and preferably not a PPP interface)
586 	 * as the best match.
587 	 */
588 	if (bestmatch == NULL) {
589 		for (ifn = if_info; ifn < (if_info + n_ifs); ifn++) {
590 			if ((ifn->if_flags & IFF_LOOPBACK) == 0) {
591 				bestmatch = ifn;
592 
593 				/*
594 				 * If this isn't a PPP interface, we're
595 				 * done.  Otherwise, keep walking through
596 				 * the list in case we have a non-loopback
597 				 * iface that ISN'T a PPP further down our
598 				 * list...
599 				 */
600 				if ((ifn->if_flags & IFF_POINTOPOINT) == 0) {
601 					break;
602 				}
603 			}
604 		}
605 	}
606 
607 	if (bestmatch != NULL)
608 		return (bestmatch->if_address.s_addr);
609 	else
610 		return (0);
611 }
612 
613 static int
614 is_myself(struct sockaddr_in6 *sa6)
615 {
616 	struct sioc_addrreq areq;
617 	int s;
618 
619 	if ((s = open("/dev/udp6", O_RDONLY)) < 0) {
620 		syslog(LOG_ERR, "is_myself: can't open /dev/udp6: %m");
621 		return (0);
622 	}
623 
624 	(void) memcpy(&areq.sa_addr, sa6, sizeof (struct sockaddr_storage));
625 	areq.sa_res = -1;
626 
627 	if (ioctl(s, SIOCTMYADDR, (caddr_t)&areq) < 0) {
628 		syslog(LOG_ERR, "is_myself:SIOCTMYADDR failed: %m");
629 		(void) close(s);
630 		return (0);
631 	}
632 
633 	(void) close(s);
634 	return (areq.sa_res);
635 
636 }
637 /*
638  * For a given destination address, determine a source address to use.
639  * Returns wildcard address if it cannot determine the source address.
640  * copied from ping.c.
641  */
642 union any_in_addr {
643 	struct in6_addr addr6;
644 	struct in_addr addr;
645 };
646 
647 static bool_t
648 select_server_addr(union any_in_addr *dst_addr, int family,
649     union any_in_addr *src_addr)
650 {
651 	struct sockaddr *sock;
652 	struct sockaddr_in *sin;
653 	struct sockaddr_in6 *sin6;
654 	int tmp_fd;
655 	socklen_t sock_len;
656 
657 	sock = calloc(1, sizeof (struct sockaddr_in6));
658 	if (sock == NULL) {
659 		return (FALSE);
660 	}
661 
662 	if (family == AF_INET) {
663 		/* LINTED pointer cast */
664 		sin = (struct sockaddr_in *)sock;
665 		sin->sin_family = AF_INET;
666 		sin->sin_port = 111;
667 		sin->sin_addr = dst_addr->addr;
668 		sock_len = sizeof (struct sockaddr_in);
669 	} else {
670 		/* LINTED pointer cast */
671 		sin6 = (struct sockaddr_in6 *)sock;
672 		sin6->sin6_family = AF_INET6;
673 		sin6->sin6_port = 111;
674 		sin6->sin6_addr = dst_addr->addr6;
675 		sock_len = sizeof (struct sockaddr_in6);
676 	}
677 
678 	/* open a UDP socket */
679 	tmp_fd = _so_socket(family, SOCK_DGRAM, 0, NULL, SOV_SOCKBSD);
680 	if (tmp_fd < 0) {
681 		syslog(LOG_ERR, "select_server_addr: connect failed\n");
682 		return (FALSE);
683 	}
684 
685 	/* connect it */
686 	if (_so_connect(tmp_fd, sock, sock_len, SOV_SOCKBSD) < 0) {
687 		/*
688 		 * If there's no route to the destination, this connect() call
689 		 * fails. We just return all-zero (wildcard) as the source
690 		 * address, so that user can get to see "no route to dest"
691 		 * message, as it'll try to send the probe packet out and will
692 		 * receive ICMP unreachable.
693 		 */
694 		if (family == AF_INET) {
695 			src_addr->addr.s_addr = INADDR_ANY;
696 		} else {
697 			/*
698 			 * Since in6addr_any is not in the scope
699 			 * use the following hack
700 			 */
701 			(void) memset(src_addr->addr6.s6_addr,
702 			    0, sizeof (struct in6_addr));
703 		}
704 		(void) close(tmp_fd);
705 		free(sock);
706 		return (FALSE);
707 	}
708 
709 	/* get the local sock info */
710 	if (_so_getsockname(tmp_fd, sock, &sock_len, SOV_DEFAULT) < 0) {
711 		syslog(LOG_ERR, "select_server_addr: getsockname failed\n");
712 		(void) close(tmp_fd);
713 		free(sock);
714 		return (FALSE);
715 	}
716 
717 	if (family == AF_INET) {
718 		/* LINTED pointer cast */
719 		sin = (struct sockaddr_in *)sock;
720 		src_addr->addr = sin->sin_addr;
721 	} else {
722 		/* LINTED pointer cast */
723 		sin6 = (struct sockaddr_in6 *)sock;
724 		src_addr->addr6 = sin6->sin6_addr;
725 	}
726 
727 	(void) close(tmp_fd);
728 	free(sock);
729 	return (TRUE);
730 }
731 
732 /*
733  * This internal routine will merge one of those "universal" addresses
734  * to the one which will make sense to the remote caller.
735  */
736 static char *
737 inet_netdir_mergeaddr(struct netconfig *tp, char *ruaddr, char *uaddr)
738 {
739 	char	tmp[SYS_NMLN], *cp;
740 	int	j;
741 	struct	in_addr clientaddr, bestmatch;
742 	time_t	curtime;
743 	int af;
744 
745 	if (!uaddr || !ruaddr || !tp) {
746 		_nderror = ND_BADARG;
747 		return (NULL);
748 	}
749 	(void) bzero(tmp, SYS_NMLN);
750 
751 	if (strcmp(tp->nc_protofmly, NC_INET) == 0)
752 		af = AF_INET;
753 	else
754 		af = AF_INET6;
755 
756 	if (af == AF_INET) {
757 		if (strncmp(ruaddr, "0.0.0.0.", strlen("0.0.0.0.")) == 0)
758 			/* thats me: return the way it is */
759 			return (strdup(uaddr));
760 
761 		/*
762 		 * Convert remote uaddr into an in_addr so that we can compare
763 		 * to it.  Shave off last two dotted-decimal values.
764 		 */
765 		for (cp = ruaddr, j = 0; j < 4; j++, cp++)
766 			if ((cp = strchr(cp, '.')) == NULL)
767 				break;
768 
769 		if (cp != NULL)
770 			*--cp = '\0';	/* null out the dot after the IP addr */
771 		else {
772 			_nderror = ND_NOHOST;
773 			return (NULL);
774 		}
775 
776 		clientaddr.s_addr = inet_addr(ruaddr);
777 
778 		/* We know cp is not NULL due to the check above */
779 		*cp = '.';	/* Put the dot back in the IP addr */
780 
781 		(void) time(&curtime);
782 		if ((curtime - last_updated) >= if_cache_refresh_time) {
783 			/*
784 			 * Cache needs to be refreshed.
785 			 */
786 			if (!update_if_cache())
787 				return (NULL);
788 		}
789 
790 		/*
791 		 * Find the best match now.
792 		 */
793 		(void) rw_rdlock(&iflock);
794 		bestmatch.s_addr = get_best_match(clientaddr);
795 		(void) rw_unlock(&iflock);
796 
797 		if (bestmatch.s_addr)
798 			_nderror = ND_OK;
799 		else {
800 			_nderror = ND_NOHOST;
801 			return (NULL);
802 		}
803 
804 		/* prepare the reply */
805 		(void) memset(tmp, '\0', sizeof (tmp));
806 
807 		/* reply consists of the IP addr of the closest interface */
808 		(void) strcpy(tmp, inet_ntoa(bestmatch));
809 
810 		/*
811 		 * ... and the port number part (last two dotted-decimal values)
812 		 * of uaddr
813 		 */
814 		for (cp = uaddr, j = 0; j < 4; j++, cp++)
815 			cp = strchr(cp, '.');
816 		(void) strcat(tmp, --cp);
817 
818 	} else {
819 		/* IPv6 */
820 		char *dot;
821 		char *truaddr;
822 		struct sockaddr_in6 sa;
823 		struct sockaddr_in6 server_addr;
824 		union any_in_addr in_addr, out_addr;
825 
826 		if (strncmp(ruaddr, "::", strlen("::")) == 0)
827 			if (*(ruaddr + strlen("::")) == '\0')
828 				/* thats me: return the way it is */
829 				return (strdup(uaddr));
830 
831 		bzero(&sa, sizeof (sa));
832 		bzero(&server_addr, sizeof (server_addr));
833 		truaddr = &tmp[0];
834 		(void) strcpy(truaddr, ruaddr);
835 
836 		/*
837 		 * now extract the server ip address from
838 		 * the address supplied by client.  It can be
839 		 * client's own IP address.
840 		 */
841 
842 		if ((dot = strrchr(truaddr, '.')) != 0) {
843 			*dot = '\0';
844 			if ((dot = strrchr(truaddr, '.')) != 0)
845 				*dot = '\0';
846 		}
847 
848 		if (dot == 0) {
849 			_nderror = ND_NOHOST;
850 			return (NULL);
851 		}
852 
853 		if (inet_pton(af, truaddr, sa.sin6_addr.s6_addr)
854 		    != 1) {
855 			_nderror = ND_NOHOST;
856 			return (NULL);
857 		}
858 
859 		in_addr.addr6 = sa.sin6_addr;
860 		sa.sin6_family = AF_INET6;
861 
862 		/* is it my IP address */
863 		if (!is_myself(&sa)) {
864 			/* have the kernel select one for me */
865 			if (select_server_addr(&in_addr, af, &out_addr) ==
866 			    FALSE)
867 				return (NULL);
868 			server_addr.sin6_addr = out_addr.addr6;
869 		} else {
870 			(void) memcpy(&server_addr, &sa, sizeof (server_addr));
871 		}
872 
873 		if (inet_ntop(af, server_addr.sin6_addr.s6_addr, tmp,
874 		    sizeof (tmp)) == NULL) {
875 			_nderror = ND_NOHOST;
876 			return (NULL);
877 		}
878 
879 		/* now extract the port info */
880 		if ((dot = strrchr(uaddr, '.')) != 0) {
881 			char *p = --dot;
882 
883 			while (*p-- != '.')
884 				;
885 			p++;
886 			(void) strcat(tmp + strlen(tmp), p);
887 			_nderror = ND_OK;
888 		} else {
889 			_nderror = ND_NOHOST;
890 			return (NULL);
891 		}
892 
893 	}
894 	return (strdup(tmp));
895 }
896 
897 static int
898 bindresvport(struct netconfig *nconf, int fd, struct netbuf *addr)
899 {
900 	int res;
901 	struct sockaddr_in myaddr;
902 	struct sockaddr_in6 myaddr6;
903 	struct sockaddr_in *sin;
904 	struct sockaddr_in6 *sin6;
905 	int i;
906 	struct t_bind tbindstr, *tres;
907 	struct t_info tinfo;
908 	struct t_optmgmt req, resp;
909 	struct opthdr *opt;
910 	int reqbuf[64/sizeof (int)];
911 	int *optval;
912 
913 	union {
914 		struct sockaddr_in *sin;
915 		struct sockaddr_in6 *sin6;
916 		char *buf;
917 	} u;
918 
919 	_nderror = ND_SYSTEM;
920 	if (geteuid()) {
921 		errno = EACCES;
922 		return (-1);
923 	}
924 	if ((i = t_getstate(fd)) != T_UNBND) {
925 		if (t_errno == TBADF)
926 			errno = EBADF;
927 		if (i != -1)
928 			errno = EISCONN;
929 		return (-1);
930 	}
931 
932 	if (strcmp(nconf->nc_protofmly, NC_INET) == 0) {
933 		if (addr == NULL) {
934 			sin = &myaddr;
935 			(void) memset(sin, 0, sizeof (*sin));
936 			sin->sin_family = AF_INET;
937 			u.buf = (char *)sin;
938 		} else
939 			u.buf = (char *)addr->buf;
940 	} else if (strcmp(nconf->nc_protofmly, NC_INET6) == 0) {
941 		if (addr == NULL) {
942 			sin6 = &myaddr6;
943 			(void) memset(sin6, 0, sizeof (*sin6));
944 			sin6->sin6_family = AF_INET6;
945 			u.buf = (char *)sin6;
946 		} else
947 			u.buf = addr->buf;
948 
949 	} else {
950 		errno = EPFNOSUPPORT;
951 		return (-1);
952 	}
953 
954 	/* Transform sockaddr_in to netbuf */
955 	if (t_getinfo(fd, &tinfo) == -1)
956 		return (-1);
957 	/* LINTED pointer cast */
958 	tres = (struct t_bind *)t_alloc(fd, T_BIND, T_ADDR);
959 	if (tres == NULL) {
960 		_nderror = ND_NOMEM;
961 		return (-1);
962 	}
963 
964 	tbindstr.qlen = 0; /* Always 0; user should change if they want to */
965 	tbindstr.addr.buf = (char *)u.buf;
966 	tbindstr.addr.len = tbindstr.addr.maxlen = __rpc_get_a_size(tinfo.addr);
967 
968 	/*
969 	 * Use *_ANONPRIVBIND to ask the kernel to pick a port in the
970 	 * priviledged range for us.
971 	 */
972 	opt = (struct opthdr *)reqbuf;
973 	if (strcmp(nconf->nc_proto, NC_TCP) == 0) {
974 		opt->level = IPPROTO_TCP;
975 		opt->name = TCP_ANONPRIVBIND;
976 	} else if (strcmp(nconf->nc_proto, NC_UDP) == 0) {
977 		opt->level = IPPROTO_UDP;
978 		opt->name = UDP_ANONPRIVBIND;
979 	} else {
980 		errno = EPROTONOSUPPORT;
981 		(void) t_free((char *)tres, T_BIND);
982 		return (-1);
983 	}
984 
985 	opt->len = sizeof (int);
986 	req.flags = T_NEGOTIATE;
987 	req.opt.len = sizeof (struct opthdr) + opt->len;
988 	req.opt.buf = (char *)opt;
989 	/* LINTED pointer cast */
990 	optval = (int *)((char *)reqbuf + sizeof (struct opthdr));
991 	*optval = 1;
992 	resp.flags = 0;
993 	resp.opt.buf = (char *)reqbuf;
994 	resp.opt.maxlen = sizeof (reqbuf);
995 	if (t_optmgmt(fd, &req, &resp) < 0 || resp.flags != T_SUCCESS) {
996 		(void) t_free((char *)tres, T_BIND);
997 		return (-1);
998 	}
999 
1000 	if (u.sin->sin_family == AF_INET)
1001 		u.sin->sin_port = htons(0);
1002 	else
1003 		u.sin6->sin6_port = htons(0);
1004 	res = t_bind(fd, &tbindstr, tres);
1005 	if (res != 0) {
1006 		if (t_errno == TNOADDR) {
1007 			_nderror = ND_FAILCTRL;
1008 			res = 1;
1009 		}
1010 	} else {
1011 		_nderror = ND_OK;
1012 	}
1013 
1014 	/*
1015 	 * Always turn off the option when we are done.  Note that by doing
1016 	 * this, if the caller has set this option before calling
1017 	 * bindresvport(), it will be unset.  Better be safe...
1018 	 */
1019 	*optval = 0;
1020 	resp.flags = 0;
1021 	resp.opt.buf = (char *)reqbuf;
1022 	resp.opt.maxlen = sizeof (reqbuf);
1023 	if (t_optmgmt(fd, &req, &resp) < 0 || resp.flags != T_SUCCESS) {
1024 		(void) t_free((char *)tres, T_BIND);
1025 		if (res == 0)
1026 			(void) t_unbind(fd);
1027 		_nderror = ND_FAILCTRL;
1028 		return (-1);
1029 	}
1030 
1031 	(void) t_free((char *)tres, T_BIND);
1032 	return (res);
1033 }
1034 
1035 static int
1036 checkresvport(struct netbuf *addr)
1037 {
1038 	struct sockaddr_in *sin;
1039 	unsigned short port;
1040 
1041 	if (addr == NULL) {
1042 		_nderror = ND_FAILCTRL;
1043 		return (-1);
1044 	}
1045 	/*
1046 	 * Still works for IPv6 since the first two memebers of
1047 	 * both address structure point to family and port # respectively
1048 	 */
1049 	/* LINTED pointer cast */
1050 	sin = (struct sockaddr_in *)(addr->buf);
1051 	port = ntohs(sin->sin_port);
1052 	if (port < IPPORT_RESERVED)
1053 		return (0);
1054 	return (1);
1055 }
1056