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