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