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