blob: 2d181ec6e02699e955eabe9571273ee03927691c [file] [log] [blame]
// Copyright (c) 2012 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 "ppapi/shared_impl/private/net_address_private_impl.h"
#include <string.h>
#include <string>
#include "base/basictypes.h"
#include "base/logging.h"
#include "base/stringprintf.h"
#include "base/sys_byteorder.h"
#include "build/build_config.h"
#include "net/base/address_list.h"
#include "net/base/ip_endpoint.h"
#include "net/base/net_util.h"
#include "ppapi/c/pp_var.h"
#include "ppapi/c/private/ppb_net_address_private.h"
#include "ppapi/shared_impl/var.h"
#include "ppapi/thunk/thunk.h"
#if defined(OS_MACOSX)
// This is a bit evil, but it's standard operating procedure for |s6_addr|....
#define s6_addr16 __u6_addr.__u6_addr16
#endif
#if defined(OS_WIN)
// The type of |sockaddr::sa_family|.
typedef ADDRESS_FAMILY sa_family_t;
#define s6_addr16 u.Word
#define ntohs(x) _byteswap_ushort(x)
#define htons(x) _byteswap_ushort(x)
#endif // OS_WIN
// The net address interface doesn't have a normal C -> C++ thunk since it
// doesn't actually have any proxy wrapping or associated objects; it's just a
// call into base. So we implement the entire interface here, using the thunk
// namespace so it magically gets hooked up in the proper places.
namespace ppapi {
namespace {
// This assert fails on OpenBSD for an unknown reason at the moment.
#if !defined(OS_OPENBSD)
// Make sure the storage in |PP_NetAddress_Private| is big enough. (Do it here
// since the data is opaque elsewhere.)
COMPILE_ASSERT(sizeof(reinterpret_cast<PP_NetAddress_Private*>(0)->data) >=
sizeof(sockaddr_storage), PP_NetAddress_Private_data_too_small);
#endif
sa_family_t GetFamilyInternal(const PP_NetAddress_Private* addr) {
return reinterpret_cast<const sockaddr*>(addr->data)->sa_family;
}
PP_NetAddressFamily_Private GetFamily(const PP_NetAddress_Private* addr) {
switch (GetFamilyInternal(addr)) {
case AF_INET:
return PP_NETADDRESSFAMILY_IPV4;
case AF_INET6:
return PP_NETADDRESSFAMILY_IPV6;
default:
return PP_NETADDRESSFAMILY_UNSPECIFIED;
}
}
uint16_t GetPort(const PP_NetAddress_Private* addr) {
switch (GetFamilyInternal(addr)) {
case AF_INET: {
const sockaddr_in* a = reinterpret_cast<const sockaddr_in*>(addr->data);
return base::NetToHost16(a->sin_port);
}
case AF_INET6: {
const sockaddr_in6* a = reinterpret_cast<const sockaddr_in6*>(addr->data);
return base::NetToHost16(a->sin6_port);
}
default:
return 0;
}
}
PP_Bool GetAddress(const PP_NetAddress_Private* addr,
void* address,
uint16_t address_size) {
switch (GetFamilyInternal(addr)) {
case AF_INET: {
const sockaddr_in* a = reinterpret_cast<const sockaddr_in*>(addr->data);
if (address_size >= sizeof(a->sin_addr.s_addr)) {
memcpy(address, &(a->sin_addr.s_addr), sizeof(a->sin_addr.s_addr));
return PP_TRUE;
}
break;
}
case AF_INET6: {
const sockaddr_in6* a = reinterpret_cast<const sockaddr_in6*>(addr->data);
if (address_size >= sizeof(a->sin6_addr.s6_addr)) {
memcpy(address, &(a->sin6_addr.s6_addr), sizeof(a->sin6_addr.s6_addr));
return PP_TRUE;
}
break;
}
default:
break;
}
return PP_FALSE;
}
uint32_t GetScopeID(const PP_NetAddress_Private* addr) {
switch (GetFamilyInternal(addr)) {
case AF_INET6: {
const sockaddr_in6* a = reinterpret_cast<const sockaddr_in6*>(addr->data);
return a->sin6_scope_id;
}
default:
return 0;
}
}
PP_Bool AreHostsEqual(const PP_NetAddress_Private* addr1,
const PP_NetAddress_Private* addr2) {
if (!NetAddressPrivateImpl::ValidateNetAddress(*addr1) ||
!NetAddressPrivateImpl::ValidateNetAddress(*addr2))
return PP_FALSE;
sa_family_t addr1_family = GetFamilyInternal(addr1);
if (addr1_family != GetFamilyInternal(addr2))
return PP_FALSE;
switch (addr1_family) {
case AF_INET: {
const sockaddr_in* a1 = reinterpret_cast<const sockaddr_in*>(addr1->data);
const sockaddr_in* a2 = reinterpret_cast<const sockaddr_in*>(addr2->data);
return PP_FromBool(a1->sin_addr.s_addr == a2->sin_addr.s_addr);
}
case AF_INET6: {
const sockaddr_in6* a1 =
reinterpret_cast<const sockaddr_in6*>(addr1->data);
const sockaddr_in6* a2 =
reinterpret_cast<const sockaddr_in6*>(addr2->data);
return PP_FromBool(a1->sin6_flowinfo == a2->sin6_flowinfo &&
memcmp(&a1->sin6_addr, &a2->sin6_addr,
sizeof(a1->sin6_addr)) == 0 &&
a1->sin6_scope_id == a2->sin6_scope_id);
}
default:
return PP_FALSE;
}
}
PP_Bool AreEqual(const PP_NetAddress_Private* addr1,
const PP_NetAddress_Private* addr2) {
// |AreHostsEqual()| will also validate the addresses and return false if
// either is invalid.
if (!AreHostsEqual(addr1, addr2))
return PP_FALSE;
// Note: Here, we know that |addr1| and |addr2| have the same family.
switch (GetFamilyInternal(addr1)) {
case AF_INET: {
const sockaddr_in* a1 = reinterpret_cast<const sockaddr_in*>(addr1->data);
const sockaddr_in* a2 = reinterpret_cast<const sockaddr_in*>(addr2->data);
return PP_FromBool(a1->sin_port == a2->sin_port);
}
case AF_INET6: {
const sockaddr_in6* a1 =
reinterpret_cast<const sockaddr_in6*>(addr1->data);
const sockaddr_in6* a2 =
reinterpret_cast<const sockaddr_in6*>(addr2->data);
return PP_FromBool(a1->sin6_port == a2->sin6_port);
}
default:
return PP_FALSE;
}
}
#if defined(OS_WIN) || defined(OS_MACOSX)
std::string ConvertIPv4AddressToString(const sockaddr_in* a,
bool include_port) {
unsigned ip = base::NetToHost32(a->sin_addr.s_addr);
unsigned port = base::NetToHost16(a->sin_port);
std::string description = base::StringPrintf(
"%u.%u.%u.%u",
(ip >> 24) & 0xff, (ip >> 16) & 0xff, (ip >> 8) & 0xff, ip & 0xff);
if (include_port)
base::StringAppendF(&description, ":%u", port);
return description;
}
// Format an IPv6 address for human consumption, basically according to RFC
// 5952.
// - If the scope is nonzero, it is appended to the address as "%<scope>" (this
// is not in RFC 5952, but consistent with |getnameinfo()| on Linux and
// Windows).
// - If |include_port| is true, the address (possibly including the scope) is
// enclosed in square brackets and ":<port>" is appended, i.e., the overall
// format is "[<address>]:<port>".
// - If the address is an IPv4 address embedded IPv6 (per RFC 4291), then the
// mixed format is used, e.g., "::ffff:192.168.1.2". This is optional per RFC
// 5952, but consistent with |getnameinfo()|.
std::string ConvertIPv6AddressToString(const sockaddr_in6* a,
bool include_port) {
unsigned port = base::NetToHost16(a->sin6_port);
unsigned scope = a->sin6_scope_id;
std::string description(include_port ? "[" : "");
// IPv4 address embedded in IPv6.
if (a->sin6_addr.s6_addr16[0] == 0 && a->sin6_addr.s6_addr16[1] == 0 &&
a->sin6_addr.s6_addr16[2] == 0 && a->sin6_addr.s6_addr16[3] == 0 &&
a->sin6_addr.s6_addr16[4] == 0 &&
(a->sin6_addr.s6_addr16[5] == 0 || a->sin6_addr.s6_addr16[5] == 0xffff)) {
base::StringAppendF(
&description,
a->sin6_addr.s6_addr16[5] == 0 ? "::%u.%u.%u.%u" : "::ffff:%u.%u.%u.%u",
static_cast<unsigned>(a->sin6_addr.s6_addr[12]),
static_cast<unsigned>(a->sin6_addr.s6_addr[13]),
static_cast<unsigned>(a->sin6_addr.s6_addr[14]),
static_cast<unsigned>(a->sin6_addr.s6_addr[15]));
// "Real" IPv6 addresses.
} else {
// Find the first longest run of 0s (of length > 1), to collapse to "::".
int longest_start = 0;
int longest_length = 0;
int curr_start = 0;
int curr_length = 0;
for (int i = 0; i < 8; i++) {
if (base::NetToHost16(a->sin6_addr.s6_addr16[i]) != 0) {
curr_length = 0;
} else {
if (!curr_length)
curr_start = i;
curr_length++;
if (curr_length > longest_length) {
longest_start = curr_start;
longest_length = curr_length;
}
}
}
bool need_sep = false; // Whether the next item needs a ':' to separate.
for (int i = 0; i < 8;) {
if (longest_length > 1 && i == longest_start) {
description.append("::");
need_sep = false;
i += longest_length;
} else {
unsigned v = base::NetToHost16(a->sin6_addr.s6_addr16[i]);
base::StringAppendF(&description, need_sep ? ":%x" : "%x", v);
need_sep = true;
i++;
}
}
}
// Nonzero scopes, e.g., 123, are indicated by appending, e.g., "%123".
if (scope != 0)
base::StringAppendF(&description, "%%%u", scope);
if (include_port)
base::StringAppendF(&description, "]:%u", port);
return description;
}
#endif // OS_WIN || OS_MACOSX
PP_Var Describe(PP_Module /*module*/,
const struct PP_NetAddress_Private* addr,
PP_Bool include_port) {
if (!NetAddressPrivateImpl::ValidateNetAddress(*addr))
return PP_MakeUndefined();
#if defined(OS_WIN) || defined(OS_MACOSX)
// On Windows, |NetAddressToString()| doesn't work in the sandbox. On Mac,
// the output isn't consistent with RFC 5952, at least on Mac OS 10.6:
// |getnameinfo()| collapses length-one runs of zeros (and also doesn't
// display the scope).
// TODO(viettrungluu): Consider switching to this on Linux.
switch (GetFamilyInternal(addr)) {
case AF_INET: {
const sockaddr_in* a = reinterpret_cast<const sockaddr_in*>(addr->data);
return StringVar::StringToPPVar(
ConvertIPv4AddressToString(a, !!include_port));
}
case AF_INET6: {
const sockaddr_in6* a = reinterpret_cast<const sockaddr_in6*>(addr->data);
return StringVar::StringToPPVar(
ConvertIPv6AddressToString(a, !!include_port));
}
default:
NOTREACHED();
break;
}
return PP_MakeUndefined();
#else
const sockaddr* a = reinterpret_cast<const sockaddr*>(addr->data);
socklen_t l = addr->size;
std::string description =
include_port ? net::NetAddressToStringWithPort(a, l) :
net::NetAddressToString(a, l);
return StringVar::StringToPPVar(description);
#endif
}
PP_Bool ReplacePort(const struct PP_NetAddress_Private* src_addr,
uint16_t port,
struct PP_NetAddress_Private* dest_addr) {
if (!NetAddressPrivateImpl::ValidateNetAddress(*src_addr))
return PP_FALSE;
switch (GetFamilyInternal(src_addr)) {
case AF_INET: {
memmove(dest_addr, src_addr, sizeof(*src_addr));
reinterpret_cast<sockaddr_in*>(dest_addr->data)->sin_port =
base::HostToNet16(port);
return PP_TRUE;
}
case AF_INET6: {
memmove(dest_addr, src_addr, sizeof(*src_addr));
reinterpret_cast<sockaddr_in6*>(dest_addr->data)->sin6_port =
base::HostToNet16(port);
return PP_TRUE;
}
default:
return PP_FALSE;
}
}
void GetAnyAddress(PP_Bool is_ipv6, PP_NetAddress_Private* addr) {
memset(addr->data, 0, arraysize(addr->data) * sizeof(addr->data[0]));
if (is_ipv6) {
sockaddr_in6* a = reinterpret_cast<sockaddr_in6*>(addr->data);
addr->size = sizeof(*a);
a->sin6_family = AF_INET6;
a->sin6_addr = in6addr_any;
} else {
sockaddr_in* a = reinterpret_cast<sockaddr_in*>(addr->data);
addr->size = sizeof(*a);
a->sin_family = AF_INET;
a->sin_addr.s_addr = INADDR_ANY;
}
}
void CreateFromIPv4Address(const uint8_t ip[4],
uint16_t port,
struct PP_NetAddress_Private* addr_out) {
memset(addr_out->data, 0,
arraysize(addr_out->data) * sizeof(addr_out->data[0]));
sockaddr_in* a = reinterpret_cast<sockaddr_in*>(addr_out->data);
addr_out->size = sizeof(*a);
a->sin_family = AF_INET;
memcpy(&(a->sin_addr), ip, sizeof(a->sin_addr));
a->sin_port = htons(port);
}
void CreateFromIPv6Address(const uint8_t ip[16],
uint32_t scope_id,
uint16_t port,
struct PP_NetAddress_Private* addr_out) {
memset(addr_out->data, 0,
arraysize(addr_out->data) * sizeof(addr_out->data[0]));
sockaddr_in6* a = reinterpret_cast<sockaddr_in6*>(addr_out->data);
addr_out->size = sizeof(*a);
a->sin6_family = AF_INET6;
memcpy(&(a->sin6_addr), ip, sizeof(a->sin6_addr));
a->sin6_port = htons(port);
a->sin6_scope_id = scope_id;
}
const PPB_NetAddress_Private_0_1 net_address_private_interface_0_1 = {
&AreEqual,
&AreHostsEqual,
&Describe,
&ReplacePort,
&GetAnyAddress
};
const PPB_NetAddress_Private_1_0 net_address_private_interface_1_0 = {
&AreEqual,
&AreHostsEqual,
&Describe,
&ReplacePort,
&GetAnyAddress,
&GetFamily,
&GetPort,
&GetAddress
};
const PPB_NetAddress_Private_1_1 net_address_private_interface_1_1 = {
&AreEqual,
&AreHostsEqual,
&Describe,
&ReplacePort,
&GetAnyAddress,
&GetFamily,
&GetPort,
&GetAddress,
&GetScopeID,
&CreateFromIPv4Address,
&CreateFromIPv6Address
};
} // namespace
namespace thunk {
PPAPI_THUNK_EXPORT const PPB_NetAddress_Private_0_1*
GetPPB_NetAddress_Private_0_1_Thunk() {
return &net_address_private_interface_0_1;
}
PPAPI_THUNK_EXPORT const PPB_NetAddress_Private_1_0*
GetPPB_NetAddress_Private_1_0_Thunk() {
return &net_address_private_interface_1_0;
}
PPAPI_THUNK_EXPORT const PPB_NetAddress_Private_1_1*
GetPPB_NetAddress_Private_1_1_Thunk() {
return &net_address_private_interface_1_1;
}
} // namespace thunk
// static
const PP_NetAddress_Private NetAddressPrivateImpl::kInvalidNetAddress = { 0 };
// static
bool NetAddressPrivateImpl::ValidateNetAddress(
const PP_NetAddress_Private& addr) {
if (addr.size < sizeof(reinterpret_cast<sockaddr*>(0)->sa_family))
return false;
// TODO(viettrungluu): more careful validation?
switch (GetFamilyInternal(&addr)) {
case AF_INET:
// Just do a size check for AF_INET.
if (addr.size >= sizeof(sockaddr_in))
return true;
break;
case AF_INET6:
// Ditto for AF_INET6.
if (addr.size >= sizeof(sockaddr_in6))
return true;
break;
default:
break;
}
// Reject everything else.
return false;
}
// static
bool NetAddressPrivateImpl::SockaddrToNetAddress(
const sockaddr* sa,
uint32_t sa_length,
PP_NetAddress_Private* net_addr) {
if (!sa || sa_length == 0 || !net_addr)
return false;
CHECK_LE(sa_length, sizeof(net_addr->data));
net_addr->size = sa_length;
memcpy(net_addr->data, sa, net_addr->size);
return true;
}
// static
bool NetAddressPrivateImpl::IPEndPointToNetAddress(
const net::IPEndPoint& ip,
PP_NetAddress_Private* net_addr) {
net::SockaddrStorage storage;
return ip.ToSockAddr(storage.addr, &storage.addr_len) &&
SockaddrToNetAddress(storage.addr, storage.addr_len, net_addr);
}
// static
bool NetAddressPrivateImpl::AddressListToNetAddress(
const net::AddressList& address_list,
PP_NetAddress_Private* net_addr) {
return !address_list.empty() && IPEndPointToNetAddress(address_list.front(),
net_addr);
}
// static
bool NetAddressPrivateImpl::NetAddressToIPEndPoint(
const PP_NetAddress_Private& net_addr,
net::IPEndPoint* ip_end_point) {
if (!ip_end_point || !ValidateNetAddress(net_addr))
return false;
if (!ip_end_point->FromSockAddr(
reinterpret_cast<const sockaddr*>(net_addr.data), net_addr.size)) {
return false;
}
return true;
}
// static
bool NetAddressPrivateImpl::NetAddressToAddressList(
const PP_NetAddress_Private& net_addr, net::AddressList* address_list) {
if (!address_list)
return false;
net::IPEndPoint ip_end_point;
if (!NetAddressToIPEndPoint(net_addr, &ip_end_point))
return false;
*address_list = net::AddressList(ip_end_point);
return true;
}
} // namespace ppapi