#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 #include #include #include #include #include #include #include #include #include #include #if SUPPORT_UNIXSOCKETS #include #endif #include #include #if IL2CPP_SUPPORT_SEND_FILE && (IL2CPP_TARGET_LINUX || IL2CPP_TARGET_ANDROID) #include #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 &aliases, std::vector &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 &aliases, std::vector &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 &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 &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 &aliases, std::vector &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 &aliases, std::vector &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& addresses) { for (std::vector::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 &aliases, std::vector &addr_list, int32_t &addr_size) { std::vector 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::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::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 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 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 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 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 &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 &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 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 #include #include #include 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