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