Pepper: Implement PPB_NetAddress_Private Describe() for IPv6 addresses on Windows.

See RFC 5952. I might have even implemented it mostly correctly.

BUG=103969,103968
TEST=ui_tests {PPAPITest,OutOfProcessPPAPITest}.NetAddressPrivate (DescribeIPv6)
TBR=dmichael@chromium.org

Review URL: http://codereview.chromium.org/8590006

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@111052 0039d316-1c4b-4281-b951-d872f2087c98
parent b5c97351
...@@ -425,8 +425,18 @@ TEST_F(OutOfProcessPPAPITest, FAILS_UMA) { ...@@ -425,8 +425,18 @@ TEST_F(OutOfProcessPPAPITest, FAILS_UMA) {
RunTest("UMA"); RunTest("UMA");
} }
TEST_PPAPI_IN_PROCESS(NetAddressPrivate) TEST_PPAPI_IN_PROCESS(NetAddressPrivate_AreEqual)
TEST_PPAPI_OUT_OF_PROCESS(NetAddressPrivate) TEST_PPAPI_IN_PROCESS(NetAddressPrivate_AreHostsEqual)
TEST_PPAPI_IN_PROCESS(NetAddressPrivate_Describe)
TEST_PPAPI_IN_PROCESS(NetAddressPrivate_ReplacePort)
TEST_PPAPI_IN_PROCESS(NetAddressPrivate_GetAnyAddress)
TEST_PPAPI_IN_PROCESS(NetAddressPrivate_DescribeIPv6)
TEST_PPAPI_OUT_OF_PROCESS(NetAddressPrivate_AreEqual)
TEST_PPAPI_OUT_OF_PROCESS(NetAddressPrivate_AreHostsEqual)
TEST_PPAPI_OUT_OF_PROCESS(NetAddressPrivate_Describe)
TEST_PPAPI_OUT_OF_PROCESS(NetAddressPrivate_ReplacePort)
TEST_PPAPI_OUT_OF_PROCESS(NetAddressPrivate_GetAnyAddress)
TEST_PPAPI_OUT_OF_PROCESS(NetAddressPrivate_DescribeIPv6)
// PPB_TCPSocket_Private currently isn't supported in-process. // PPB_TCPSocket_Private currently isn't supported in-process.
TEST_F(OutOfProcessPPAPITest, TCPSocketPrivate) { TEST_F(OutOfProcessPPAPITest, TCPSocketPrivate) {
......
...@@ -20,6 +20,18 @@ ...@@ -20,6 +20,18 @@
#include "ppapi/shared_impl/var.h" #include "ppapi/shared_impl/var.h"
#include "ppapi/thunk/thunk.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
#endif
// The net address interface doesn't have a normal C -> C++ thunk since it // 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 // 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 // call into base. So we implement the entire interface here, using the thunk
...@@ -29,11 +41,6 @@ namespace ppapi { ...@@ -29,11 +41,6 @@ namespace ppapi {
namespace { namespace {
#if defined(OS_WIN)
// The type of |sockaddr::sa_family|.
typedef ADDRESS_FAMILY sa_family_t;
#endif
inline sa_family_t GetFamily(const PP_NetAddress_Private& addr) { inline sa_family_t GetFamily(const PP_NetAddress_Private& addr) {
return reinterpret_cast<const sockaddr*>(addr.data)->sa_family; return reinterpret_cast<const sockaddr*>(addr.data)->sa_family;
} }
...@@ -88,32 +95,119 @@ PP_Bool AreEqual(const PP_NetAddress_Private* addr1, ...@@ -88,32 +95,119 @@ PP_Bool AreEqual(const PP_NetAddress_Private* addr1,
return PP_FALSE; return PP_FALSE;
} }
#if defined(OS_WIN) || defined(OS_MACOSX)
std::string ConvertIPv4AddressToString(const sockaddr_in* a,
bool include_port) {
unsigned ip = ntohl(a->sin_addr.s_addr);
unsigned port = ntohs(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 = ntohs(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 (ntohs(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 = ntohs(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_MAC
PP_Var Describe(PP_Module module, PP_Var Describe(PP_Module module,
const struct PP_NetAddress_Private* addr, const struct PP_NetAddress_Private* addr,
PP_Bool include_port) { PP_Bool include_port) {
if (!NetAddressPrivateImpl::ValidateNetAddress(*addr)) if (!NetAddressPrivateImpl::ValidateNetAddress(*addr))
return PP_MakeUndefined(); return PP_MakeUndefined();
#if defined(OS_WIN) #if defined(OS_WIN) || defined(OS_MACOSX)
// On Windows, |NetAddressToString()| doesn't work in the sandbox. // On Windows, |NetAddressToString()| doesn't work in the sandbox. On Mac,
// TODO(viettrungluu): Consider switching to this everywhere once it's fully // the output isn't consistent with RFC 5952, at least on Mac OS 10.6:
// implemented. // |getnameinfo()| collapses length-one runs of zeros (and also doesn't
// display the scope).
// TODO(viettrungluu): Consider switching to this on Linux.
switch (GetFamily(*addr)) { switch (GetFamily(*addr)) {
case AF_INET: { case AF_INET: {
const sockaddr_in* a = reinterpret_cast<const sockaddr_in*>(addr->data); const sockaddr_in* a = reinterpret_cast<const sockaddr_in*>(addr->data);
unsigned ip = ntohl(a->sin_addr.s_addr); return StringVar::StringToPPVar(
unsigned port = ntohs(a->sin_port); module, ConvertIPv4AddressToString(a, !!include_port));
std::string description = base::StringPrintf( }
"%u.%u.%u.%u", case AF_INET6: {
(ip >> 24) & 0xff, (ip >> 16) & 0xff, (ip >> 8) & 0xff, ip & 0xff); const sockaddr_in6* a = reinterpret_cast<const sockaddr_in6*>(addr->data);
if (include_port) return StringVar::StringToPPVar(
description.append(base::StringPrintf(":%u", port)); module, ConvertIPv6AddressToString(a, !!include_port));
return StringVar::StringToPPVar(module, description);
} }
case AF_INET6:
// TODO(viettrungluu): crbug.com/103969
NOTIMPLEMENTED();
break;
default: default:
NOTREACHED(); NOTREACHED();
break; break;
......
...@@ -4,6 +4,8 @@ ...@@ -4,6 +4,8 @@
#include "ppapi/tests/test_net_address_private.h" #include "ppapi/tests/test_net_address_private.h"
#include "base/basictypes.h"
#include "build/build_config.h"
#include "ppapi/cpp/private/net_address_private.h" #include "ppapi/cpp/private/net_address_private.h"
#include "ppapi/c/private/ppb_net_address_private.h" #include "ppapi/c/private/ppb_net_address_private.h"
#include "ppapi/tests/testing_instance.h" #include "ppapi/tests/testing_instance.h"
...@@ -11,29 +13,30 @@ ...@@ -11,29 +13,30 @@
// Other than |GetAnyAddress()|, there's no way to actually get // Other than |GetAnyAddress()|, there's no way to actually get
// |PP_NetAddress_Private| structs from just this interface. We'll cheat and // |PP_NetAddress_Private| structs from just this interface. We'll cheat and
// synthesize some. // synthesize some.
// TODO(viettrungluu): This is very fragile and implementation-dependent. :(
#if defined(_WIN32)
#define OS_WIN
#elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || \
defined(__OpenBSD__) || defined(__sun) || defined(__native_client__)
#define OS_POSIX
#else
#error "Unsupported platform."
#endif
#if defined(OS_WIN) #if defined(OS_POSIX)
#include <ws2tcpip.h>
#elif defined(OS_POSIX)
#include <arpa/inet.h> #include <arpa/inet.h>
#include <netdb.h> #include <netdb.h>
#include <netinet/in.h> #include <netinet/in.h>
#include <sys/socket.h> #include <sys/socket.h>
#endif #endif
#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)
#include <ws2tcpip.h>
#define s6_addr16 u.Word
#endif
using pp::NetAddressPrivate; using pp::NetAddressPrivate;
namespace { namespace {
// |host| should be an IP address represented as text, e.g., "192.168.0.1".
PP_NetAddress_Private MakeIPv4NetAddress(const char* host, int port) { PP_NetAddress_Private MakeIPv4NetAddress(const char* host, int port) {
PP_NetAddress_Private addr = PP_NetAddress_Private(); PP_NetAddress_Private addr = PP_NetAddress_Private();
addr.size = sizeof(sockaddr_in); addr.size = sizeof(sockaddr_in);
...@@ -44,7 +47,20 @@ PP_NetAddress_Private MakeIPv4NetAddress(const char* host, int port) { ...@@ -44,7 +47,20 @@ PP_NetAddress_Private MakeIPv4NetAddress(const char* host, int port) {
return addr; return addr;
} }
// TODO(viettrungluu): Also add IPv6 tests. // |host| should be an array of eight 16-bit numbers.
PP_NetAddress_Private MakeIPv6NetAddress(const uint16_t host[], uint16_t port,
uint32_t scope_id) {
PP_NetAddress_Private addr = PP_NetAddress_Private();
addr.size = sizeof(sockaddr_in6);
sockaddr_in6* a = reinterpret_cast<sockaddr_in6*>(addr.data);
a->sin6_family = AF_INET6;
a->sin6_port = htons(port);
a->sin6_flowinfo = 0;
for (int i = 0; i < 8; i++)
a->sin6_addr.s6_addr16[i] = htons(host[i]);
a->sin6_scope_id = scope_id;
return addr;
}
} // namespace } // namespace
...@@ -64,6 +80,7 @@ void TestNetAddressPrivate::RunTests(const std::string& filter) { ...@@ -64,6 +80,7 @@ void TestNetAddressPrivate::RunTests(const std::string& filter) {
RUN_TEST(Describe, filter); RUN_TEST(Describe, filter);
RUN_TEST(ReplacePort, filter); RUN_TEST(ReplacePort, filter);
RUN_TEST(GetAnyAddress, filter); RUN_TEST(GetAnyAddress, filter);
RUN_TEST(DescribeIPv6, filter);
} }
std::string TestNetAddressPrivate::TestAreEqual() { std::string TestNetAddressPrivate::TestAreEqual() {
...@@ -155,3 +172,76 @@ std::string TestNetAddressPrivate::TestGetAnyAddress() { ...@@ -155,3 +172,76 @@ std::string TestNetAddressPrivate::TestGetAnyAddress() {
PASS(); PASS();
} }
// TODO(viettrungluu): More IPv6 tests needed.
std::string TestNetAddressPrivate::TestDescribeIPv6() {
static const struct {
uint16_t address[8];
uint16_t port;
uint32_t scope;
const char* expected_without_port;
const char* expected_with_port;
} test_cases[] = {
{ // Generic test case (unique longest run of zeros to collapse).
{ 0x12, 0xabcd, 0, 0x0001, 0, 0, 0, 0xcdef }, 12, 0,
"12:abcd:0:1::cdef", "[12:abcd:0:1::cdef]:12"
},
{ // Non-zero scope.
{ 0x1234, 0xabcd, 0, 0x0001, 0, 0, 0, 0xcdef }, 1234, 789,
"1234:abcd:0:1::cdef%789", "[1234:abcd:0:1::cdef%789]:1234"
},
{ // Ignore the first (non-longest) run of zeros.
{ 0, 0, 0, 0x0123, 0, 0, 0, 0 }, 123, 0,
"0:0:0:123::", "[0:0:0:123::]:123"
},
{ // Collapse the first (equally-longest) run of zeros.
{ 0x1234, 0xabcd, 0, 0, 0xff, 0, 0, 0xcdef }, 123, 0,
"1234:abcd::ff:0:0:cdef", "[1234:abcd::ff:0:0:cdef]:123"
},
{ // Don't collapse "runs" of zeros of length 1.
{ 0, 0xa, 1, 2, 3, 0, 5, 0 }, 123, 0,
"0:a:1:2:3:0:5:0", "[0:a:1:2:3:0:5:0]:123"
},
{ // Collapse a run of zeros at the beginning.
{ 0, 0, 0, 2, 3, 0, 0, 0 }, 123, 0,
"::2:3:0:0:0", "[::2:3:0:0:0]:123"
},
{ // Collapse a run of zeros at the end.
{ 0, 0xa, 1, 2, 3, 0, 0, 0 }, 123, 0,
"0:a:1:2:3::", "[0:a:1:2:3::]:123"
},
{ // IPv4 192.168.1.2 embedded in IPv6 in the deprecated way.
{ 0, 0, 0, 0, 0, 0, 0xc0a8, 0x102 }, 123, 0,
"::192.168.1.2", "[::192.168.1.2]:123"
},
{ // ... with non-zero scope.
{ 0, 0, 0, 0, 0, 0, 0xc0a8, 0x102 }, 123, 789,
"::192.168.1.2%789", "[::192.168.1.2%789]:123"
},
{ // IPv4 192.168.1.2 embedded in IPv6.
{ 0, 0, 0, 0, 0, 0xffff, 0xc0a8, 0x102 }, 123, 0,
"::ffff:192.168.1.2", "[::ffff:192.168.1.2]:123"
},
{ // ... with non-zero scope.
{ 0, 0, 0, 0, 0, 0xffff, 0xc0a8, 0x102 }, 123, 789,
"::ffff:192.168.1.2%789", "[::ffff:192.168.1.2%789]:123"
},
{ // *Not* IPv4 embedded in IPv6.
{ 0, 0, 0, 0, 0, 0x1234, 0xc0a8, 0x102 }, 123, 0,
"::1234:c0a8:102", "[::1234:c0a8:102]:123"
}
};
for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_cases); i++) {
PP_NetAddress_Private addr = MakeIPv6NetAddress(test_cases[i].address,
test_cases[i].port,
test_cases[i].scope);
ASSERT_EQ(test_cases[i].expected_without_port,
NetAddressPrivate::Describe(addr, false));
ASSERT_EQ(test_cases[i].expected_with_port,
NetAddressPrivate::Describe(addr, true));
}
PASS();
}
...@@ -23,6 +23,8 @@ class TestNetAddressPrivate : public TestCase { ...@@ -23,6 +23,8 @@ class TestNetAddressPrivate : public TestCase {
std::string TestDescribe(); std::string TestDescribe();
std::string TestReplacePort(); std::string TestReplacePort();
std::string TestGetAnyAddress(); std::string TestGetAnyAddress();
std::string TestDescribeIPv6();
}; };
#endif // PAPPI_TESTS_TEST_NET_ADDRESS_PRIVATE_H_ #endif // PAPPI_TESTS_TEST_NET_ADDRESS_PRIVATE_H_
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment