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
__inet_netdir_options(struct netconfig * tp,int opts,int fd,char * par)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 *
__inet_taddr2uaddr(struct netconfig * tp,struct netbuf * addr)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 *
__inet_uaddr2taddr(struct netconfig * tp,char * addr)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 *
ip_uaddr2taddr(char * addr)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 *
ipv6_uaddr2taddr(char * addr)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
get_if_info(void)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
update_if_cache(void)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
is_my_address(struct in_addr addr)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
__inet_netdir_is_my_host(const char * host)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
get_best_match(struct in_addr addr)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
is_myself(struct sockaddr_in6 * sa6)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
select_server_addr(union any_in_addr * dst_addr,int family,union any_in_addr * src_addr)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 *
inet_netdir_mergeaddr(struct netconfig * tp,char * ruaddr,char * uaddr)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
bindresvport(struct netconfig * nconf,int fd,struct netbuf * addr)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
checkresvport(struct netbuf * addr)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