xref: /illumos-gate/usr/src/uts/common/rpc/rpc_subr.c (revision b6805bf78d2bbbeeaea8909a05623587b42d58b3)
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