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 #pragma ident   "%Z%%M% %I%     %E% SMI"
  28 
  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>
  36 
  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 *);
  40 
  41 /*
  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.
  56  */
  57 /* ARGSUSED */
  58 char *
  59 inet_ntop(int af, const void *addr, char *buf, int addrlen)
  60 {
  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;
  67 
  68         /*
  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.
  74          */
  75 
  76         ASSERT(buf != NULL);
  77         if (buf == NULL)
  78                 buf = local_buf;
  79         buf[0] = '\0';
  80 
  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);
  87         }
  88 
  89 
  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);
  98 
  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);
 116                 }
 117                 return (buf);
 118 
 119         default:
 120                 return (err_buf2);
 121         }
 122 #undef UC
 123 }
 124 
 125 /*
 126  *
 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
 131  */
 132 static void
 133 convert2ascii(char *buf, const in6_addr_t *addr)
 134 {
 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;
 146 
 147         addr_component = (uint16_t *)addr;
 148         ptr = buf;
 149 
 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++;
 157                 }
 158                 addr_component++;
 159         }
 160         addr_component = (uint16_t *)addr;
 161         if (tail_zero > head_zero && (head_zero + tail_zero) != 7)
 162                 end_zero = B_TRUE;
 163 
 164         for (hexdigits = 0; hexdigits < 8; hexdigits++) {
 165 
 166                 /* if entry is a 0 */
 167 
 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 {
 174                                         /*
 175                                          * address starts with 0s ..
 176                                          * stick in leading ':' of pair
 177                                          */
 178                                         if (hexdigits == 0)
 179                                                 *ptr++ = ':';
 180                                         /* add another */
 181                                         *ptr++ = ':';
 182                                         first = B_TRUE;
 183                                         med_zero = B_TRUE;
 184                                 }
 185                         } else if (first && med_zero) {
 186                                 if (hexdigits == 7)
 187                                         *ptr++ = ':';
 188                                 addr_component++;
 189                                 continue;
 190                         } else {
 191                                 *ptr++ = '0';
 192                                 *ptr++ = ':';
 193                         }
 194                         addr_component++;
 195                         continue;
 196                 }
 197                 if (med_zero)
 198                         med_zero = B_FALSE;
 199 
 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++;
 206         }
 207         *--ptr = '\0';
 208 }
 209 
 210 /*
 211  * search for char c, terminate on trailing white space
 212  */
 213 static char *
 214 strchr_w(const char *sp, int c)
 215 {
 216         /* skip leading white space */
 217         while (*sp && (*sp == ' ' || *sp == '\t')) {
 218                 sp++;
 219         }
 220 
 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);
 228 }
 229 
 230 static int
 231 str2inet_addr(char *cp, ipaddr_t *addrp)
 232 {
 233         char *end;
 234         long byte;
 235         int i;
 236         ipaddr_t addr = 0;
 237 
 238         for (i = 0; i < 4; i++) {
 239                 if (ddi_strtol(cp, &end, 10, &byte) != 0 || byte < 0 ||
 240                     byte > 255) {
 241                         return (0);
 242                 }
 243                 addr = (addr << 8) | (uint8_t)byte;
 244                 if (i < 3) {
 245                         if (*end != '.') {
 246                                 return (0);
 247                         } else {
 248                                 cp = end + 1;
 249                         }
 250                 } else {
 251                         cp = end;
 252                 }
 253         }
 254         *addrp = addr;
 255         return (1);
 256 }
 257 
 258 /*
 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.
 262  * It returns 0 for invalid IPv4 and IPv6 address
 263  *            1 when successfully converts ascii to binary
 264  *            -1 when af is not AF_INET or AF_INET6
 265  */
 266 int
 267 inet_pton(int af, char *inp, void *outp)
 268 {
 269         int i;
 270         long byte;
 271         char *end;
 272 
 273         switch (af) {
 274         case AF_INET:
 275                 return (str2inet_addr(inp, (ipaddr_t *)outp));
 276         case AF_INET6: {
 277                 union v6buf_u {
 278                         uint16_t v6words_u[8];
 279                         in6_addr_t v6addr_u;
 280                 } v6buf, *v6outp;
 281                 uint16_t        *dbl_col = NULL;
 282                 char lastbyte = NULL;
 283 
 284                 v6outp = (union v6buf_u *)outp;
 285 
 286                 if (strchr_w(inp, '.') != NULL) {
 287                         /* v4 mapped or v4 compatable */
 288                         if (strncmp(inp, "::ffff:", 7) == 0) {
 289                                 ipaddr_t ipv4_all_zeroes = 0;
 290                                 /* mapped - first init prefix and then fill */
 291                                 IN6_IPADDR_TO_V4MAPPED(ipv4_all_zeroes,
 292                                     &v6outp->v6addr_u);
 293                                 return (str2inet_addr(inp + 7,
 294                                     &(v6outp->v6addr_u.s6_addr32[3])));
 295                         } else if (strncmp(inp, "::", 2) == 0) {
 296                                 /* v4 compatable - prefix all zeroes */
 297                                 bzero(&v6outp->v6addr_u, sizeof (in6_addr_t));
 298                                 return (str2inet_addr(inp + 2,
 299                                     &(v6outp->v6addr_u.s6_addr32[3])));
 300                         }
 301                         return (0);
 302                 }
 303                 for (i = 0; i < 8; i++) {
 304                         int error;
 305                         /*
 306                          * if ddi_strtol() fails it could be because
 307                          * the string is "::".  That is valid and
 308                          * checked for below so just set the value to
 309                          * 0 and continue.
 310                          */
 311                         if ((error = ddi_strtol(inp, &end, 16, &byte)) != 0) {
 312                                 if (error == ERANGE)
 313                                         return (0);
 314                                 byte = 0;
 315                         }
 316                         if (byte < 0 || byte > 0x0ffff) {
 317                                 return (0);
 318                         }
 319                         v6buf.v6words_u[i] = (uint16_t)byte;
 320                         if (*end == NULL || i == 7) {
 321                                 inp = end;
 322                                 break;
 323                         }
 324                         if (inp == end) {       /* not a number must be */
 325                                 if (*inp == ':' &&
 326                                     ((i == 0 && *(inp + 1) == ':') ||
 327                                     lastbyte == ':')) {
 328                                         if (dbl_col) {
 329                                                 return (0);
 330                                         }
 331                                         if (byte != 0)
 332                                                 i++;
 333                                         dbl_col = &v6buf.v6words_u[i];
 334                                         if (i == 0)
 335                                                 inp++;
 336                                 } else if (*inp == NULL || *inp == ' ' ||
 337                                     *inp == '\t') {
 338                                         break;
 339                                 } else {
 340                                         return (0);
 341                                 }
 342                         } else {
 343                                 inp = end;
 344                         }
 345                         if (*inp != ':') {
 346                                 return (0);
 347                         }
 348                         inp++;
 349                         if (*inp == NULL || *inp == ' ' || *inp == '\t') {
 350                                 break;
 351                         }
 352                         lastbyte = *inp;
 353                 }
 354                 if (*inp != NULL && *inp != ' ' && *inp != '\t') {
 355                         return (0);
 356                 }
 357                 /*
 358                  * v6words now contains the bytes we could translate
 359                  * dbl_col points to the word (should be 0) where
 360                  * a double colon was found
 361                  */
 362                 if (i == 7) {
 363                         v6outp->v6addr_u = v6buf.v6addr_u;
 364                 } else {
 365                         int rem;
 366                         int word;
 367                         int next;
 368                         if (dbl_col == NULL) {
 369                                 return (0);
 370                         }
 371                         bzero(&v6outp->v6addr_u, sizeof (in6_addr_t));
 372                         rem = dbl_col - &v6buf.v6words_u[0];
 373                         for (next = 0; next < rem; next++) {
 374                                 v6outp->v6words_u[next] = v6buf.v6words_u[next];
 375                         }
 376                         next++; /* skip dbl_col 0 */
 377                         rem = i - rem;
 378                         word = 8 - rem;
 379                         while (rem > 0) {
 380                                 v6outp->v6words_u[word] = v6buf.v6words_u[next];
 381                                 word++;
 382                                 rem--;
 383                                 next++;
 384                         }
 385                 }
 386                 return (1);     /* Success */
 387         }
 388         }       /* switch */
 389         return (-1);    /* return -1 for default case */
 390 }