inet_net_ntop.c revision 225736
1229970Sadrian/*
2229970Sadrian * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
3229970Sadrian * Copyright (c) 1996,1999 by Internet Software Consortium.
4229970Sadrian *
5229970Sadrian * Permission to use, copy, modify, and distribute this software for any
6229970Sadrian * purpose with or without fee is hereby granted, provided that the above
7229970Sadrian * copyright notice and this permission notice appear in all copies.
8229970Sadrian *
9229970Sadrian * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
10229970Sadrian * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11229970Sadrian * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
12229970Sadrian * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13229970Sadrian * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14229970Sadrian * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
15229970Sadrian * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16229970Sadrian */
17229970Sadrian
18229970Sadrian#if defined(LIBC_SCCS) && !defined(lint)
19229970Sadrianstatic const char rcsid[] = "$Id: inet_net_ntop.c,v 1.3.18.2 2006/06/20 02:51:32 marka Exp $";
20229970Sadrian#endif
21229970Sadrian#include <sys/cdefs.h>
22229970Sadrian__FBSDID("$FreeBSD: stable/9/lib/libc/inet/inet_net_ntop.c 170244 2007-06-03 17:20:27Z ume $");
23229970Sadrian
24229970Sadrian#include "port_before.h"
25229970Sadrian
26229970Sadrian#include <sys/types.h>
27229970Sadrian#include <sys/socket.h>
28229970Sadrian#include <netinet/in.h>
29229970Sadrian#include <arpa/inet.h>
30229970Sadrian
31229970Sadrian#include <errno.h>
32229970Sadrian#include <stdio.h>
33229970Sadrian#include <string.h>
34229970Sadrian#include <stdlib.h>
35229970Sadrian
36229970Sadrian#include "port_after.h"
37229970Sadrian
38229970Sadrian#ifdef SPRINTF_CHAR
39229970Sadrian# define SPRINTF(x) strlen(sprintf/**/x)
40229970Sadrian#else
41229970Sadrian# define SPRINTF(x) ((size_t)sprintf x)
42229970Sadrian#endif
43229970Sadrian
44229970Sadrianstatic char *	inet_net_ntop_ipv4(const u_char *src, int bits, char *dst,
45229970Sadrian		    size_t size);
46229970Sadrianstatic char *	inet_net_ntop_ipv6(const u_char *src, int bits, char *dst,
47229970Sadrian		    size_t size);
48229970Sadrian
49229970Sadrian/*%
50229970Sadrian * char *
51229970Sadrian * inet_net_ntop(af, src, bits, dst, size)
52229970Sadrian *	convert network number from network to presentation format.
53229970Sadrian *	generates CIDR style result always.
54229970Sadrian * return:
55229970Sadrian *	pointer to dst, or NULL if an error occurred (check errno).
56229970Sadrian * author:
57229970Sadrian *	Paul Vixie (ISC), July 1996
58229970Sadrian */
59229970Sadrianchar *
60229970Sadrianinet_net_ntop(af, src, bits, dst, size)
61229970Sadrian	int af;
62229970Sadrian	const void *src;
63229970Sadrian	int bits;
64229970Sadrian	char *dst;
65229970Sadrian	size_t size;
66229970Sadrian{
67229970Sadrian	switch (af) {
68229970Sadrian	case AF_INET:
69229970Sadrian		return (inet_net_ntop_ipv4(src, bits, dst, size));
70229970Sadrian	case AF_INET6:
71229970Sadrian		return (inet_net_ntop_ipv6(src, bits, dst, size));
72229970Sadrian	default:
73229970Sadrian		errno = EAFNOSUPPORT;
74229970Sadrian		return (NULL);
75229970Sadrian	}
76229970Sadrian}
77229970Sadrian
78229970Sadrian/*%
79229970Sadrian * static char *
80229970Sadrian * inet_net_ntop_ipv4(src, bits, dst, size)
81229970Sadrian *	convert IPv4 network number from network to presentation format.
82229970Sadrian *	generates CIDR style result always.
83229970Sadrian * return:
84229970Sadrian *	pointer to dst, or NULL if an error occurred (check errno).
85229970Sadrian * note:
86229970Sadrian *	network byte order assumed.  this means 192.5.5.240/28 has
87229970Sadrian *	0b11110000 in its fourth octet.
88229970Sadrian * author:
89229970Sadrian *	Paul Vixie (ISC), July 1996
90229970Sadrian */
91229970Sadrianstatic char *
92229970Sadrianinet_net_ntop_ipv4(src, bits, dst, size)
93229970Sadrian	const u_char *src;
94229970Sadrian	int bits;
95229970Sadrian	char *dst;
96229970Sadrian	size_t size;
97229970Sadrian{
98229970Sadrian	char *odst = dst;
99229970Sadrian	char *t;
100229970Sadrian	u_int m;
101229970Sadrian	int b;
102229970Sadrian
103229970Sadrian	if (bits < 0 || bits > 32) {
104229970Sadrian		errno = EINVAL;
105229970Sadrian		return (NULL);
106229970Sadrian	}
107229970Sadrian
108229970Sadrian	if (bits == 0) {
109229970Sadrian		if (size < sizeof "0")
110229970Sadrian			goto emsgsize;
111229970Sadrian		*dst++ = '0';
112229970Sadrian		size--;
113229970Sadrian		*dst = '\0';
114229970Sadrian	}
115229970Sadrian
116229970Sadrian	/* Format whole octets. */
117229970Sadrian	for (b = bits / 8; b > 0; b--) {
118229970Sadrian		if (size <= sizeof "255.")
119229970Sadrian			goto emsgsize;
120229970Sadrian		t = dst;
121229970Sadrian		dst += SPRINTF((dst, "%u", *src++));
122229970Sadrian		if (b > 1) {
123229970Sadrian			*dst++ = '.';
124229970Sadrian			*dst = '\0';
125229970Sadrian		}
126229970Sadrian		size -= (size_t)(dst - t);
127229970Sadrian	}
128229970Sadrian
129229970Sadrian	/* Format partial octet. */
130229970Sadrian	b = bits % 8;
131229970Sadrian	if (b > 0) {
132229970Sadrian		if (size <= sizeof ".255")
133229970Sadrian			goto emsgsize;
134229970Sadrian		t = dst;
135229970Sadrian		if (dst != odst)
136229970Sadrian			*dst++ = '.';
137229970Sadrian		m = ((1 << b) - 1) << (8 - b);
138229970Sadrian		dst += SPRINTF((dst, "%u", *src & m));
139229970Sadrian		size -= (size_t)(dst - t);
140229970Sadrian	}
141229970Sadrian
142229970Sadrian	/* Format CIDR /width. */
143229970Sadrian	if (size <= sizeof "/32")
144229970Sadrian		goto emsgsize;
145229970Sadrian	dst += SPRINTF((dst, "/%u", bits));
146229970Sadrian	return (odst);
147229970Sadrian
148229970Sadrian emsgsize:
149229970Sadrian	errno = EMSGSIZE;
150229970Sadrian	return (NULL);
151229970Sadrian}
152229970Sadrian
153229970Sadrian/*%
154229970Sadrian * static char *
155229970Sadrian * inet_net_ntop_ipv6(src, bits, fakebits, dst, size)
156229970Sadrian *	convert IPv6 network number from network to presentation format.
157229970Sadrian *	generates CIDR style result always. Picks the shortest representation
158229970Sadrian *	unless the IP is really IPv4.
159229970Sadrian *	always prints specified number of bits (bits).
160229970Sadrian * return:
161229970Sadrian *	pointer to dst, or NULL if an error occurred (check errno).
162229970Sadrian * note:
163229970Sadrian *	network byte order assumed.  this means 192.5.5.240/28 has
164229970Sadrian *	0b11110000 in its fourth octet.
165229970Sadrian * author:
166229970Sadrian *	Vadim Kogan (UCB), June 2001
167229970Sadrian *  Original version (IPv4) by Paul Vixie (ISC), July 1996
168229970Sadrian */
169229970Sadrian
170229970Sadrianstatic char *
171229970Sadrianinet_net_ntop_ipv6(const u_char *src, int bits, char *dst, size_t size) {
172229970Sadrian	u_int	m;
173229970Sadrian	int	b;
174229970Sadrian	int	p;
175229970Sadrian	int	zero_s, zero_l, tmp_zero_s, tmp_zero_l;
176229970Sadrian	int	i;
177229970Sadrian	int	is_ipv4 = 0;
178229970Sadrian	unsigned char inbuf[16];
179229970Sadrian	char outbuf[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128")];
180229970Sadrian	char	*cp;
181229970Sadrian	int	words;
182229970Sadrian	u_char	*s;
183229970Sadrian
184229970Sadrian	if (bits < 0 || bits > 128) {
185229970Sadrian		errno = EINVAL;
186229970Sadrian		return (NULL);
187229970Sadrian	}
188229970Sadrian
189229970Sadrian	cp = outbuf;
190229970Sadrian
191229970Sadrian	if (bits == 0) {
192229970Sadrian		*cp++ = ':';
193229970Sadrian		*cp++ = ':';
194229970Sadrian		*cp = '\0';
195229970Sadrian	} else {
196229970Sadrian		/* Copy src to private buffer.  Zero host part. */
197229970Sadrian		p = (bits + 7) / 8;
198229970Sadrian		memcpy(inbuf, src, p);
199229970Sadrian		memset(inbuf + p, 0, 16 - p);
200229970Sadrian		b = bits % 8;
201229970Sadrian		if (b != 0) {
202229970Sadrian			m = ~0 << (8 - b);
203229970Sadrian			inbuf[p-1] &= m;
204229970Sadrian		}
205229970Sadrian
206229970Sadrian		s = inbuf;
207229970Sadrian
208229970Sadrian		/* how many words need to be displayed in output */
209229970Sadrian		words = (bits + 15) / 16;
210229970Sadrian		if (words == 1)
211229970Sadrian			words = 2;
212229970Sadrian
213229970Sadrian		/* 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