1 /*
   2  * CDDL HEADER START
   3  *
   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.
   8  *
   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.
  13  *
  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]
  19  *
  20  * CDDL HEADER END
  21  */
  22 /*
  23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
  24  * Use is subject to license terms.
  25  */
  26 /*
  27  * Copyright 2012 Nexenta Systems, Inc. All rights reserved.
  28  */
  29 
  30 #include <sys/types.h>
  31 #include <sys/cmn_err.h>
  32 #include <sys/systm.h>
  33 #include <sys/socket.h>
  34 #include <sys/sunddi.h>
  35 #include <netinet/in.h>
  36 #include <inet/led.h>
  37 
  38 static void     convert2ascii(char *, const in6_addr_t *);
  39 static char     *strchr_w(const char *, int);
  40 static int      str2inet_addr(char *, ipaddr_t *);
  41 
  42 /*
  43  * inet_ntop -- Convert an IPv4 or IPv6 address in binary form into
  44  * printable form, and return a pointer to that string. Caller should
  45  * provide a buffer of correct length to store string into.
  46  * Note: this routine is kernel version of inet_ntop. It has similar
  47  * format as inet_ntop() defined in rfc2553. But it does not do
  48  * error handling operations exactly as rfc2553 defines. This function
  49  * is used by kernel inet directory routines only for debugging.
  50  * This inet_ntop() function, does not return NULL if third argument
  51  * is NULL. The reason is simple that we don't want kernel to panic
  52  * as the output of this function is directly fed to ip<n>dbg macro.
  53  * Instead it uses a local buffer for destination address for
  54  * those calls which purposely pass NULL ptr for the destination
  55  * buffer. This function is thread-safe when the caller passes a non-
  56  * null buffer with the third argument.
  57  */
  58 /* ARGSUSED */
  59 char *
  60 inet_ntop(int af, const void *addr, char *buf, int addrlen)
  61 {
  62         static char local_buf[INET6_ADDRSTRLEN];
  63         static char *err_buf1 = "<badaddr>";
  64         static char *err_buf2 = "<badfamily>";
  65         in6_addr_t      *v6addr;
  66         uchar_t         *v4addr;
  67         char            *caddr;
  68 
  69         /*
  70          * We don't allow thread unsafe inet_ntop calls, they
  71          * must pass a non-null buffer pointer. For DEBUG mode
  72          * we use the ASSERT() and for non-debug kernel it will
  73          * silently allow it for now. Someday we should remove
  74          * the static buffer from this function.
  75          */
  76 
  77         ASSERT(buf != NULL);
  78         if (buf == NULL)
  79                 buf = local_buf;
  80         buf[0] = '\0';
  81 
  82         /* Let user know politely not to send NULL or unaligned addr */
  83         if (addr == NULL || !(OK_32PTR(addr))) {
  84 #ifdef DEBUG
  85                 cmn_err(CE_WARN, "inet_ntop: addr is <null> or unaligned");
  86 #endif
  87                 return (err_buf1);
  88         }
  89 
  90 
  91 #define UC(b)   (((int)b) & 0xff)
  92         switch (af) {
  93         case AF_INET:
  94                 ASSERT(addrlen >= INET_ADDRSTRLEN);
  95                 v4addr = (uchar_t *)addr;
  96                 (void) sprintf(buf, "%03d.%03d.%03d.%03d",
  97                     UC(v4addr[0]), UC(v4addr[1]), UC(v4addr[2]), UC(v4addr[3]));
  98                 return (buf);
  99 
 100         case AF_INET6:
 101                 ASSERT(addrlen >= INET6_ADDRSTRLEN);
 102                 v6addr = (in6_addr_t *)addr;
 103                 if (IN6_IS_ADDR_V4MAPPED(v6addr)) {
 104                         caddr = (char *)addr;
 105                         (void) sprintf(buf, "::ffff:%d.%d.%d.%d",
 106                             UC(caddr[12]), UC(caddr[13]),
 107                             UC(caddr[14]), UC(caddr[15]));
 108                 } else if (IN6_IS_ADDR_V4COMPAT(v6addr)) {
 109                         caddr = (char *)addr;
 110                         (void) sprintf(buf, "::%d.%d.%d.%d",
 111                             UC(caddr[12]), UC(caddr[13]), UC(caddr[14]),
 112                             UC(caddr[15]));
 113                 } else if (IN6_IS_ADDR_UNSPECIFIED(v6addr)) {
 114                         (void) sprintf(buf, "::");
 115                 } else {
 116                         convert2ascii(buf, v6addr);
 117                 }
 118                 return (buf);
 119 
 120         default:
 121                 return (err_buf2);
 122         }
 123 #undef UC
 124 }
 125 
 126 /*
 127  *
 128  * v6 formats supported
 129  * General format xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx
 130  * The short hand notation :: is used for COMPAT addr
 131  * Other forms : fe80::xxxx:xxxx:xxxx:xxxx
 132  */
 133 static void
 134 convert2ascii(char *buf, const in6_addr_t *addr)
 135 {
 136         int             hexdigits;
 137         int             head_zero = 0;
 138         int             tail_zero = 0;
 139         /* tempbuf must be big enough to hold ffff:\0 */
 140         char            tempbuf[6];
 141         char            *ptr;
 142         uint16_t        *addr_component;
 143         size_t          len;
 144         boolean_t       first = B_FALSE;
 145         boolean_t       med_zero = B_FALSE;
 146         boolean_t       end_zero = B_FALSE;
 147 
 148         addr_component = (uint16_t *)addr;
 149         ptr = buf;
 150 
 151         /* First count if trailing zeroes higher in number */
 152         for (hexdigits = 0; hexdigits < 8; hexdigits++) {
 153                 if (*addr_component == 0) {
 154                         if (hexdigits < 4)
 155                                 head_zero++;
 156                         else
 157                                 tail_zero++;
 158                 }
 159                 addr_component++;
 160         }
 161         addr_component = (uint16_t *)addr;
 162         if (tail_zero > head_zero && (head_zero + tail_zero) != 7)
 163                 end_zero = B_TRUE;
 164 
 165         for (hexdigits = 0; hexdigits < 8; hexdigits++) {
 166 
 167                 /* if entry is a 0 */
 168 
 169                 if (*addr_component == 0) {
 170                         if (!first && *(addr_component + 1) == 0) {
 171                                 if (end_zero && (hexdigits < 4)) {
 172                                         *ptr++ = '0';
 173                                         *ptr++ = ':';
 174                                 } else {
 175                                         /*
 176                                          * address starts with 0s ..
 177                                          * stick in leading ':' of pair
 178                                          */
 179                                         if (hexdigits == 0)
 180                                                 *ptr++ = ':';
 181                                         /* add another */
 182                                         *ptr++ = ':';
 183                                         first = B_TRUE;
 184                                         med_zero = B_TRUE;
 185                                 }
 186                         } else if (first && med_zero) {
 187                                 if (hexdigits == 7)
 188                                         *ptr++ = ':';
 189                                 addr_component++;
 190                                 continue;
 191                         } else {
 192                                 *ptr++ = '0';
 193                                 *ptr++ = ':';
 194                         }
 195                         addr_component++;
 196                         continue;
 197                 }
 198                 if (med_zero)
 199                         med_zero = B_FALSE;
 200 
 201                 tempbuf[0] = '\0';
 202                 (void) sprintf(tempbuf, "%x:", ntohs(*addr_component) & 0xffff);
 203                 len = strlen(tempbuf);
 204                 bcopy(tempbuf, ptr, len);
 205                 ptr = ptr + len;
 206                 addr_component++;
 207         }
 208         *--ptr = '\0';
 209 }
 210 
 211 /*
 212  * search for char c, terminate on trailing white space
 213  */
 214 static char *
 215 strchr_w(const char *sp, int c)
 216 {
 217         /* skip leading white space */
 218         while (*sp && (*sp == ' ' || *sp == '\t')) {
 219                 sp++;
 220         }
 221 
 222         do {
 223                 if (*sp == (char)c)
 224                         return ((char *)sp);
 225                 if (*sp == ' ' || *sp == '\t')
 226                         return (NULL);
 227         } while (*sp++);
 228         return (NULL);
 229 }
 230 
 231 static int
 232 str2inet_addr(char *cp, ipaddr_t *addrp)
 233 {
 234         char *end;
 235         long byte;
 236         int i;
 237         uint8_t *addr = (uint8_t *)addrp;
 238 
 239         *addrp = 0;
 240 
 241         for (i = 0; i < 4; i++) {
 242                 if (ddi_strtol(cp, &end, 10, &byte) != 0 || byte < 0 ||
 243                     byte > 255) {
 244                         return (0);
 245                 }
 246                 addr[i] = (uint8_t)byte;
 247                 if (i < 3) {
 248                         if (*end != '.') {
 249                                 return (0);
 250                         } else {
 251                                 cp = end + 1;
 252                         }
 253                 } else {
 254                         cp = end;
 255                 }
 256         }
 257 
 258         return (1);
 259 }
 260 
 261 /*
 262  * inet_pton: This function takes string format IPv4 or IPv6 address and
 263  * converts it to binary form. The format of this function corresponds to
 264  * inet_pton() in the socket library.
 265  * It returns 0 for invalid IPv4 and IPv6 address
 266  *            1 when successfully converts ascii to binary
 267  *            -1 when af is not AF_INET or AF_INET6
 268  */
 269 int
 270 m_inet_pton(int af, char *inp, void *outp, int revert)
 271 {
 272         int i;
 273         long byte;
 274         char *end;
 275 
 276         switch (af) {
 277         case AF_INET:
 278                 if (str2inet_addr(inp, (ipaddr_t *)outp)) {
 279                         if (! revert)
 280                                 *(uint32_t *)outp = ntohl(*(uint32_t *)outp);
 281                         return (1);
 282                 } else {
 283                         return (0);
 284                 }
 285         case AF_INET6: {
 286                 union v6buf_u {
 287                         uint16_t v6words_u[8];
 288                         in6_addr_t v6addr_u;
 289                 } v6buf, *v6outp;
 290                 uint16_t        *dbl_col = NULL;
 291                 char lastbyte = NULL;
 292 
 293                 v6outp = (union v6buf_u *)outp;
 294 
 295                 if (strchr_w(inp, '.') != NULL) {
 296                         /* v4 mapped or v4 compatable */
 297                         if (strncmp(inp, "::ffff:", 7) == 0) {
 298                                 ipaddr_t ipv4_all_zeroes = 0;
 299                                 /* mapped - first init prefix and then fill */
 300                                 IN6_IPADDR_TO_V4MAPPED(ipv4_all_zeroes,
 301                                     &v6outp->v6addr_u);
 302                                 return (str2inet_addr(inp + 7,
 303                                     &(v6outp->v6addr_u.s6_addr32[3])));
 304                         } else if (strncmp(inp, "::", 2) == 0) {
 305                                 /* v4 compatable - prefix all zeroes */
 306                                 bzero(&v6outp->v6addr_u, sizeof (in6_addr_t));
 307                                 return (str2inet_addr(inp + 2,
 308                                     &(v6outp->v6addr_u.s6_addr32[3])));
 309                         }
 310                         return (0);
 311                 }
 312                 for (i = 0; i < 8; i++) {
 313                         int error;
 314                         /*
 315                          * if ddi_strtol() fails it could be because
 316                          * the string is "::".  That is valid and
 317                          * checked for below so just set the value to
 318                          * 0 and continue.
 319                          */
 320                         if ((error = ddi_strtol(inp, &end, 16, &byte)) != 0) {
 321                                 if (error == ERANGE)
 322                                         return (0);
 323                                 byte = 0;
 324                         }
 325                         if (byte < 0 || byte > 0x0ffff) {
 326                                 return (0);
 327                         }
 328                         if (revert) {
 329                                 v6buf.v6words_u[i] = htons((uint16_t)byte);
 330                         } else {
 331                                 v6buf.v6words_u[i] = (uint16_t)byte;
 332                         }
 333                         if (*end == NULL || i == 7) {
 334                                 inp = end;
 335                                 break;
 336                         }
 337                         if (inp == end) {       /* not a number must be */
 338                                 if (*inp == ':' &&
 339                                     ((i == 0 && *(inp + 1) == ':') ||
 340                                     lastbyte == ':')) {
 341                                         if (dbl_col) {
 342                                                 return (0);
 343                                         }
 344                                         if (byte != 0)
 345                                                 i++;
 346                                         dbl_col = &v6buf.v6words_u[i];
 347                                         if (i == 0)
 348                                                 inp++;
 349                                 } else if (*inp == NULL || *inp == ' ' ||
 350                                     *inp == '\t') {
 351                                         break;
 352                                 } else {
 353                                         return (0);
 354                                 }
 355                         } else {
 356                                 inp = end;
 357                         }
 358                         if (*inp != ':') {
 359                                 return (0);
 360                         }
 361                         inp++;
 362                         if (*inp == NULL || *inp == ' ' || *inp == '\t') {
 363                                 break;
 364                         }
 365                         lastbyte = *inp;
 366                 }
 367                 if (*inp != NULL && *inp != ' ' && *inp != '\t') {
 368                         return (0);
 369                 }
 370                 /*
 371                  * v6words now contains the bytes we could translate
 372                  * dbl_col points to the word (should be 0) where
 373                  * a double colon was found
 374                  */
 375                 if (i == 7) {
 376                         v6outp->v6addr_u = v6buf.v6addr_u;
 377                 } else {
 378                         int rem;
 379                         int word;
 380                         int next;
 381                         if (dbl_col == NULL) {
 382                                 return (0);
 383                         }
 384                         bzero(&v6outp->v6addr_u, sizeof (in6_addr_t));
 385                         rem = dbl_col - &v6buf.v6words_u[0];
 386                         for (next = 0; next < rem; next++) {
 387                                 v6outp->v6words_u[next] = v6buf.v6words_u[next];
 388                         }
 389                         next++; /* skip dbl_col 0 */
 390                         rem = i - rem;
 391                         word = 8 - rem;
 392                         while (rem > 0) {
 393                                 v6outp->v6words_u[word] = v6buf.v6words_u[next];
 394                                 word++;
 395                                 rem--;
 396                                 next++;
 397                         }
 398                 }
 399                 return (1);     /* Success */
 400         }
 401         }       /* switch */
 402         return (-1);    /* return -1 for default case */
 403 }
 404 
 405 
 406 int
 407 _inet_pton(int af, char *inp, void *outp)
 408 {
 409         return (m_inet_pton(af, inp, outp, 1));
 410 }
 411 
 412 /*
 413  * We need this inet_pton to preserve compatibility with old closed binaries.
 414  * Earlier, inet_pton returned address in hardware native order,
 415  * not in network one. (See http://www.illumos.org/issue/3105).
 416  * Having fixed that, we still need to support binaries, that use bad inet_pton
 417  * and reverse returned address manually. All new inet_pton calls will be
 418  * redirected to _inet_pton with #define in the header file.
 419  */
 420 int
 421 inet_pton(int af, char *inp, void *outp)
 422 {
 423         return (m_inet_pton(af, inp, outp, 0));
 424 }