diff --git a/starboard/nplb/posix_compliance/posix_socket_resolve_test.cc b/starboard/nplb/posix_compliance/posix_socket_resolve_test.cc index 8a0320aed2ce..6b53bf0a2523 100644 --- a/starboard/nplb/posix_compliance/posix_socket_resolve_test.cc +++ b/starboard/nplb/posix_compliance/posix_socket_resolve_test.cc @@ -21,6 +21,7 @@ #include #include "starboard/nplb/posix_compliance/posix_socket_helpers.h" +#include "testing/gmock/include/gmock/gmock.h" namespace starboard { namespace nplb { @@ -225,22 +226,42 @@ TEST_P(PosixSocketResolveTest, Localhost) { int address_count = 0; struct sockaddr_in* ai_addr = nullptr; + struct sockaddr_in6* ai_addr6 = nullptr; for (const struct addrinfo* i = ai; i != nullptr; i = i->ai_next) { ++address_count; if (ai_addr == nullptr && i->ai_addr != nullptr) { ai_addr = reinterpret_cast(i->ai_addr); - break; + ai_addr6 = reinterpret_cast(i->ai_addr); + + EXPECT_TRUE(ai_addr->sin_family == AF_INET || + ai_addr->sin_family == AF_INET6); + if (GetAddressFamily() != AF_UNSPEC) { + EXPECT_EQ(ai_addr->sin_family, GetAddressFamily()); + if (GetAddressFamily() == AF_INET) { + struct in_addr loopback_addr; + loopback_addr.s_addr = htonl(INADDR_LOOPBACK); + EXPECT_EQ(ai_addr->sin_addr.s_addr, loopback_addr.s_addr); + + struct in_addr expected_addr; + expected_addr.s_addr = htonl((127 << 24) | 1); + EXPECT_EQ(ai_addr->sin_addr.s_addr, expected_addr.s_addr); + } + if (GetAddressFamily() == AF_INET6) { + struct in6_addr loopback = IN6ADDR_LOOPBACK_INIT; + EXPECT_EQ(memcmp(&ai_addr6->sin6_addr, &loopback, sizeof(loopback)), + 0); + + const unsigned char kExpectedAddress[16] = {0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1}; + EXPECT_THAT(ai_addr6->sin6_addr.s6_addr, + testing::ElementsAreArray(kExpectedAddress)); + } + } } } EXPECT_LT(0, address_count); EXPECT_NE(nullptr, ai_addr); - EXPECT_TRUE(ai_addr->sin_family == AF_INET || - ai_addr->sin_family == AF_INET6); - if (GetAddressFamily() != AF_UNSPEC) { - EXPECT_EQ(ai_addr->sin_family, GetAddressFamily()); - } - freeaddrinfo(ai); } diff --git a/starboard/shared/modular/starboard_layer_posix_socket_abi_wrappers.cc b/starboard/shared/modular/starboard_layer_posix_socket_abi_wrappers.cc index 119f59c62108..b2c5786fe3a8 100644 --- a/starboard/shared/modular/starboard_layer_posix_socket_abi_wrappers.cc +++ b/starboard/shared/modular/starboard_layer_posix_socket_abi_wrappers.cc @@ -193,6 +193,50 @@ SB_EXPORT int __abi_wrap_getaddrinfo(const char* node, return -1; } + // Recognize "localhost" as special and resolve to the loopback address. + // https://datatracker.ietf.org/doc/html/rfc6761#section-6.3 + // Note: This returns the IPv4 localhost first, and falls back to system + // resolution when a service is supplied. + if (!service && node && strcmp("localhost", node) == 0) { + struct musl_addrinfo* last_ai = nullptr; + *res = nullptr; + const int ai_family[2] = {MUSL_AF_INET, MUSL_AF_INET6}; + for (int family_idx = 0; family_idx < 2; ++family_idx) { + int family = ai_family[family_idx]; + if (!hints || hints->ai_family == AF_UNSPEC || + hints->ai_family == family) { + struct musl_addrinfo* musl_ai = + (struct musl_addrinfo*)calloc(1, sizeof(struct musl_addrinfo)); + musl_ai->ai_addrlen = family == MUSL_AF_INET + ? sizeof(struct sockaddr_in) + : sizeof(struct sockaddr_in6); + musl_ai->ai_addr = reinterpret_cast( + calloc(1, sizeof(struct musl_sockaddr))); + musl_ai->ai_addr->sa_family = family; + + if (family == MUSL_AF_INET) { + struct sockaddr_in* address = + reinterpret_cast(musl_ai->ai_addr); + address->sin_addr.s_addr = htonl(INADDR_LOOPBACK); + // address->sin_addr.s_addr = -1; + } else if (family == MUSL_AF_INET6) { + struct sockaddr_in6* address = + reinterpret_cast(musl_ai->ai_addr); + address->sin6_addr = IN6ADDR_LOOPBACK_INIT; + // address->sin6_addr.__in6_u.__u6_addr8[0] = 5; + } + if (*res == nullptr) { + *res = musl_ai; + last_ai = musl_ai; + } else { + last_ai->ai_next = musl_ai; + last_ai = musl_ai; + } + } + } + return 0; + } + // musl addrinfo definition might differ from platform definition. // So we need to do a manual conversion to avoid header mismatches. struct addrinfo new_hints = {0}; diff --git a/starboard/shared/posix/socket_resolve.cc b/starboard/shared/posix/socket_resolve.cc index 283aee965ca5..d80837cd9d04 100644 --- a/starboard/shared/posix/socket_resolve.cc +++ b/starboard/shared/posix/socket_resolve.cc @@ -16,6 +16,7 @@ #include #include +#include #include #include "starboard/common/log.h" @@ -27,6 +28,48 @@ SbSocketResolution* SbSocketResolve(const char* hostname, int filters) { struct addrinfo* ai = NULL; struct addrinfo hints = {0}; + // Recognize "localhost" as special and resolve to the loopback address. + // https://datatracker.ietf.org/doc/html/rfc6761#section-6.3 + // Note: This returns the IPv4 localhost first. + if (hostname && strcmp("localhost", hostname) == 0) { + SbSocketResolution* result = new SbSocketResolution(); + int address = 0; + result->address_count = 0; + if (filters == kSbSocketResolveFilterNone) { + result->address_count = 2; + } else { + if (filters & kSbSocketResolveFilterIpv4) { + result->address_count++; + } +#if SB_HAS(IPV6) + if (filters & kSbSocketResolveFilterIpv6) { + result->address_count++; + } +#endif + } + result->addresses = + new SbSocketAddress[result->address_count](); // ensure zero + // initialization + + if ((filters == kSbSocketResolveFilterNone) || + (filters & kSbSocketResolveFilterIpv4)) { + result->addresses[address].port = 0; + result->addresses[address].type = kSbSocketAddressTypeIpv4; + result->addresses[address].address[0] = 127; + result->addresses[address].address[3] = 1; + address++; + } +#if SB_HAS(IPV6) + if ((filters == kSbSocketResolveFilterNone) || + (filters & kSbSocketResolveFilterIpv6)) { + result->addresses[address].port = 0; + result->addresses[address].type = kSbSocketAddressTypeIpv6; + result->addresses[address].address[15] = 1; + } +#endif + return result; + } + if (filters & kSbSocketResolveFilterIpv4) { if (filters & kSbSocketResolveFilterIpv6) { hints.ai_family = AF_UNSPEC;