// 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 #include #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(0)->data) >= sizeof(sockaddr_storage), PP_NetAddress_Private_data_too_small); #endif sa_family_t GetFamilyInternal(const PP_NetAddress_Private* addr) { return reinterpret_cast(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(addr->data); return base::NetToHost16(a->sin_port); } case AF_INET6: { const sockaddr_in6* a = reinterpret_cast(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(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(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(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(addr1->data); const sockaddr_in* a2 = reinterpret_cast(addr2->data); return PP_FromBool(a1->sin_addr.s_addr == a2->sin_addr.s_addr); } case AF_INET6: { const sockaddr_in6* a1 = reinterpret_cast(addr1->data); const sockaddr_in6* a2 = reinterpret_cast(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(addr1->data); const sockaddr_in* a2 = reinterpret_cast(addr2->data); return PP_FromBool(a1->sin_port == a2->sin_port); } case AF_INET6: { const sockaddr_in6* a1 = reinterpret_cast(addr1->data); const sockaddr_in6* a2 = reinterpret_cast(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 "%" (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 ":" is appended, i.e., the overall // format is "[
]:". // - 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(a->sin6_addr.s6_addr[12]), static_cast(a->sin6_addr.s6_addr[13]), static_cast(a->sin6_addr.s6_addr[14]), static_cast(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(addr->data); return StringVar::StringToPPVar( ConvertIPv4AddressToString(a, !!include_port)); } case AF_INET6: { const sockaddr_in6* a = reinterpret_cast(addr->data); return StringVar::StringToPPVar( ConvertIPv6AddressToString(a, !!include_port)); } default: NOTREACHED(); break; } return PP_MakeUndefined(); #else const sockaddr* a = reinterpret_cast(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(dest_addr->data)->sin_port = base::HostToNet16(port); return PP_TRUE; } case AF_INET6: { memmove(dest_addr, src_addr, sizeof(*src_addr)); reinterpret_cast(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(addr->data); addr->size = sizeof(*a); a->sin6_family = AF_INET6; a->sin6_addr = in6addr_any; } else { sockaddr_in* a = reinterpret_cast(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(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(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(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(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