xref: /illumos-gate/usr/src/lib/libresolv2/common/inet/inet_cidr_ntop.c (revision 4de2612967d06c4fdbf524a62556a1e8118a006f)
1 /*
2  * Copyright 1999-2002 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 /*
7  * Copyright (c) 1998,1999 by Internet Software Consortium.
8  *
9  * Permission to use, copy, modify, and distribute this software for any
10  * purpose with or without fee is hereby granted, provided that the above
11  * copyright notice and this permission notice appear in all copies.
12  *
13  * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
14  * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
15  * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
16  * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
17  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
18  * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
19  * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
20  * SOFTWARE.
21  */
22 
23 #pragma ident	"%Z%%M%	%I%	%E% SMI"
24 
25 #if defined(LIBC_SCCS) && !defined(lint)
26 static const char rcsid[] = "$Id: inet_cidr_ntop.c,v 8.7 2001/09/28 05:19:36 marka Exp $";
27 #endif
28 
29 #include "port_before.h"
30 
31 #include <sys/types.h>
32 #include <sys/socket.h>
33 #include <netinet/in.h>
34 #include <arpa/nameser.h>
35 #include <arpa/inet.h>
36 
37 #include <errno.h>
38 #include <stdio.h>
39 #include <string.h>
40 #include <stdlib.h>
41 
42 #include "port_after.h"
43 
44 #ifdef SPRINTF_CHAR
45 # define SPRINTF(x) strlen(sprintf/**/x)
46 #else
47 # define SPRINTF(x) ((size_t)sprintf x)
48 #endif
49 
50 static char *	inet_cidr_ntop_ipv4 __P((const u_char *src, int bits,
51 					 char *dst, size_t size));
52 static char *	inet_cidr_ntop_ipv6 __P((const u_char *src, int bits,
53 					 char *dst, size_t size));
54 
55 /*
56  * char *
57  * inet_cidr_ntop(af, src, bits, dst, size)
58  *	convert network address from network to presentation format.
59  *	"src"'s size is determined from its "af".
60  * return:
61  *	pointer to dst, or NULL if an error occurred (check errno).
62  * note:
63  *	192.5.5.1/28 has a nonzero host part, which means it isn't a network
64  *	as called for by inet_net_ntop() but it can be a host address with
65  *	an included netmask.
66  * author:
67  *	Paul Vixie (ISC), October 1998
68  */
69 char *
70 inet_cidr_ntop(int af, const void *src, int bits, char *dst, size_t size) {
71 	switch (af) {
72 	case AF_INET:
73 		return (inet_cidr_ntop_ipv4(src, bits, dst, size));
74 	case AF_INET6:
75 		return (inet_cidr_ntop_ipv6(src, bits, dst, size));
76 	default:
77 		errno = EAFNOSUPPORT;
78 		return (NULL);
79 	}
80 }
81 
82 static int
83 decoct(const u_char *src, int bytes, char *dst, size_t size) {
84 	char *odst = dst;
85 	char *t;
86 	int b;
87 
88 	for (b = 1; b <= bytes; b++) {
89 		if (size < sizeof "255.")
90 			return (0);
91 		t = dst;
92 		dst += SPRINTF((dst, "%u", *src++));
93 		if (b != bytes) {
94 			*dst++ = '.';
95 			*dst = '\0';
96 		}
97 		size -= (size_t)(dst - t);
98 	}
99 	return (dst - odst);
100 }
101 
102 /*
103  * static char *
104  * inet_cidr_ntop_ipv4(src, bits, dst, size)
105  *	convert IPv4 network address from network to presentation format.
106  *	"src"'s size is determined from its "af".
107  * return:
108  *	pointer to dst, or NULL if an error occurred (check errno).
109  * note:
110  *	network byte order assumed.  this means 192.5.5.240/28 has
111  *	0b11110000 in its fourth octet.
112  * author:
113  *	Paul Vixie (ISC), October 1998
114  */
115 static char *
116 inet_cidr_ntop_ipv4(const u_char *src, int bits, char *dst, size_t size) {
117 	char *odst = dst;
118 	size_t len = 4;
119 	size_t b;
120 	size_t bytes;
121 
122 	if ((bits < -1) || (bits > 32)) {
123 		errno = EINVAL;
124 		return (NULL);
125 	}
126 
127 	/* Find number of significant bytes in address. */
128 	if (bits == -1)
129 		len = 4;
130 	else
131 		for (len = 1, b = 1 ; b < 4; b++)
132 			if (*(src + b))
133 				len = b + 1;
134 
135 	/* Format whole octets plus nonzero trailing octets. */
136 	bytes = (((bits <= 0) ? 1 : bits) + 7) / 8;
137 	if (len > bytes)
138 		bytes = len;
139 	b = decoct(src, bytes, dst, size);
140 	if (b == 0)
141 		goto emsgsize;
142 	dst += b;
143 	size -= b;
144 
145 	if (bits != -1) {
146 		/* Format CIDR /width. */
147 		if (size < sizeof "/32")
148 			goto emsgsize;
149 		dst += SPRINTF((dst, "/%u", bits));
150 	}
151 
152 	return (odst);
153 
154  emsgsize:
155 	errno = EMSGSIZE;
156 	return (NULL);
157 }
158 
159 static char *
160 inet_cidr_ntop_ipv6(const u_char *src, int bits, char *dst, size_t size) {
161 	/*
162 	 * Note that int32_t and int16_t need only be "at least" large enough
163 	 * to contain a value of the specified size.  On some systems, like
164 	 * Crays, there is no such thing as an integer variable with 16 bits.
165 	 * Keep this in mind if you think this function should have been coded
166 	 * to use pointer overlays.  All the world's not a VAX.
167 	 */
168 	char tmp[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255/128"];
169 	char *tp;
170 	struct { int base, len; } best, cur;
171 	u_int words[NS_IN6ADDRSZ / NS_INT16SZ];
172 	int i;
173 
174 	if ((bits < -1) || (bits > 128)) {
175 		errno = EINVAL;
176 		return (NULL);
177 	}
178 
179 	/*
180 	 * Preprocess:
181 	 *	Copy the input (bytewise) array into a wordwise array.
182 	 *	Find the longest run of 0x00's in src[] for :: shorthanding.
183 	 */
184 	memset(words, '\0', sizeof words);
185 	for (i = 0; i < NS_IN6ADDRSZ; i++)
186 		words[i / 2] |= (src[i] << ((1 - (i % 2)) << 3));
187 	best.base = -1;
188 	cur.base = -1;
189 	for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) {
190 		if (words[i] == 0) {
191 			if (cur.base == -1)
192 				cur.base = i, cur.len = 1;
193 			else
194 				cur.len++;
195 		} else {
196 			if (cur.base != -1) {
197 				if (best.base == -1 || cur.len > best.len)
198 					best = cur;
199 				cur.base = -1;
200 			}
201 		}
202 	}
203 	if (cur.base != -1) {
204 		if (best.base == -1 || cur.len > best.len)
205 			best = cur;
206 	}
207 	if (best.base != -1 && best.len < 2)
208 		best.base = -1;
209 
210 	/*
211 	 * Format the result.
212 	 */
213 	tp = tmp;
214 	for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) {
215 		/* Are we inside the best run of 0x00's? */
216 		if (best.base != -1 && i >= best.base &&
217 		    i < (best.base + best.len)) {
218 			if (i == best.base)
219 				*tp++ = ':';
220 			continue;
221 		}
222 		/* Are we following an initial run of 0x00s or any real hex? */
223 		if (i != 0)
224 			*tp++ = ':';
225 		/* Is this address an encapsulated IPv4? */
226 		if (i == 6 && best.base == 0 && (best.len == 6 ||
227 		    (best.len == 7 && words[7] != 0x0001) ||
228 		    (best.len == 5 && words[5] == 0xffff))) {
229 			int n;
230 
231 			if (src[15] || bits == -1 || bits > 120)
232 				n = 4;
233 			else if (src[14] || bits > 112)
234 				n = 3;
235 			else
236 				n = 2;
237 			n = decoct(src+12, n, tp, sizeof tmp - (tp - tmp));
238 			if (n == 0) {
239 				errno = EMSGSIZE;
240 				return (NULL);
241 			}
242 			tp += strlen(tp);
243 			break;
244 		}
245 		tp += SPRINTF((tp, "%x", words[i]));
246 	}
247 
248 	/* Was it a trailing run of 0x00's? */
249 	if (best.base != -1 && (best.base + best.len) ==
250 	    (NS_IN6ADDRSZ / NS_INT16SZ))
251 		*tp++ = ':';
252 	*tp = '\0';
253 
254 	if (bits != -1)
255 		tp += SPRINTF((tp, "/%u", bits));
256 
257 	/*
258 	 * Check for overflow, copy, and we're done.
259 	 */
260 	if ((size_t)(tp - tmp) > size) {
261 		errno = EMSGSIZE;
262 		return (NULL);
263 	}
264 	strcpy(dst, tmp);
265 	return (dst);
266 }
267