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