| // Copyright 2014 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 <arpa/inet.h> |
| #include <netinet/in.h> |
| #include <sys/types.h> |
| #include <sys/socket.h> |
| |
| #include "fake_ppapi/fake_pepper_interface.h" |
| #include "gtest/gtest.h" |
| #include "nacl_io/kernel_intercept.h" |
| |
| using namespace nacl_io; |
| using namespace sdk_util; |
| |
| namespace { |
| |
| class HostResolverTest : public ::testing::Test { |
| public: |
| HostResolverTest() {} |
| |
| void SetUp() { |
| ASSERT_EQ(0, ki_push_state_for_testing()); |
| ASSERT_EQ(0, ki_init(NULL)); |
| } |
| |
| void TearDown() { |
| ki_uninit(); |
| } |
| }; |
| |
| #define FAKE_HOSTNAME "example.com" |
| #define FAKE_IP 0x01020304 |
| |
| class FakeHostResolverTest : public ::testing::Test { |
| public: |
| FakeHostResolverTest() : fake_resolver_(NULL) {} |
| |
| void SetUp() { |
| fake_resolver_ = static_cast<FakeHostResolverInterface*>( |
| pepper_.GetHostResolverInterface()); |
| |
| // Seed the fake resolver with some data |
| fake_resolver_->fake_hostname = FAKE_HOSTNAME; |
| AddFakeAddress(AF_INET); |
| |
| ASSERT_EQ(0, ki_push_state_for_testing()); |
| ASSERT_EQ(0, ki_init_interface(NULL, &pepper_)); |
| } |
| |
| void AddFakeAddress(int family) { |
| if (family == AF_INET) { |
| int address_count = fake_resolver_->fake_addresses_v4.size(); |
| // Each new address we add is FAKE_IP incremented by 1 |
| // each time to be unique. |
| sockaddr_in fake_addr; |
| fake_addr.sin_family = family; |
| fake_addr.sin_addr.s_addr = htonl(FAKE_IP + address_count); |
| fake_resolver_->fake_addresses_v4.push_back(fake_addr); |
| } else if (family == AF_INET6) { |
| sockaddr_in6 fake_addr; |
| fake_addr.sin6_family = family; |
| int address_count = fake_resolver_->fake_addresses_v6.size(); |
| for (uint8_t i = 0; i < 16; i++) { |
| fake_addr.sin6_addr.s6_addr[i] = i + address_count; |
| } |
| fake_resolver_->fake_addresses_v6.push_back(fake_addr); |
| } |
| } |
| |
| void TearDown() { |
| ki_uninit(); |
| } |
| |
| protected: |
| FakePepperInterface pepper_; |
| FakeHostResolverInterface* fake_resolver_; |
| }; |
| |
| } // namespace |
| |
| #define NULL_INFO ((struct addrinfo*)NULL) |
| #define NULL_ADDR ((struct sockaddr*)NULL) |
| #define NULL_HOST (static_cast<hostent*>(NULL)) |
| |
| TEST_F(HostResolverTest, Getaddrinfo_Numeric) { |
| struct addrinfo* ai = NULL; |
| struct sockaddr_in* in; |
| struct addrinfo hints; |
| |
| // Numeric only |
| memset(&hints, 0, sizeof(hints)); |
| hints.ai_family = AF_INET; |
| hints.ai_socktype = SOCK_STREAM; |
| |
| uint32_t expected_addr = htonl(0x01020304); |
| ASSERT_EQ(0, ki_getaddrinfo("1.2.3.4", NULL, &hints, &ai)); |
| ASSERT_NE(NULL_INFO, ai); |
| ASSERT_NE(NULL_ADDR, ai->ai_addr); |
| ASSERT_EQ(AF_INET, ai->ai_family); |
| ASSERT_EQ(SOCK_STREAM, ai->ai_socktype); |
| in = (struct sockaddr_in*)ai->ai_addr; |
| ASSERT_EQ(expected_addr, in->sin_addr.s_addr); |
| ASSERT_EQ(NULL_INFO, ai->ai_next); |
| |
| ki_freeaddrinfo(ai); |
| } |
| |
| TEST_F(HostResolverTest, Getaddrinfo_NumericService) { |
| struct addrinfo* ai = NULL; |
| struct sockaddr_in* in; |
| struct addrinfo hints; |
| |
| memset(&hints, 0, sizeof(hints)); |
| hints.ai_family = AF_INET; |
| hints.ai_socktype = SOCK_STREAM; |
| |
| ASSERT_EQ(0, ki_getaddrinfo("1.2.3.4", "0", &hints, &ai)); |
| ASSERT_NE(NULL_INFO, ai); |
| ASSERT_NE(NULL_ADDR, ai->ai_addr); |
| in = (struct sockaddr_in*)ai->ai_addr; |
| uint16_t expected_port = htons(0); |
| ASSERT_EQ(expected_port, in->sin_port); |
| ASSERT_EQ(NULL_INFO, ai->ai_next); |
| ki_freeaddrinfo(ai); |
| |
| ASSERT_EQ(0, ki_getaddrinfo("1.2.3.4", "65000", &hints, &ai)); |
| ASSERT_NE(NULL_INFO, ai); |
| ASSERT_NE(NULL_ADDR, ai->ai_addr); |
| in = (struct sockaddr_in*)ai->ai_addr; |
| expected_port = htons(65000); |
| ASSERT_EQ(expected_port, in->sin_port); |
| ASSERT_EQ(NULL_INFO, ai->ai_next); |
| ki_freeaddrinfo(ai); |
| } |
| |
| TEST_F(HostResolverTest, Getnameinfo_Numeric) { |
| char host[64]; |
| char serv[64]; |
| |
| // IPv4 host + service to strings. |
| struct sockaddr_in in; |
| |
| memset(&in, 0, sizeof(in)); |
| memset(host, 0, sizeof(host)); |
| memset(serv, 0, sizeof(serv)); |
| in.sin_family = AF_INET; |
| in.sin_port = ntohs(443); |
| in.sin_addr.s_addr = ntohl(0x01020304); |
| |
| ASSERT_EQ(0, ki_getnameinfo(reinterpret_cast<struct sockaddr*>(&in), |
| sizeof(in), host, sizeof(host), serv, |
| sizeof(serv), NI_NUMERICSERV)); |
| ASSERT_STREQ(host, "1.2.3.4"); |
| ASSERT_STREQ(serv, "443"); |
| |
| // IPv4 host only. |
| memset(host, 0, sizeof(host)); |
| ASSERT_EQ(0, |
| ki_getnameinfo(reinterpret_cast<struct sockaddr*>(&in), sizeof(in), |
| host, sizeof(host), NULL, 0, NI_NUMERICSERV)); |
| ASSERT_STREQ(host, "1.2.3.4"); |
| |
| // IPv6 host + service. |
| struct sockaddr_in6 in6; |
| |
| memset(&in6, 0, sizeof(in6)); |
| memset(host, 0, sizeof(host)); |
| memset(serv, 0, sizeof(serv)); |
| in6.sin6_family = AF_INET6; |
| in6.sin6_port = ntohs(80); |
| in6.sin6_addr.s6_addr[0] = 0xfe; |
| in6.sin6_addr.s6_addr[1] = 0x80; |
| in6.sin6_addr.s6_addr[12] = 0x05; |
| in6.sin6_addr.s6_addr[13] = 0x06; |
| in6.sin6_addr.s6_addr[14] = 0x07; |
| in6.sin6_addr.s6_addr[15] = 0x08; |
| |
| ASSERT_EQ(0, ki_getnameinfo(reinterpret_cast<struct sockaddr*>(&in6), |
| sizeof(in6), host, sizeof(host), serv, |
| sizeof(serv), NI_NUMERICSERV)); |
| ASSERT_STREQ(host, "fe80::506:708"); |
| ASSERT_STREQ(serv, "80"); |
| |
| // IPv6 service only. |
| memset(serv, 0, sizeof(serv)); |
| ASSERT_EQ( |
| 0, ki_getnameinfo(reinterpret_cast<struct sockaddr*>(&in6), sizeof(in6), |
| NULL, 0, serv, sizeof(serv), NI_NUMERICSERV)); |
| ASSERT_STREQ(serv, "80"); |
| } |
| |
| TEST_F(HostResolverTest, Getnameinfo_ErrorHandling) { |
| struct sockaddr_in in; |
| char host[64]; |
| char serv[64]; |
| |
| memset(&in, 0, sizeof(in)); |
| memset(host, 0, sizeof(host)); |
| memset(serv, 0, sizeof(serv)); |
| in.sin_family = AF_INET; |
| in.sin_port = ntohs(443); |
| in.sin_addr.s_addr = ntohl(0x01020304); |
| |
| // Bogus salen, hostlen, or servlen. |
| ASSERT_EQ(EAI_FAMILY, ki_getnameinfo(reinterpret_cast<struct sockaddr*>(&in), |
| sizeof(in) - 4, host, sizeof(host), serv, |
| sizeof(serv), NI_NUMERICSERV)); |
| ASSERT_EQ(EAI_OVERFLOW, |
| ki_getnameinfo(reinterpret_cast<struct sockaddr*>(&in), sizeof(in), |
| host, 7, serv, sizeof(serv), NI_NUMERICSERV)); |
| ASSERT_EQ(EAI_OVERFLOW, |
| ki_getnameinfo(reinterpret_cast<struct sockaddr*>(&in), sizeof(in), |
| host, sizeof(host), serv, 3, NI_NUMERICSERV)); |
| |
| // User insists on names, but we can only provide numbers. |
| ASSERT_EQ(EAI_NONAME, |
| ki_getnameinfo(reinterpret_cast<struct sockaddr*>(&in), sizeof(in), |
| host, sizeof(host), serv, 3, NI_NAMEREQD)); |
| |
| // User forgot to pass a host or serv buffer. |
| ASSERT_EQ(EAI_NONAME, |
| ki_getnameinfo(reinterpret_cast<struct sockaddr*>(&in), sizeof(in), |
| NULL, 0, NULL, 0, NI_NUMERICSERV)); |
| |
| // Wrong socket type. |
| struct sockaddr unix_sock; |
| memset(&unix_sock, 0, sizeof(unix_sock)); |
| memset(host, 0, sizeof(host)); |
| memset(serv, 0, sizeof(serv)); |
| unix_sock.sa_family = AF_UNIX; |
| ASSERT_EQ(EAI_FAMILY, |
| ki_getnameinfo(reinterpret_cast<struct sockaddr*>(&unix_sock), |
| sizeof(unix_sock), host, sizeof(host), serv, |
| sizeof(serv), NI_NUMERICSERV)); |
| ASSERT_STREQ(host, ""); |
| ASSERT_STREQ(serv, ""); |
| } |
| |
| TEST_F(HostResolverTest, Getaddrinfo_MissingPPAPI) { |
| // Verify that full lookups fail due to lack of PPAPI interfaces |
| struct addrinfo* ai = NULL; |
| ASSERT_EQ(EAI_SYSTEM, ki_getaddrinfo("google.com", NULL, NULL, &ai)); |
| } |
| |
| TEST_F(HostResolverTest, Getaddrinfo_Passive) { |
| struct addrinfo* ai = NULL; |
| struct sockaddr_in* in; |
| struct sockaddr_in6* in6; |
| struct addrinfo hints; |
| memset(&hints, 0, sizeof(hints)); |
| |
| uint32_t expected_port = htons(22); |
| in_addr_t expected_addr = htonl(INADDR_ANY); |
| in6_addr expected_addr6 = IN6ADDR_ANY_INIT; |
| |
| // AI_PASSIVE means that the returned address will be a wildcard |
| // address suitable for binding and listening. This should not |
| // hit PPAPI at all, so we don't need fakes. |
| hints.ai_family = AF_INET; |
| hints.ai_flags = AI_PASSIVE; |
| hints.ai_socktype = SOCK_DGRAM; |
| ASSERT_EQ(0, ki_getaddrinfo(NULL, "22", &hints, &ai)); |
| ASSERT_NE(NULL_INFO, ai); |
| ASSERT_NE(NULL_ADDR, ai->ai_addr); |
| ASSERT_EQ(NULL_INFO, ai->ai_next); |
| in = (struct sockaddr_in*)ai->ai_addr; |
| ASSERT_EQ(expected_addr, in->sin_addr.s_addr); |
| ASSERT_EQ(expected_port, in->sin_port); |
| ASSERT_EQ(AF_INET, in->sin_family); |
| ki_freeaddrinfo(ai); |
| |
| // Same test with AF_INET6 |
| hints.ai_family = AF_INET6; |
| ASSERT_EQ(0, ki_getaddrinfo(NULL, "22", &hints, &ai)); |
| ASSERT_NE(NULL_INFO, ai); |
| ASSERT_NE(NULL_ADDR, ai->ai_addr); |
| ASSERT_EQ(NULL_INFO, ai->ai_next); |
| in6 = (struct sockaddr_in6*)ai->ai_addr; |
| ASSERT_EQ(expected_port, in6->sin6_port); |
| ASSERT_EQ(AF_INET6, in6->sin6_family); |
| ASSERT_EQ(0, memcmp(in6->sin6_addr.s6_addr, |
| &expected_addr6, |
| sizeof(expected_addr6))); |
| ki_freeaddrinfo(ai); |
| } |
| |
| TEST_F(HostResolverTest, Getaddrinfo_Passive_Any) { |
| // Similar to Getaddrinfo_Passive but don't set |
| // ai_family in the hints, so we should get muplitple |
| // results back for the different families. |
| struct addrinfo* ai = NULL; |
| struct addrinfo* ai_orig = NULL; |
| struct sockaddr_in* in; |
| struct sockaddr_in6* in6; |
| struct addrinfo hints; |
| memset(&hints, 0, sizeof(hints)); |
| |
| uint32_t expected_port = htons(22); |
| in_addr_t expected_addr = htonl(INADDR_ANY); |
| in6_addr expected_addr6 = IN6ADDR_ANY_INIT; |
| |
| hints.ai_flags = AI_PASSIVE; |
| hints.ai_socktype = SOCK_DGRAM; |
| ASSERT_EQ(0, ki_getaddrinfo(NULL, "22", &hints, &ai)); |
| ai_orig = ai; |
| ASSERT_NE(NULL_INFO, ai); |
| int count = 0; |
| bool got_v4 = false; |
| bool got_v6 = false; |
| while (ai) { |
| ASSERT_NE(NULL_ADDR, ai->ai_addr); |
| switch (ai->ai_addr->sa_family) { |
| case AF_INET: |
| in = (struct sockaddr_in*)ai->ai_addr; |
| ASSERT_EQ(expected_port, in->sin_port); |
| ASSERT_EQ(AF_INET, in->sin_family); |
| ASSERT_EQ(expected_addr, in->sin_addr.s_addr); |
| got_v4 = true; |
| break; |
| case AF_INET6: |
| in6 = (struct sockaddr_in6*)ai->ai_addr; |
| ASSERT_EQ(expected_port, in6->sin6_port); |
| ASSERT_EQ(AF_INET6, in6->sin6_family); |
| ASSERT_EQ(0, memcmp(in6->sin6_addr.s6_addr, |
| &expected_addr6, |
| sizeof(expected_addr6))); |
| got_v6 = true; |
| break; |
| default: |
| ASSERT_TRUE(false) << "Unknown address type: " << ai->ai_addr; |
| break; |
| } |
| ai = ai->ai_next; |
| count++; |
| } |
| |
| ASSERT_EQ(2, count); |
| ASSERT_TRUE(got_v4); |
| ASSERT_TRUE(got_v6); |
| |
| ki_freeaddrinfo(ai_orig); |
| } |
| |
| TEST_F(FakeHostResolverTest, Getaddrinfo_Lookup) { |
| struct addrinfo* ai = NULL; |
| struct sockaddr_in* in; |
| struct addrinfo hints; |
| memset(&hints, 0, sizeof(hints)); |
| |
| in_addr_t expected_addr = htonl(FAKE_IP); |
| |
| // Lookup the fake hostname using getaddrinfo |
| hints.ai_family = AF_INET; |
| hints.ai_socktype = SOCK_STREAM; |
| ASSERT_EQ(0, ki_getaddrinfo(FAKE_HOSTNAME, NULL, &hints, &ai)); |
| ASSERT_NE(NULL_INFO, ai); |
| ASSERT_NE(NULL_ADDR, ai->ai_addr); |
| ASSERT_EQ(AF_INET, ai->ai_family); |
| ASSERT_EQ(SOCK_STREAM, ai->ai_socktype); |
| in = (struct sockaddr_in*)ai->ai_addr; |
| ASSERT_EQ(expected_addr, in->sin_addr.s_addr); |
| ASSERT_EQ(NULL_INFO, ai->ai_next); |
| |
| ki_freeaddrinfo(ai); |
| } |
| |
| TEST_F(FakeHostResolverTest, Getaddrinfo_Multi) { |
| struct addrinfo* ai = NULL; |
| struct addrinfo hints; |
| memset(&hints, 0, sizeof(hints)); |
| |
| // Add four fake address on top of the initial one |
| // that the fixture creates. |
| AddFakeAddress(AF_INET); |
| AddFakeAddress(AF_INET); |
| AddFakeAddress(AF_INET6); |
| AddFakeAddress(AF_INET6); |
| |
| hints.ai_socktype = SOCK_STREAM; |
| |
| // First we test with AF_INET |
| hints.ai_family = AF_INET; |
| ASSERT_EQ(0, ki_getaddrinfo(FAKE_HOSTNAME, NULL, &hints, &ai)); |
| ASSERT_NE(NULL_INFO, ai); |
| |
| // We expect to be returned 3 AF_INET address with |
| // address FAKE_IP, FAKE_IP+1 and FAKE_IP+2, since that |
| // is that the fake was seeded with. |
| uint32_t expected_addr = htonl(FAKE_IP); |
| int count = 0; |
| struct addrinfo* current = ai; |
| while (current != NULL) { |
| ASSERT_NE(NULL_ADDR, current->ai_addr); |
| ASSERT_EQ(AF_INET, current->ai_family); |
| ASSERT_EQ(SOCK_STREAM, current->ai_socktype); |
| sockaddr_in* in = (sockaddr_in*)current->ai_addr; |
| ASSERT_EQ(expected_addr, in->sin_addr.s_addr); |
| expected_addr += htonl(1); |
| current = current->ai_next; |
| count++; |
| } |
| ASSERT_EQ(3, count); |
| ki_freeaddrinfo(ai); |
| |
| // Same test but with AF_INET6 |
| hints.ai_family = AF_INET6; |
| ASSERT_EQ(0, ki_getaddrinfo(FAKE_HOSTNAME, NULL, &hints, &ai)); |
| ASSERT_NE(NULL_INFO, ai); |
| |
| count = 0; |
| current = ai; |
| while (current != NULL) { |
| ASSERT_NE(NULL_ADDR, current->ai_addr); |
| ASSERT_EQ(AF_INET6, current->ai_family); |
| ASSERT_EQ(SOCK_STREAM, current->ai_socktype); |
| sockaddr_in6* in = (sockaddr_in6*)current->ai_addr; |
| for (int i = 0; i < 16; i++) { |
| ASSERT_EQ(i + count, in->sin6_addr.s6_addr[i]); |
| } |
| current = current->ai_next; |
| count++; |
| } |
| ASSERT_EQ(2, count); |
| ki_freeaddrinfo(ai); |
| |
| // Same test but with AF_UNSPEC. Here we expect to get |
| // 5 address back: 3 * v4 and 2 * v6. |
| hints.ai_family = AF_UNSPEC; |
| ASSERT_EQ(0, ki_getaddrinfo(FAKE_HOSTNAME, NULL, &hints, &ai)); |
| ASSERT_NE(NULL_INFO, ai); |
| |
| count = 0; |
| current = ai; |
| while (current != NULL) { |
| ASSERT_NE(NULL_ADDR, ai->ai_addr); |
| ASSERT_EQ(SOCK_STREAM, ai->ai_socktype); |
| current = current->ai_next; |
| count++; |
| } |
| ASSERT_EQ(5, count); |
| |
| ki_freeaddrinfo(ai); |
| } |
| |
| TEST_F(FakeHostResolverTest, Gethostbyname) { |
| hostent* host = ki_gethostbyname(FAKE_HOSTNAME); |
| |
| // Verify the returned hostent structure |
| ASSERT_NE(NULL_HOST, host); |
| ASSERT_EQ(AF_INET, host->h_addrtype); |
| ASSERT_EQ(sizeof(in_addr_t), host->h_length); |
| ASSERT_STREQ(FAKE_HOSTNAME, host->h_name); |
| |
| in_addr_t** addr_list = reinterpret_cast<in_addr_t**>(host->h_addr_list); |
| ASSERT_NE(reinterpret_cast<in_addr_t**>(NULL), addr_list); |
| ASSERT_EQ(NULL, addr_list[1]); |
| in_addr_t expected_addr = htonl(FAKE_IP); |
| ASSERT_EQ(expected_addr, *addr_list[0]); |
| // Check that h_addr also matches as in some libc's it may be a separate |
| // member. |
| in_addr_t* first_addr = reinterpret_cast<in_addr_t*>(host->h_addr); |
| ASSERT_EQ(expected_addr, *first_addr); |
| } |
| |
| TEST_F(FakeHostResolverTest, Gethostbyname_Failure) { |
| hostent* host = ki_gethostbyname("nosuchhost.com"); |
| ASSERT_EQ(NULL_HOST, host); |
| ASSERT_EQ(HOST_NOT_FOUND, h_errno); |
| } |
| |
| // Looking up purely numeric hostnames should work without PPAPI |
| // so we don't need the fakes for this test |
| TEST_F(HostResolverTest, Gethostbyname_Numeric) { |
| struct hostent* host = ki_gethostbyname("8.8.8.8"); |
| |
| // Verify the returned hostent structure |
| ASSERT_NE(NULL_HOST, host); |
| ASSERT_EQ(AF_INET, host->h_addrtype); |
| ASSERT_EQ(sizeof(in_addr_t), host->h_length); |
| ASSERT_STREQ("8.8.8.8", host->h_name); |
| |
| in_addr_t** addr_list = reinterpret_cast<in_addr_t**>(host->h_addr_list); |
| ASSERT_NE(reinterpret_cast<in_addr_t**>(NULL), addr_list); |
| ASSERT_EQ(NULL, addr_list[1]); |
| ASSERT_EQ(inet_addr("8.8.8.8"), *addr_list[0]); |
| // Check that h_addr also matches as in some libc's it may be a separate |
| // member. |
| in_addr_t* first_addr = reinterpret_cast<in_addr_t*>(host->h_addr); |
| ASSERT_EQ(inet_addr("8.8.8.8"), *first_addr); |
| } |
| |
| // These utility functions are only used for newlib (glibc provides its own |
| // implementations of these functions). |
| #if !defined(__GLIBC__) |
| |
| TEST(SocketUtilityFunctions, Hstrerror) { |
| EXPECT_STREQ("Unknown error in gethostbyname: 2718.", hstrerror(2718)); |
| } |
| |
| TEST(SocketUtilityFunctions, Gai_Strerror) { |
| EXPECT_STREQ("Unknown error in getaddrinfo: 2719.", gai_strerror(2719)); |
| } |
| |
| #endif // !defined(__GLIBC__) |