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