1/*-
2 * SPDX-License-Identifier: ISC
3 *
4 * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
5 * Copyright (c) 1998,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_cidr_ntop.c,v 1.7 2006/10/11 02:18:18 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/nameser.h>
32#include <arpa/inet.h>
33
34#include <errno.h>
35#include <stdio.h>
36#include <string.h>
37#include <stdlib.h>
38
39#include "port_after.h"
40
41#ifdef SPRINTF_CHAR
42# define SPRINTF(x) strlen(sprintf/**/x)
43#else
44# define SPRINTF(x) ((size_t)sprintf x)
45#endif
46
47static char *
48inet_cidr_ntop_ipv4(const u_char *src, int bits, char *dst, size_t size);
49static char *
50inet_cidr_ntop_ipv6(const u_char *src, int bits, char *dst, size_t size);
51
52/*%
53 * char *
54 * inet_cidr_ntop(af, src, bits, dst, size)
55 *	convert network address from network to presentation format.
56 *	"src"'s size is determined from its "af".
57 * return:
58 *	pointer to dst, or NULL if an error occurred (check errno).
59 * note:
60 *	192.5.5.1/28 has a nonzero host part, which means it isn't a network
61 *	as called for by inet_net_ntop() but it can be a host address with
62 *	an included netmask.
63 * author:
64 *	Paul Vixie (ISC), October 1998
65 */
66char *
67inet_cidr_ntop(int af, const void *src, int bits, char *dst, size_t size) {
68	switch (af) {
69	case AF_INET:
70		return (inet_cidr_ntop_ipv4(src, bits, dst, size));
71	case AF_INET6:
72		return (inet_cidr_ntop_ipv6(src, bits, dst, size));
73	default:
74		errno = EAFNOSUPPORT;
75		return (NULL);
76	}
77}
78
79static int
80decoct(const u_char *src, int bytes, char *dst, size_t size) {
81	char *odst = dst;
82	char *t;
83	int b;
84
85	for (b = 1; b <= bytes; b++) {
86		if (size < sizeof "255.")
87			return (0);
88		t = dst;
89		dst += SPRINTF((dst, "%u", *src++));
90		if (b != bytes) {
91			*dst++ = '.';
92			*dst = '\0';
93		}
94		size -= (size_t)(dst - t);
95	}
96	return (dst - odst);
97}
98
99/*%
100 * static char *
101 * inet_cidr_ntop_ipv4(src, bits, dst, size)
102 *	convert IPv4 network address from network to presentation format.
103 *	"src"'s size is determined from its "af".
104 * return:
105 *	pointer to dst, or NULL if an error occurred (check errno).
106 * note:
107 *	network byte order assumed.  this means 192.5.5.240/28 has
108 *	0b11110000 in its fourth octet.
109 * author:
110 *	Paul Vixie (ISC), October 1998
111 */
112static char *
113inet_cidr_ntop_ipv4(const u_char *src, int bits, char *dst, size_t size) {
114	char *odst = dst;
115	size_t len = 4;
116	size_t b;
117	size_t bytes;
118
119	if ((bits < -1) || (bits > 32)) {
120		errno = EINVAL;
121		return (NULL);
122	}
123
124	/* Find number of significant bytes in address. */
125	if (bits == -1)
126		len = 4;
127	else
128		for (len = 1, b = 1 ; b < 4U; b++)
129			if (*(src + b))
130				len = b + 1;
131
132	/* Format whole octets plus nonzero trailing octets. */
133	bytes = (((bits <= 0) ? 1 : bits) + 7) / 8;
134	if (len > bytes)
135		bytes = len;
136	b = decoct(src, bytes, dst, size);
137	if (b == 0U)
138		goto emsgsize;
139	dst += b;
140	size -= b;
141
142	if (bits != -1) {
143		/* Format CIDR /width. */
144		if (size < sizeof "/32")
145			goto emsgsize;
146		dst += SPRINTF((dst, "/%u", bits));
147	}
148
149	return (odst);
150
151 emsgsize:
152	errno = EMSGSIZE;
153	return (NULL);
154}
155
156static char *
157inet_cidr_ntop_ipv6(const u_char *src, int bits, char *dst, size_t size) {
158	/*
159	 * Note that int32_t and int16_t need only be "at least" large enough
160	 * to contain a value of the specified size.  On some systems, like
161	 * Crays, there is no such thing as an integer variable with 16 bits.
162	 * Keep this in mind if you think this function should have been coded
163	 * to use pointer overlays.  All the world's not a VAX.
164	 */
165	char tmp[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255/128"];
166	char *tp;
167	struct { int base, len; } best, cur;
168	u_int words[NS_IN6ADDRSZ / NS_INT16SZ];
169	int i;
170
171	if ((bits < -1) || (bits > 128)) {
172		errno = EINVAL;
173		return (NULL);
174	}
175
176	/*
177	 * Preprocess:
178	 *	Copy the input (bytewise) array into a wordwise array.
179	 *	Find the longest run of 0x00's in src[] for :: shorthanding.
180	 */
181	memset(words, '\0', sizeof words);
182	for (i = 0; i < NS_IN6ADDRSZ; i++)
183		words[i / 2] |= (src[i] << ((1 - (i % 2)) << 3));
184	best.base = -1;
185	best.len = 0;
186	cur.base = -1;
187	cur.len = 0;
188	for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) {
189		if (words[i] == 0) {
190			if (cur.base == -1)
191				cur.base = i, cur.len = 1;
192			else
193				cur.len++;
194		} else {
195			if (cur.base != -1) {
196				if (best.base == -1 || cur.len > best.len)
197					best = cur;
198				cur.base = -1;
199			}
200		}
201	}
202	if (cur.base != -1) {
203		if (best.base == -1 || cur.len > best.len)
204			best = cur;
205	}
206	if (best.base != -1 && best.len < 2)
207		best.base = -1;
208
209	/*
210	 * Format the result.
211	 */
212	tp = tmp;
213	for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) {
214		/* Are we inside the best run of 0x00's? */
215		if (best.base != -1 && i >= best.base &&
216		    i < (best.base + best.len)) {
217			if (i == best.base)
218				*tp++ = ':';
219			continue;
220		}
221		/* Are we following an initial run of 0x00s or any real hex? */
222		if (i != 0)
223			*tp++ = ':';
224		/* Is this address an encapsulated IPv4? */
225		if (i == 6 && best.base == 0 && (best.len == 6 ||
226		    (best.len == 7 && words[7] != 0x0001) ||
227		    (best.len == 5 && words[5] == 0xffff))) {
228			int n;
229
230			if (src[15] || bits == -1 || bits > 120)
231				n = 4;
232			else if (src[14] || bits > 112)
233				n = 3;
234			else
235				n = 2;
236			n = decoct(src+12, n, tp, sizeof tmp - (tp - tmp));
237			if (n == 0) {
238				errno = EMSGSIZE;
239				return (NULL);
240			}
241			tp += strlen(tp);
242			break;
243		}
244		tp += SPRINTF((tp, "%x", words[i]));
245	}
246
247	/* Was it a trailing run of 0x00's? */
248	if (best.base != -1 && (best.base + best.len) ==
249	    (NS_IN6ADDRSZ / NS_INT16SZ))
250		*tp++ = ':';
251	*tp = '\0';
252
253	if (bits != -1)
254		tp += SPRINTF((tp, "/%u", bits));
255
256	/*
257	 * Check for overflow, copy, and we're done.
258	 */
259	if ((size_t)(tp - tmp) > size) {
260		errno = EMSGSIZE;
261		return (NULL);
262	}
263	strcpy(dst, tmp);
264	return (dst);
265}
266
267/*! \file */
268