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