blob: 4ebcbe707c76c2e888ca1b4a1354040c85cd9f6c [file] [log] [blame]
// 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 "posix_translation/socket_util.h"
#include <arpa/inet.h>
#include <errno.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <algorithm>
#include "common/alog.h"
#include "posix_translation/virtual_file_system.h"
#include "ppapi/cpp/net_address.h"
namespace posix_translation {
namespace internal {
// Because the trailing padding is not actually necessary, the min size of
// the addrlen is slightly less than the size of the sockaddr_{in,in6}.
const socklen_t kIPv4MinAddrLen =
offsetof(sockaddr_in, sin_addr) + sizeof(in_addr);
const socklen_t kIPv6MinAddrLen =
offsetof(sockaddr_in6, sin6_addr) + sizeof(in6_addr);
namespace {
// Converts PP_NetAddress_IPv4 to sockaddr_in.
void NetAddressIPv4ToSockAddrIn(
const PP_NetAddress_IPv4& net_address, sockaddr_in* saddr) {
saddr->sin_family = AF_INET;
// Copy the value as is to keep network byte order.
saddr->sin_port = net_address.port;
memcpy(&saddr->sin_addr.s_addr, net_address.addr, sizeof(net_address.addr));
}
// Convert PP_NetAddress_IPv4 to sockaddr_in6 as v4mapped address.
void NetAddressIPv4ToSockAddrIn6V4Mapped(
const PP_NetAddress_IPv4& net_address, sockaddr_in6* saddr6) {
saddr6->sin6_family = AF_INET6;
// Copy the value as is to keep network byte order.
saddr6->sin6_port = net_address.port;
// V4Mapped address forms: 0::FFFF:xxx.yyy.zzz.www.
memset(saddr6->sin6_addr.s6_addr, 0, 10); // Leading 10 bytes are 0.
saddr6->sin6_addr.s6_addr[10] = 0xFF;
saddr6->sin6_addr.s6_addr[11] = 0xFF;
memcpy(&saddr6->sin6_addr.s6_addr[12], net_address.addr,
sizeof(net_address.addr));
}
// Converts PP_NetAddress_IPv6 to sockaddr_in6.
void NetAddressIPv6ToSockAddrIn6(
const PP_NetAddress_IPv6& net_address, sockaddr_in6* saddr6) {
saddr6->sin6_family = AF_INET6;
// Copy the value as is to keep network byte order.
saddr6->sin6_port = net_address.port;
memcpy(&saddr6->sin6_addr.s6_addr, net_address.addr,
sizeof(net_address.addr));
}
// Converts sockaddr_in to PP_NetAddress_IPv4.
// sockaddr_in may have trailing padding, but it is ensured in this function
// that the padding is not touched in this function.
// In other words, although saddr has type sockaddr_in, the min size of the
// buffer is IPv4MinAddrLen defined above, which can be smaller than
// sizeof(sockaddr_in).
void SockAddrInToNetAddressIPv4(
const sockaddr_in* saddr, PP_NetAddress_IPv4* net_address) {
ALOG_ASSERT(saddr->sin_family == AF_INET);
// Copy the value as is to keep network byte order.
net_address->port = saddr->sin_port;
memcpy(net_address->addr, &saddr->sin_addr.s_addr,
sizeof(net_address->addr));
}
// Converts sockaddr_in6 to PP_NetAddress_IPv6.
// Similar to sockaddr_in, sockaddr_in6 also may have trailing padding, and
// the min size of saddr6 is IPv6MinAddrLen. See also the comment for
// SockAddrInToNetAddressIPv4.
void SockAddrIn6ToNetAddressIPv6(
const sockaddr_in6* saddr6, PP_NetAddress_IPv6* net_address) {
ALOG_ASSERT(saddr6->sin6_family == AF_INET6);
// Copy the value as is to keep network byte order.
net_address->port = saddr6->sin6_port;
memcpy(net_address->addr, &saddr6->sin6_addr.s6_addr,
sizeof(net_address->addr));
}
} // namespace
int VerifyInputSocketAddress(
const sockaddr* addr, socklen_t addrlen, int address_family) {
ALOG_ASSERT(address_family == AF_INET || address_family == AF_INET6);
if (addrlen <= 0) {
ALOGW("addrlen is not positive: %d", addrlen);
return EINVAL;
}
if (!addr) {
ALOGW("Given addr is NULL");
return EFAULT;
}
// If the addr size is too small or too large, raise EINVAL.
const socklen_t kMinAddrLen =
address_family == AF_INET ? kIPv4MinAddrLen : kIPv6MinAddrLen;
if (addrlen < kMinAddrLen || addrlen > SIZEOF_AS_SOCKLEN(sockaddr_storage)) {
ALOGW("The addr has invalid size: %d, %d", address_family, addrlen);
return EINVAL;
}
if (addr->sa_family != address_family) {
ALOGW("The family is differnt from what is expected: %d, %d",
addr->sa_family, address_family);
// Note: for bind(), there seems no spec on man in this case.
// However, as same as connect(), practically bind() raises
// EAFNOSUPPORT in this case.
return EAFNOSUPPORT;
}
return 0;
}
int VerifyOutputSocketAddress(
const sockaddr* addr, const socklen_t* addrlen) {
if (!addrlen) {
return EFAULT;
}
if (*addrlen < 0) {
return EINVAL;
}
// Note that if addrlen is 0, addr can be NULL, because we will not copy
// the data to it.
if (*addrlen != 0 && addr == NULL) {
return EFAULT;
}
return 0;
}
void CopySocketAddress(
const sockaddr_storage& address, sockaddr* name, socklen_t* namelen) {
int family = address.ss_family;
ALOG_ASSERT(family == AF_INET || family == AF_INET6);
const socklen_t address_length =
(family == AF_INET) ? sizeof(sockaddr_in) : sizeof(sockaddr_in6);
if (name) {
memcpy(name, &address, std::min(*namelen, address_length));
}
*namelen = address_length;
}
bool SocketAddressEqual(
const sockaddr_storage& addr1, const sockaddr_storage& addr2) {
if (addr1.ss_family != addr2.ss_family)
return false;
if (addr1.ss_family == AF_INET) {
const sockaddr_in& saddr_in1 = reinterpret_cast<const sockaddr_in&>(addr1);
const sockaddr_in& saddr_in2 = reinterpret_cast<const sockaddr_in&>(addr2);
return (saddr_in1.sin_port == saddr_in2.sin_port) &&
(saddr_in1.sin_addr.s_addr == saddr_in2.sin_addr.s_addr);
}
if (addr1.ss_family == AF_INET6) {
const sockaddr_in6& saddr_in6_1 =
reinterpret_cast<const sockaddr_in6&>(addr1);
const sockaddr_in6& saddr_in6_2 =
reinterpret_cast<const sockaddr_in6&>(addr2);
return (saddr_in6_1.sin6_port == saddr_in6_2.sin6_port) &&
(memcmp(&saddr_in6_1.sin6_addr, &saddr_in6_2.sin6_addr,
sizeof(in6_addr)) == 0);
}
// Unknown family.
ALOGE("SocketAddressEqual Unknown socket family: %d", addr1.ss_family);
return false;
}
bool NetAddressToSockAddrStorage(
const pp::NetAddress& net_address,
int dest_family, bool allow_v4mapped, sockaddr_storage* storage) {
ALOG_ASSERT(dest_family == AF_UNSPEC ||
dest_family == AF_INET ||
dest_family == AF_INET6);
memset(storage, 0, sizeof(sockaddr_storage));
switch (net_address.GetFamily()) {
case PP_NETADDRESS_FAMILY_IPV4: {
// If IPv6 address is required but the v4map is prohibited, there is
// no way to return the address.
if (dest_family == AF_INET6 && !allow_v4mapped)
return false;
PP_NetAddress_IPv4 ipv4 = {};
if (!net_address.DescribeAsIPv4Address(&ipv4)) {
return false;
}
if (dest_family == AF_INET6) {
NetAddressIPv4ToSockAddrIn6V4Mapped(
ipv4, reinterpret_cast<sockaddr_in6*>(storage));
} else {
NetAddressIPv4ToSockAddrIn(
ipv4, reinterpret_cast<sockaddr_in*>(storage));
}
return true;
}
case PP_NETADDRESS_FAMILY_IPV6: {
// IPv6 address cannot return in IPv4 address format.
if (dest_family == AF_INET)
return false;
PP_NetAddress_IPv6 ipv6 = {};
if (!net_address.DescribeAsIPv6Address(&ipv6)) {
return false;
}
NetAddressIPv6ToSockAddrIn6(
ipv6, reinterpret_cast<sockaddr_in6*>(storage));
return true;
}
default:
return false;
}
}
pp::NetAddress SockAddrToNetAddress(
const pp::InstanceHandle& instance, const sockaddr* saddr) {
ALOG_ASSERT(saddr->sa_family == AF_INET || saddr->sa_family == AF_INET6);
if (saddr->sa_family == AF_INET) {
PP_NetAddress_IPv4 ipv4;
SockAddrInToNetAddressIPv4(
reinterpret_cast<const sockaddr_in*>(saddr), &ipv4);
return pp::NetAddress(instance, ipv4);
}
if (saddr->sa_family == AF_INET6) {
PP_NetAddress_IPv6 ipv6;
SockAddrIn6ToNetAddressIPv6(
reinterpret_cast<const sockaddr_in6*>(saddr), &ipv6);
return pp::NetAddress(instance, ipv6);
}
return pp::NetAddress();
}
// TODO(hidehiko): Clean up the code as the some part of code inside this
// function can be shared with above methods.
bool StringToSockAddrStorage(
const char* hostname, uint16_t port,
int dest_family, bool allow_v4mapped, sockaddr_storage* storage) {
ALOG_ASSERT(dest_family == AF_UNSPEC ||
dest_family == AF_INET ||
dest_family == AF_INET6);
memset(storage, 0, sizeof(*storage));
in6_addr addr6;
if (inet_pton(AF_INET6, hostname, &addr6) == 1) {
if (dest_family == AF_INET)
return false;
// TODO(crbug.com/243012): handle scope_id
sockaddr_in6* saddr6 = reinterpret_cast<sockaddr_in6*>(storage);
saddr6->sin6_family = AF_INET6;
saddr6->sin6_port = port;
memcpy(saddr6->sin6_addr.s6_addr, &addr6, sizeof(addr6));
return true;
}
in_addr addr4;
if (inet_pton(AF_INET, hostname, &addr4) == 1) {
if (dest_family == AF_INET6) {
// Convert to V4Mapped address.
if (!allow_v4mapped)
return false;
sockaddr_in6* saddr6 = reinterpret_cast<sockaddr_in6*>(storage);
saddr6->sin6_family = AF_INET6;
saddr6->sin6_port = port;
// V4Mapped address forms: 0::FFFF:xxx.yyy.zzz.www.
memset(saddr6->sin6_addr.s6_addr, 0, 10); // Leading 10 bytes are 0.
saddr6->sin6_addr.s6_addr[10] = 0xFF;
saddr6->sin6_addr.s6_addr[11] = 0xFF;
memcpy(&saddr6->sin6_addr.s6_addr[12], &addr4, sizeof(addr4));
return true;
}
sockaddr_in* saddr = reinterpret_cast<sockaddr_in*>(storage);
saddr->sin_family = AF_INET;
saddr->sin_port = port;
memcpy(&saddr->sin_addr.s_addr, &addr4, sizeof(addr4));
return true;
}
// Failed to convert into sockaddr_storage.
return false;
}
uint16_t ServiceNameToPort(const char* service_name) {
if (service_name == NULL)
return 0;
char* end;
long int port = strtol(service_name, &end, 10); // NOLINT(runtime/int)
if (port < 0 || port >= 65536 || *end != '\0') {
ALOGW("Unsupported network service name %s", service_name);
return 0;
}
return htons(static_cast<uint16_t>(port));
}
addrinfo* SockAddrStorageToAddrInfo(
const sockaddr_storage& storage, int socktype, int protocol,
const std::string& name) {
ALOG_ASSERT(storage.ss_family == AF_INET || storage.ss_family == AF_INET6);
size_t addrlen = storage.ss_family == AF_INET ?
sizeof(sockaddr_in) : sizeof(sockaddr_in6);
sockaddr* saddr = static_cast<sockaddr*>(malloc(addrlen));
memcpy(saddr, &storage, addrlen);
addrinfo* info = static_cast<addrinfo*>(malloc(sizeof(addrinfo)));
info->ai_flags = 0;
info->ai_family = storage.ss_family;
info->ai_socktype = socktype ? socktype : SOCK_STREAM;
info->ai_protocol = protocol;
info->ai_addrlen = addrlen;
info->ai_addr = saddr;
// Use malloc + memcpy, instead of stdup. Valgrind seems to detect strdup
// has some invalid memory access.
info->ai_canonname = static_cast<char*>(malloc(name.size() + 1));
memcpy(info->ai_canonname, name.c_str(), name.size() + 1);
info->ai_next = NULL;
return info;
}
void ReleaseAddrInfo(addrinfo* info) {
free(info->ai_canonname);
free(info->ai_addr);
free(info);
}
int VerifyGetSocketOption(const void* optval, const socklen_t* optlen) {
if (!optlen) {
return EFAULT;
}
if (*optlen < 0) {
return EINVAL;
}
// Note that if optlen is 0, optval can be NULL, because we will not copy
// the data to it.
if (*optlen != 0 && !optval) {
return EFAULT;
}
return 0;
}
int VerifySetSocketOption(
const void* optval, socklen_t optlen, socklen_t expected_optlen) {
if (optlen < expected_optlen) {
return EINVAL;
}
if (!optval) {
return EFAULT;
}
return 0;
}
int VerifyTimeoutSocketOption(const timeval& timeout) {
// tv_usec must be in the range of [0, 1000000).
if (timeout.tv_usec < 0 || timeout.tv_usec >= 1000000) {
return EDOM;
}
return 0;
}
void CopySocketOption(const void* storage, socklen_t storage_length,
void* optval, socklen_t* optlen) {
ALOG_ASSERT(storage);
*optlen = std::min(*optlen, storage_length);
if (optval)
memcpy(optval, storage, *optlen);
}
} // namespace internal
} // namespace posix_translation