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