Home | History | Annotate | Download | only in socket
      1 /* Copyright 2013 The Chromium Authors. All rights reserved.
      2  * Use of this source code is governed by a BSD-style license that can be
      3  * found in the LICENSE file. */
      4 
      5 #include "nacl_io/ossocket.h"
      6 #if defined(PROVIDES_SOCKET_API) && !defined(__GLIBC__) && !defined(__BIONIC__)
      7 
      8 #include <ctype.h>
      9 #include <errno.h>
     10 #include <stdlib.h>
     11 #include <string.h>
     12 
     13 #include "sdk_util/macros.h"
     14 
     15 enum {
     16   kIpv4AddressSize = sizeof(in_addr_t),
     17   kIpv6AddressSize = sizeof(struct in6_addr),
     18 };
     19 
     20 /* Helper function for inet_pton() for IPv4 addresses. */
     21 static int inet_pton_v4(const char* src, void* dst) {
     22   const char* pos = src;
     23   uint8_t result[kIpv4AddressSize] = {0};
     24 
     25   int i;
     26   for (i = 0; i < kIpv4AddressSize; ++i) {
     27     /* strtol() won't treat whitespace characters in the beginning as an error,
     28      * so check to ensure this is started with digit before passing to strtol().
     29      */
     30     if (isspace((int)(*pos)))
     31       return 0;
     32     char* end_pos;
     33     unsigned long value = strtoul(pos, &end_pos, 10);
     34     if (value > 255 || pos == end_pos)
     35       return 0;
     36     result[i] = (unsigned char)value;
     37     pos = end_pos;
     38 
     39     if (i < (kIpv4AddressSize - 1)) {
     40       if (*pos != '.')
     41         return 0;
     42       ++pos;
     43     }
     44   }
     45   if (*pos != '\0')
     46     return 0;
     47   memcpy(dst, result, sizeof(result));
     48   return 1;
     49 }
     50 
     51 /* Helper function for inet_pton() for IPv6 addresses. */
     52 int inet_pton_v6(const char* src, void* dst) {
     53   /* strtol() skips 0x in from of a number, while it's not allowed in IPv6
     54    * addresses. Check that there is no 'x' in the string. */
     55   const char* pos = src;
     56   while (*pos != '\0') {
     57     if (*pos == 'x')
     58       return 0;
     59     pos++;
     60   }
     61   pos = src;
     62 
     63   uint8_t result[kIpv6AddressSize];
     64   memset(&result, 0, sizeof(result));
     65   int double_colon_pos = -1;
     66   int result_pos = 0;
     67 
     68   if (*pos == ':') {
     69     if (*(pos + 1) != ':')
     70       return 0;
     71     pos += 2;
     72     double_colon_pos = 0;
     73   }
     74 
     75   while (*pos != '\0') {
     76     /* strtol() won't treat whitespace characters in the beginning as an error,
     77      * so check to ensure this is started with digit before passing to strtol().
     78      */
     79     if (isspace((int)(*pos)))
     80       return 0;
     81     char* end_pos;
     82     unsigned long word = strtoul(pos, &end_pos, 16);
     83     if (word > 0xffff || pos == end_pos)
     84       return 0;
     85 
     86     if (*end_pos == '.')  {
     87       if (result_pos + kIpv4AddressSize > kIpv6AddressSize)
     88         return 0;
     89       /* Parse rest of address as IPv4 address. */
     90       if (!inet_pton_v4(pos, result + result_pos))
     91         return 0;
     92       result_pos += 4;
     93       break;
     94     }
     95 
     96     if (result_pos > kIpv6AddressSize - 2)
     97       return 0;
     98     result[result_pos] = (word & 0xFF00) >> 8;
     99     result[result_pos + 1] = word & 0xFF;
    100     result_pos += 2;
    101 
    102     if (*end_pos == '\0')
    103       break;
    104 
    105     if (*end_pos != ':')
    106       return 0;
    107 
    108     pos = end_pos + 1;
    109     if (*pos == ':') {
    110       if (double_colon_pos != -1)
    111         return 0;
    112       double_colon_pos = result_pos;
    113       ++pos;
    114     }
    115   }
    116 
    117   /* Finally move the data to the end in case the address contained '::'. */
    118   if (result_pos < kIpv6AddressSize) {
    119     if (double_colon_pos == -1)
    120       return 0;
    121     int move_size = result_pos - double_colon_pos;
    122     int gap_size = kIpv6AddressSize - result_pos;
    123     memmove(result + kIpv6AddressSize - move_size,
    124             result + double_colon_pos, move_size);
    125     memset(result + double_colon_pos, 0, gap_size);
    126   }
    127 
    128   /* Finally copy the result to the output buffer. */
    129   memcpy(dst, result, sizeof(result));
    130 
    131   return 1;
    132 }
    133 
    134 int inet_pton(int af, const char *src, void *dst) {
    135   if (!src || !dst) {
    136     return 0;
    137   }
    138   if (af == AF_INET) {
    139     return inet_pton_v4(src, dst);
    140   } else if (af == AF_INET6) {
    141     return inet_pton_v6(src, dst);
    142   }
    143   errno = EAFNOSUPPORT;
    144   return -1;
    145 }
    146 
    147 #endif  /* defined(PROVIDES_SOCKET_API) && !defined(__GLIBC__) ... */
    148