import less(1)
[unleashed/tickless.git] / usr / src / lib / libc / port / nsl / netdir_inet_sundry.c
blob8009e8b03978ba92e75a87bbc6d41f0903f6f7f9
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 * Copyright (c) 2016 by Delphix. All rights reserved.
27 * lib/libnsl/nss/netdir_inet_sundry.c
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).
39 * Copied mostly from erstwhile lib/nametoaddr/tcpip/tcpip.c.
42 #include "mt.h"
43 #include <stdlib.h>
44 #include <stdio.h>
45 #include <string.h>
46 #include <strings.h>
47 #include <unistd.h>
48 #include <sys/types.h>
49 #include <sys/stat.h>
50 #include <fcntl.h>
51 #include <errno.h>
52 #include <thread.h>
53 #include <netconfig.h>
54 #include <netdir.h>
55 #include <nss_netdir.h>
56 #include <tiuser.h>
57 #include <sys/socket.h>
58 #include <net/if.h>
59 #include <sys/sockio.h>
60 #include <sys/fcntl.h>
61 #include <netinet/in.h>
62 #include <netinet/tcp.h>
63 #include <netinet/udp.h>
64 #include <arpa/inet.h>
65 #include <rpc/types.h>
66 #include <rpc/rpc_com.h>
67 #include <syslog.h>
68 #include <values.h>
69 #include <limits.h>
70 #include <nss_dbdefs.h>
71 #include "nss.h"
73 #define MAXIFS 32
75 extern int _so_socket(int, int, int, char *);
76 extern int _so_connect(int, struct sockaddr *, socklen_t);
77 extern int _so_getsockname(int, struct sockaddr *, socklen_t *);
80 static char *inet_netdir_mergeaddr(struct netconfig *, char *, char *);
81 static int bindresvport(struct netconfig *, int, struct netbuf *);
82 static int checkresvport(struct netbuf *);
83 static struct netbuf *ip_uaddr2taddr(char *);
84 static struct netbuf *ipv6_uaddr2taddr(char *);
87 extern char *inet_ntoa_r(struct in_addr, char *);
89 int
90 __inet_netdir_options(struct netconfig *tp, int opts, int fd, char *par)
92 struct nd_mergearg *ma;
94 switch (opts) {
95 case ND_SET_BROADCAST:
96 /* Every one is allowed to broadcast without asking */
97 return (ND_OK);
98 case ND_SET_RESERVEDPORT: /* bind to a resered port */
99 /* LINTED pointer cast */
100 return (bindresvport(tp, fd, (struct netbuf *)par));
101 case ND_CHECK_RESERVEDPORT: /* check if reserved prot */
102 /* LINTED pointer cast */
103 return (checkresvport((struct netbuf *)par));
104 case ND_MERGEADDR: /* Merge two addresses */
105 /* LINTED pointer cast */
106 ma = (struct nd_mergearg *)(par);
107 ma->m_uaddr = inet_netdir_mergeaddr(tp, ma->c_uaddr,
108 ma->s_uaddr);
109 return (_nderror);
110 default:
111 return (ND_NOCTRL);
117 * This routine will convert a TCP/IP internal format address
118 * into a "universal" format address. In our case it prints out the
119 * decimal dot equivalent. h1.h2.h3.h4.p1.p2 where h1-h4 are the host
120 * address and p1-p2 are the port number.
122 char *
123 __inet_taddr2uaddr(struct netconfig *tp, struct netbuf *addr)
125 struct sockaddr_in *sa; /* our internal format */
126 struct sockaddr_in6 *sa6; /* our internal format */
127 char tmp[RPC_INET6_MAXUADDRSIZE];
128 unsigned short myport;
130 if (addr == NULL || tp == NULL || addr->buf == NULL) {
131 _nderror = ND_BADARG;
132 return (NULL);
134 if (strcmp(tp->nc_protofmly, NC_INET) == 0) {
135 /* LINTED pointer cast */
136 sa = (struct sockaddr_in *)(addr->buf);
137 myport = ntohs(sa->sin_port);
138 (void) inet_ntoa_r(sa->sin_addr, tmp);
139 } else {
140 /* LINTED pointer cast */
141 sa6 = (struct sockaddr_in6 *)(addr->buf);
142 myport = ntohs(sa6->sin6_port);
143 if (inet_ntop(AF_INET6, sa6->sin6_addr.s6_addr, tmp,
144 sizeof (tmp)) == NULL) {
145 _nderror = ND_BADARG;
146 return (NULL);
150 (void) sprintf(tmp + strlen(tmp), ".%d.%d", myport >> 8, myport & 255);
151 return (strdup(tmp)); /* Doesn't return static data ! */
155 * This internal routine will convert one of those "universal" addresses
156 * to the internal format used by the Sun TLI TCP/IP provider.
158 struct netbuf *
159 __inet_uaddr2taddr(struct netconfig *tp, char *addr)
161 if (!addr || !tp) {
162 _nderror = ND_BADARG;
163 return (NULL);
165 if (strcmp(tp->nc_protofmly, NC_INET) == 0)
166 return (ip_uaddr2taddr(addr));
167 else
168 return (ipv6_uaddr2taddr(addr));
171 static struct netbuf *
172 ip_uaddr2taddr(char *addr)
175 struct sockaddr_in *sa;
176 uint32_t inaddr;
177 unsigned short inport;
178 int h1, h2, h3, h4, p1, p2;
179 struct netbuf *result;
181 result = malloc(sizeof (struct netbuf));
182 if (!result) {
183 _nderror = ND_NOMEM;
184 return (NULL);
187 sa = calloc(1, sizeof (*sa));
189 if (!sa) {
190 free(result);
191 _nderror = ND_NOMEM;
192 return (NULL);
195 result->buf = (char *)(sa);
196 result->maxlen = sizeof (struct sockaddr_in);
197 result->len = sizeof (struct sockaddr_in);
199 /* XXX there is probably a better way to do this. */
200 if (sscanf(addr, "%d.%d.%d.%d.%d.%d", &h1, &h2, &h3, &h4,
201 &p1, &p2) != 6) {
202 free(result);
203 _nderror = ND_NO_RECOVERY;
204 return (NULL);
207 /* convert the host address first */
208 inaddr = (h1 << 24) + (h2 << 16) + (h3 << 8) + h4;
209 sa->sin_addr.s_addr = htonl(inaddr);
211 /* convert the port */
212 inport = (p1 << 8) + p2;
213 sa->sin_port = htons(inport);
215 sa->sin_family = AF_INET;
217 return (result);
220 static struct netbuf *
221 ipv6_uaddr2taddr(char *addr)
223 struct sockaddr_in6 *sa;
224 unsigned short inport;
225 int p1, p2;
226 struct netbuf *result;
227 char tmpaddr[RPC_INET6_MAXUADDRSIZE];
228 char *dot;
230 result = malloc(sizeof (struct netbuf));
231 if (!result) {
232 _nderror = ND_NOMEM;
233 return (NULL);
236 sa = calloc(1, sizeof (struct sockaddr_in6));
237 if (!sa) {
238 free(result);
239 _nderror = ND_NOMEM;
240 return (NULL);
242 result->buf = (char *)(sa);
243 result->maxlen = sizeof (struct sockaddr_in6);
244 result->len = sizeof (struct sockaddr_in6);
246 /* retrieve the ipv6 address and port info */
248 if (strlen(addr) > sizeof (tmpaddr) - 1) {
249 free(result);
250 _nderror = ND_NOMEM;
251 return (NULL);
254 (void) strcpy(tmpaddr, addr);
256 if ((dot = strrchr(tmpaddr, '.')) != 0) {
257 *dot = '\0';
258 p2 = atoi(dot+1);
259 if ((dot = strrchr(tmpaddr, '.')) != 0) {
260 *dot = '\0';
261 p1 = atoi(dot+1);
265 if (dot == 0) {
266 free(result);
267 _nderror = ND_NOMEM;
268 return (NULL);
271 if (inet_pton(AF_INET6, tmpaddr, sa->sin6_addr.s6_addr) == 0) {
272 free(result);
273 _nderror = ND_NOMEM;
274 return (NULL);
277 /* convert the port */
278 inport = (p1 << 8) + p2;
279 sa->sin6_port = htons(inport);
281 sa->sin6_family = AF_INET6;
283 return (result);
287 * Interface caching routines. The cache is refreshed every
288 * IF_CACHE_REFRESH_TIME seconds. A read-write lock is used to
289 * protect the cache.
291 #define IF_CACHE_REFRESH_TIME 10
293 static int if_cache_refresh_time = IF_CACHE_REFRESH_TIME;
294 static rwlock_t iflock = DEFAULTRWLOCK;
295 static time_t last_updated = 0; /* protected by iflock */
298 * Changing the data type of if_flags from uint_t to uint64_t to accomodate
299 * extra flags. Refer <net/if.h> for the extra flags.
301 typedef struct if_info_s {
302 struct in_addr if_netmask; /* netmask in network order */
303 struct in_addr if_address; /* address in network order */
304 uint64_t if_flags; /* interface flags */
305 } if_info_t;
307 static if_info_t *if_info = NULL; /* if cache, protected by iflock */
308 static int n_ifs = 0; /* number of cached interfaces */
309 static int numifs_last = 0; /* number of interfaces last seen */
312 * Builds the interface cache. Write lock on iflock is needed
313 * for calling this routine. It sets _nderror for error returns.
314 * Returns TRUE if successful, FALSE otherwise.
315 * Changing the structures ifreq and ifconf to lifreq and lifconf to
316 * have larger flag field. This is to accomodate the extra flags associated
317 * with the interface. Also introducing lifn which will contain the number
318 * of IPV4 interfaces present.
320 static bool_t
321 get_if_info(void)
323 size_t needed;
324 struct lifreq *buf = NULL;
325 int numifs;
326 struct lifconf lifc;
327 struct lifreq *lifr;
328 struct lifnum lifn;
330 lifn.lifn_family = AF_INET;
331 lifn.lifn_flags = 0;
332 getifnum:
333 if (nss_ioctl(AF_INET, SIOCGLIFNUM, &lifn) == -1) {
334 numifs = MAXIFS;
335 } else {
336 numifs = lifn.lifn_count;
339 * Add a small fudge factor in case interfaces are plumbed
340 * between the SIOCGLIFNUM and SIOCGLIFCONF.
342 needed = (numifs + 4) * sizeof (struct lifreq);
343 if (buf == NULL)
344 buf = malloc(needed);
345 else
346 buf = realloc(buf, needed);
347 if (buf == NULL) {
348 _nderror = ND_NOMEM;
349 return (FALSE);
352 lifc.lifc_family = AF_INET;
353 lifc.lifc_flags = 0;
354 lifc.lifc_len = needed;
355 lifc.lifc_buf = (char *)buf;
356 if (nss_ioctl(AF_INET, SIOCGLIFCONF, &lifc) == -1) {
358 * IP returns EINVAL if the buffer was too small to fit
359 * all of the entries. If that's the case, go back and
360 * try again.
362 if (errno == EINVAL)
363 goto getifnum;
365 free(buf);
366 free(if_info);
367 if_info = NULL;
368 _nderror = ND_SYSTEM;
369 return (FALSE);
371 numifs = lifc.lifc_len / (int)sizeof (struct lifreq);
373 if (if_info == NULL || numifs > numifs_last) {
374 if (if_info == NULL)
375 if_info = malloc(numifs * sizeof (if_info_t));
376 else
377 if_info = reallocarray(if_info, numifs,
378 sizeof (if_info_t));
379 if (if_info == NULL) {
380 free(buf);
381 _nderror = ND_NOMEM;
382 return (FALSE);
384 numifs_last = numifs;
387 n_ifs = 0;
388 for (lifr = buf; lifr < (buf + numifs); lifr++) {
389 if (lifr->lifr_addr.ss_family != AF_INET)
390 continue;
392 if_info[n_ifs].if_address =
393 ((struct sockaddr_in *)&lifr->lifr_addr)->sin_addr;
395 if (nss_ioctl(AF_INET, SIOCGLIFFLAGS, lifr) < 0)
396 continue;
398 if ((lifr->lifr_flags & IFF_UP) == 0)
399 continue;
400 if_info[n_ifs].if_flags = lifr->lifr_flags;
402 if (nss_ioctl(AF_INET, SIOCGLIFNETMASK, lifr) < 0)
403 continue;
405 if_info[n_ifs].if_netmask =
406 ((struct sockaddr_in *)&lifr->lifr_addr)->sin_addr;
407 n_ifs++;
409 free(buf);
410 return (TRUE);
415 * Update the interface cache based on last update time.
417 static bool_t
418 update_if_cache(void)
420 time_t curtime;
422 (void) rw_wrlock(&iflock);
424 * Check if some other thread has beaten this one to it.
426 (void) time(&curtime);
427 if ((curtime - last_updated) >= if_cache_refresh_time) {
428 if (!get_if_info()) {
429 (void) rw_unlock(&iflock);
430 return (FALSE);
432 (void) time(&last_updated);
434 (void) rw_unlock(&iflock);
435 return (TRUE);
440 * Given an IP address, check if this matches any of the interface
441 * addresses. If an error occurs, return FALSE so that the caller
442 * will not assume that this address belongs to this machine.
444 static bool_t
445 is_my_address(struct in_addr addr)
447 time_t curtime;
448 if_info_t *ifn;
450 (void) time(&curtime);
451 if ((curtime - last_updated) >= if_cache_refresh_time) {
453 * Cache needs to be refreshed.
455 if (!update_if_cache())
456 return (FALSE);
458 (void) rw_rdlock(&iflock);
459 for (ifn = if_info; ifn < (if_info + n_ifs); ifn++) {
460 if (addr.s_addr == ifn->if_address.s_addr) {
461 (void) rw_unlock(&iflock);
462 return (TRUE);
465 (void) rw_unlock(&iflock);
466 return (FALSE);
471 * Given a host name, check if it is this host.
473 bool_t
474 __inet_netdir_is_my_host(const char *host)
476 int error;
477 char buf[NSS_BUFLEN_HOSTS];
478 struct hostent res, *h;
479 char **c;
480 struct in_addr in;
482 h = gethostbyname_r(host, (void *)&res, buf, sizeof (buf), &error);
483 if (h == NULL)
484 return (FALSE);
485 if (h->h_addrtype != AF_INET)
486 return (FALSE);
487 for (c = h->h_addr_list; *c != NULL; c++) {
488 (void) memcpy(&in.s_addr, *c, sizeof (in.s_addr));
489 if (is_my_address(in))
490 return (TRUE);
492 return (FALSE);
497 * Given an IP address, find the interface address that has the best
498 * prefix match. Return the address in network order.
500 static uint32_t
501 get_best_match(struct in_addr addr)
503 if_info_t *bestmatch, *ifn;
504 int bestcount, count, limit;
505 uint32_t mask, netmask, clnt_addr, if_addr;
506 bool_t found, subnet_match;
507 int subnet_count;
509 bestmatch = NULL; /* no match yet */
510 bestcount = BITSPERBYTE * sizeof (uint32_t); /* worst match */
511 clnt_addr = ntohl(addr.s_addr); /* host order */
513 subnet_match = FALSE; /* subnet match not found yet */
514 subnet_count = bestcount; /* worst subnet match */
516 for (ifn = if_info; ifn < (if_info + n_ifs); ifn++) {
517 netmask = ntohl(ifn->if_netmask.s_addr); /* host order */
518 if_addr = ntohl(ifn->if_address.s_addr); /* host order */
521 * set initial count to first bit set in netmask, with
522 * zero being the number of the least significant bit.
524 count = 0;
525 for (mask = netmask; mask && ((mask & 1) == 0); mask >>= 1)
526 count++;
529 * Set limit so that we don't try to match prefixes shorter
530 * than the inherent netmask for the class (A, B, C, etc).
532 if (IN_CLASSC(if_addr))
533 limit = IN_CLASSC_NSHIFT;
534 else if (IN_CLASSB(if_addr))
535 limit = IN_CLASSB_NSHIFT;
536 else if (IN_CLASSA(if_addr))
537 limit = IN_CLASSA_NSHIFT;
538 else
539 limit = 0;
542 * We assume that the netmask consists of a contiguous
543 * sequence of 1-bits starting with the most significant bit.
544 * Prefix comparison starts at the subnet mask level.
545 * The prefix mask used for comparison is progressively
546 * reduced until it equals the inherent mask for the
547 * interface address class. The algorithm finds an
548 * interface in the following order of preference:
550 * (1) the longest subnet match
551 * (2) the best partial subnet match
552 * (3) the first non-loopback && non-PPP interface
553 * (4) the first non-loopback interface (PPP is OK)
555 found = FALSE;
556 while (netmask && count < subnet_count) {
557 if ((netmask & clnt_addr) == (netmask & if_addr)) {
558 bestcount = count;
559 bestmatch = ifn;
560 found = TRUE;
561 break;
563 netmask <<= 1;
564 count++;
565 if (count >= bestcount || count > limit || subnet_match)
566 break;
569 * If a subnet level match occurred, note this for
570 * comparison with future subnet matches.
572 if (found && (netmask == ntohl(ifn->if_netmask.s_addr))) {
573 subnet_match = TRUE;
574 subnet_count = count;
579 * If we don't have a match, select the first interface that
580 * is not a loopback interface (and preferably not a PPP interface)
581 * as the best match.
583 if (bestmatch == NULL) {
584 for (ifn = if_info; ifn < (if_info + n_ifs); ifn++) {
585 if ((ifn->if_flags & IFF_LOOPBACK) == 0) {
586 bestmatch = ifn;
589 * If this isn't a PPP interface, we're
590 * done. Otherwise, keep walking through
591 * the list in case we have a non-loopback
592 * iface that ISN'T a PPP further down our
593 * list...
595 if ((ifn->if_flags & IFF_POINTOPOINT) == 0) {
596 break;
602 if (bestmatch != NULL)
603 return (bestmatch->if_address.s_addr);
604 else
605 return (0);
608 static int
609 is_myself(struct sockaddr_in6 *sa6)
611 struct sioc_addrreq areq;
612 int s;
614 if ((s = open("/dev/udp6", O_RDONLY)) < 0) {
615 syslog(LOG_ERR, "is_myself: can't open /dev/udp6: %m");
616 return (0);
619 (void) memcpy(&areq.sa_addr, sa6, sizeof (struct sockaddr_storage));
620 areq.sa_res = -1;
622 if (ioctl(s, SIOCTMYADDR, (caddr_t)&areq) < 0) {
623 syslog(LOG_ERR, "is_myself:SIOCTMYADDR failed: %m");
624 (void) close(s);
625 return (0);
628 (void) close(s);
629 return (areq.sa_res);
633 * For a given destination address, determine a source address to use.
634 * Returns wildcard address if it cannot determine the source address.
635 * copied from ping.c.
637 union any_in_addr {
638 struct in6_addr addr6;
639 struct in_addr addr;
642 static bool_t
643 select_server_addr(union any_in_addr *dst_addr, int family,
644 union any_in_addr *src_addr)
646 struct sockaddr *sock;
647 struct sockaddr_in *sin;
648 struct sockaddr_in6 *sin6;
649 int tmp_fd;
650 socklen_t sock_len;
652 sock = calloc(1, sizeof (struct sockaddr_in6));
653 if (sock == NULL) {
654 return (FALSE);
657 if (family == AF_INET) {
658 /* LINTED pointer cast */
659 sin = (struct sockaddr_in *)sock;
660 sin->sin_family = AF_INET;
661 sin->sin_port = 111;
662 sin->sin_addr = dst_addr->addr;
663 sock_len = sizeof (struct sockaddr_in);
664 } else {
665 /* LINTED pointer cast */
666 sin6 = (struct sockaddr_in6 *)sock;
667 sin6->sin6_family = AF_INET6;
668 sin6->sin6_port = 111;
669 sin6->sin6_addr = dst_addr->addr6;
670 sock_len = sizeof (struct sockaddr_in6);
673 /* open a UDP socket */
674 tmp_fd = _so_socket(family, SOCK_DGRAM, 0, NULL);
675 if (tmp_fd < 0) {
676 syslog(LOG_ERR, "select_server_addr: connect failed\n");
677 return (FALSE);
680 /* connect it */
681 if (_so_connect(tmp_fd, sock, sock_len) < 0) {
683 * If there's no route to the destination, this connect() call
684 * fails. We just return all-zero (wildcard) as the source
685 * address, so that user can get to see "no route to dest"
686 * message, as it'll try to send the probe packet out and will
687 * receive ICMP unreachable.
689 if (family == AF_INET) {
690 src_addr->addr.s_addr = INADDR_ANY;
691 } else {
693 * Since in6addr_any is not in the scope
694 * use the following hack
696 (void) memset(src_addr->addr6.s6_addr,
697 0, sizeof (struct in6_addr));
699 (void) close(tmp_fd);
700 free(sock);
701 return (FALSE);
704 /* get the local sock info */
705 if (_so_getsockname(tmp_fd, sock, &sock_len) < 0) {
706 syslog(LOG_ERR, "select_server_addr: getsockname failed\n");
707 (void) close(tmp_fd);
708 free(sock);
709 return (FALSE);
712 if (family == AF_INET) {
713 /* LINTED pointer cast */
714 sin = (struct sockaddr_in *)sock;
715 src_addr->addr = sin->sin_addr;
716 } else {
717 /* LINTED pointer cast */
718 sin6 = (struct sockaddr_in6 *)sock;
719 src_addr->addr6 = sin6->sin6_addr;
722 (void) close(tmp_fd);
723 free(sock);
724 return (TRUE);
728 * This internal routine will merge one of those "universal" addresses
729 * to the one which will make sense to the remote caller.
731 static char *
732 inet_netdir_mergeaddr(struct netconfig *tp, char *ruaddr, char *uaddr)
734 char tmp[SYS_NMLN], *cp;
735 int j;
736 struct in_addr clientaddr, bestmatch;
737 time_t curtime;
738 int af;
740 if (!uaddr || !ruaddr || !tp) {
741 _nderror = ND_BADARG;
742 return (NULL);
744 (void) bzero(tmp, SYS_NMLN);
746 if (strcmp(tp->nc_protofmly, NC_INET) == 0)
747 af = AF_INET;
748 else
749 af = AF_INET6;
751 if (af == AF_INET) {
752 if (strncmp(ruaddr, "0.0.0.0.", strlen("0.0.0.0.")) == 0)
753 /* thats me: return the way it is */
754 return (strdup(uaddr));
757 * Convert remote uaddr into an in_addr so that we can compare
758 * to it. Shave off last two dotted-decimal values.
760 for (cp = ruaddr, j = 0; j < 4; j++, cp++)
761 if ((cp = strchr(cp, '.')) == NULL)
762 break;
764 if (cp != NULL)
765 *--cp = '\0'; /* null out the dot after the IP addr */
766 else {
767 _nderror = ND_NOHOST;
768 return (NULL);
771 clientaddr.s_addr = inet_addr(ruaddr);
773 /* We know cp is not NULL due to the check above */
774 *cp = '.'; /* Put the dot back in the IP addr */
776 (void) time(&curtime);
777 if ((curtime - last_updated) >= if_cache_refresh_time) {
779 * Cache needs to be refreshed.
781 if (!update_if_cache())
782 return (NULL);
786 * Find the best match now.
788 (void) rw_rdlock(&iflock);
789 bestmatch.s_addr = get_best_match(clientaddr);
790 (void) rw_unlock(&iflock);
792 if (bestmatch.s_addr)
793 _nderror = ND_OK;
794 else {
795 _nderror = ND_NOHOST;
796 return (NULL);
799 /* prepare the reply */
800 (void) memset(tmp, '\0', sizeof (tmp));
802 /* reply consists of the IP addr of the closest interface */
803 (void) strcpy(tmp, inet_ntoa(bestmatch));
806 * ... and the port number part (last two dotted-decimal values)
807 * of uaddr
809 for (cp = uaddr, j = 0; j < 4; j++, cp++)
810 cp = strchr(cp, '.');
811 (void) strcat(tmp, --cp);
813 } else {
814 /* IPv6 */
815 char *dot;
816 char *truaddr;
817 struct sockaddr_in6 sa;
818 struct sockaddr_in6 server_addr;
819 union any_in_addr in_addr, out_addr;
821 if (strncmp(ruaddr, "::", strlen("::")) == 0)
822 if (*(ruaddr + strlen("::")) == '\0')
823 /* thats me: return the way it is */
824 return (strdup(uaddr));
826 bzero(&sa, sizeof (sa));
827 bzero(&server_addr, sizeof (server_addr));
828 truaddr = &tmp[0];
829 (void) strcpy(truaddr, ruaddr);
832 * now extract the server ip address from
833 * the address supplied by client. It can be
834 * client's own IP address.
837 if ((dot = strrchr(truaddr, '.')) != 0) {
838 *dot = '\0';
839 if ((dot = strrchr(truaddr, '.')) != 0)
840 *dot = '\0';
843 if (dot == 0) {
844 _nderror = ND_NOHOST;
845 return (NULL);
848 if (inet_pton(af, truaddr, sa.sin6_addr.s6_addr)
849 != 1) {
850 _nderror = ND_NOHOST;
851 return (NULL);
854 in_addr.addr6 = sa.sin6_addr;
855 sa.sin6_family = AF_INET6;
857 /* is it my IP address */
858 if (!is_myself(&sa)) {
859 /* have the kernel select one for me */
860 if (select_server_addr(&in_addr, af, &out_addr) ==
861 FALSE)
862 return (NULL);
863 server_addr.sin6_addr = out_addr.addr6;
864 } else {
865 (void) memcpy(&server_addr, &sa, sizeof (server_addr));
868 if (inet_ntop(af, server_addr.sin6_addr.s6_addr, tmp,
869 sizeof (tmp)) == NULL) {
870 _nderror = ND_NOHOST;
871 return (NULL);
874 /* now extract the port info */
875 if ((dot = strrchr(uaddr, '.')) != 0) {
876 char *p = --dot;
878 while (*p-- != '.')
880 p++;
881 (void) strcat(tmp + strlen(tmp), p);
882 _nderror = ND_OK;
883 } else {
884 _nderror = ND_NOHOST;
885 return (NULL);
889 return (strdup(tmp));
892 static int
893 bindresvport(struct netconfig *nconf, int fd, struct netbuf *addr)
895 int res;
896 struct sockaddr_in myaddr;
897 struct sockaddr_in6 myaddr6;
898 struct sockaddr_in *sin;
899 struct sockaddr_in6 *sin6;
900 int i;
901 struct t_bind tbindstr, *tres;
902 struct t_info tinfo;
903 struct t_optmgmt req, resp;
904 struct opthdr *opt;
905 int reqbuf[64/sizeof (int)];
906 int *optval;
908 union {
909 struct sockaddr_in *sin;
910 struct sockaddr_in6 *sin6;
911 char *buf;
912 } u;
914 _nderror = ND_SYSTEM;
915 if (geteuid()) {
916 errno = EACCES;
917 return (-1);
919 if ((i = t_getstate(fd)) != T_UNBND) {
920 if (t_errno == TBADF)
921 errno = EBADF;
922 if (i != -1)
923 errno = EISCONN;
924 return (-1);
927 if (strcmp(nconf->nc_protofmly, NC_INET) == 0) {
928 if (addr == NULL) {
929 sin = &myaddr;
930 (void) memset(sin, 0, sizeof (*sin));
931 sin->sin_family = AF_INET;
932 u.buf = (char *)sin;
933 } else
934 u.buf = (char *)addr->buf;
935 } else if (strcmp(nconf->nc_protofmly, NC_INET6) == 0) {
936 if (addr == NULL) {
937 sin6 = &myaddr6;
938 (void) memset(sin6, 0, sizeof (*sin6));
939 sin6->sin6_family = AF_INET6;
940 u.buf = (char *)sin6;
941 } else
942 u.buf = addr->buf;
944 } else {
945 errno = EPFNOSUPPORT;
946 return (-1);
949 /* Transform sockaddr_in to netbuf */
950 if (t_getinfo(fd, &tinfo) == -1)
951 return (-1);
952 /* LINTED pointer cast */
953 tres = (struct t_bind *)t_alloc(fd, T_BIND, T_ADDR);
954 if (tres == NULL) {
955 _nderror = ND_NOMEM;
956 return (-1);
959 tbindstr.qlen = 0; /* Always 0; user should change if they want to */
960 tbindstr.addr.buf = (char *)u.buf;
961 tbindstr.addr.len = tbindstr.addr.maxlen = __rpc_get_a_size(tinfo.addr);
964 * Use *_ANONPRIVBIND to ask the kernel to pick a port in the
965 * priviledged range for us.
967 opt = (struct opthdr *)reqbuf;
968 if (strcmp(nconf->nc_proto, NC_TCP) == 0) {
969 opt->level = IPPROTO_TCP;
970 opt->name = TCP_ANONPRIVBIND;
971 } else if (strcmp(nconf->nc_proto, NC_UDP) == 0) {
972 opt->level = IPPROTO_UDP;
973 opt->name = UDP_ANONPRIVBIND;
974 } else {
975 errno = EPROTONOSUPPORT;
976 (void) t_free((char *)tres, T_BIND);
977 return (-1);
980 opt->len = sizeof (int);
981 req.flags = T_NEGOTIATE;
982 req.opt.len = sizeof (struct opthdr) + opt->len;
983 req.opt.buf = (char *)opt;
984 /* LINTED pointer cast */
985 optval = (int *)((char *)reqbuf + sizeof (struct opthdr));
986 *optval = 1;
987 resp.flags = 0;
988 resp.opt.buf = (char *)reqbuf;
989 resp.opt.maxlen = sizeof (reqbuf);
990 if (t_optmgmt(fd, &req, &resp) < 0 || resp.flags != T_SUCCESS) {
991 (void) t_free((char *)tres, T_BIND);
992 return (-1);
995 if (u.sin->sin_family == AF_INET)
996 u.sin->sin_port = htons(0);
997 else
998 u.sin6->sin6_port = htons(0);
999 res = t_bind(fd, &tbindstr, tres);
1000 if (res != 0) {
1001 if (t_errno == TNOADDR) {
1002 _nderror = ND_FAILCTRL;
1003 res = 1;
1005 } else {
1006 _nderror = ND_OK;
1010 * Always turn off the option when we are done. Note that by doing
1011 * this, if the caller has set this option before calling
1012 * bindresvport(), it will be unset. Better be safe...
1014 *optval = 0;
1015 resp.flags = 0;
1016 resp.opt.buf = (char *)reqbuf;
1017 resp.opt.maxlen = sizeof (reqbuf);
1018 if (t_optmgmt(fd, &req, &resp) < 0 || resp.flags != T_SUCCESS) {
1019 (void) t_free((char *)tres, T_BIND);
1020 if (res == 0)
1021 (void) t_unbind(fd);
1022 _nderror = ND_FAILCTRL;
1023 return (-1);
1026 (void) t_free((char *)tres, T_BIND);
1027 return (res);
1030 static int
1031 checkresvport(struct netbuf *addr)
1033 struct sockaddr_in *sin;
1034 unsigned short port;
1036 if (addr == NULL) {
1037 _nderror = ND_FAILCTRL;
1038 return (-1);
1041 * Still works for IPv6 since the first two memebers of
1042 * both address structure point to family and port # respectively
1044 /* LINTED pointer cast */
1045 sin = (struct sockaddr_in *)(addr->buf);
1046 port = ntohs(sin->sin_port);
1047 if (port < IPPORT_RESERVED)
1048 return (0);
1049 return (1);