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