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
15enum {
16  kIpv4AddressSize = sizeof(in_addr_t),
17  kIpv6AddressSize = sizeof(struct in6_addr),
18};
19
20/* Helper function for inet_pton() for IPv4 addresses. */
21static 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. */
52int 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
134int 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