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