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