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)
21 static 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
46 static char * inet_net_ntop_ipv4(const u_char *src, int bits, char *dst,
47 size_t size);
48 static 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 */
61 char *
inet_net_ntop(int af,const void * src,int bits,char * dst,size_t size)62 inet_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 */
88 static char *
inet_net_ntop_ipv4(const u_char * src,int bits,char * dst,size_t size)89 inet_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
163 static char *
inet_net_ntop_ipv6(const u_char * src,int bits,char * dst,size_t size)164 inet_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
269 emsgsize:
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