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