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