4931 kernel's inet_pton still botches v4 mapped and compat addresses byte order
[illumos-gate.git] / usr / src / uts / common / inet / ip / inet_ntop.c
blob544e180c72b7bcd6605fc3d79c85c444f778ff0d
1 /*
2 * CDDL HEADER START
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
7 * with the License.
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
20 * CDDL HEADER END
24 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
25 * Use is subject to license terms.
26 * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
29 #include <sys/types.h>
30 #include <sys/cmn_err.h>
31 #include <sys/systm.h>
32 #include <sys/socket.h>
33 #include <sys/sunddi.h>
34 #include <netinet/in.h>
35 #include <inet/led.h>
37 static void convert2ascii(char *, const in6_addr_t *);
38 static char *strchr_w(const char *, int);
39 static int str2inet_addr(char *, ipaddr_t *);
42 * inet_ntop -- Convert an IPv4 or IPv6 address in binary form into
43 * printable form, and return a pointer to that string. Caller should
44 * provide a buffer of correct length to store string into.
45 * Note: this routine is kernel version of inet_ntop. It has similar
46 * format as inet_ntop() defined in rfc2553. But it does not do
47 * error handling operations exactly as rfc2553 defines. This function
48 * is used by kernel inet directory routines only for debugging.
49 * This inet_ntop() function, does not return NULL if third argument
50 * is NULL. The reason is simple that we don't want kernel to panic
51 * as the output of this function is directly fed to ip<n>dbg macro.
52 * Instead it uses a local buffer for destination address for
53 * those calls which purposely pass NULL ptr for the destination
54 * buffer. This function is thread-safe when the caller passes a non-
55 * null buffer with the third argument.
57 /* ARGSUSED */
58 char *
59 inet_ntop(int af, const void *addr, char *buf, int addrlen)
61 static char local_buf[INET6_ADDRSTRLEN];
62 static char *err_buf1 = "<badaddr>";
63 static char *err_buf2 = "<badfamily>";
64 in6_addr_t *v6addr;
65 uchar_t *v4addr;
66 char *caddr;
69 * We don't allow thread unsafe inet_ntop calls, they
70 * must pass a non-null buffer pointer. For DEBUG mode
71 * we use the ASSERT() and for non-debug kernel it will
72 * silently allow it for now. Someday we should remove
73 * the static buffer from this function.
76 ASSERT(buf != NULL);
77 if (buf == NULL)
78 buf = local_buf;
79 buf[0] = '\0';
81 /* Let user know politely not to send NULL or unaligned addr */
82 if (addr == NULL || !(OK_32PTR(addr))) {
83 #ifdef DEBUG
84 cmn_err(CE_WARN, "inet_ntop: addr is <null> or unaligned");
85 #endif
86 return (err_buf1);
90 #define UC(b) (((int)b) & 0xff)
91 switch (af) {
92 case AF_INET:
93 ASSERT(addrlen >= INET_ADDRSTRLEN);
94 v4addr = (uchar_t *)addr;
95 (void) sprintf(buf, "%03d.%03d.%03d.%03d",
96 UC(v4addr[0]), UC(v4addr[1]), UC(v4addr[2]), UC(v4addr[3]));
97 return (buf);
99 case AF_INET6:
100 ASSERT(addrlen >= INET6_ADDRSTRLEN);
101 v6addr = (in6_addr_t *)addr;
102 if (IN6_IS_ADDR_V4MAPPED(v6addr)) {
103 caddr = (char *)addr;
104 (void) sprintf(buf, "::ffff:%d.%d.%d.%d",
105 UC(caddr[12]), UC(caddr[13]),
106 UC(caddr[14]), UC(caddr[15]));
107 } else if (IN6_IS_ADDR_V4COMPAT(v6addr)) {
108 caddr = (char *)addr;
109 (void) sprintf(buf, "::%d.%d.%d.%d",
110 UC(caddr[12]), UC(caddr[13]), UC(caddr[14]),
111 UC(caddr[15]));
112 } else if (IN6_IS_ADDR_UNSPECIFIED(v6addr)) {
113 (void) sprintf(buf, "::");
114 } else {
115 convert2ascii(buf, v6addr);
117 return (buf);
119 default:
120 return (err_buf2);
122 #undef UC
127 * v6 formats supported
128 * General format xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx
129 * The short hand notation :: is used for COMPAT addr
130 * Other forms : fe80::xxxx:xxxx:xxxx:xxxx
132 static void
133 convert2ascii(char *buf, const in6_addr_t *addr)
135 int hexdigits;
136 int head_zero = 0;
137 int tail_zero = 0;
138 /* tempbuf must be big enough to hold ffff:\0 */
139 char tempbuf[6];
140 char *ptr;
141 uint16_t *addr_component;
142 size_t len;
143 boolean_t first = B_FALSE;
144 boolean_t med_zero = B_FALSE;
145 boolean_t end_zero = B_FALSE;
147 addr_component = (uint16_t *)addr;
148 ptr = buf;
150 /* First count if trailing zeroes higher in number */
151 for (hexdigits = 0; hexdigits < 8; hexdigits++) {
152 if (*addr_component == 0) {
153 if (hexdigits < 4)
154 head_zero++;
155 else
156 tail_zero++;
158 addr_component++;
160 addr_component = (uint16_t *)addr;
161 if (tail_zero > head_zero && (head_zero + tail_zero) != 7)
162 end_zero = B_TRUE;
164 for (hexdigits = 0; hexdigits < 8; hexdigits++) {
166 /* if entry is a 0 */
168 if (*addr_component == 0) {
169 if (!first && *(addr_component + 1) == 0) {
170 if (end_zero && (hexdigits < 4)) {
171 *ptr++ = '0';
172 *ptr++ = ':';
173 } else {
175 * address starts with 0s ..
176 * stick in leading ':' of pair
178 if (hexdigits == 0)
179 *ptr++ = ':';
180 /* add another */
181 *ptr++ = ':';
182 first = B_TRUE;
183 med_zero = B_TRUE;
185 } else if (first && med_zero) {
186 if (hexdigits == 7)
187 *ptr++ = ':';
188 addr_component++;
189 continue;
190 } else {
191 *ptr++ = '0';
192 *ptr++ = ':';
194 addr_component++;
195 continue;
197 if (med_zero)
198 med_zero = B_FALSE;
200 tempbuf[0] = '\0';
201 (void) sprintf(tempbuf, "%x:", ntohs(*addr_component) & 0xffff);
202 len = strlen(tempbuf);
203 bcopy(tempbuf, ptr, len);
204 ptr = ptr + len;
205 addr_component++;
207 *--ptr = '\0';
211 * search for char c, terminate on trailing white space
213 static char *
214 strchr_w(const char *sp, int c)
216 /* skip leading white space */
217 while (*sp && (*sp == ' ' || *sp == '\t')) {
218 sp++;
221 do {
222 if (*sp == (char)c)
223 return ((char *)sp);
224 if (*sp == ' ' || *sp == '\t')
225 return (NULL);
226 } while (*sp++);
227 return (NULL);
230 static int
231 str2inet_addr(char *cp, ipaddr_t *addrp)
233 char *end;
234 long byte;
235 int i;
236 ipaddr_t addr = 0;
238 for (i = 0; i < 4; i++) {
239 if (ddi_strtol(cp, &end, 10, &byte) != 0 || byte < 0 ||
240 byte > 255) {
241 return (0);
243 addr = (addr << 8) | (uint8_t)byte;
244 if (i < 3) {
245 if (*end != '.') {
246 return (0);
247 } else {
248 cp = end + 1;
250 } else {
251 cp = end;
254 *addrp = addr;
255 return (1);
259 * inet_pton: This function takes string format IPv4 or IPv6 address and
260 * converts it to binary form. The format of this function corresponds to
261 * inet_pton() in the socket library.
263 * Return values:
264 * 0 invalid IPv4 or IPv6 address
265 * 1 successful conversion
266 * -1 af is not AF_INET or AF_INET6
269 __inet_pton(int af, char *inp, void *outp, int compat)
271 int i;
272 long byte;
273 char *end;
275 switch (af) {
276 case AF_INET:
277 if (str2inet_addr(inp, (ipaddr_t *)outp) != 0) {
278 if (!compat)
279 *(uint32_t *)outp = htonl(*(uint32_t *)outp);
280 return (1);
281 } else {
282 return (0);
284 case AF_INET6: {
285 union v6buf_u {
286 uint16_t v6words_u[8];
287 in6_addr_t v6addr_u;
288 } v6buf, *v6outp;
289 uint16_t *dbl_col = NULL;
290 char lastbyte = NULL;
292 v6outp = (union v6buf_u *)outp;
294 if (strchr_w(inp, '.') != NULL) {
295 int ret = 0;
297 /* v4 mapped or v4 compatable */
298 if (strncmp(inp, "::ffff:", 7) == 0) {
299 ipaddr_t ipv4_all_zeroes = 0;
300 /* mapped - first init prefix and then fill */
301 IN6_IPADDR_TO_V4MAPPED(ipv4_all_zeroes,
302 &v6outp->v6addr_u);
303 ret = str2inet_addr(inp + 7,
304 &(v6outp->v6addr_u.s6_addr32[3]));
305 } else if (strncmp(inp, "::", 2) == 0) {
306 /* v4 compatable - prefix all zeroes */
307 bzero(&v6outp->v6addr_u, sizeof (in6_addr_t));
308 ret = str2inet_addr(inp + 2,
309 &(v6outp->v6addr_u.s6_addr32[3]));
311 if (ret > 0 && !compat) {
312 v6outp->v6addr_u.s6_addr32[3] =
313 htonl(v6outp->v6addr_u.s6_addr32[3]);
315 return (ret);
317 for (i = 0; i < 8; i++) {
318 int error;
320 * if ddi_strtol() fails it could be because
321 * the string is "::". That is valid and
322 * checked for below so just set the value to
323 * 0 and continue.
325 if ((error = ddi_strtol(inp, &end, 16, &byte)) != 0) {
326 if (error == ERANGE)
327 return (0);
328 byte = 0;
330 if (byte < 0 || byte > 0x0ffff) {
331 return (0);
333 if (compat) {
334 v6buf.v6words_u[i] = (uint16_t)byte;
335 } else {
336 v6buf.v6words_u[i] = htons((uint16_t)byte);
338 if (*end == NULL || i == 7) {
339 inp = end;
340 break;
342 if (inp == end) { /* not a number must be */
343 if (*inp == ':' &&
344 ((i == 0 && *(inp + 1) == ':') ||
345 lastbyte == ':')) {
346 if (dbl_col) {
347 return (0);
349 if (byte != 0)
350 i++;
351 dbl_col = &v6buf.v6words_u[i];
352 if (i == 0)
353 inp++;
354 } else if (*inp == NULL || *inp == ' ' ||
355 *inp == '\t') {
356 break;
357 } else {
358 return (0);
360 } else {
361 inp = end;
363 if (*inp != ':') {
364 return (0);
366 inp++;
367 if (*inp == NULL || *inp == ' ' || *inp == '\t') {
368 break;
370 lastbyte = *inp;
372 if (*inp != NULL && *inp != ' ' && *inp != '\t') {
373 return (0);
376 * v6words now contains the bytes we could translate
377 * dbl_col points to the word (should be 0) where
378 * a double colon was found
380 if (i == 7) {
381 v6outp->v6addr_u = v6buf.v6addr_u;
382 } else {
383 int rem;
384 int word;
385 int next;
386 if (dbl_col == NULL) {
387 return (0);
389 bzero(&v6outp->v6addr_u, sizeof (in6_addr_t));
390 rem = dbl_col - &v6buf.v6words_u[0];
391 for (next = 0; next < rem; next++) {
392 v6outp->v6words_u[next] = v6buf.v6words_u[next];
394 next++; /* skip dbl_col 0 */
395 rem = i - rem;
396 word = 8 - rem;
397 while (rem > 0) {
398 v6outp->v6words_u[word] = v6buf.v6words_u[next];
399 word++;
400 rem--;
401 next++;
404 return (1); /* Success */
406 } /* switch */
407 return (-1); /* return -1 for default case */
411 * Provide fixed inet_pton() implementation.
414 _inet_pton(int af, char *inp, void *outp)
416 return (__inet_pton(af, inp, outp, 0));
420 * Provide broken inet_pton() implementation by default for binary
421 * compatibility.
424 inet_pton(int af, char *inp, void *outp)
426 return (__inet_pton(af, inp, outp, 1));