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