inet_ntop.c revision 132:e3f7eaf7dde4
1/*
2 * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
3 * Use is subject to license terms.
4 */
5
6/*
7 * Copyright (c) 1996 by Internet Software Consortium.
8 *
9 * Permission to use, copy, modify, and distribute this software for any
10 * purpose with or without fee is hereby granted, provided that the above
11 * copyright notice and this permission notice appear in all copies.
12 *
13 * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
14 * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
15 * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
16 * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
17 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
18 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
19 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
20 * SOFTWARE.
21 */
22
23#pragma ident	"%Z%%M%	%I%	%E% SMI"
24
25#include <stdlib.h>
26#include <ctype.h>
27#include <string.h>
28#include <strings.h>
29#include <netdb.h>
30#include <stdio.h>
31#include <arpa/inet.h>
32#include <netinet/in.h>
33#include <sys/socket.h>
34#include <errno.h>
35
36static const char *inet_ntop4(const uchar_t *, char *, socklen_t);
37static const char *inet_ntop6(const uchar_t *, char *, socklen_t);
38
39/*
40 * char *
41 * inet_ntop(af, src, dst, size)
42 *	convert a network format address to presentation format.
43 * return:
44 *	pointer to presentation format address (`dst'), or NULL (see errno).
45 */
46const char *
47inet_ntop(int af, const void *src, char *dst, socklen_t size)
48{
49	switch (af) {
50	case AF_INET:
51		return (inet_ntop4(src, dst, size));
52	case AF_INET6:
53		return (inet_ntop6(src, dst, size));
54	default:
55		errno = EAFNOSUPPORT;
56		return (NULL);
57	}
58	/* NOTREACHED */
59}
60
61/*
62 * const char *
63 * inet_ntop4(src, dst, size)
64 *	format an IPv4 address, more or less like inet_ntoa()
65 * return:
66 *	`dst' (as a const)
67 * notes:
68 *	(1) uses no statics
69 *	(2) takes a uchar_t* not an in_addr as input
70 */
71
72#ifdef SPRINTF_CHAR
73/* CSTYLED */
74#define	SPRINTF(x) strlen(sprintf/**/x)
75#else
76#define	SPRINTF(x) ((size_t)sprintf x)
77#endif
78
79static const char *
80inet_ntop4(const uchar_t *src, char *dst, socklen_t size)
81{
82	static const char fmt[] = "%u.%u.%u.%u";
83	char tmp[sizeof ("255.255.255.255")];
84
85	if (SPRINTF((tmp, fmt, src[0], src[1], src[2], src[3])) > size) {
86		errno = ENOSPC;
87		return (NULL);
88	}
89	(void) strcpy(dst, tmp);
90	return (dst);
91}
92
93/*
94 * const char *
95 * inet_ntop6(src, dst, size)
96 *	convert IPv6 binary address into presentation (printable) format
97 */
98#define	INADDRSZ	4
99#define	IN6ADDRSZ	16
100#define	INT16SZ		2
101static const char *
102inet_ntop6(const uchar_t *src, char *dst, socklen_t size)
103{
104	/*
105	 * Note that int32_t and int16_t need only be "at least" large enough
106	 * to contain a value of the specified size.  On some systems, like
107	 * Crays, there is no such thing as an integer variable with 16 bits.
108	 * Keep this in mind if you think this function should have been coded
109	 * to use pointer overlays.  All the world's not a VAX.
110	 */
111	char tmp[sizeof ("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")], *tp;
112	struct { int base, len; } best, cur;
113	uint_t words[IN6ADDRSZ / INT16SZ];
114	int i;
115
116	/*
117	 * Preprocess:
118	 *	Copy the input (bytewise) array into a wordwise array.
119	 *	Find the longest run of 0x00's in src[] for :: shorthanding.
120	 */
121	(void) memset(words, '\0', sizeof (words));
122	for (i = 0; i < IN6ADDRSZ; i++)
123		words[i / 2] |= (src[i] << ((1 - (i % 2)) << 3));
124	best.base = -1;
125	cur.base = -1;
126	for (i = 0; i < (IN6ADDRSZ / INT16SZ); i++) {
127		if (words[i] == 0) {
128			if (cur.base == -1)
129				cur.base = i, cur.len = 1;
130			else
131				cur.len++;
132		} else {
133			if (cur.base != -1) {
134				if (best.base == -1 || cur.len > best.len)
135					best = cur;
136				cur.base = -1;
137			}
138		}
139	}
140	if (cur.base != -1) {
141		if (best.base == -1 || cur.len > best.len)
142			best = cur;
143	}
144	if (best.base != -1 && best.len < 2)
145		best.base = -1;
146
147	/*
148	 * Format the result.
149	 */
150	tp = tmp;
151	for (i = 0; i < (IN6ADDRSZ / INT16SZ); i++) {
152		/* Are we inside the best run of 0x00's? */
153		if (best.base != -1 && i >= best.base &&
154		    i < (best.base + best.len)) {
155			if (i == best.base)
156				*tp++ = ':';
157			continue;
158		}
159		/* Are we following an initial run of 0x00s or any real hex? */
160		if (i != 0)
161			*tp++ = ':';
162		/* Is this address an encapsulated IPv4? */
163		if (i == 6 && best.base == 0 &&
164		    (best.len == 6 || (best.len == 5 && words[5] == 0xffff))) {
165			if (!inet_ntop4(src+12, tp, sizeof (tmp) - (tp - tmp)))
166				return (NULL);
167			tp += strlen(tp);
168			break;
169		}
170		tp += SPRINTF((tp, "%x", words[i]));
171	}
172	/* Was it a trailing run of 0x00's? */
173	if (best.base != -1 && (best.base + best.len) == (IN6ADDRSZ / INT16SZ))
174		*tp++ = ':';
175	*tp++ = '\0';
176
177	/*
178	 * Check for overflow, copy, and we're done.
179	 */
180	if ((int)(tp - tmp) > size) {
181		errno = ENOSPC;
182		return (NULL);
183	}
184	(void) strcpy(dst, tmp);
185	return (dst);
186}
187