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