blob: b1a2d7a17b4859a5123faa038b4aa40f39b3f795 [file] [log] [blame]
/* Copyright 2013 The Chromium Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file. */
#include "nacl_io/ossocket.h"
#if defined(PROVIDES_SOCKET_API) && !defined(__GLIBC__)
#include <ctype.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include "sdk_util/macros.h"
enum {
kIpv4AddressSize = 4,
kIpv6AddressSize = 16,
};
/* Helper function for inet_pton() for IPv4 addresses. */
static int inet_pton_v4(const char* src, void* dst) {
const char* pos = src;
uint8_t result[kIpv4AddressSize] = {0};
int i;
for (i = 0; i < kIpv4AddressSize; ++i) {
/* strtol() won't treat whitespace characters in the beginning as an error,
* so check to ensure this is started with digit before passing to strtol().
*/
if (isspace((int)(*pos)))
return 0;
char* end_pos;
unsigned long value = strtoul(pos, &end_pos, 10);
if (value > 255 || pos == end_pos)
return 0;
result[i] = (unsigned char)value;
pos = end_pos;
if (i < (kIpv4AddressSize - 1)) {
if (*pos != '.')
return 0;
++pos;
}
}
if (*pos != '\0')
return 0;
memcpy(dst, result, sizeof(result));
return 1;
}
/* Helper function for inet_pton() for IPv6 addresses. */
int inet_pton_v6(const char* src, void* dst) {
/* strtol() skips 0x in from of a number, while it's not allowed in IPv6
* addresses. Check that there is no 'x' in the string. */
const char* pos = src;
while (*pos != '\0') {
if (*pos == 'x')
return 0;
pos++;
}
pos = src;
uint8_t result[kIpv6AddressSize];
memset(&result, 0, sizeof(result));
int double_colon_pos = -1;
int result_pos = 0;
if (*pos == ':') {
if (*(pos + 1) != ':')
return 0;
pos += 2;
double_colon_pos = 0;
}
while (*pos != '\0') {
/* strtol() won't treat whitespace characters in the beginning as an error,
* so check to ensure this is started with digit before passing to strtol().
*/
if (isspace((int)(*pos)))
return 0;
char* end_pos;
unsigned long word = strtoul(pos, &end_pos, 16);
if (word > 0xffff || pos == end_pos)
return 0;
if (*end_pos == '.') {
if (result_pos + kIpv4AddressSize > kIpv6AddressSize)
return 0;
/* Parse rest of address as IPv4 address. */
if (!inet_pton_v4(pos, result + result_pos))
return 0;
result_pos += 4;
break;
}
if (result_pos > kIpv6AddressSize - 2)
return 0;
result[result_pos] = (word & 0xFF00) >> 8;
result[result_pos + 1] = word & 0xFF;
result_pos += 2;
if (*end_pos == '\0')
break;
if (*end_pos != ':')
return 0;
pos = end_pos + 1;
if (*pos == ':') {
if (double_colon_pos != -1)
return 0;
double_colon_pos = result_pos;
++pos;
}
}
/* Finally move the data to the end in case the address contained '::'. */
if (result_pos < kIpv6AddressSize) {
if (double_colon_pos == -1)
return 0;
int move_size = result_pos - double_colon_pos;
int gap_size = kIpv6AddressSize - result_pos;
memmove(result + kIpv6AddressSize - move_size,
result + double_colon_pos, move_size);
memset(result + double_colon_pos, 0, gap_size);
}
/* Finally copy the result to the output buffer. */
memcpy(dst, result, sizeof(result));
return 1;
}
int inet_pton(int af, const char *src, void *dst) {
if (!src || !dst) {
return 0;
}
if (af == AF_INET) {
return inet_pton_v4(src, dst);
} else if (af == AF_INET6) {
return inet_pton_v6(src, dst);
}
errno = EAFNOSUPPORT;
return -1;
}
#endif /* defined(PROVIDES_SOCKET_API) && !defined(__GLIBC__) */