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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 23 /* All Rights Reserved */ 24 25 26 /* 27 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 28 * Use is subject to license terms. 29 */ 30 /* 31 * Copyright 2012 Nexenta Systems, Inc. All rights reserved. 32 */ 33 34 /* SVr4.0 1.1 */ 35 36 /* 37 * Miscellaneous support routines for kernel implementation of RPC. 38 */ 39 40 #include <sys/param.h> 41 #include <sys/t_lock.h> 42 #include <sys/user.h> 43 #include <sys/vnode.h> 44 #include <sys/stream.h> 45 #include <sys/stropts.h> 46 #include <sys/strsubr.h> 47 #include <sys/socket.h> 48 #include <sys/tihdr.h> 49 #include <sys/timod.h> 50 #include <sys/tiuser.h> 51 #include <sys/systm.h> 52 #include <sys/cmn_err.h> 53 #include <sys/debug.h> 54 #include <sys/sdt.h> 55 #include <netinet/in.h> 56 #include <rpc/types.h> 57 #include <rpc/auth.h> 58 #include <rpc/clnt.h> 59 #include <rpc/rpcb_prot.h> 60 #include <rpc/pmap_prot.h> 61 62 static int strtoi(char *, char **); 63 static void grow_netbuf(struct netbuf *, size_t); 64 static void loopb_u2t(const char *, struct netbuf *); 65 66 #define RPC_PMAP_TIMEOUT 15 67 /* 68 * define for max length of an ip address and port address, the value was 69 * calculated using INET6_ADDRSTRLEN (46) + max port address (12) + 70 * seperator "."'s in port address (2) + null (1) = 61. 71 * Then there is IPV6_TOKEN_LEN which is 64, so the value is 64 to be safe. 72 */ 73 #define RPC_MAX_IP_LENGTH 64 74 75 /* 76 * Kernel level debugging aid. The global variable "rpclog" is a bit 77 * mask which allows various types of debugging messages to be printed 78 * out. 79 * 80 * rpclog & 1 will cause actual failures to be printed. 81 * rpclog & 2 will cause informational messages to be 82 * printed on the client side of rpc. 83 * rpclog & 4 will cause informational messages to be 84 * printed on the server side of rpc. 85 * rpclog & 8 will cause informational messages for rare events to be 86 * printed on the client side of rpc. 87 * rpclog & 16 will cause informational messages for rare events to be 88 * printed on the server side of rpc. 89 * rpclog & 32 will cause informational messages for rare events to be 90 * printed on the common client/server code paths of rpc. 91 * rpclog & 64 will cause informational messages for manipulation 92 * client-side COTS dispatch list to be printed. 93 */ 94 95 uint_t rpclog = 0; 96 97 98 void 99 rpc_poptimod(vnode_t *vp) 100 { 101 int error, isfound, ret; 102 103 error = strioctl(vp, I_FIND, (intptr_t)"timod", 0, K_TO_K, kcred, 104 &isfound); 105 if (error) { 106 RPCLOG(1, "rpc_poptimod: I_FIND strioctl error %d\n", error); 107 return; 108 } 109 if (isfound) { 110 /* 111 * Pop timod module 112 */ 113 error = strioctl(vp, I_POP, 0, 0, K_TO_K, kcred, &ret); 114 if (error) { 115 RPCLOG(1, "rpc_poptimod: I_POP strioctl error %d\n", 116 error); 117 return; 118 } 119 } 120 } 121 122 /* 123 * Check the passed in ip address for correctness (limited) and return its 124 * type. 125 * 126 * an ipv4 looks like this: 127 * "IP.IP.IP.IP.PORT[top byte].PORT[bottom byte]" 128 * 129 * an ipv6 looks like this: 130 * fec0:A02::2:202:4FCD 131 * or 132 * ::10.9.2.1 133 */ 134 int 135 rpc_iptype( 136 char *ipaddr, 137 int *typeval) 138 { 139 char *cp; 140 int chcnt = 0; 141 int coloncnt = 0; 142 int dotcnt = 0; 143 int numcnt = 0; 144 int hexnumcnt = 0; 145 int othercnt = 0; 146 147 cp = ipaddr; 148 149 /* search for the different type of characters in the ip address */ 150 while ((*cp != '\0') && (chcnt < RPC_MAX_IP_LENGTH)) { 151 switch (*cp) { 152 case ':': 153 coloncnt++; 154 break; 155 case '.': 156 dotcnt++; 157 break; 158 case '0': 159 case '1': 160 case '2': 161 case '3': 162 case '4': 163 case '5': 164 case '6': 165 case '7': 166 case '8': 167 case '9': 168 numcnt++; 169 break; 170 case 'a': 171 case 'A': 172 case 'b': 173 case 'B': 174 case 'c': 175 case 'C': 176 case 'd': 177 case 'D': 178 case 'e': 179 case 'E': 180 case 'f': 181 case 'F': 182 hexnumcnt++; 183 break; 184 default: 185 othercnt++; 186 break; 187 } 188 chcnt++; 189 cp++; 190 } 191 192 /* check for bad ip strings */ 193 if ((chcnt == RPC_MAX_IP_LENGTH) || (othercnt)) 194 return (-1); 195 196 /* if we have a coloncnt, it can only be an ipv6 address */ 197 if (coloncnt) { 198 if ((coloncnt < 2) || (coloncnt > 7)) 199 return (-1); 200 201 *typeval = AF_INET6; 202 } else { 203 /* since there are no colons, make sure it is ipv4 */ 204 if ((hexnumcnt) || (dotcnt != 5)) 205 return (-1); 206 207 *typeval = AF_INET; 208 } 209 return (0); 210 } 211 212 /* 213 * Return a port number from a sockaddr_in expressed in universal address 214 * format. Note that this routine does not work for address families other 215 * than INET. Eventually, we should replace this routine with one that 216 * contacts the rpcbind running locally. 217 */ 218 int 219 rpc_uaddr2port(int af, char *addr) 220 { 221 int p1; 222 int p2; 223 char *next, *p; 224 225 if (af == AF_INET) { 226 /* 227 * A struct sockaddr_in expressed in universal address 228 * format looks like: 229 * 230 * "IP.IP.IP.IP.PORT[top byte].PORT[bottom byte]" 231 * 232 * Where each component expresses as a character, 233 * the corresponding part of the IP address 234 * and port number. 235 * Thus 127.0.0.1, port 2345 looks like: 236 * 237 * 49 50 55 46 48 46 48 46 49 46 57 46 52 49 238 * 1 2 7 . 0 . 0 . 1 . 9 . 4 1 239 * 240 * 2345 = 929base16 = 9.32+9 = 9.41 241 */ 242 (void) strtoi(addr, &next); 243 (void) strtoi(next, &next); 244 (void) strtoi(next, &next); 245 (void) strtoi(next, &next); 246 p1 = strtoi(next, &next); 247 p2 = strtoi(next, &next); 248 249 } else if (af == AF_INET6) { 250 /* 251 * An IPv6 address is expressed in following two formats 252 * fec0:A02::2:202:4FCD or 253 * ::10.9.2.1 254 * An universal address will have porthi.portlo appended to 255 * v6 address. So always look for the last two dots when 256 * extracting port number. 257 */ 258 next = addr; 259 while (next = strchr(next, '.')) { 260 p = ++next; 261 next = strchr(next, '.'); 262 next++; 263 } 264 p1 = strtoi(p, &p); 265 p2 = strtoi(p, &p); 266 RPCLOG(1, "rpc_uaddr2port: IPv6 port %d\n", ((p1 << 8) + p2)); 267 } 268 269 return ((p1 << 8) + p2); 270 } 271 272 /* 273 * Modified strtol(3). Should we be using mi_strtol() instead? 274 */ 275 static int 276 strtoi(char *str, char **ptr) 277 { 278 int c; 279 int val; 280 281 for (val = 0, c = *str++; c >= '0' && c <= '9'; c = *str++) { 282 val *= 10; 283 val += c - '0'; 284 } 285 *ptr = str; 286 return (val); 287 } 288 289 /* 290 * Utilities for manipulating netbuf's. 291 * 292 * Note that loopback addresses are not null-terminated, so these utilities 293 * typically use the strn* string routines. 294 */ 295 296 /* 297 * Utilities to patch a port number (for NC_INET protocols) or a 298 * port name (for NC_LOOPBACK) into a network address. 299 */ 300 301 302 /* 303 * PSARC 2003/523 Contract Private Interface 304 * put_inet_port 305 * Changes must be reviewed by Solaris File Sharing 306 * Changes must be communicated to contract-2003-523@sun.com 307 */ 308 void 309 put_inet_port(struct netbuf *addr, ushort_t port) 310 { 311 /* 312 * Easy - we always patch an unsigned short on top of an 313 * unsigned short. No changes to addr's len or maxlen are 314 * necessary. 315 */ 316 ((struct sockaddr_in *)(addr->buf))->sin_port = port; 317 } 318 319 void 320 put_inet6_port(struct netbuf *addr, ushort_t port) 321 { 322 ((struct sockaddr_in6 *)(addr->buf))->sin6_port = port; 323 } 324 325 void 326 put_loopback_port(struct netbuf *addr, char *port) 327 { 328 char *dot; 329 char *newbuf; 330 int newlen; 331 332 333 /* 334 * We must make sure the addr has enough space for us, 335 * patch in `port', and then adjust addr's len and maxlen 336 * to reflect the change. 337 */ 338 if ((dot = strnrchr(addr->buf, '.', addr->len)) == (char *)NULL) 339 return; 340 341 newlen = (int)((dot - addr->buf + 1) + strlen(port)); 342 if (newlen > addr->maxlen) { 343 newbuf = kmem_zalloc(newlen, KM_SLEEP); 344 bcopy(addr->buf, newbuf, addr->len); 345 kmem_free(addr->buf, addr->maxlen); 346 addr->buf = newbuf; 347 addr->len = addr->maxlen = newlen; 348 dot = strnrchr(addr->buf, '.', addr->len); 349 } else { 350 addr->len = newlen; 351 } 352 353 (void) strncpy(++dot, port, strlen(port)); 354 } 355 356 /* 357 * Convert a loopback universal address to a loopback transport address. 358 */ 359 static void 360 loopb_u2t(const char *ua, struct netbuf *addr) 361 { 362 size_t stringlen = strlen(ua) + 1; 363 const char *univp; /* ptr into universal addr */ 364 char *transp; /* ptr into transport addr */ 365 366 /* Make sure the netbuf will be big enough. */ 367 if (addr->maxlen < stringlen) { 368 grow_netbuf(addr, stringlen); 369 } 370 371 univp = ua; 372 transp = addr->buf; 373 while (*univp != NULL) { 374 if (*univp == '\\' && *(univp+1) == '\\') { 375 *transp = '\\'; 376 univp += 2; 377 } else if (*univp == '\\') { 378 /* octal character */ 379 *transp = (((*(univp+1) - '0') & 3) << 6) + 380 (((*(univp+2) - '0') & 7) << 3) + 381 ((*(univp+3) - '0') & 7); 382 univp += 4; 383 } else { 384 *transp = *univp; 385 univp++; 386 } 387 transp++; 388 } 389 390 addr->len = (unsigned int)(transp - addr->buf); 391 ASSERT(addr->len <= addr->maxlen); 392 } 393 394 /* 395 * Make sure the given netbuf has a maxlen at least as big as the given 396 * length. 397 */ 398 static void 399 grow_netbuf(struct netbuf *nb, size_t length) 400 { 401 char *newbuf; 402 403 if (nb->maxlen >= length) 404 return; 405 406 newbuf = kmem_zalloc(length, KM_SLEEP); 407 bcopy(nb->buf, newbuf, nb->len); 408 kmem_free(nb->buf, nb->maxlen); 409 nb->buf = newbuf; 410 nb->maxlen = (unsigned int)length; 411 } 412 413 /* 414 * XXX: xdr_pmap is here, because it's the only XDR function 415 * of portmap protocol. If there'll be more portmap functions, 416 * it would be better to put them to a separate file. 417 */ 418 bool_t 419 xdr_pmap(XDR *xdrs, PMAP *objp) 420 { 421 if (!xdr_rpcprog(xdrs, &objp->pm_prog)) 422 return (FALSE); 423 if (!xdr_rpcvers(xdrs, &objp->pm_vers)) 424 return (FALSE); 425 if (!xdr_rpcprot(xdrs, &objp->pm_prot)) 426 return (FALSE); 427 if (!xdr_u_int(xdrs, &objp->pm_port)) 428 return (FALSE); 429 430 return (TRUE); 431 } 432 433 /* 434 * Get remote port via PORTMAP protocol version 2 (works for IPv4 only) 435 * according to RFC 1833, section 3. 436 */ 437 static enum clnt_stat 438 portmap_getport(struct knetconfig *config, rpcprog_t prog, rpcvers_t vers, 439 struct netbuf *addr, struct timeval tmo) 440 { 441 enum clnt_stat status; 442 CLIENT *client = NULL; 443 k_sigset_t oldmask; 444 k_sigset_t newmask; 445 ushort_t port = 0; 446 struct pmap parms; 447 448 ASSERT(strcmp(config->knc_protofmly, NC_INET) == 0); 449 450 bzero(&parms, sizeof (parms)); 451 parms.pm_prog = prog; 452 parms.pm_vers = vers; 453 if (strcmp(config->knc_proto, NC_TCP) == 0) { 454 parms.pm_prot = IPPROTO_TCP; 455 } else { /* NC_UDP */ 456 parms.pm_prot = IPPROTO_UDP; 457 } 458 459 460 /* 461 * Mask all signals before doing RPC network operations 462 * in the same way rpcbind_getaddr() does (see comments 463 * there). 464 */ 465 sigfillset(&newmask); 466 sigreplace(&newmask, &oldmask); 467 468 if (clnt_tli_kcreate(config, addr, PMAPPROG, 469 PMAPVERS, 0, 0, CRED(), &client)) { 470 sigreplace(&oldmask, (k_sigset_t *)NULL); 471 return (RPC_TLIERROR); 472 } 473 474 client->cl_nosignal = 1; 475 status = CLNT_CALL(client, PMAPPROC_GETPORT, 476 xdr_pmap, (char *)&parms, 477 xdr_u_short, (char *)&port, tmo); 478 479 sigreplace(&oldmask, (k_sigset_t *)NULL); 480 if (status != RPC_SUCCESS) 481 goto out; 482 if (port == 0) { 483 status = RPC_PROGNOTREGISTERED; 484 goto out; 485 } 486 487 put_inet_port(addr, ntohs(port)); 488 489 out: 490 auth_destroy(client->cl_auth); 491 clnt_destroy(client); 492 493 return (status); 494 } 495 496 /* 497 * Try to get the address for the desired service by using the rpcbind 498 * protocol. Ignores signals. If addr is a loopback address, it is 499 * expected to be initialized to "<hostname>.". 500 * rpcbind_getaddr() is able to work with RPCBIND protocol version 3 and 4 501 * and PORTMAP protocol version 2. 502 * It tries version 4 at first, then version 3 and finally (if both failed) 503 * it tries portmapper protocol version 2. 504 */ 505 enum clnt_stat 506 rpcbind_getaddr(struct knetconfig *config, rpcprog_t prog, rpcvers_t vers, 507 struct netbuf *addr) 508 { 509 char *ua = NULL; 510 enum clnt_stat status; 511 RPCB parms; 512 struct timeval tmo; 513 k_sigset_t oldmask; 514 k_sigset_t newmask; 515 ushort_t port; 516 int iptype; 517 rpcvers_t rpcbv; 518 519 /* 520 * Call rpcbind (local or remote) to get an address we can use 521 * in an RPC client handle. 522 */ 523 tmo.tv_sec = RPC_PMAP_TIMEOUT; 524 tmo.tv_usec = 0; 525 parms.r_prog = prog; 526 parms.r_vers = vers; 527 parms.r_addr = parms.r_owner = ""; 528 529 if (strcmp(config->knc_protofmly, NC_INET) == 0) { 530 put_inet_port(addr, htons(PMAPPORT)); 531 532 if (strcmp(config->knc_proto, NC_TCP) == 0) 533 parms.r_netid = "tcp"; 534 else 535 parms.r_netid = "udp"; 536 537 } else if (strcmp(config->knc_protofmly, NC_INET6) == 0) { 538 if (strcmp(config->knc_proto, NC_TCP) == 0) 539 parms.r_netid = "tcp6"; 540 else 541 parms.r_netid = "udp6"; 542 put_inet6_port(addr, htons(PMAPPORT)); 543 } else if (strcmp(config->knc_protofmly, NC_LOOPBACK) == 0) { 544 ASSERT(strnrchr(addr->buf, '.', addr->len) != NULL); 545 if (config->knc_semantics == NC_TPI_COTS_ORD) 546 parms.r_netid = "ticotsord"; 547 else if (config->knc_semantics == NC_TPI_COTS) 548 parms.r_netid = "ticots"; 549 else 550 parms.r_netid = "ticlts"; 551 552 put_loopback_port(addr, "rpc"); 553 } else { 554 status = RPC_UNKNOWNPROTO; 555 goto out; 556 } 557 558 /* 559 * Try RPCBIND versions 4 and 3 (if 4 fails). 560 */ 561 for (rpcbv = RPCBVERS4; rpcbv >= RPCBVERS; rpcbv--) { 562 CLIENT *client = NULL; 563 564 if (ua != NULL) { 565 xdr_free(xdr_wrapstring, (char *)&ua); 566 ua = NULL; 567 } 568 569 /* 570 * Mask signals for the duration of the handle creation and 571 * RPC calls. This allows relatively normal operation with a 572 * signal already posted to our thread (e.g., when we are 573 * sending an NLM_CANCEL in response to catching a signal). 574 * 575 * Any further exit paths from this routine must restore 576 * the original signal mask. 577 */ 578 sigfillset(&newmask); 579 sigreplace(&newmask, &oldmask); 580 581 if (clnt_tli_kcreate(config, addr, RPCBPROG, 582 rpcbv, 0, 0, CRED(), &client)) { 583 status = RPC_TLIERROR; 584 sigreplace(&oldmask, (k_sigset_t *)NULL); 585 continue; 586 } 587 588 client->cl_nosignal = 1; 589 status = CLNT_CALL(client, RPCBPROC_GETADDR, 590 xdr_rpcb, (char *)&parms, 591 xdr_wrapstring, (char *)&ua, tmo); 592 593 sigreplace(&oldmask, (k_sigset_t *)NULL); 594 auth_destroy(client->cl_auth); 595 clnt_destroy(client); 596 597 if (status == RPC_SUCCESS) { 598 if (ua == NULL || *ua == NULL) { 599 status = RPC_PROGNOTREGISTERED; 600 continue; 601 } 602 603 break; 604 } 605 } 606 if (status != RPC_SUCCESS) 607 goto try_portmap; 608 609 /* 610 * Convert the universal address to the transport address. 611 * Theoretically, we should call the local rpcbind to translate 612 * from the universal address to the transport address, but it gets 613 * complicated (e.g., there's no direct way to tell rpcbind that we 614 * want an IP address instead of a loopback address). Note that 615 * the transport address is potentially host-specific, so we can't 616 * just ask the remote rpcbind, because it might give us the wrong 617 * answer. 618 */ 619 if (strcmp(config->knc_protofmly, NC_INET) == 0) { 620 /* make sure that the ip address is the correct type */ 621 if (rpc_iptype(ua, &iptype) != 0) { 622 status = RPC_UNKNOWNADDR; 623 goto try_portmap; 624 } 625 port = rpc_uaddr2port(iptype, ua); 626 put_inet_port(addr, ntohs(port)); 627 } else if (strcmp(config->knc_protofmly, NC_INET6) == 0) { 628 /* make sure that the ip address is the correct type */ 629 if (rpc_iptype(ua, &iptype) != 0) { 630 status = RPC_UNKNOWNADDR; 631 goto try_portmap; 632 } 633 port = rpc_uaddr2port(iptype, ua); 634 put_inet6_port(addr, ntohs(port)); 635 } else if (strcmp(config->knc_protofmly, NC_LOOPBACK) == 0) { 636 loopb_u2t(ua, addr); 637 } else { 638 /* "can't happen" - should have been checked for above */ 639 cmn_err(CE_PANIC, "rpcbind_getaddr: bad protocol family"); 640 } 641 642 try_portmap: 643 if (status != RPC_SUCCESS && 644 strcmp(config->knc_protofmly, NC_INET) == 0) { 645 /* 646 * For IPv4 try to get remote port via PORTMAP protocol. 647 * NOTE: if we're here, then all attempts to get remote 648 * port via RPCBIND protocol failed. 649 */ 650 651 DTRACE_PROBE1(try__portmap, enum clnt_stat, status); 652 status = portmap_getport(config, prog, vers, addr, tmo); 653 } 654 655 out: 656 if (ua != NULL) 657 xdr_free(xdr_wrapstring, (char *)&ua); 658 return (status); 659 } 660 661 static const char *tpiprims[] = { 662 "T_CONN_REQ 0 connection request", 663 "T_CONN_RES 1 connection response", 664 "T_DISCON_REQ 2 disconnect request", 665 "T_DATA_REQ 3 data request", 666 "T_EXDATA_REQ 4 expedited data request", 667 "T_INFO_REQ 5 information request", 668 "T_BIND_REQ 6 bind request", 669 "T_UNBIND_REQ 7 unbind request", 670 "T_UNITDATA_REQ 8 unitdata request", 671 "T_OPTMGMT_REQ 9 manage options req", 672 "T_ORDREL_REQ 10 orderly release req", 673 "T_CONN_IND 11 connection indication", 674 "T_CONN_CON 12 connection confirmation", 675 "T_DISCON_IND 13 disconnect indication", 676 "T_DATA_IND 14 data indication", 677 "T_EXDATA_IND 15 expeditied data indication", 678 "T_INFO_ACK 16 information acknowledgment", 679 "T_BIND_ACK 17 bind acknowledment", 680 "T_ERROR_ACK 18 error acknowledgment", 681 "T_OK_ACK 19 ok acknowledgment", 682 "T_UNITDATA_IND 20 unitdata indication", 683 "T_UDERROR_IND 21 unitdata error indication", 684 "T_OPTMGMT_ACK 22 manage options ack", 685 "T_ORDREL_IND 23 orderly release ind" 686 }; 687 688 689 const char * 690 rpc_tpiprim2name(uint_t prim) 691 { 692 if (prim > (sizeof (tpiprims) / sizeof (tpiprims[0]) - 1)) 693 return ("unknown primitive"); 694 695 return (tpiprims[prim]); 696 } 697 698 static const char *tpierrs[] = { 699 "error zero 0", 700 "TBADADDR 1 incorrect addr format", 701 "TBADOPT 2 incorrect option format", 702 "TACCES 3 incorrect permissions", 703 "TBADF 4 illegal transport fd", 704 "TNOADDR 5 couldn't allocate addr", 705 "TOUTSTATE 6 out of state", 706 "TBADSEQ 7 bad call sequnce number", 707 "TSYSERR 8 system error", 708 "TLOOK 9 event requires attention", 709 "TBADDATA 10 illegal amount of data", 710 "TBUFOVFLW 11 buffer not large enough", 711 "TFLOW 12 flow control", 712 "TNODATA 13 no data", 713 "TNODIS 14 discon_ind not found on q", 714 "TNOUDERR 15 unitdata error not found", 715 "TBADFLAG 16 bad flags", 716 "TNOREL 17 no ord rel found on q", 717 "TNOTSUPPORT 18 primitive not supported", 718 "TSTATECHNG 19 state is in process of changing" 719 }; 720 721 722 const char * 723 rpc_tpierr2name(uint_t err) 724 { 725 if (err > (sizeof (tpierrs) / sizeof (tpierrs[0]) - 1)) 726 return ("unknown error"); 727 728 return (tpierrs[err]); 729 } 730 731 /* 732 * derive the code from user land inet_top6 733 * convert IPv6 binary address into presentation (printable) format 734 */ 735 #define INADDRSZ 4 736 #define IN6ADDRSZ 16 737 #define INT16SZ 2 738 const char * 739 kinet_ntop6(src, dst, size) 740 uchar_t *src; 741 char *dst; 742 size_t size; 743 { 744 /* 745 * Note that int32_t and int16_t need only be "at least" large enough 746 * to contain a value of the specified size. On some systems, like 747 * Crays, there is no such thing as an integer variable with 16 bits. 748 * Keep this in mind if you think this function should have been coded 749 * to use pointer overlays. All the world's not a VAX. 750 */ 751 char tmp[sizeof ("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")]; 752 char *tp; 753 struct { int base, len; } best, cur; 754 uint_t words[IN6ADDRSZ / INT16SZ]; 755 int i; 756 size_t len; /* this is used to track the sprintf len */ 757 758 /* 759 * Preprocess: 760 * Copy the input (bytewise) array into a wordwise array. 761 * Find the longest run of 0x00's in src[] for :: shorthanding. 762 */ 763 764 bzero(words, sizeof (words)); 765 for (i = 0; i < IN6ADDRSZ; i++) 766 words[i / 2] |= (src[i] << ((1 - (i % 2)) << 3)); 767 best.base = -1; 768 cur.base = -1; 769 770 for (i = 0; i < (IN6ADDRSZ / INT16SZ); i++) { 771 if (words[i] == 0) { 772 if (cur.base == -1) 773 cur.base = i, cur.len = 1; 774 else 775 cur.len++; 776 } else { 777 if (cur.base != -1) { 778 if (best.base == -1 || cur.len > best.len) 779 best = cur; 780 cur.base = -1; 781 } 782 } 783 } 784 if (cur.base != -1) { 785 if (best.base == -1 || cur.len > best.len) 786 best = cur; 787 } 788 789 if (best.base != -1 && best.len < 2) 790 best.base = -1; 791 792 /* 793 * Format the result. 794 */ 795 tp = tmp; 796 for (i = 0; i < (IN6ADDRSZ / INT16SZ); i++) { 797 /* Are we inside the best run of 0x00's? */ 798 if (best.base != -1 && i >= best.base && 799 i < (best.base + best.len)) { 800 if (i == best.base) 801 *tp++ = ':'; 802 continue; 803 } 804 /* Are we following an initial run of 0x00s or any real hex? */ 805 if (i != 0) 806 *tp++ = ':'; 807 (void) sprintf(tp, "%x", words[i]); 808 len = strlen(tp); 809 tp += len; 810 } 811 /* Was it a trailing run of 0x00's? */ 812 if (best.base != -1 && (best.base + best.len) == (IN6ADDRSZ / INT16SZ)) 813 *tp++ = ':'; 814 *tp++ = '\0'; 815 816 /* 817 * Check for overflow, copy, and we're done. 818 */ 819 if ((int)(tp - tmp) > size) { 820 return (NULL); 821 } 822 (void) strcpy(dst, tmp); 823 return (dst); 824 } 825