// 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" #if defined(OS_WIN) #include #include #include #elif defined(OS_POSIX) && !defined(OS_NACL) #include #include #include #endif #include #include #include "base/basictypes.h" #include "base/logging.h" #include "base/strings/stringprintf.h" #include "build/build_config.h" #include "ppapi/c/pp_var.h" #include "ppapi/c/private/ppb_net_address_private.h" #include "ppapi/shared_impl/proxy_lock.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 // defined(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 { // Define our own net-host-net conversion, rather than reuse the one in // base/sys_byteorder.h, to simplify the NaCl port. NaCl has no byte swap // primitives. uint16 ConvertFromNetEndian16(uint16 x) { #if defined(ARCH_CPU_LITTLE_ENDIAN) return (x << 8) | (x >> 8); #else return x; #endif } uint16 ConvertToNetEndian16(uint16 x) { #if defined(ARCH_CPU_LITTLE_ENDIAN) return (x << 8) | (x >> 8); #else return x; #endif } static const size_t kIPv4AddressSize = 4; static const size_t kIPv6AddressSize = 16; // This structure is a platform-independent representation of a network address. // It is a private format that we embed in PP_NetAddress_Private and is NOT part // of the stable Pepper API. struct NetAddress { bool is_valid; bool is_ipv6; // if true, IPv6, otherwise IPv4. uint16_t port; // host order, not network order. int32_t flow_info; // 0 for IPv4 int32_t scope_id; // 0 for IPv4 // IPv4 addresses are 4 bytes. IPv6 are 16 bytes. Addresses are stored in net // order (big-endian), which only affects IPv6 addresses, which consist of 8 // 16-bit components. These will be byte-swapped on small-endian hosts. uint8_t address[kIPv6AddressSize]; }; // Make sure that sizeof(NetAddress) is the same for all compilers. This ensures // that the alignment is the same on both sides of the NaCl proxy, which is // important because we serialize and deserialize PP_NetAddress_Private by // simply copying the raw bytes. COMPILE_ASSERT(sizeof(NetAddress) == 28, NetAddress_different_for_compiler); // 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(NetAddress), PP_NetAddress_Private_data_too_small); size_t GetAddressSize(const NetAddress* net_addr) { return net_addr->is_ipv6 ? kIPv6AddressSize : kIPv4AddressSize; } // Convert to embedded struct if it has been initialized. NetAddress* ToNetAddress(PP_NetAddress_Private* addr) { if (!addr || addr->size != sizeof(NetAddress)) return NULL; return reinterpret_cast(addr->data); } const NetAddress* ToNetAddress(const PP_NetAddress_Private* addr) { return ToNetAddress(const_cast(addr)); } // Initializes the NetAddress struct embedded in a PP_NetAddress_Private struct. // Zeroes the memory, so net_addr->is_valid == false. NetAddress* InitNetAddress(PP_NetAddress_Private* addr) { addr->size = sizeof(NetAddress); NetAddress* net_addr = ToNetAddress(addr); DCHECK(net_addr); memset(net_addr, 0, sizeof(NetAddress)); return net_addr; } bool IsValid(const NetAddress* net_addr) { return net_addr && net_addr->is_valid; } PP_NetAddressFamily_Private GetFamily(const PP_NetAddress_Private* addr) { const NetAddress* net_addr = ToNetAddress(addr); if (!IsValid(net_addr)) return PP_NETADDRESSFAMILY_PRIVATE_UNSPECIFIED; return net_addr->is_ipv6 ? PP_NETADDRESSFAMILY_PRIVATE_IPV6 : PP_NETADDRESSFAMILY_PRIVATE_IPV4; } uint16_t GetPort(const PP_NetAddress_Private* addr) { const NetAddress* net_addr = ToNetAddress(addr); if (!IsValid(net_addr)) return 0; return net_addr->port; } PP_Bool GetAddress(const PP_NetAddress_Private* addr, void* address, uint16_t address_size) { const NetAddress* net_addr = ToNetAddress(addr); if (!IsValid(net_addr)) return PP_FALSE; size_t net_addr_size = GetAddressSize(net_addr); // address_size must be big enough. if (net_addr_size > address_size) return PP_FALSE; memcpy(address, net_addr->address, net_addr_size); return PP_TRUE; } uint32_t GetScopeID(const PP_NetAddress_Private* addr) { const NetAddress* net_addr = ToNetAddress(addr); if (!IsValid(net_addr)) return 0; return net_addr->scope_id; } PP_Bool AreHostsEqual(const PP_NetAddress_Private* addr1, const PP_NetAddress_Private* addr2) { const NetAddress* net_addr1 = ToNetAddress(addr1); const NetAddress* net_addr2 = ToNetAddress(addr2); if (!IsValid(net_addr1) || !IsValid(net_addr2)) return PP_FALSE; if ((net_addr1->is_ipv6 != net_addr2->is_ipv6) || (net_addr1->flow_info != net_addr2->flow_info) || (net_addr1->scope_id != net_addr2->scope_id)) return PP_FALSE; size_t net_addr_size = GetAddressSize(net_addr1); for (size_t i = 0; i < net_addr_size; i++) { if (net_addr1->address[i] != net_addr2->address[i]) return PP_FALSE; } return PP_TRUE; } 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; // AreHostsEqual has validated these net addresses. const NetAddress* net_addr1 = ToNetAddress(addr1); const NetAddress* net_addr2 = ToNetAddress(addr2); return PP_FromBool(net_addr1->port == net_addr2->port); } std::string ConvertIPv4AddressToString(const NetAddress* net_addr, bool include_port) { std::string description = base::StringPrintf( "%u.%u.%u.%u", net_addr->address[0], net_addr->address[1], net_addr->address[2], net_addr->address[3]); if (include_port) base::StringAppendF(&description, ":%u", net_addr->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 NetAddress* net_addr, bool include_port) { std::string description(include_port ? "[" : ""); const uint16_t* address16 = reinterpret_cast(net_addr->address); // IPv4 address embedded in IPv6. if (address16[0] == 0 && address16[1] == 0 && address16[2] == 0 && address16[3] == 0 && address16[4] == 0 && (address16[5] == 0 || address16[5] == 0xffff)) { base::StringAppendF( &description, address16[5] == 0 ? "::%u.%u.%u.%u" : "::ffff:%u.%u.%u.%u", net_addr->address[12], net_addr->address[13], net_addr->address[14], net_addr->address[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 (address16[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 { uint16_t v = ConvertFromNetEndian16(address16[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 (net_addr->scope_id != 0) base::StringAppendF(&description, "%%%u", net_addr->scope_id); if (include_port) base::StringAppendF(&description, "]:%u", net_addr->port); return description; } PP_Var Describe(PP_Module /*module*/, const struct PP_NetAddress_Private* addr, PP_Bool include_port) { std::string str = NetAddressPrivateImpl::DescribeNetAddress( *addr, PP_ToBool(include_port)); if (str.empty()) return PP_MakeUndefined(); // We must acquire the lock while accessing the VarTracker, which is part of // the critical section of the proxy which may be accessed by other threads. ProxyAutoLock lock; return StringVar::StringToPPVar(str); } PP_Bool ReplacePort(const struct PP_NetAddress_Private* src_addr, uint16_t port, struct PP_NetAddress_Private* dest_addr) { const NetAddress* src_net_addr = ToNetAddress(src_addr); if (!IsValid(src_net_addr) || !dest_addr) return PP_FALSE; dest_addr->size = sizeof(NetAddress); // make sure 'size' is valid. NetAddress* dest_net_addr = ToNetAddress(dest_addr); *dest_net_addr = *src_net_addr; dest_net_addr->port = port; return PP_TRUE; } void GetAnyAddress(PP_Bool is_ipv6, PP_NetAddress_Private* addr) { if (addr) { NetAddress* net_addr = InitNetAddress(addr); net_addr->is_valid = true; net_addr->is_ipv6 = (is_ipv6 == PP_TRUE); } } void CreateFromIPv4Address(const uint8_t ip[4], uint16_t port, struct PP_NetAddress_Private* addr) { if (addr) { NetAddress* net_addr = InitNetAddress(addr); net_addr->is_valid = true; net_addr->is_ipv6 = false; net_addr->port = port; memcpy(net_addr->address, ip, kIPv4AddressSize); } } void CreateFromIPv6Address(const uint8_t ip[16], uint32_t scope_id, uint16_t port, struct PP_NetAddress_Private* addr) { if (addr) { NetAddress* net_addr = InitNetAddress(addr); net_addr->is_valid = true; net_addr->is_ipv6 = true; net_addr->port = port; net_addr->scope_id = scope_id; memcpy(net_addr->address, ip, kIPv6AddressSize); } } 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 // For the NaCl target, all we need are the API functions and the thunk. #if !defined(OS_NACL) // static bool NetAddressPrivateImpl::ValidateNetAddress( const PP_NetAddress_Private& addr) { return IsValid(ToNetAddress(&addr)); } // static bool NetAddressPrivateImpl::SockaddrToNetAddress( const sockaddr* sa, uint32_t sa_length, PP_NetAddress_Private* addr) { if (!sa || sa_length == 0 || !addr) return false; // Our platform neutral format stores ports in host order, not net order, // so convert them here. NetAddress* net_addr = InitNetAddress(addr); switch (sa->sa_family) { case AF_INET: { const struct sockaddr_in* addr4 = reinterpret_cast(sa); net_addr->is_valid = true; net_addr->is_ipv6 = false; net_addr->port = ConvertFromNetEndian16(addr4->sin_port); memcpy(net_addr->address, &addr4->sin_addr.s_addr, kIPv4AddressSize); break; } case AF_INET6: { const struct sockaddr_in6* addr6 = reinterpret_cast(sa); net_addr->is_valid = true; net_addr->is_ipv6 = true; net_addr->port = ConvertFromNetEndian16(addr6->sin6_port); net_addr->flow_info = addr6->sin6_flowinfo; net_addr->scope_id = addr6->sin6_scope_id; memcpy(net_addr->address, addr6->sin6_addr.s6_addr, kIPv6AddressSize); break; } default: // InitNetAddress sets net_addr->is_valid to false. return false; } return true;} // static bool NetAddressPrivateImpl::IPEndPointToNetAddress( const std::vector& address, int port, PP_NetAddress_Private* addr) { if (!addr) return false; NetAddress* net_addr = InitNetAddress(addr); switch (address.size()) { case kIPv4AddressSize: { net_addr->is_valid = true; net_addr->is_ipv6 = false; net_addr->port = static_cast(port); std::copy(address.begin(), address.end(), net_addr->address); break; } case kIPv6AddressSize: { net_addr->is_valid = true; net_addr->is_ipv6 = true; net_addr->port = static_cast(port); std::copy(address.begin(), address.end(), net_addr->address); break; } default: // InitNetAddress sets net_addr->is_valid to false. return false; } return true; } // static bool NetAddressPrivateImpl::NetAddressToIPEndPoint( const PP_NetAddress_Private& addr, std::vector* address, int* port) { if (!address || !port) return false; const NetAddress* net_addr = ToNetAddress(&addr); if (!IsValid(net_addr)) return false; *port = net_addr->port; size_t address_size = GetAddressSize(net_addr); address->assign(&net_addr->address[0], &net_addr->address[address_size]); return true; } #endif // !defined(OS_NACL) // static std::string NetAddressPrivateImpl::DescribeNetAddress( const PP_NetAddress_Private& addr, bool include_port) { const NetAddress* net_addr = ToNetAddress(&addr); if (!IsValid(net_addr)) return std::string(); // 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). if (net_addr->is_ipv6) return ConvertIPv6AddressToString(net_addr, include_port); return ConvertIPv4AddressToString(net_addr, include_port); } // static void NetAddressPrivateImpl::CreateNetAddressPrivateFromIPv4Address( const PP_NetAddress_IPv4& ipv4_addr, PP_NetAddress_Private* addr) { CreateFromIPv4Address(ipv4_addr.addr, ConvertFromNetEndian16(ipv4_addr.port), addr); } // static void NetAddressPrivateImpl::CreateNetAddressPrivateFromIPv6Address( const PP_NetAddress_IPv6& ipv6_addr, PP_NetAddress_Private* addr) { CreateFromIPv6Address(ipv6_addr.addr, 0, ConvertFromNetEndian16(ipv6_addr.port), addr); } // static PP_NetAddress_Family NetAddressPrivateImpl::GetFamilyFromNetAddressPrivate( const PP_NetAddress_Private& addr) { const NetAddress* net_addr = ToNetAddress(&addr); if (!IsValid(net_addr)) return PP_NETADDRESS_FAMILY_UNSPECIFIED; return net_addr->is_ipv6 ? PP_NETADDRESS_FAMILY_IPV6 : PP_NETADDRESS_FAMILY_IPV4; } // static bool NetAddressPrivateImpl::DescribeNetAddressPrivateAsIPv4Address( const PP_NetAddress_Private& addr, PP_NetAddress_IPv4* ipv4_addr) { if (!ipv4_addr) return false; const NetAddress* net_addr = ToNetAddress(&addr); if (!IsValid(net_addr) || net_addr->is_ipv6) return false; ipv4_addr->port = ConvertToNetEndian16(net_addr->port); COMPILE_ASSERT(sizeof(ipv4_addr->addr) == kIPv4AddressSize, mismatched_IPv4_address_size); memcpy(ipv4_addr->addr, net_addr->address, kIPv4AddressSize); return true; } // static bool NetAddressPrivateImpl::DescribeNetAddressPrivateAsIPv6Address( const PP_NetAddress_Private& addr, PP_NetAddress_IPv6* ipv6_addr) { if (!ipv6_addr) return false; const NetAddress* net_addr = ToNetAddress(&addr); if (!IsValid(net_addr) || !net_addr->is_ipv6) return false; ipv6_addr->port = ConvertToNetEndian16(net_addr->port); COMPILE_ASSERT(sizeof(ipv6_addr->addr) == kIPv6AddressSize, mismatched_IPv6_address_size); memcpy(ipv6_addr->addr, net_addr->address, kIPv6AddressSize); return true; } } // namespace ppapi