1 /*
2  * Copyright 1997-2002 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 /*
7  * Copyright (c) 1996,1999 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 #if defined(LIBC_SCCS) && !defined(lint)
26 static const char rcsid[] = "$Id: inet_net_ntop.c,v 1.8 2001/09/27 15:08:36 marka Exp $";
27 #endif
28 
29 #include "port_before.h"
30 
31 #include <sys/types.h>
32 #include <sys/socket.h>
33 #include <netinet/in.h>
34 #include <arpa/inet.h>
35 
36 #include <errno.h>
37 #include <stdio.h>
38 #include <string.h>
39 #include <stdlib.h>
40 
41 #include "port_after.h"
42 
43 #ifdef SPRINTF_CHAR
44 # define SPRINTF(x) strlen(sprintf/**/x)
45 #else
46 # define SPRINTF(x) ((size_t)sprintf x)
47 #endif
48 
49 static char *	inet_net_ntop_ipv4 __P((const u_char *src, int bits,
50 					char *dst, size_t size));
51 static char *	inet_net_ntop_ipv6 __P((const u_char *src, int bits,
52 					char *dst, size_t size));
53 
54 /*
55  * char *
56  * inet_net_ntop(af, src, bits, dst, size)
57  *	convert network number from network to presentation format.
58  *	generates CIDR style result always.
59  * return:
60  *	pointer to dst, or NULL if an error occurred (check errno).
61  * author:
62  *	Paul Vixie (ISC), July 1996
63  */
64 char *
65 inet_net_ntop(af, src, bits, dst, size)
66 	int af;
67 	const void *src;
68 	int bits;
69 	char *dst;
70 	size_t size;
71 {
72 	switch (af) {
73 	case AF_INET:
74 		return (inet_net_ntop_ipv4(src, bits, dst, size));
75 	case AF_INET6:
76 		return (inet_net_ntop_ipv6(src, bits, dst, size));
77 	default:
78 		errno = EAFNOSUPPORT;
79 		return (NULL);
80 	}
81 }
82 
83 /*
84  * static char *
85  * inet_net_ntop_ipv4(src, bits, dst, size)
86  *	convert IPv4 network number from network to presentation format.
87  *	generates CIDR style result always.
88  * return:
89  *	pointer to dst, or NULL if an error occurred (check errno).
90  * note:
91  *	network byte order assumed.  this means 192.5.5.240/28 has
92  *	0b11110000 in its fourth octet.
93  * author:
94  *	Paul Vixie (ISC), July 1996
95  */
96 static char *
97 inet_net_ntop_ipv4(src, bits, dst, size)
98 	const u_char *src;
99 	int bits;
100 	char *dst;
101 	size_t size;
102 {
103 	char *odst = dst;
104 	char *t;
105 	u_int m;
106 	int b;
107 
108 	if (bits < 0 || bits > 32) {
109 		errno = EINVAL;
110 		return (NULL);
111 	}
112 
113 	if (bits == 0) {
114 		if (size < sizeof "0")
115 			goto emsgsize;
116 		*dst++ = '0';
117 		size--;
118 		*dst = '\0';
119 	}
120 
121 	/* Format whole octets. */
122 	for (b = bits / 8; b > 0; b--) {
123 		if (size <= sizeof "255.")
124 			goto emsgsize;
125 		t = dst;
126 		dst += SPRINTF((dst, "%u", *src++));
127 		if (b > 1) {
128 			*dst++ = '.';
129 			*dst = '\0';
130 		}
131 		size -= (size_t)(dst - t);
132 	}
133 
134 	/* Format partial octet. */
135 	b = bits % 8;
136 	if (b > 0) {
137 		if (size <= sizeof ".255")
138 			goto emsgsize;
139 		t = dst;
140 		if (dst != odst)
141 			*dst++ = '.';
142 		m = ((1 << b) - 1) << (8 - b);
143 		dst += SPRINTF((dst, "%u", *src & m));
144 		size -= (size_t)(dst - t);
145 	}
146 
147 	/* Format CIDR /width. */
148 	if (size <= sizeof "/32")
149 		goto emsgsize;
150 	dst += SPRINTF((dst, "/%u", bits));
151 	return (odst);
152 
153  emsgsize:
154 	errno = EMSGSIZE;
155 	return (NULL);
156 }
157 
158 /*
159  * static char *
160  * inet_net_ntop_ipv6(src, bits, fakebits, dst, size)
161  *	convert IPv6 network number from network to presentation format.
162  *	generates CIDR style result always. Picks the shortest representation
163  *	unless the IP is really IPv4.
164  *	always prints specified number of bits (bits).
165  * return:
166  *	pointer to dst, or NULL if an error occurred (check errno).
167  * note:
168  *	network byte order assumed.  this means 192.5.5.240/28 has
169  *	0b11110000 in its fourth octet.
170  * author:
171  *	Vadim Kogan (UCB), June 2001
172  *  Original version (IPv4) by Paul Vixie (ISC), July 1996
173  */
174 
175 static char *
176 inet_net_ntop_ipv6(const u_char *src, int bits, char *dst, size_t size) {
177 	u_int	m;
178 	int	b;
179 	int	p;
180 	int	zero_s, zero_l, tmp_zero_s, tmp_zero_l;
181 	int	i;
182 	int	is_ipv4 = 0;
183 	unsigned char inbuf[16];
184 	char outbuf[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128")];
185 	char	*cp;
186 	int	words;
187 	u_char	*s;
188 
189 	if (bits < 0 || bits > 128) {
190 		errno = EINVAL;
191 		return (NULL);
192 	}
193 
194 	cp = outbuf;
195 
196 	if (bits == 0) {
197 		*cp++ = ':';
198 		*cp++ = ':';
199 		*cp = '\0';
200 	} else {
201 		/* Copy src to private buffer.  Zero host part. */
202 		p = (bits + 7) / 8;
203 		memcpy(inbuf, src, p);
204 		memset(inbuf + p, 0, 16 - p);
205 		b = bits % 8;
206 		if (b != 0) {
207 			m = ~0 << (8 - b);
208 			inbuf[p-1] &= m;
209 		}
210 
211 		s = inbuf;
212 
213 		/* how many words need to be displayed in output */
214 		words = (bits + 15) / 16;
215 		if (words == 1)
216 			words = 2;
217 
218 		/* Find the longest substring of zero's */
219 		zero_s = zero_l = tmp_zero_s = tmp_zero_l = 0;
220 		for (i = 0; i < (words * 2); i += 2) {
221 			if ((s[i] | s[i+1]) == 0) {
222 				if (tmp_zero_l == 0)
223 					tmp_zero_s = i / 2;
224 				tmp_zero_l++;
225 			} else {
226 				if (tmp_zero_l && zero_l < tmp_zero_l) {
227 					zero_s = tmp_zero_s;
228 					zero_l = tmp_zero_l;
229 					tmp_zero_l = 0;
230 				}
231 			}
232 		}
233 
234 		if (tmp_zero_l && zero_l < tmp_zero_l) {
235 			zero_s = tmp_zero_s;
236 			zero_l = tmp_zero_l;
237 		}
238 
239 		if (zero_l != words && zero_s == 0 && ((zero_l == 6) ||
240 		    ((zero_l == 5 && s[10] == 0xff && s[11] == 0xff) ||
241 		    ((zero_l == 7 && s[14] != 0 && s[15] != 1)))))
242 			is_ipv4 = 1;
243 
244 		/* Format whole words. */
245 		for (p = 0; p < words; p++) {
246 			if (zero_l != 0 && p >= zero_s && p < zero_s + zero_l) {
247 				/* Time to skip some zeros */
248 				if (p == zero_s)
249 					*cp++ = ':';
250 				if (p == words - 1)
251 					*cp++ = ':';
252 				s++;
253 				s++;
254 				continue;
255 			}
256 
257 			if (is_ipv4 && p > 5 ) {
258 				*cp++ = (p == 6) ? ':' : '.';
259 				cp += SPRINTF((cp, "%u", *s++));
260 				/* we can potentially drop the last octet */
261 				if (p != 7 || bits > 120) {
262 					*cp++ = '.';
263 					cp += SPRINTF((cp, "%u", *s++));
264 				}
265 			} else {
266 				if (cp != outbuf)
267 					*cp++ = ':';
268 				cp += SPRINTF((cp, "%x", *s * 256 + s[1]));
269 				s += 2;
270 			}
271 		}
272 	}
273 	/* Format CIDR /width. */
274 	SPRINTF((cp, "/%u", bits));
275 	if (strlen(outbuf) + 1 > size)
276 		goto emsgsize;
277 	strcpy(dst, outbuf);
278 
279 	return (dst);
280 
281 emsgsize:
282 	errno = EMSGSIZE;
283 	return (NULL);
284 }
285