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 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 * 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 * 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 * 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 * 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 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 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 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 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 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 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 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 * 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 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 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