Files
RedHotRoast-ios/HybridCLRData/LocalIl2CppData-OSXEditor/il2cpp/libil2cpp/os/Posix/SocketImpl.cpp
T
2026-07-17 14:03:00 +08:00

2921 lines
81 KiB
C++

#include "il2cpp-config.h"
#if !IL2CPP_USE_GENERIC_SOCKET_IMPL && (IL2CPP_TARGET_POSIX || IL2CPP_SUPPORT_SOCKETS_POSIX_API) && IL2CPP_SUPPORT_SOCKETS
// enable support for AF_UNIX and socket paths
#define SUPPORT_UNIXSOCKETS (1)
// some platforms require a different function to close sockets
#define SOCK_CLOSE close
// allow option include file to configure platform
#if IL2CPP_USE_POSIX_SOCKET_PLATFORM_CONFIG
#include "SocketImplPlatformConfig.h"
#endif
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <errno.h>
#include <fcntl.h>
#if SUPPORT_UNIXSOCKETS
#include <sys/un.h>
#endif
#include <sys/poll.h>
#include <sys/stat.h>
#if IL2CPP_SUPPORT_SEND_FILE && (IL2CPP_TARGET_LINUX || IL2CPP_TARGET_ANDROID)
#include <sys/sendfile.h>
#endif
#include "os/Error.h"
#include "os/Socket.h"
#include "os/ErrorCodes.h"
#include "os/Posix/Error.h"
#include "os/Posix/PosixHelpers.h"
#include "os/Posix/SocketImpl.h"
#include "os/Posix/ThreadImpl.h"
#include "utils/Memory.h"
#include "utils/Il2CppError.h"
#include "utils/StringUtils.h"
namespace il2cpp
{
namespace os
{
static bool is_loopback(int32_t family, uint8_t *addr)
{
if (family == AF_INET)
return addr[0] == 127;
#if IL2CPP_SUPPORT_IPV6
else if (family == AF_INET6)
return (IN6_IS_ADDR_LOOPBACK((struct in6_addr *)addr));
#endif
return false;
}
static bool is_loopback(const char* address)
{
if (strcmp(address, "localhost") == 0)
{
return true;
}
{
sockaddr_in sin = {0};
if (inet_pton(AF_INET, address, &sin.sin_addr) > 0)
{
return is_loopback(AF_INET, (uint8_t*)&sin.sin_addr);
}
}
#if IL2CPP_SUPPORT_IPV6
{
sockaddr_in6 sin = {0};
if (inet_pton(AF_INET6, address, &sin.sin6_addr) > 0)
{
return is_loopback(AF_INET6, (uint8_t*)&sin.sin6_addr);
}
}
#endif
return false;
}
static bool is_limited_broadcast(const struct sockaddr *sa, socklen_t sa_size)
{
if (sa != NULL && sa_size >= sizeof(sockaddr_in))
{
const sockaddr_in *sin = (const sockaddr_in *)sa;
return sin->sin_family == AF_INET && sin->sin_addr.s_addr == htonl(INADDR_BROADCAST);
}
return false;
}
bool SocketImpl::is_private(const struct sockaddr *sa, socklen_t sa_size)
{
if (is_limited_broadcast(sa, sa_size))
{
return true;
}
if (sa != NULL)
{
if (sa_size >= sizeof(const sockaddr_in) && sa->sa_family == AF_INET)
{
const uint8_t *addr = (uint8_t *)&(((const sockaddr_in *)sa)->sin_addr);
if (addr[0] == 10) // Class A
{
return true;
}
if (addr[0] == 172 && (addr[1] & 0xf0) == 16) // Class B
{
return true;
}
if (addr[0] == 192 && addr[1] == 168) // Class C
{
return true;
}
}
#if IL2CPP_SUPPORT_IPV6
else if (sa_size >= sizeof(struct sockaddr_in6) && sa->sa_family == AF_INET6)
{
const uint8_t *addr = (uint8_t *)&(((const sockaddr_in6 *)sa)->sin6_addr);
if (addr[0] == 0xf && (addr[1] & 0xe) == 0xc) // Unique local unicast address(ULA)
{
return true;
}
if (addr[0] == 0xf && addr[1] == 0xe && (addr[2] & 0xc) == 0x8) // Link local unicast address
{
return true;
}
}
#endif
}
return false;
}
bool SocketImpl::is_private(const char* address)
{
if (address == 0 || address[0] == 0)
{
return false;
}
{
sockaddr_in sin = {0};
if (inet_pton(AF_INET, address, &sin.sin_addr) > 0)
{
sin.sin_family = AF_INET;
return is_private((const sockaddr *)&sin, sizeof(sin));
}
}
#if IL2CPP_SUPPORT_IPV6
{
sockaddr_in6 sin = {0};
if (inet_pton(AF_INET6, address, &sin.sin6_addr) > 0)
{
sin.sin6_family = AF_INET6;
return is_private((const sockaddr *)&sin, sizeof(sin));
}
}
#endif
return false;
}
#define IFCONF_BUFF_SIZE 1024
#ifndef _SIZEOF_ADDR_IFREQ
#define _SIZEOF_ADDR_IFREQ(ifr) (sizeof (struct ifreq))
#endif
#define FOREACH_IFR(IFR, IFC) \
for (IFR = (IFC).ifc_req; \
ifr < (struct ifreq*)((char*)(IFC).ifc_req + (IFC).ifc_len); \
ifr = (struct ifreq*)((char*)(IFR) + _SIZEOF_ADDR_IFREQ (*(IFR))))
static int address_size_for_family(int family)
{
switch (family)
{
case AF_INET:
return sizeof(struct in_addr);
#if IL2CPP_SUPPORT_IPV6
case AF_INET6:
return sizeof(struct in6_addr);
#endif
}
return 0;
}
static void*
get_address_from_sockaddr(struct sockaddr *sa)
{
switch (sa->sa_family)
{
case AF_INET:
return &((struct sockaddr_in*)sa)->sin_addr;
#if IL2CPP_SUPPORT_IPV6
case AF_INET6:
return &((struct sockaddr_in6*)sa)->sin6_addr;
#endif
}
return NULL;
}
static struct in_addr *get_local_ips(int32_t family, int32_t *interface_count)
{
int fd;
struct ifconf ifc;
struct ifreq *ifr;
int if_count = 0;
bool ignore_loopback = false;
void *result = NULL;
char *result_ptr;
*interface_count = 0;
if (!address_size_for_family(family))
return NULL;
fd = socket(family, SOCK_STREAM, 0);
if (fd == -1)
return NULL;
memset(&ifc, 0, sizeof(ifc));
ifc.ifc_len = IFCONF_BUFF_SIZE;
ifc.ifc_buf = (char*)malloc(IFCONF_BUFF_SIZE); /* We can't have such huge buffers on the stack. */
if (ioctl(fd, SIOCGIFCONF, &ifc) < 0)
goto done;
FOREACH_IFR(ifr, ifc) {
struct ifreq iflags;
//only return addresses of the same type as @family
if (ifr->ifr_addr.sa_family != family)
{
ifr->ifr_name[0] = '\0';
continue;
}
strcpy(iflags.ifr_name, ifr->ifr_name);
//ignore interfaces we can't get props for
if (ioctl(fd, SIOCGIFFLAGS, &iflags) < 0)
{
ifr->ifr_name[0] = '\0';
continue;
}
//ignore interfaces that are down
if ((iflags.ifr_flags & IFF_UP) == 0)
{
ifr->ifr_name[0] = '\0';
continue;
}
//If we have a non-loopback iface, don't return any loopback
if ((iflags.ifr_flags & IFF_LOOPBACK) == 0)
{
ignore_loopback = true;
ifr->ifr_name[0] = 1; //1 means non-loopback
}
else
{
ifr->ifr_name[0] = 2; //2 means loopback
}
++if_count;
}
result = (char*)malloc(if_count * address_size_for_family(family));
result_ptr = (char*)result;
FOREACH_IFR(ifr, ifc) {
if (ifr->ifr_name[0] == '\0')
continue;
if (ignore_loopback && ifr->ifr_name[0] == 2)
{
--if_count;
continue;
}
memcpy(result_ptr, get_address_from_sockaddr(&ifr->ifr_addr), address_size_for_family(family));
result_ptr += address_size_for_family(family);
}
IL2CPP_ASSERT(result_ptr <= (char*)result + if_count * address_size_for_family(family));
done:
*interface_count = if_count;
free(ifc.ifc_buf);
close(fd);
return (struct in_addr*)result;
}
static bool hostent_get_info(struct hostent *he, std::string &name, std::vector<std::string> &aliases, std::vector<std::string> &addr_list)
{
if (he == NULL)
return false;
if (he->h_length != 4 || he->h_addrtype != AF_INET)
return false;
name.assign(he->h_name);
for (int32_t i = 0; he->h_aliases[i] != NULL; ++i)
aliases.push_back(he->h_aliases[i]);
for (int32_t i = 0; he->h_addr_list[i] != NULL; ++i)
addr_list.push_back(
utils::StringUtils::NPrintf("%u.%u.%u.%u", 16,
(uint8_t)he->h_addr_list[i][0],
(uint8_t)he->h_addr_list[i][1],
(uint8_t)he->h_addr_list[i][2],
(uint8_t)he->h_addr_list[i][3]));
return true;
}
static bool hostent_get_info_with_local_ips(struct hostent *he, std::string &name, std::vector<std::string> &aliases, std::vector<std::string> &addr_list)
{
int32_t nlocal_in = 0;
if (he != NULL)
{
if (he->h_length != 4 || he->h_addrtype != AF_INET)
return false;
name.assign(he->h_name);
for (int32_t i = 0; he->h_aliases[i] != NULL; ++i)
aliases.push_back(he->h_aliases[i]);
}
struct in_addr *local_in = get_local_ips(AF_INET, &nlocal_in);
if (nlocal_in)
{
for (int32_t i = 0; i < nlocal_in; ++i)
{
const uint8_t *ptr = (uint8_t*)&local_in[i];
addr_list.push_back(
utils::StringUtils::NPrintf("%u.%u.%u.%u", 16,
(uint8_t)ptr[0],
(uint8_t)ptr[1],
(uint8_t)ptr[2],
(uint8_t)ptr[3]));
}
free(local_in);
}
else if (he == NULL)
{
// If requesting "" and there are no other interfaces up, MS returns 127.0.0.1
addr_list.push_back("127.0.0.1");
return true;
}
if (nlocal_in == 0 && he != NULL)
{
for (int32_t i = 0; he->h_addr_list[i] != NULL; ++i)
{
addr_list.push_back(
utils::StringUtils::NPrintf("%u.%u.%u.%u", 16,
(uint8_t)he->h_addr_list[i][0],
(uint8_t)he->h_addr_list[i][1],
(uint8_t)he->h_addr_list[i][2],
(uint8_t)he->h_addr_list[i][3]));
}
}
return true;
}
static int32_t convert_socket_flags(os::SocketFlags flags)
{
int32_t c_flags = 0;
if (flags)
{
// Check if contains invalid flag values
if (flags & ~(os::kSocketFlagsOutOfBand | os::kSocketFlagsMaxIOVectorLength | os::kSocketFlagsPeek | os::kSocketFlagsDontRoute | os::kSocketFlagsPartial))
{
return -1;
}
#ifdef MSG_OOB
if (flags & os::kSocketFlagsOutOfBand)
c_flags |= MSG_OOB;
#endif
if (flags & os::kSocketFlagsPeek)
c_flags |= MSG_PEEK;
if (flags & os::kSocketFlagsDontRoute)
c_flags |= MSG_DONTROUTE;
// Ignore Partial - see bug 349688. Don't return -1, because
// according to the comment in that bug ms runtime doesn't for
// UDP sockets (this means we will silently ignore it for TCP
// too)
#ifdef MSG_MORE
if (flags & os::kSocketFlagsPartial)
c_flags |= MSG_MORE;
#endif
}
return c_flags;
}
void SocketImpl::Startup()
{
}
void SocketImpl::Cleanup()
{
}
#if IL2CPP_SUPPORT_IPV6
static void AddrinfoGetAddresses(struct addrinfo *info, std::string& name, bool add_local_ips,
std::vector<std::string> &addr_list)
{
if (add_local_ips)
{
bool any_local_ips_added = false;
int nlocal_in = 0;
int nlocal_in6 = 0;
in_addr* local_in = (struct in_addr *)get_local_ips(AF_INET, &nlocal_in);
in6_addr* local_in6 = (struct in6_addr *)get_local_ips(AF_INET6, &nlocal_in6);
if (nlocal_in || nlocal_in6)
{
if (nlocal_in)
{
for (int i = 0; i < nlocal_in; i++)
{
char addr[16];
inet_ntop(AF_INET, &local_in[i], addr, sizeof(addr));
addr_list.push_back(std::string(addr));
any_local_ips_added = true;
}
}
if (nlocal_in6)
{
for (int i = 0; i < nlocal_in6; i++)
{
char addr[48];
const char* ret = inet_ntop(AF_INET6, &local_in6[i], addr, sizeof(addr));
if (ret != NULL)
{
addr_list.push_back(std::string(addr));
any_local_ips_added = true;
}
}
}
}
free(local_in);
free(local_in6);
if (any_local_ips_added)
return;
}
bool nameSet = false;
for (addrinfo* ai = info; ai != NULL; ai = ai->ai_next)
{
const char *ret;
char buffer[48]; /* Max. size for IPv6 */
if ((ai->ai_family != PF_INET) && (ai->ai_family != PF_INET6))
continue;
if (ai->ai_family == PF_INET)
ret = inet_ntop(ai->ai_family, (void*)&(((struct sockaddr_in*)ai->ai_addr)->sin_addr), buffer, 16);
else
ret = inet_ntop(ai->ai_family, (void*)&(((struct sockaddr_in6*)ai->ai_addr)->sin6_addr), buffer, 48);
if (ret)
addr_list.push_back(std::string(buffer));
else
addr_list.push_back(std::string());
if (!nameSet)
{
if (ai->ai_canonname != NULL)
name = std::string(ai->ai_canonname);
else
name = std::string();
nameSet = true;
}
}
}
WaitStatus GetAddressInfo(const char* hostname, bool add_local_ips, std::string &name, std::vector<std::string> &addr_list)
{
NetworkAccessHandler::Auto scopedAccess;
if (!is_loopback(hostname) || add_local_ips)
{
if (!scopedAccess.RequestAccessForAddressInfo(SocketImpl::is_private(hostname)))
{
return kWaitStatusFailure;
}
}
addrinfo *info = NULL;
addrinfo hints;
memset(&hints, 0, sizeof(hints));
// Here Mono inspects the ipv4Supported and ipv6Supported properties on the managed Socket class.
// This seems to be unnecessary though, as we can use PF_UNSPEC in all cases, and getaddrinfo works.
hints.ai_family = PF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
#if IL2CPP_TARGET_QNX
// there is no AI_ADDRCONFIG flag on QNX:
hints.ai_flags = AI_CANONNAME;
#else
hints.ai_flags = AI_CANONNAME | AI_ADDRCONFIG;
#endif
if (*hostname && getaddrinfo(hostname, NULL, &hints, &info) == -1)
return kWaitStatusFailure;
AddrinfoGetAddresses(info, name, add_local_ips, addr_list);
if (info)
freeaddrinfo(info);
if (name.empty())
name.assign(hostname);
return kWaitStatusSuccess;
}
#endif
WaitStatus SocketImpl::GetHostByAddr(const std::string &address, std::string &name, std::vector<std::string> &aliases, std::vector<std::string> &addr_list)
{
NetworkAccessHandler::Auto scopedAccess;
if (!is_loopback(address.c_str()))
{
if (!scopedAccess.RequestAccessForAddressInfo(is_private(address.c_str())))
{
return kWaitStatusFailure;
}
}
#if IL2CPP_SUPPORT_IPV6
struct sockaddr_in saddr;
struct sockaddr_in6 saddr6;
int32_t family;
char hostname[1024] = {0};
int flags = 0;
if (inet_pton(AF_INET, address.c_str(), &saddr.sin_addr) <= 0)
{
/* Maybe an ipv6 address */
if (inet_pton(AF_INET6, address.c_str(), &saddr6.sin6_addr) <= 0)
{
return kWaitStatusFailure;
}
else
{
family = AF_INET6;
saddr6.sin6_family = AF_INET6;
}
}
else
{
family = AF_INET;
saddr.sin_family = AF_INET;
}
if (family == AF_INET)
{
#if HAVE_SOCKADDR_IN_SIN_LEN
saddr.sin_len = sizeof(saddr);
#endif
if (getnameinfo((struct sockaddr*)&saddr, sizeof(saddr),
hostname, sizeof(hostname), NULL, 0,
flags) != 0)
{
return kWaitStatusFailure;
}
}
else if (family == AF_INET6)
{
#if HAVE_SOCKADDR_IN6_SIN_LEN
saddr6.sin6_len = sizeof(saddr6);
#endif
if (getnameinfo((struct sockaddr*)&saddr6, sizeof(saddr6),
hostname, sizeof(hostname), NULL, 0,
flags) != 0)
{
return kWaitStatusFailure;
}
}
return GetAddressInfo(hostname, false, name, addr_list);
#else
struct in_addr inaddr;
if (inet_pton(AF_INET, address.c_str(), &inaddr) <= 0)
return kWaitStatusFailure;
struct hostent *he = gethostbyaddr((char*)&inaddr, sizeof(inaddr), AF_INET);
if (he == NULL)
{
name = address;
addr_list.push_back(name);
return kWaitStatusSuccess;
}
return hostent_get_info(he, name, aliases, addr_list)
? kWaitStatusSuccess
: kWaitStatusFailure;
#endif
}
WaitStatus SocketImpl::GetHostByName(const std::string &host, std::string &name, std::vector<std::string> &aliases, std::vector<std::string> &addresses)
{
char this_hostname[256] = {0};
const char *hostname = host.c_str();
bool add_local_ips = (*hostname == '\0');
if (!add_local_ips && gethostname(this_hostname, sizeof(this_hostname)) != -1)
{
if (!strcmp(hostname, this_hostname))
add_local_ips = true;
}
#if IL2CPP_SUPPORT_IPV6
return GetAddressInfo(hostname, add_local_ips, name, addresses);
#else
struct hostent *he = NULL;
if (*hostname)
he = gethostbyname(hostname);
if (*hostname && he == NULL)
return kWaitStatusFailure;
return (add_local_ips
? hostent_get_info_with_local_ips(he, name, aliases, addresses)
: hostent_get_info(he, name, aliases, addresses))
? kWaitStatusSuccess
: kWaitStatusFailure;
#endif
}
static bool HasAnyIPv4Addresses(const std::vector<std::string>& addresses)
{
for (std::vector<std::string>::const_iterator it = addresses.begin(); it != addresses.end(); ++it)
{
in_addr address;
if (inet_pton(AF_INET, it->c_str(), &address))
return true;
}
return false;
}
WaitStatus SocketImpl::GetHostByName(const std::string &host, std::string &name, int32_t &family, std::vector<std::string> &aliases, std::vector<void*> &addr_list, int32_t &addr_size)
{
std::vector<std::string> addresses;
WaitStatus result = GetHostByName(host, name, aliases, addresses);
// If we got an IPv4 address, use that and any others, skipping IPv6 addresses.
// We can only return one address size, so we need to choose.
if (HasAnyIPv4Addresses(addresses))
{
addr_size = sizeof(in_addr);
family = AF_INET;
for (std::vector<std::string>::iterator it = addresses.begin(); it != addresses.end(); ++it)
{
in_addr address;
if (inet_pton(family, it->c_str(), &address))
{
void* addressLocation = il2cpp::utils::Memory::Malloc(addr_size);
memcpy(addressLocation, &address.s_addr, addr_size);
addr_list.push_back(addressLocation);
}
}
}
#if IL2CPP_SUPPORT_IPV6
else
{
addr_size = sizeof(in6_addr);
family = AF_INET6;
for (std::vector<std::string>::iterator it = addresses.begin(); it != addresses.end(); ++it)
{
in6_addr address;
if (inet_pton(family, it->c_str(), &address))
{
void* addressLocation = il2cpp::utils::Memory::Malloc(addr_size);
memcpy(addressLocation, &address.s6_addr, addr_size);
addr_list.push_back(addressLocation);
}
}
}
#endif
return result;
}
WaitStatus SocketImpl::GetHostName(std::string &name)
{
char hostname[256];
int32_t ret = gethostname(hostname, sizeof(hostname));
if (ret == -1)
return kWaitStatusFailure;
name.assign(hostname);
return kWaitStatusSuccess;
}
SocketImpl::SocketImpl(ThreadStatusCallback thread_status_callback)
: _is_valid(false)
, _fd(-1)
, _domain(-1)
, _type(-1)
, _protocol(-1)
, _saved_error(kErrorCodeSuccess)
, _still_readable(0)
, _thread_status_callback(thread_status_callback)
{
}
SocketImpl::~SocketImpl()
{
}
static int32_t convert_address_family(AddressFamily family)
{
switch (family)
{
case kAddressFamilyUnspecified:
return AF_UNSPEC;
case kAddressFamilyUnix:
return AF_UNIX;
case kAddressFamilyInterNetwork:
return AF_INET;
#ifdef AF_IPX
case kAddressFamilyIpx:
return AF_IPX;
#endif
#ifdef AF_SNA
case kAddressFamilySna:
return AF_SNA;
#endif
#ifdef AF_DECnet
case kAddressFamilyDecNet:
return AF_DECnet;
#endif
#ifdef AF_APPLETALK
case kAddressFamilyAppleTalk:
return AF_APPLETALK;
#endif
#ifdef AF_INET6
case kAddressFamilyInterNetworkV6:
return AF_INET6;
#endif
#ifdef AF_IRDA
case kAddressFamilyIrda:
return AF_IRDA;
#endif
default:
break;
}
return -1;
}
static AddressFamily convert_define_to_address_family(int32_t family)
{
switch (family)
{
case AF_UNSPEC:
return kAddressFamilyUnspecified;
case AF_UNIX:
return kAddressFamilyUnix;
case AF_INET:
return kAddressFamilyInterNetwork;
#ifdef AF_IPX
case AF_IPX:
return kAddressFamilyIpx;
#endif
#ifdef AF_SNA
case AF_SNA:
return kAddressFamilySna;
#endif
#ifdef AF_DECnet
case AF_DECnet:
return kAddressFamilyDecNet;
#endif
#ifdef AF_APPLETALK
case AF_APPLETALK:
return kAddressFamilyAppleTalk;
#endif
#ifdef AF_INET6
case AF_INET6:
return kAddressFamilyInterNetworkV6;
#endif
#ifdef AF_IRDA
case AF_IRDA:
return kAddressFamilyIrda;
#endif
default:
break;
}
return kAddressFamilyError;
}
static int32_t convert_socket_type(SocketType type)
{
switch (type)
{
case kSocketTypeStream:
return SOCK_STREAM;
case kSocketTypeDgram:
return SOCK_DGRAM;
case kSocketTypeRaw:
return SOCK_RAW;
#ifdef SOCK_RDM
case kSocketTypeRdm:
return SOCK_RDM;
#endif
#ifdef SOCK_SEQPACKET
case kSocketTypeSeqpacket:
return SOCK_SEQPACKET;
#endif
default:
break;
}
return -1;
}
static int32_t convert_socket_protocol(ProtocolType protocol)
{
switch (protocol)
{
case kProtocolTypeIP:
case kProtocolTypeIPv6:
case kProtocolTypeIcmp:
case kProtocolTypeIgmp:
case kProtocolTypeGgp:
case kProtocolTypeTcp:
case kProtocolTypePup:
case kProtocolTypeUdp:
case kProtocolTypeIdp:
// In this case the enum values map exactly.
return (int32_t)protocol;
default:
break;
}
// Everything else in unsupported and unexpected
return -1;
}
WaitStatus SocketImpl::Create(AddressFamily family, SocketType type, ProtocolType protocol)
{
_fd = -1;
_is_valid = false;
_still_readable = 1;
_domain = convert_address_family(family);
_type = convert_socket_type(type);
_protocol = convert_socket_protocol(protocol);
IL2CPP_ASSERT(_type != -1 && "Unsupported socket type");
IL2CPP_ASSERT(_domain != -1 && "Unsupported address family");
IL2CPP_ASSERT(_protocol != -1 && "Unsupported protocol type");
_fd = socket(_domain, _type, _protocol);
if (_fd == -1 && _domain == AF_INET && _type == SOCK_RAW && _protocol == 0)
{
// Retry with protocol == 4 (see bug #54565)
_protocol = 4;
_fd = socket(AF_INET, SOCK_RAW, 4);
}
if (_fd == -1)
{
StoreLastError();
return kWaitStatusFailure;
}
// if (fd >= _wapi_fd_reserve)
// {
// WSASetLastError (WSASYSCALLFAILURE);
// close (fd);
// return(INVALID_SOCKET);
// }
/* .net seems to set this by default for SOCK_STREAM, not for
* SOCK_DGRAM (see bug #36322)
*
* It seems winsock has a rather different idea of what
* SO_REUSEADDR means. If it's set, then a new socket can be
* bound over an existing listening socket. There's a new
* windows-specific option called SO_EXCLUSIVEADDRUSE but
* using that means the socket MUST be closed properly, or a
* denial of service can occur. Luckily for us, winsock
* behaves as though any other system would when SO_REUSEADDR
* is true, so we don't need to do anything else here. See
* bug 53992.
*/
{
int32_t v = 1;
const int32_t ret = setsockopt(_fd, SOL_SOCKET, SO_REUSEADDR, &v, sizeof(v));
if (ret == -1)
{
if (SOCK_CLOSE(_fd) == -1)
StoreLastError();
return kWaitStatusFailure;
}
}
#if IL2CPP_TARGET_DARWIN
int32_t value = 1;
setsockopt(_fd, SOL_SOCKET, SO_NOSIGPIPE, &value, sizeof(value));
#endif
// mono_once (&socket_ops_once, socket_ops_init);
// handle = _wapi_handle_new_fd (WAPI_HANDLE_SOCKET, fd, &socket_handle);
// if (handle == _WAPI_HANDLE_INVALID) {
// g_warning ("%s: error creating socket handle", __func__);
// WSASetLastError (WSASYSCALLFAILURE);
// close (fd);
// return(INVALID_SOCKET);
// }
_is_valid = true;
_networkAccess.InheritNetworkAccessState(_fd);
return kWaitStatusSuccess;
}
WaitStatus SocketImpl::Create(SocketDescriptor fd, int32_t family, int32_t type, int32_t protocol)
{
_fd = fd;
_is_valid = (fd != -1);
_still_readable = 1;
_domain = family;
_type = type;
_protocol = protocol;
_networkAccess.InheritNetworkAccessState(_fd);
IL2CPP_ASSERT(_type != -1 && "Unsupported socket type");
IL2CPP_ASSERT(_domain != -1 && "Unsupported address family");
IL2CPP_ASSERT(_protocol != -1 && "Unsupported protocol type");
return kWaitStatusSuccess;
}
WaitStatus SocketImpl::Close()
{
_saved_error = kErrorCodeSuccess;
if (_is_valid && _fd != -1)
{
if (SOCK_CLOSE(_fd) == -1)
StoreLastError();
}
_fd = -1;
_is_valid = false;
_still_readable = 0;
_domain = -1;
_type = -1;
_protocol = -1;
_networkAccess.CancelNetworkAccess();
return kWaitStatusSuccess;
}
WaitStatus SocketImpl::SetBlocking(bool blocking)
{
#if IL2CPP_USE_SOCKET_SETBLOCKING
return (WaitStatus)setBlocking(_fd, blocking);
#else
int32_t flags = fcntl(_fd, F_GETFL, 0);
if (flags == -1)
{
StoreLastError();
return kWaitStatusFailure;
}
flags = blocking
? (flags & ~O_NONBLOCK)
: (flags | O_NONBLOCK);
if (fcntl(_fd, F_SETFL, flags) == -1)
{
StoreLastError();
return kWaitStatusFailure;
}
return kWaitStatusSuccess;
#endif
}
ErrorCode SocketImpl::GetLastError() const
{
return _saved_error;
}
void SocketImpl::StoreLastError()
{
const ErrorCode error = SocketErrnoToErrorCode(errno);
Error::SetLastError(error);
_saved_error = error;
}
void SocketImpl::StoreLastError(int32_t error_no)
{
const ErrorCode error = SocketErrnoToErrorCode(error_no);
Error::SetLastError(error);
_saved_error = error;
}
#if SUPPORT_UNIXSOCKETS
static struct sockaddr* sockaddr_from_path(const char *path, socklen_t *sa_size)
{
struct sockaddr_un* sa_un;
const size_t len = strlen(path);
if (len >= sizeof(sa_un->sun_path))
return NULL;
sa_un = (struct sockaddr_un*)IL2CPP_CALLOC(1, sizeof(sockaddr_un));
sa_un->sun_family = AF_UNIX;
memcpy(sa_un->sun_path, path, len);
*sa_size = sizeof(sockaddr_un);
return (struct sockaddr *)sa_un;
}
#endif
#if IL2CPP_SUPPORT_IPV6
static void sockaddr_from_address(uint8_t address[ipv6AddressSize], uint32_t scope, uint16_t port, sockaddr_in6* sa, socklen_t *sa_size)
{
sa->sin6_family = AF_INET6;
sa->sin6_port = port;
memcpy(&sa->sin6_addr, &address[0], 16);
sa->sin6_scope_id = scope;
*sa_size = sizeof(struct sockaddr_in6);
}
#endif
static void sockaddr_from_address(uint32_t address, uint16_t port, struct sockaddr *sa, socklen_t *sa_size)
{
struct sockaddr_in sa_in = {0};
sa_in.sin_family = AF_INET;
sa_in.sin_port = port;
sa_in.sin_addr.s_addr = address;
*sa_size = sizeof(struct sockaddr_in);
*sa = *((struct sockaddr*)&sa_in);
}
static bool socketaddr_to_endpoint_info(const struct sockaddr *address, socklen_t address_len, EndPointInfo &info)
{
info.family = convert_define_to_address_family(address->sa_family);
if (info.family == os::kAddressFamilyInterNetwork)
{
const struct sockaddr_in *address_in = (const struct sockaddr_in *)address;
info.data.inet.port = ntohs(address_in->sin_port);
info.data.inet.address = ntohl(address_in->sin_addr.s_addr);
return true;
}
if (info.family == os::kAddressFamilyUnix)
{
for (int32_t i = 0; i < address_len; i++)
info.data.path[i] = address->sa_data[i];
return true;
}
#if IL2CPP_SUPPORT_IPV6
if (info.family == os::kAddressFamilyInterNetworkV6)
{
const struct sockaddr_in6 *address_in = (const struct sockaddr_in6 *)address;
uint16_t port = ntohs(address_in->sin6_port);
info.data.raw[2] = (port >> 8) & 0xff;
info.data.raw[3] = port & 0xff;
for (int i = 0; i < 16; i++)
info.data.raw[i + 8] = address_in->sin6_addr.s6_addr[i];
info.data.raw[24] = address_in->sin6_scope_id & 0xff;
info.data.raw[25] = (address_in->sin6_scope_id >> 8) & 0xff;
info.data.raw[26] = (address_in->sin6_scope_id >> 16) & 0xff;
info.data.raw[27] = (address_in->sin6_scope_id >> 24) & 0xff;
return true;
}
#endif
return false;
}
WaitStatus SocketImpl::Bind(const char *path)
{
#if SUPPORT_UNIXSOCKETS
socklen_t sa_size = 0;
struct sockaddr* sa = sockaddr_from_path(path, &sa_size);
int result = bind(_fd, sa, sa_size);
IL2CPP_FREE(sa);
if (result == -1)
{
StoreLastError();
return kWaitStatusFailure;
}
return kWaitStatusSuccess;
#else
return kWaitStatusFailure;
#endif
}
WaitStatus SocketImpl::Bind(const char *address, uint16_t port)
{
struct sockaddr sa = {0};
socklen_t sa_size = 0;
sockaddr_from_address(inet_addr(address), htons(port), &sa, &sa_size);
if (!_networkAccess.PrepareForBind(_fd, &sa, sa_size))
{
StoreLastError(_networkAccess.GetError());
return kWaitStatusFailure;
}
if (bind(_fd, &sa, sa_size) == -1)
{
StoreLastError();
return kWaitStatusFailure;
}
return kWaitStatusSuccess;
}
WaitStatus SocketImpl::Bind(uint32_t address, uint16_t port)
{
struct sockaddr sa = {0};
socklen_t sa_size = 0;
sockaddr_from_address(htonl(address), htons(port), &sa, &sa_size);
if (!_networkAccess.PrepareForBind(_fd, &sa, sa_size))
{
StoreLastError(_networkAccess.GetError());
return kWaitStatusFailure;
}
if (bind(_fd, &sa, sa_size) == -1)
{
StoreLastError();
return kWaitStatusFailure;
}
return kWaitStatusSuccess;
}
utils::Expected<WaitStatus> SocketImpl::Bind(uint8_t address[ipv6AddressSize], uint32_t scope, uint16_t port)
{
#if IL2CPP_SUPPORT_IPV6
struct sockaddr_in6 sa = { 0 };
socklen_t sa_size = 0;
sockaddr_from_address(address, scope, htons(port), &sa, &sa_size);
if (!_networkAccess.PrepareForBind(_fd, &sa, sa_size))
{
StoreLastError(_networkAccess.GetError());
return kWaitStatusFailure;
}
if (bind(_fd, (sockaddr*)&sa, sa_size) == -1)
{
StoreLastError();
return kWaitStatusFailure;
}
return kWaitStatusSuccess;
#else
return utils::Il2CppError(utils::NotSupported, "IPv6 is not supported on this platform.");
#endif
}
WaitStatus SocketImpl::ConnectInternal(struct sockaddr *sa, int32_t sa_size)
{
if (!_networkAccess.PrepareForConnect(_fd, sa, sa_size))
{
StoreLastError(_networkAccess.GetError());
return kWaitStatusFailure;
}
if (connect(_fd, sa, (socklen_t)sa_size) != -1)
return kWaitStatusSuccess;
if (errno != EINTR)
{
// errnum = errno_to_WSA (errnum, __func__);
// if (errnum == WSAEINPROGRESS)
// errnum = WSAEWOULDBLOCK; /* see bug #73053 */
StoreLastError();
return kWaitStatusFailure;
}
struct pollfd fds = {0};
fds.fd = _fd;
fds.events = POLLOUT;
while (poll(&fds, 1, -1) == -1)
{
if (errno != EINTR)
{
StoreLastError();
return kWaitStatusFailure;
}
}
int32_t so_error = 0;
socklen_t len = sizeof(so_error);
if (getsockopt(_fd, SOL_SOCKET, SO_ERROR, &so_error, &len) == -1)
{
StoreLastError();
return kWaitStatusFailure;
}
if (so_error != 0)
{
StoreLastError(so_error);
return kWaitStatusFailure;
}
return kWaitStatusSuccess;
}
WaitStatus SocketImpl::Connect(const char *path)
{
#if SUPPORT_UNIXSOCKETS
socklen_t sa_size = 0;
struct sockaddr* sa = sockaddr_from_path(path, &sa_size);
WaitStatus status = ConnectInternal(sa, sa_size);
IL2CPP_FREE(sa);
return status;
#else
return kWaitStatusFailure;
#endif
}
utils::Expected<WaitStatus> SocketImpl::Connect(uint8_t address[ipv6AddressSize], uint32_t scope, uint16_t port)
{
#if IL2CPP_SUPPORT_IPV6
struct sockaddr_in6 sa = { 0 };
socklen_t sa_size = 0;
sockaddr_from_address(address, scope, htons(port), &sa, &sa_size);
return ConnectInternal((struct sockaddr *)&sa, sa_size);
#else
return utils::Il2CppError(utils::NotSupported, "IPv6 is not supported on this platform.");
#endif
}
WaitStatus SocketImpl::Connect(uint32_t address, uint16_t port)
{
struct sockaddr sa = {0};
socklen_t sa_size = 0;
sockaddr_from_address(htonl(address), htons(port), &sa, &sa_size);
return ConnectInternal((struct sockaddr *)&sa, sa_size);
}
WaitStatus SocketImpl::GetLocalEndPointInfo(EndPointInfo &info)
{
// Note: the size here could probably be smaller
uint8_t buffer[END_POINT_MAX_PATH_LEN + 3] = {0};
socklen_t address_len = sizeof(buffer);
if (getsockname(_fd, (struct sockaddr *)buffer, &address_len) == -1)
{
StoreLastError();
return kWaitStatusFailure;
}
if (!socketaddr_to_endpoint_info((struct sockaddr *)buffer, address_len, info))
{
_saved_error = kWSAeafnosupport;
return kWaitStatusFailure;
}
return kWaitStatusSuccess;
}
WaitStatus SocketImpl::GetRemoteEndPointInfo(EndPointInfo &info)
{
// Note: the size here could probably be smaller
uint8_t buffer[END_POINT_MAX_PATH_LEN + 3] = {0};
socklen_t address_len = sizeof(buffer);
if (getpeername(_fd, (struct sockaddr *)buffer, &address_len) == -1)
{
StoreLastError();
return kWaitStatusFailure;
}
if (!socketaddr_to_endpoint_info((struct sockaddr *)buffer, address_len, info))
{
_saved_error = kWSAeafnosupport;
return kWaitStatusFailure;
}
return kWaitStatusSuccess;
}
WaitStatus SocketImpl::Listen(int32_t backlog)
{
if (listen(_fd, backlog) == -1)
{
StoreLastError();
return kWaitStatusFailure;
}
return kWaitStatusSuccess;
}
WaitStatus SocketImpl::Shutdown(int32_t how)
{
if (shutdown(_fd, how) == -1)
{
StoreLastError();
return kWaitStatusFailure;
}
if (how == SHUT_RD || how == SHUT_RDWR)
_still_readable = 0;
return kWaitStatusSuccess;
}
WaitStatus SocketImpl::Accept(os::Socket **socket)
{
int32_t new_fd = 0;
*socket = NULL;
do
{
new_fd = accept(_fd, NULL, 0);
}
while (new_fd == -1 && errno == EINTR);
if (new_fd == -1)
{
StoreLastError();
return kWaitStatusFailure;
}
*socket = new os::Socket(_thread_status_callback);
const WaitStatus status = (*socket)->Create(new_fd, _domain, _type, _protocol);
if (status != kWaitStatusSuccess)
{
delete *socket;
*socket = NULL;
return status;
}
return kWaitStatusSuccess;
}
WaitStatus SocketImpl::Disconnect(bool reuse)
{
int32_t new_sock = socket(_domain, _type, _protocol);
if (new_sock == -1)
{
StoreLastError();
return kWaitStatusFailure;
}
// According to Stevens "Advanced Programming in the UNIX
// Environment: UNIX File I/O" dup2() is atomic so there
// should not be a race condition between the old fd being
// closed and the new socket fd being copied over
int32_t ret = 0;
do
{
ret = dup2(new_sock, _fd);
}
while (ret == -1 && errno == EAGAIN);
if (ret == -1)
{
StoreLastError();
return kWaitStatusFailure;
}
SOCK_CLOSE(new_sock);
return kWaitStatusSuccess;
}
WaitStatus SocketImpl::Receive(const uint8_t *data, int32_t count, os::SocketFlags flags, int32_t *len)
{
*len = 0;
const int32_t c_flags = convert_socket_flags(flags);
if (c_flags == -1)
{
_saved_error = kWSAeopnotsupp;
return kWaitStatusFailure;
}
return ReceiveFromInternal(data, count, c_flags, len, NULL, 0);
}
WaitStatus SocketImpl::ReceiveFromInternal(const uint8_t *data, size_t count, int32_t flags, int32_t *len, struct sockaddr *from, int32_t *fromlen)
{
int32_t ret = 0;
if (!_networkAccess.WaitForNetworkStatus(_fd))
{
StoreLastError(_networkAccess.GetError());
return kWaitStatusFailure;
}
do
{
ret = (int32_t)recvfrom(_fd, (void*)data, count, flags, from, (socklen_t*)fromlen);
}
while (ret == -1 && errno == EINTR);
if (ret == 0 && count > 0)
{
// According to the Linux man page, recvfrom only
// returns 0 when the socket has been shut down
// cleanly. Turn this into an EINTR to simulate win32
// behaviour of returning EINTR when a socket is
// closed while the recvfrom is blocking (we use a
// shutdown() in socket_close() to trigger this.) See
// bug 75705.
// Distinguish between the socket being shut down at
// the local or remote ends, and reads that request 0
// bytes to be read
// If this returns FALSE, it means the socket has been
// closed locally. If it returns TRUE, but
// still_readable != 1 then shutdown
// (SHUT_RD|SHUT_RDWR) has been called locally.
if (_still_readable != 1)
{
ret = -1;
errno = EINTR;
}
}
if (ret == -1)
{
StoreLastError();
return kWaitStatusFailure;
}
*len = ret;
return kWaitStatusSuccess;
}
WaitStatus SocketImpl::Send(const uint8_t *data, int32_t count, os::SocketFlags flags, int32_t *len)
{
*len = 0;
int32_t c_flags = convert_socket_flags(flags);
if (c_flags == -1)
{
_saved_error = kWSAeopnotsupp;
return kWaitStatusFailure;
}
#if IL2CPP_USE_SEND_NOSIGNAL
c_flags |= MSG_NOSIGNAL;
#endif
int32_t ret = 0;
do
{
ret = (int32_t)send(_fd, (void*)data, count, c_flags);
}
while (ret == -1 && errno == EINTR);
if (ret == -1)
{
StoreLastError();
return kWaitStatusFailure;
}
*len = ret;
return kWaitStatusSuccess;
}
WaitStatus SocketImpl::SendArray(WSABuf *wsabufs, int32_t count, int32_t *sent, SocketFlags flags)
{
#if IL2CPP_SUPPORT_SEND_MSG
int32_t c_flags = convert_socket_flags(flags);
if (c_flags == -1)
{
_saved_error = kWSAeopnotsupp;
return kWaitStatusFailure;
}
struct msghdr hdr = {0};
hdr.msg_iovlen = count;
hdr.msg_iov = (struct iovec*)malloc(sizeof(struct iovec) * count);
for (int32_t i = 0; i < count; ++i)
{
hdr.msg_iov[i].iov_base = wsabufs[i].buffer;
hdr.msg_iov[i].iov_len = wsabufs[i].length;
}
#if IL2CPP_USE_SEND_NOSIGNAL
c_flags |= MSG_NOSIGNAL;
#endif
int32_t ret = 0;
do
{
ret = (int32_t)sendmsg(_fd, &hdr, c_flags);
}
while (ret == -1 && errno == EINTR);
free(hdr.msg_iov);
if (ret == -1)
{
*sent = 0;
StoreLastError();
return kWaitStatusFailure;
}
*sent = ret;
return kWaitStatusSuccess;
#else
if (sent != NULL)
{
*sent = 0; // Sent bytes.
}
if (wsabufs == NULL && count > 0)
{
_saved_error = kErrorInvalidFunction;
return kWaitStatusFailure;
}
int32_t c_flags = convert_socket_flags(flags);
for (int32_t i = 0; i < count; ++i)
{
ssize_t ret = 0;
do
{
ret = send(_fd, wsabufs[i].buffer, wsabufs[i].length, c_flags);
}
while (ret == EINTR);
if (ret == -1)
{
StoreLastError();
return kWaitStatusFailure;
}
if (sent != NULL)
{
*sent += ret;
}
}
return kWaitStatusSuccess;
#endif
}
WaitStatus SocketImpl::ReceiveArray(WSABuf *wsabufs, int32_t count, int32_t *len, SocketFlags flags)
{
#if IL2CPP_SUPPORT_RECV_MSG
const int32_t c_flags = convert_socket_flags(flags);
if (c_flags == -1)
{
_saved_error = kWSAeopnotsupp;
return kWaitStatusFailure;
}
struct msghdr hdr = {0};
hdr.msg_iovlen = count;
hdr.msg_iov = (struct iovec*)malloc(sizeof(struct iovec) * count);
for (int32_t i = 0; i < count; ++i)
{
hdr.msg_iov[i].iov_base = wsabufs[i].buffer;
hdr.msg_iov[i].iov_len = wsabufs[i].length;
}
int32_t ret = 0;
do
{
ret = (int32_t)recvmsg(_fd, &hdr, c_flags);
}
while (ret == -1 && errno == EINTR);
if (ret == 0)
{
// See SocketImpl::ReceiveFromInternal
if (_still_readable != 1)
{
ret = -1;
errno = EINTR;
}
}
free(hdr.msg_iov);
if (ret == -1)
{
*len = 0;
StoreLastError();
return kWaitStatusFailure;
}
*len = ret;
return kWaitStatusSuccess;
#else
if (len != NULL)
{
*len = 0;
}
if (wsabufs == NULL && count > 0)
{
_saved_error = kErrorInvalidFunction;
return kWaitStatusFailure;
}
int32_t c_flags = convert_socket_flags(flags);
for (int32_t i = 0; i < count; ++i)
{
int32_t ret = 0;
do
{
ret = recvfrom(_fd, wsabufs[i].buffer, wsabufs[i].length, c_flags, NULL, NULL);
}
while (ret == EINTR);
if (ret == 0 && count > 0)
{
if (_still_readable != 1)
{
_saved_error = SocketErrnoToErrorCode(EINTR);
return kWaitStatusFailure;
}
}
if (ret == -1)
{
StoreLastError();
return kWaitStatusFailure;
}
if (len != NULL)
{
*len += ret;
}
}
return kWaitStatusSuccess;
#endif
}
WaitStatus SocketImpl::SendToInternal(struct sockaddr *sa, int32_t sa_size, const uint8_t *data, int32_t count, os::SocketFlags flags, int32_t *len)
{
int32_t c_flags = convert_socket_flags(flags);
if (c_flags == -1)
{
_saved_error = kWSAeopnotsupp;
return kWaitStatusFailure;
}
#if IL2CPP_USE_SEND_NOSIGNAL
c_flags |= MSG_NOSIGNAL;
#endif
if (!_networkAccess.RequestNetwork(_fd, sa, sa_size))
{
StoreLastError(_networkAccess.GetError());
return kWaitStatusFailure;
}
int32_t ret = 0;
do
{
ret = (int32_t)sendto(_fd, (void*)data, count, c_flags, sa, sa_size);
}
while (ret == -1 && errno == EINTR);
if (ret == -1)
{
StoreLastError();
return kWaitStatusFailure;
}
*len = ret;
return kWaitStatusSuccess;
}
WaitStatus SocketImpl::SendTo(uint32_t address, uint16_t port, const uint8_t *data, int32_t count, os::SocketFlags flags, int32_t *len)
{
*len = 0;
struct sockaddr sa = {0};
socklen_t sa_size = 0;
sockaddr_from_address(htonl(address), htons(port), &sa, &sa_size);
return SendToInternal(&sa, sa_size, data, count, flags, len);
}
WaitStatus SocketImpl::SendTo(const char *path, const uint8_t *data, int32_t count, os::SocketFlags flags, int32_t *len)
{
#if SUPPORT_UNIXSOCKETS
*len = 0;
socklen_t sa_size = 0;
struct sockaddr* sa = sockaddr_from_path(path, &sa_size);
WaitStatus status = SendToInternal(sa, sa_size, data, count, flags, len);
IL2CPP_FREE(sa);
return status;
#else
return kWaitStatusFailure;
#endif
}
utils::Expected<WaitStatus> SocketImpl::SendTo(uint8_t address[ipv6AddressSize], uint32_t scope, uint16_t port, const uint8_t *data, int32_t count, os::SocketFlags flags, int32_t *len)
{
#if IL2CPP_SUPPORT_IPV6
struct sockaddr_in6 sa = { 0 };
socklen_t sa_size = 0;
sockaddr_from_address(address, scope, htons(port), &sa, &sa_size);
return SendToInternal((sockaddr*)&sa, sa_size, data, count, flags, len);
#else
return utils::Il2CppError(utils::NotSupported, "IPv6 is not supported on this platform.");
#endif
}
WaitStatus SocketImpl::RecvFrom(uint32_t address, uint16_t port, const uint8_t *data, int32_t count, os::SocketFlags flags, int32_t *len, os::EndPointInfo &ep)
{
*len = 0;
struct sockaddr sa = {0};
socklen_t sa_size = 0;
sockaddr_from_address(htonl(address), htons(port), &sa, &sa_size);
const int32_t c_flags = convert_socket_flags(flags);
if (c_flags == -1)
{
_saved_error = kWSAeopnotsupp;
return kWaitStatusFailure;
}
const WaitStatus status = ReceiveFromInternal(data, count, c_flags, len, &sa, (int32_t*)&sa_size);
if (status != kWaitStatusSuccess)
{
ep.family = os::kAddressFamilyError;
return status;
}
if (sa_size == 0)
return kWaitStatusSuccess;
if (!socketaddr_to_endpoint_info(&sa, sa_size, ep))
{
ep.family = os::kAddressFamilyError;
_saved_error = kWSAeafnosupport;
return kWaitStatusFailure;
}
return kWaitStatusSuccess;
}
WaitStatus SocketImpl::RecvFrom(const char *path, const uint8_t *data, int32_t count, os::SocketFlags flags, int32_t *len, os::EndPointInfo &ep)
{
#if SUPPORT_UNIXSOCKETS
*len = 0;
socklen_t sa_size = 0;
struct sockaddr* sa = sockaddr_from_path(path, &sa_size);
const int32_t c_flags = convert_socket_flags(flags);
if (c_flags == -1)
{
_saved_error = kWSAeopnotsupp;
IL2CPP_FREE(sa);
return kWaitStatusFailure;
}
const WaitStatus status = ReceiveFromInternal(data, count, c_flags, len, sa, (int32_t*)&sa_size);
if (status != kWaitStatusSuccess)
{
ep.family = os::kAddressFamilyError;
IL2CPP_FREE(sa);
return kWaitStatusFailure;
}
if (sa_size == 0)
{
IL2CPP_FREE(sa);
return kWaitStatusSuccess;
}
if (!socketaddr_to_endpoint_info(sa, sa_size, ep))
{
ep.family = os::kAddressFamilyError;
_saved_error = kWSAeafnosupport;
IL2CPP_FREE(sa);
return kWaitStatusFailure;
}
IL2CPP_FREE(sa);
return kWaitStatusSuccess;
#else
return kWaitStatusFailure;
#endif
}
utils::Expected<WaitStatus> SocketImpl::RecvFrom(uint8_t address[ipv6AddressSize], uint32_t scope, uint16_t port, const uint8_t *data, int32_t count, os::SocketFlags flags, int32_t *len, os::EndPointInfo &ep)
{
#if IL2CPP_SUPPORT_IPV6
struct sockaddr_in6 sa = { 0 };
socklen_t sa_size = 0;
sockaddr_from_address(address, scope, htons(port), &sa, &sa_size);
const int32_t c_flags = convert_socket_flags(flags);
if (c_flags == -1)
{
_saved_error = kWSAeopnotsupp;
return kWaitStatusFailure;
}
const WaitStatus status = ReceiveFromInternal(data, count, c_flags, len, (sockaddr*)&sa, (int32_t*)&sa_size);
if (status != kWaitStatusSuccess)
{
ep.family = os::kAddressFamilyError;
return kWaitStatusFailure;
}
if (sa_size == 0)
return kWaitStatusSuccess;
if (!socketaddr_to_endpoint_info((sockaddr*)&sa, sa_size, ep))
{
ep.family = os::kAddressFamilyError;
_saved_error = kWSAeafnosupport;
return kWaitStatusFailure;
}
return kWaitStatusSuccess;
#else
return utils::Il2CppError(utils::NotSupported, "IPv6 is not supported on this platform.");
#endif
}
WaitStatus SocketImpl::Available(int32_t *amount)
{
// ioctl (fd, FIONREAD, XXX) returns the size of
// the UDP header as well on Darwin.
//
// Use getsockopt SO_NREAD instead to get the
// right values for TCP and UDP.
//
// ai_canonname can be null in some cases on darwin, where the runtime assumes it will
// be the value of the ip buffer.
*amount = 0;
#if IL2CPP_TARGET_DARWIN
socklen_t optlen = sizeof(int32_t);
if (getsockopt(_fd, SOL_SOCKET, SO_NREAD, amount, &optlen) == -1)
#else
if (ioctl(_fd, FIONREAD, amount) == -1)
#endif
{
StoreLastError();
return kWaitStatusFailure;
}
return kWaitStatusSuccess;
}
WaitStatus SocketImpl::Ioctl(int32_t command, const uint8_t *in_data, int32_t in_len, uint8_t *out_data, int32_t out_len, int32_t *written)
{
IL2CPP_ASSERT(command != 0xC8000006 /* SIO_GET_EXTENSION_FUNCTION_POINTER */ && "SIO_GET_EXTENSION_FUNCTION_POINTER ioctl command not supported");
if (command == 0x98000004 /* SIO_KEEPALIVE_VALS */)
{
if (in_len < 3 * sizeof(uint32_t))
{
StoreLastError();
return kWaitStatusFailure;
}
uint32_t onoff = *((uint32_t*)in_data);
int32_t ret = setsockopt(_fd, SOL_SOCKET, SO_KEEPALIVE, &onoff, sizeof(uint32_t));
if (ret < 0)
{
StoreLastError();
return kWaitStatusFailure;
}
}
else
{
uint8_t *buffer = NULL;
if (in_len > 0)
{
buffer = (uint8_t*)malloc(in_len);
memcpy(buffer, in_data, in_len);
}
const int32_t ret = ioctl(_fd, command, buffer);
if (ret == -1)
{
StoreLastError();
free(buffer);
return kWaitStatusFailure;
}
if (buffer == NULL)
{
*written = 0;
return kWaitStatusSuccess;
}
// We just copy the buffer to the out_data. Some ioctls
// don't even out_data any data, but, well ...
//
// NB: windows returns WSAEFAULT if out_len is too small
const int32_t len = (in_len > out_len) ? out_len : in_len;
if (len > 0 && out_data != NULL)
memcpy(out_data, buffer, len);
free(buffer);
*written = len;
}
return kWaitStatusSuccess;
}
#define SKIP_OPTION -2
#define INVALID_OPTION_NAME -1
static int32_t level_and_name_to_system(SocketOptionLevel level, SocketOptionName name, int32_t *system_level, int32_t *system_name)
{
switch (level)
{
case kSocketOptionLevelSocket:
*system_level = SOL_SOCKET;
switch (name)
{
// This is SO_LINGER, because the setsockopt
// internal call maps DontLinger to SO_LINGER
// with l_onoff=0
case kSocketOptionNameDontLinger:
*system_name = SO_LINGER;
break;
#ifdef SO_DEBUG
case kSocketOptionNameDebug:
*system_name = SO_DEBUG;
break;
#endif
#ifdef SO_ACCEPTCONN
case kSocketOptionNameAcceptConnection:
*system_name = SO_ACCEPTCONN;
break;
#endif
case kSocketOptionNameReuseAddress:
*system_name = SO_REUSEADDR;
break;
case kSocketOptionNameKeepAlive:
*system_name = SO_KEEPALIVE;
break;
#ifdef SO_DONTROUTE
case kSocketOptionNameDontRoute:
*system_name = SO_DONTROUTE;
break;
#endif
case kSocketOptionNameBroadcast:
*system_name = SO_BROADCAST;
break;
case kSocketOptionNameLinger:
*system_name = SO_LINGER;
break;
#ifdef SO_OOBINLINE
case kSocketOptionNameOutOfBandInline:
*system_name = SO_OOBINLINE;
break;
#endif
case kSocketOptionNameSendBuffer:
*system_name = SO_SNDBUF;
break;
case kSocketOptionNameReceiveBuffer:
*system_name = SO_RCVBUF;
break;
case kSocketOptionNameSendLowWater:
*system_name = SO_SNDLOWAT;
break;
case kSocketOptionNameReceiveLowWater:
*system_name = SO_RCVLOWAT;
break;
case kSocketOptionNameSendTimeout:
*system_name = SO_SNDTIMEO;
break;
case kSocketOptionNameReceiveTimeout:
*system_name = SO_RCVTIMEO;
break;
case kSocketOptionNameError:
*system_name = SO_ERROR;
break;
case kSocketOptionNameType:
*system_name = SO_TYPE;
break;
case kSocketOptionNameExclusiveAddressUse:
#ifdef SO_EXCLUSIVEADDRUSE
*system_name = SO_EXCLUSIVEADDRUSE;
break;
#elif SO_REUSEADDR
*system_name = SO_REUSEADDR;
break;
#endif
case kSocketOptionNameUseLoopback:
#ifdef SO_USELOOPBACK
*system_name = SO_USELOOPBACK;
break;
#endif
case kSocketOptionNameMaxConnections:
#ifdef SO_MAXCONN
*system_name = SO_MAXCONN;
break;
#elif defined(SOMAXCONN)
*system_name = SOMAXCONN;
break;
#endif
default:
return INVALID_OPTION_NAME;
}
break;
case kSocketOptionLevelIP:
#ifdef SOL_IP
*system_level = SOL_IP;
#else
*system_level = IPPROTO_IP;
#endif
switch (name)
{
#ifdef IP_OPTIONS
case kSocketOptionNameIPOptions:
*system_name = IP_OPTIONS;
break;
#endif
case kSocketOptionNameHeaderIncluded:
*system_name = IP_HDRINCL;
break;
case kSocketOptionNameTypeOfService:
*system_name = IP_TOS;
break;
case kSocketOptionNameIpTimeToLive:
*system_name = IP_TTL;
break;
case kSocketOptionNameMulticastInterface:
*system_name = IP_MULTICAST_IF;
break;
case kSocketOptionNameMulticastTimeToLive:
*system_name = IP_MULTICAST_TTL;
break;
case kSocketOptionNameMulticastLoopback:
*system_name = IP_MULTICAST_LOOP;
break;
case kSocketOptionNameAddMembership:
*system_name = IP_ADD_MEMBERSHIP;
break;
case kSocketOptionNameDropMembership:
*system_name = IP_DROP_MEMBERSHIP;
break;
#ifdef HAVE_IP_PKTINFO
case kSocketOptionNamePacketInformation:
*system_name = IP_PKTINFO;
break;
#endif
case kSocketOptionNameDontFragment:
#ifdef IP_DONTFRAGMENT
*system_name = IP_DONTFRAGMENT;
#elif IP_MTU_DISCOVER
*system_name = IP_MTU_DISCOVER;
#elif IP_DONTFRAG
*system_name = IP_DONTFRAG;
#else
return SKIP_OPTION;
#endif
break;
case kSocketOptionNameAddSourceMembership:
case kSocketOptionNameDropSourceMembership:
case kSocketOptionNameBlockSource:
case kSocketOptionNameUnblockSource:
// Can't figure out how to map these, so fall
// through
default:
return INVALID_OPTION_NAME;
}
break;
#if IL2CPP_SUPPORT_IPV6
case kSocketOptionLevelIPv6:
#ifdef SOL_IPV6
*system_level = SOL_IPV6;
#else
*system_level = IPPROTO_IPV6;
#endif
switch (name)
{
case kSocketOptionNameMulticastInterface:
*system_name = IPV6_MULTICAST_IF;
break;
case kSocketOptionNameMulticastTimeToLive:
*system_name = IPV6_MULTICAST_HOPS;
break;
case kSocketOptionNameMulticastLoopback:
*system_name = IPV6_MULTICAST_LOOP;
break;
case kSocketOptionNameAddMembership:
*system_name = IPV6_JOIN_GROUP;
break;
case kSocketOptionNameDropMembership:
*system_name = IPV6_LEAVE_GROUP;
break;
case kSocketOptionNamePacketInformation:
#ifdef HAVE_IPV6_PKTINFO
*system_name = IPV6_PKTINFO;
break;
#endif
case kSocketOptionNameIPv6Only:
#ifdef IPV6_V6ONLY
*system_name = IPV6_V6ONLY;
break;
#endif
case kSocketOptionNameHeaderIncluded:
case kSocketOptionNameIPOptions:
case kSocketOptionNameTypeOfService:
case kSocketOptionNameDontFragment:
case kSocketOptionNameAddSourceMembership:
case kSocketOptionNameDropSourceMembership:
case kSocketOptionNameBlockSource:
case kSocketOptionNameUnblockSource:
// Can't figure out how to map these, so fall
// through
default:
return INVALID_OPTION_NAME;
}
break;
#endif // IL2CPP_SUPPORT_IPV6
case kSocketOptionLevelTcp:
#ifdef SOL_TCP
*system_level = SOL_TCP;
#else
*system_level = IPPROTO_TCP;
#endif
switch (name)
{
case kSocketOptionNameNoDelay:
*system_name = TCP_NODELAY;
break;
default:
return INVALID_OPTION_NAME;
}
break;
case kSocketOptionLevelUdp:
default:
return INVALID_OPTION_NAME;
}
return 0;
}
WaitStatus SocketImpl::GetSocketOption(SocketOptionLevel level, SocketOptionName name, uint8_t *buffer, int32_t *length)
{
int32_t system_level = 0;
int32_t system_name = 0;
const int32_t o_res = level_and_name_to_system(level, name, &system_level, &system_name);
if (o_res == SKIP_OPTION)
{
*((int32_t*)buffer) = 0;
*length = sizeof(int32_t);
return kWaitStatusSuccess;
}
if (o_res == INVALID_OPTION_NAME)
{
_saved_error = kWSAenoprotoopt;
return kWaitStatusFailure;
}
struct timeval tv;
uint8_t *tmp_val = buffer;
if (system_level == SOL_SOCKET && (system_name == SO_RCVTIMEO || system_name == SO_SNDTIMEO))
{
tmp_val = (uint8_t*)&tv;
*length = sizeof(tv);
}
const int32_t ret = getsockopt(_fd, system_level, system_name, tmp_val, (socklen_t*)length);
if (ret == -1)
{
StoreLastError();
return kWaitStatusFailure;
}
if (system_level == SOL_SOCKET && (system_name == SO_RCVTIMEO || system_name == SO_SNDTIMEO))
{
// milliseconds from microseconds
*((int32_t*)buffer) = (int32_t)(tv.tv_sec * 1000 + (tv.tv_usec / 1000));
*length = sizeof(int32_t);
return kWaitStatusSuccess;
}
if (system_name == SO_ERROR)
{
if (*((int32_t*)buffer) != 0)
{
StoreLastError(*((int32_t*)buffer));
}
else
{
*((int32_t*)buffer) = _saved_error;
}
}
return kWaitStatusSuccess;
}
WaitStatus SocketImpl::GetSocketOptionFull(SocketOptionLevel level, SocketOptionName name, int32_t *first, int32_t *second)
{
int32_t system_level = 0;
int32_t system_name = 0;
#if !defined(SO_EXCLUSIVEADDRUSE) && defined(SO_REUSEADDR)
if (level == kSocketOptionLevelSocket && name == kSocketOptionNameExclusiveAddressUse)
{
system_level = SOL_SOCKET;
system_name = SO_REUSEADDR;
}
else
#endif
{
const int32_t o_res = level_and_name_to_system(level, name, &system_level, &system_name);
if (o_res == SKIP_OPTION)
{
*first = 0;
*second = 0;
return kWaitStatusSuccess;
}
if (o_res == INVALID_OPTION_NAME)
{
_saved_error = kWSAenoprotoopt;
return kWaitStatusFailure;
}
}
int32_t ret = -1;
switch (name)
{
case kSocketOptionNameLinger:
{
struct linger linger;
socklen_t lingersize = sizeof(linger);
ret = getsockopt(_fd, system_level, system_name, &linger, &lingersize);
*first = linger.l_onoff;
*second = linger.l_linger;
}
break;
case kSocketOptionNameDontLinger:
{
struct linger linger;
socklen_t lingersize = sizeof(linger);
ret = getsockopt(_fd, system_level, system_name, &linger, &lingersize);
*first = !linger.l_onoff;
}
break;
case kSocketOptionNameSendTimeout:
case kSocketOptionNameReceiveTimeout:
{
struct timeval time;
socklen_t time_size = sizeof(time);
ret = getsockopt(_fd, system_level, system_name, &time, &time_size);
// Use a 64-bit integer to avoid overflow
uint64_t timeInMilliseconds = (time.tv_sec * (uint64_t)1000) + (time.tv_usec / 1000);
// Truncate back to a 32-bit integer to return the value back to the caller.
*first = (int32_t)timeInMilliseconds;
}
break;
default:
{
socklen_t valsize = sizeof(*first);
ret = getsockopt(_fd, system_level, system_name, first, &valsize);
}
break;
}
if (ret == -1)
{
StoreLastError();
return kWaitStatusFailure;
}
#if !defined(SO_EXCLUSIVEADDRUSE) && defined(SO_REUSEADDR)
if (level == kSocketOptionLevelSocket && name == kSocketOptionNameExclusiveAddressUse)
*first = *first ? 0 : 1;
#endif
return kWaitStatusSuccess;
}
WaitStatus SocketImpl::Poll(std::vector<PollRequest> &requests, int32_t count, int32_t timeout, int32_t *result, int32_t *error)
{
const int32_t n_fd = count;
pollfd *p_fd = (pollfd*)calloc(n_fd, sizeof(pollfd));
for (int32_t i = 0; i < n_fd; ++i)
{
if (requests[i].fd == -1)
{
p_fd[i].fd = -1;
p_fd[i].events = kPollFlagsNone;
p_fd[i].revents = kPollFlagsNone;
}
else
{
p_fd[i].fd = requests[i].fd;
p_fd[i].events = posix::PollFlagsToPollEvents(requests[i].events);
p_fd[i].revents = kPollFlagsNone;
}
}
int32_t ret = os::posix::Poll(p_fd, n_fd, timeout);
*result = ret;
if (ret == -1)
{
free(p_fd);
*error = SocketErrnoToErrorCode(errno);
return kWaitStatusFailure;
}
if (ret == 0)
{
free(p_fd);
return kWaitStatusSuccess;
}
for (int32_t i = 0; i < n_fd; ++i)
{
requests[i].revents = posix::PollEventsToPollFlags(p_fd[i].revents);
}
free(p_fd);
return kWaitStatusSuccess;
}
WaitStatus SocketImpl::Poll(std::vector<PollRequest> &requests, int32_t timeout, int32_t *result, int32_t *error)
{
return Poll(requests, (int32_t)requests.size(), timeout, result, error);
}
WaitStatus SocketImpl::Poll(PollRequest& request, int32_t timeout, int32_t *result, int32_t *error)
{
std::vector<PollRequest> requests;
requests.push_back(request);
return Poll(requests, 1, timeout, result, error);
}
WaitStatus SocketImpl::SetSocketOption(SocketOptionLevel level, SocketOptionName name, int32_t value)
{
int32_t system_level = 0;
int32_t system_name = 0;
const int32_t o_res = level_and_name_to_system(level, name, &system_level, &system_name);
if (o_res == SKIP_OPTION)
{
return kWaitStatusSuccess;
}
if (o_res == INVALID_OPTION_NAME)
{
_saved_error = kWSAenoprotoopt;
return kWaitStatusFailure;
}
struct linger linger;
WaitStatus ret = kWaitStatusFailure;
switch (name)
{
case kSocketOptionNameDontLinger:
linger.l_onoff = !value;
linger.l_linger = 0;
ret = SetSocketOptionInternal(system_level, system_name, &linger, sizeof(linger));
break;
case kSocketOptionNameDontFragment:
#ifdef IP_PMTUDISC_DO
// Fiddle with the value slightly if we're turning DF on
if (value == 1)
value = IP_PMTUDISC_DO;
#endif
ret = SetSocketOptionInternal(system_level, system_name, (char*)&value, sizeof(value));
break;
default:
ret = SetSocketOptionInternal(system_level, system_name, (char*)&value, sizeof(value));
break;
}
return ret;
}
WaitStatus SocketImpl::SetSocketOptionLinger(SocketOptionLevel level, SocketOptionName name, bool enabled, int32_t seconds)
{
int32_t system_level = 0;
int32_t system_name = 0;
const int32_t o_res = level_and_name_to_system(level, name, &system_level, &system_name);
if (o_res == SKIP_OPTION)
{
return kWaitStatusSuccess;
}
if (o_res == INVALID_OPTION_NAME)
{
_saved_error = kWSAenoprotoopt;
return kWaitStatusFailure;
}
struct linger linger;
linger.l_onoff = enabled;
linger.l_linger = seconds;
return SetSocketOptionInternal(system_level, system_name, &linger, sizeof(linger));
}
WaitStatus SocketImpl::SetSocketOptionArray(SocketOptionLevel level, SocketOptionName name, const uint8_t *buffer, int32_t length)
{
int32_t system_level = 0;
int32_t system_name = 0;
const int32_t o_res = level_and_name_to_system(level, name, &system_level, &system_name);
if (o_res == SKIP_OPTION)
{
return kWaitStatusSuccess;
}
if (o_res == INVALID_OPTION_NAME)
{
_saved_error = kWSAenoprotoopt;
return kWaitStatusFailure;
}
struct linger linger;
WaitStatus ret = kWaitStatusFailure;
switch (name)
{
case kSocketOptionNameDontLinger:
if (length == 1)
{
linger.l_linger = 0;
linger.l_onoff = (*((char*)buffer)) ? 0 : 1;
ret = SetSocketOptionInternal(system_level, system_name, &linger, sizeof(linger));
}
else
{
_saved_error = kWSAeinval;
return kWaitStatusFailure;
}
break;
default:
ret = SetSocketOptionInternal(system_level, system_name, buffer, length);
break;
}
return ret;
}
WaitStatus SocketImpl::SetSocketOptionMembership(SocketOptionLevel level, SocketOptionName name, uint32_t group_address, uint32_t local_address)
{
int32_t system_level = 0;
int32_t system_name = 0;
const int32_t o_res = level_and_name_to_system(level, name, &system_level, &system_name);
if (o_res == SKIP_OPTION)
{
return kWaitStatusSuccess;
}
if (o_res == INVALID_OPTION_NAME)
{
_saved_error = kWSAenoprotoopt;
return kWaitStatusFailure;
}
struct ip_mreqn mreq = {{0}};
mreq.imr_multiaddr.s_addr = group_address;
mreq.imr_address.s_addr = local_address;
return SetSocketOptionInternal(system_level, system_name, &mreq, sizeof(mreq));
}
#if IL2CPP_TARGET_DARWIN || IL2CPP_TARGET_LINUX
#include <sys/types.h>
#include <ifaddrs.h>
#include <sys/socket.h>
#include <net/if.h>
static int get_local_interface_id(int family)
{
struct ifaddrs *ifap = NULL, *ptr;
int idx = 0;
if (getifaddrs(&ifap))
return 0;
for (ptr = ifap; ptr; ptr = ptr->ifa_next)
{
if (!ptr->ifa_addr || !ptr->ifa_name)
continue;
if (ptr->ifa_addr->sa_family != family)
continue;
if ((ptr->ifa_flags & IFF_LOOPBACK) != 0)
continue;
if ((ptr->ifa_flags & IFF_MULTICAST) == 0)
continue;
idx = if_nametoindex(ptr->ifa_name);
break;
}
freeifaddrs(ifap);
return idx;
}
#endif // IL2CPP_TARGET_DARWIN
#if IL2CPP_SUPPORT_IPV6
WaitStatus SocketImpl::SetSocketOptionMembership(SocketOptionLevel level, SocketOptionName name, IPv6Address ipv6, uint64_t interfaceOffset)
{
int32_t system_level = 0;
int32_t system_name = 0;
const int32_t o_res = level_and_name_to_system(level, name, &system_level, &system_name);
if (o_res == SKIP_OPTION)
{
return kWaitStatusSuccess;
}
if (o_res == INVALID_OPTION_NAME)
{
_saved_error = kWSAenoprotoopt;
return kWaitStatusFailure;
}
struct ipv6_mreq mreq6 = {{0}};
struct in6_addr in6addr;
for (int i = 0; i < 16; ++i)
in6addr.s6_addr[i] = ipv6.addr[i];
mreq6.ipv6mr_multiaddr = in6addr;
#if IL2CPP_TARGET_DARWIN || IL2CPP_TARGET_LINUX
if (interfaceOffset == 0)
interfaceOffset = get_local_interface_id(AF_INET6);
#endif
mreq6.ipv6mr_interface = interfaceOffset;
return SetSocketOptionInternal(system_level, system_name, &mreq6, sizeof(mreq6));
}
#endif
WaitStatus SocketImpl::SetSocketOptionInternal(int32_t level, int32_t name, const void *value, int32_t len)
{
const void *real_val = value;
struct timeval tv;
if (level == SOL_SOCKET && (name == SO_RCVTIMEO || name == SO_SNDTIMEO))
{
const int32_t ms = *((int32_t*)value);
tv.tv_sec = ms / 1000;
tv.tv_usec = (ms % 1000) * 1000;
real_val = &tv;
len = sizeof(tv);
}
const int32_t ret = setsockopt(_fd, level, name, real_val, (socklen_t)len);
if (ret == -1)
{
StoreLastError();
return kWaitStatusFailure;
}
#if defined(SO_REUSEPORT)
// BSD's and MacOS X multicast sockets also need SO_REUSEPORT when SO_REUSEADDR is requested.
if (level == SOL_SOCKET && name == SO_REUSEADDR)
{
int32_t type;
socklen_t type_len = sizeof(type);
if (!getsockopt(_fd, level, SO_TYPE, &type, &type_len))
{
if (type == SOCK_DGRAM)
setsockopt(_fd, level, SO_REUSEPORT, real_val, len);
}
}
#endif
return kWaitStatusSuccess;
}
#if IL2CPP_SUPPORT_IPV6_SUPPORT_QUERY
bool SocketImpl::IsIPv6Supported()
{
ifaddrs* interfaces;
if (getifaddrs(&interfaces))
return false;
bool ipv6IsSupported = false;
for (ifaddrs* iface = interfaces; iface != NULL; iface = iface->ifa_next)
{
if (iface->ifa_addr && iface->ifa_addr->sa_family == AF_INET6)
{
ipv6IsSupported = true;
break;
}
}
freeifaddrs(interfaces);
return ipv6IsSupported;
}
#endif
WaitStatus SocketImpl::SendFile(const char *filename, TransmitFileBuffers *buffers, TransmitFileOptions options)
{
#if IL2CPP_SUPPORT_SEND_FILE
int32_t file = open(filename, O_RDONLY);
if (file == -1)
{
StoreLastError();
return kWaitStatusFailure;
}
int32_t ret;
// Write the header
if (buffers != NULL && buffers->head != NULL && buffers->head_length > 0)
{
do
{
ret = (int32_t)send(_fd, (void*)buffers->head, buffers->head_length, 0);
}
while (ret == -1 && errno == EINTR);
if (ret == -1)
{
StoreLastError();
SOCK_CLOSE(file);
return kWaitStatusFailure;
}
}
struct stat statbuf;
ret = fstat(file, &statbuf);
if (ret == -1)
{
StoreLastError();
return kWaitStatusFailure;
}
do
{
#if IL2CPP_TARGET_DARWIN
ret = sendfile(file, _fd, 0, &statbuf.st_size, NULL, 0);
#else
ret = sendfile(_fd, file, NULL, statbuf.st_size);
#endif
}
while (ret != -1 && (errno == EINTR || errno == EAGAIN));
if (ret == -1)
{
StoreLastError();
SOCK_CLOSE(file);
return kWaitStatusFailure;
}
// Write the tail
if (buffers != NULL && buffers->tail != NULL && buffers->tail_length > 0)
{
do
{
ret = (int32_t)send(_fd, (void*)buffers->tail, buffers->tail_length, 0);
}
while (ret == -1 && errno == EINTR);
if (ret == -1)
{
StoreLastError();
SOCK_CLOSE(file);
return kWaitStatusFailure;
}
}
if (SOCK_CLOSE(file) == -1)
{
StoreLastError();
return kWaitStatusFailure;
}
return kWaitStatusSuccess;
#else
return kWaitStatusFailure;
#endif
}
}
}
#endif