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