#pragma once #include #include #include "il2cpp-config.h" #include "os/ErrorCodes.h" #include "os/Atomic.h" #include "os/Mutex.h" #include "os/WaitStatus.h" #include "utils/Expected.h" #include "utils/NonCopyable.h" namespace il2cpp { namespace os { class SocketImpl; enum AddressFamily { kAddressFamilyError = -1, kAddressFamilyUnspecified = 0,// AF_UNSPEC kAddressFamilyUnix = 1,// AF_UNIX kAddressFamilyInterNetwork = 2,// AF_INET kAddressFamilyIpx = 3,// AF_IPX kAddressFamilySna = 4,// AF_SNA kAddressFamilyDecNet = 5,// AF_DECnet kAddressFamilyAppleTalk = 6,// AF_APPLETALK kAddressFamilyInterNetworkV6 = 7,// AF_INET6 kAddressFamilyIrda = 8,// AF_IRDA }; enum SocketType { kSocketTypeError = -1, kSocketTypeStream = 0,// SOCK_STREAM kSocketTypeDgram = 1,// SOCK_DGRAM kSocketTypeRaw = 2,// SOCK_RAW kSocketTypeRdm = 3,// SOCK_RDM kSocketTypeSeqpacket = 4,// SOCK_SEQPACKET }; enum ProtocolType { kProtocolTypeUnknown = -1, kProtocolTypeIP = 0, kProtocolTypeIcmp = 1, kProtocolTypeIgmp = 2, kProtocolTypeGgp = 3, kProtocolTypeTcp = 6, kProtocolTypePup = 12, kProtocolTypeUdp = 17, kProtocolTypeIdp = 22, kProtocolTypeND = 77, kProtocolTypeRaw = 255, kProtocolTypeUnspecified = 0, kProtocolTypeIpx = 1000, kProtocolTypeSpx = 1256, kProtocolTypeSpxII = 1257, // #if NET_1_1 kProtocolTypeIPv6 = 41, // #endif // #if NET_2_0 kProtocolTypeIPv4 = 4, kProtocolTypeIPv6RoutingHeader = 43, kProtocolTypeIPv6FragmentHeader = 44, kProtocolTypeIPSecEncapsulatingSecurityPayload = 50, kProtocolTypeIPSecAuthenticationHeader = 51, kProtocolTypeIcmpV6 = 58, kProtocolTypeIPv6NoNextHeader = 59, kProtocolTypeIPv6DestinationOptions = 60, kProtocolTypeIPv6HopByHopOptions = 0, // #endif }; enum SocketFlags { kSocketFlagsNone = 0x00000000, kSocketFlagsOutOfBand = 0x00000001, kSocketFlagsPeek = 0x00000002, kSocketFlagsDontRoute = 0x00000004, kSocketFlagsMaxIOVectorLength = 0x00000010, // #if NET_2_0 kSocketFlagsTruncated = 0x00000100, kSocketFlagsControlDataTruncated = 0x00000200, kSocketFlagsBroadcast = 0x00000400, kSocketFlagsMulticast = 0x00000800, // #endif kSocketFlagsPartial = 0x00008000, }; enum SocketOptionLevel { kSocketOptionLevelSocket = 65535, kSocketOptionLevelIP = 0, kSocketOptionLevelTcp = 6, kSocketOptionLevelUdp = 17, //#if NET_1_1 kSocketOptionLevelIPv6 = 41, //#endif }; enum SocketOptionName { kSocketOptionNameDebug = 1, kSocketOptionNameAcceptConnection = 2, kSocketOptionNameReuseAddress = 4, kSocketOptionNameKeepAlive = 8, kSocketOptionNameDontRoute = 16, kSocketOptionNameBroadcast = 32, kSocketOptionNameUseLoopback = 64, kSocketOptionNameLinger = 128, kSocketOptionNameOutOfBandInline = 256, kSocketOptionNameDontLinger = -129, kSocketOptionNameExclusiveAddressUse = -5, kSocketOptionNameSendBuffer = 4097, kSocketOptionNameReceiveBuffer = 4098, kSocketOptionNameSendLowWater = 4099, kSocketOptionNameReceiveLowWater = 4100, kSocketOptionNameSendTimeout = 4101, kSocketOptionNameReceiveTimeout = 4102, kSocketOptionNameError = 4103, kSocketOptionNameType = 4104, kSocketOptionNameMaxConnections = 2147483647, kSocketOptionNameIPOptions = 1, kSocketOptionNameHeaderIncluded = 2, kSocketOptionNameTypeOfService = 3, kSocketOptionNameIpTimeToLive = 4, kSocketOptionNameMulticastInterface = 9, kSocketOptionNameMulticastTimeToLive = 10, kSocketOptionNameMulticastLoopback = 11, kSocketOptionNameAddMembership = 12, kSocketOptionNameDropMembership = 13, kSocketOptionNameDontFragment = 14, kSocketOptionNameAddSourceMembership = 15, kSocketOptionNameDropSourceMembership = 16, kSocketOptionNameBlockSource = 17, kSocketOptionNameUnblockSource = 18, kSocketOptionNamePacketInformation = 19, kSocketOptionNameNoDelay = 1, kSocketOptionNameBsdUrgent = 2, kSocketOptionNameExpedited = 2, kSocketOptionNameNoChecksum = 1, kSocketOptionNameChecksumCoverage = 20, kSocketOptionNameIPv6Only = 27, // #if NET_2_0 kSocketOptionNameHopLimit = 21, kSocketOptionNameUpdateAcceptContext = 28683, kSocketOptionNameUpdateConnectContext = 28688, // #endif }; enum PollFlags { kPollFlagsNone = 0, kPollFlagsIn = 1, kPollFlagsPri = 2, kPollFlagsOut = 4, kPollFlagsErr = 8, kPollFlagsHup = 0x10, kPollFlagsNVal = 0x20, kPollFlagsAny = 0xffffffff }; enum SocketError { kInterrupted = 4,// EINTR on POSIX and WSAEINTR on Windows kInvalidHandle = 9 // EBADF on POSIX and WSAEBADF on Windows }; inline void operator|=(PollFlags& left, PollFlags right) { left = static_cast(static_cast(left) | static_cast(right)); } enum TransmitFileOptions { kTransmitFileOptionsUseDefaultWorkerThread = 0x00000000, kTransmitFileOptionsDisconnect = 0x00000001, kTransmitFileOptionsReuseSocket = 0x00000002, kTransmitFileOptionsWriteBehind = 0x00000004, kTransmitFileOptionsUseSystemThread = 0x00000010, kTransmitFileOptionsUseKernelApc = 0x00000020, }; class Socket; struct PollRequest { PollRequest() : fd(-1) , events(kPollFlagsNone) , revents(kPollFlagsNone) {} PollRequest(int64_t value) : fd(value) , events(kPollFlagsNone) , revents(kPollFlagsNone) {} int64_t fd; PollFlags events; PollFlags revents; }; #if IL2CPP_SUPPORT_IPV6 struct IPv6Address { uint8_t addr[16]; }; #endif // TODO: this should really be UNIX_PATH_MAX or SUN_LEN(n) #define END_POINT_MAX_PATH_LEN 255 #if IL2CPP_COMPILER_MSVC #pragma warning( push ) #pragma warning( disable : 4200 ) #endif struct EndPointInfo { AddressFamily family; union { struct { uint32_t port; uint32_t address; } inet; char path[END_POINT_MAX_PATH_LEN]; uint8_t raw[IL2CPP_ZERO_LEN_ARRAY]; } data; }; #if IL2CPP_COMPILER_MSVC #pragma warning( pop ) #endif // NOTE(gab): this must be binary compatible with Windows's WSABUF struct WSABuf { uint32_t length; void *buffer; }; // NOTE(gab): this must be binary compatible with Window's TRANSMIT_FILE_BUFFERS struct TransmitFileBuffers { void *head; uint32_t head_length; void *tail; uint32_t tail_length; }; // Note: this callback can be invoked by the os-specific implementation when an // interrupt is received or when the native code is looping in a potentially long // loop. // If the callback retun false, the executiong of the os-specific method is // gracefully interrupted, and an error is supposed to be returned by the // os-specific implementation. // The callback is allowed to throw exceptions (for example a ThreadAborted exception): // in this case, it is up to the os-specific implementation to properly deal with // cleaning up the temporarely allocated memory (if any). typedef bool (*ThreadStatusCallback)(); /// Sockets should generally be referenced through SocketHandles for thread-safety. /// Handles are stored in a table and can be safely used even when the socket has already /// been deleted. typedef intptr_t SocketHandle; class Socket : public il2cpp::utils::NonCopyable { public: Socket(ThreadStatusCallback thread_status_callback); ~Socket(); // Note: this Create is only used internally WaitStatus Create(int64_t fd, int32_t family, int32_t type, int32_t protocol); WaitStatus Create(AddressFamily family, SocketType type, ProtocolType protocol); bool IsClosed(); void Close(); int64_t GetDescriptor(); ErrorCode GetLastError() const; WaitStatus SetBlocking(bool blocking); WaitStatus Listen(int32_t blacklog); WaitStatus Bind(const char *path); WaitStatus Bind(uint32_t address, uint16_t port); WaitStatus Bind(const char *address, uint16_t port); utils::Expected Bind(uint8_t address[ipv6AddressSize], uint32_t scope, uint16_t port); WaitStatus Connect(const char *path); WaitStatus Connect(uint32_t address, uint16_t port); utils::Expected Connect(uint8_t address[ipv6AddressSize], uint32_t scope, uint16_t port); WaitStatus Disconnect(bool reuse); WaitStatus Shutdown(int32_t how); WaitStatus GetLocalEndPointInfo(EndPointInfo &info); WaitStatus GetRemoteEndPointInfo(EndPointInfo &info); WaitStatus Receive(const uint8_t *data, int32_t count, os::SocketFlags flags, int32_t *len); WaitStatus Send(const uint8_t *data, int32_t count, os::SocketFlags flags, int32_t *len); WaitStatus SendArray(WSABuf *wsabufs, int32_t count, int32_t *sent, SocketFlags c_flags); WaitStatus ReceiveArray(WSABuf *wsabufs, int32_t count, int32_t *len, SocketFlags c_flags); WaitStatus SendTo(uint32_t address, uint16_t port, const uint8_t *data, int32_t count, os::SocketFlags flags, int32_t *len); utils::Expected SendTo(const char *path, const uint8_t *data, int32_t count, os::SocketFlags flags, int32_t *len); utils::Expected SendTo(uint8_t address[ipv6AddressSize], uint32_t scope, uint16_t port, const uint8_t *data, int32_t count, os::SocketFlags flags, int32_t *len); WaitStatus RecvFrom(uint32_t address, uint16_t port, const uint8_t *data, int32_t count, os::SocketFlags flags, int32_t *len, os::EndPointInfo &ep); utils::Expected RecvFrom(const char *path, const uint8_t *data, int32_t count, os::SocketFlags flags, int32_t *len, os::EndPointInfo &ep); utils::Expected 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); WaitStatus Available(int32_t *amount); WaitStatus Accept(Socket **socket); WaitStatus Ioctl(int32_t command, const uint8_t *in_data, int32_t in_len, uint8_t *out_data, int32_t out_len, int32_t *written); WaitStatus GetSocketOption(SocketOptionLevel level, SocketOptionName name, uint8_t *buffer, int32_t *length); WaitStatus GetSocketOptionFull(SocketOptionLevel level, SocketOptionName name, int32_t *first, int32_t *second); WaitStatus SetSocketOption(SocketOptionLevel level, SocketOptionName name, int32_t value); WaitStatus SetSocketOptionLinger(SocketOptionLevel level, SocketOptionName name, bool enabled, int32_t seconds); WaitStatus SetSocketOptionArray(SocketOptionLevel level, SocketOptionName name, const uint8_t *buffer, int32_t length); WaitStatus SetSocketOptionMembership(SocketOptionLevel level, SocketOptionName name, uint32_t group_address, uint32_t local_address); #if IL2CPP_SUPPORT_IPV6 WaitStatus SetSocketOptionMembership(SocketOptionLevel level, SocketOptionName name, IPv6Address ipv6, uint64_t interfaceOffset); #endif WaitStatus SendFile(const char *filename, TransmitFileBuffers *buffers, TransmitFileOptions options); #if IL2CPP_SUPPORT_IPV6_SUPPORT_QUERY static bool IsIPv6Supported(); #endif static WaitStatus Poll(std::vector &requests, int32_t count, int32_t timeout, int32_t *result, int32_t *error); static WaitStatus Poll(std::vector &requests, int32_t timeout, int32_t *result, int32_t *error); static WaitStatus Poll(PollRequest &request, int32_t timeout, int32_t *result, int32_t *error); static WaitStatus GetHostName(std::string &name); static WaitStatus GetHostByName(const std::string &host, std::string &name, std::vector &aliases, std::vector &addresses); // The pointers in addr_list are allocated with the il2cpp::utils::Memory::Malloc method. They should he freed by the caller using il2cpp::utils::Memory::Free. static WaitStatus GetHostByName(const std::string &host, std::string &name, int32_t &family, std::vector &aliases, std::vector &addr_list, int32_t &addr_size); static WaitStatus GetHostByAddr(const std::string &address, std::string &name, std::vector &aliases, std::vector &addr_list); static void Startup(); static void Cleanup(); private: SocketImpl* m_Socket; friend Socket* AcquireSocketHandle(SocketHandle handle); friend void ReleaseSocketHandle(SocketHandle handle, Socket* socketToRelease, bool forceTableRemove); uint32_t m_RefCount; }; enum { kInvalidSocketHandle = -1 }; SocketHandle CreateSocketHandle(Socket* socket); Socket* AcquireSocketHandle(SocketHandle handle); void ReleaseSocketHandle(SocketHandle handle, Socket* socketToRelease, bool forceTableRemove = false); inline SocketHandle PointerToSocketHandle(void* ptr) { // Double cast to avoid warnings. return static_cast(reinterpret_cast(ptr)); } /// Helper to automatically acquire and release a Socket within a scope. struct SocketHandleWrapper { SocketHandleWrapper() : m_Handle(kInvalidSocketHandle) , m_Socket(NULL) {} SocketHandleWrapper(SocketHandle handle) : m_Handle(handle) { m_Socket = AcquireSocketHandle(handle); } SocketHandleWrapper(const SocketHandleWrapper& other) { m_Handle = other.m_Handle; if (m_Handle != kInvalidSocketHandle) m_Socket = AcquireSocketHandle(m_Handle); else m_Socket = NULL; } ~SocketHandleWrapper() { Release(); } void Acquire(SocketHandle handle) { Release(); m_Handle = handle; m_Socket = AcquireSocketHandle(handle); } void Release() { if (m_Socket) ReleaseSocketHandle(m_Handle, m_Socket); m_Socket = NULL; m_Handle = kInvalidSocketHandle; } bool IsValid() const { return (m_Socket != NULL); } SocketHandle GetHandle() const { return m_Handle; } Socket* GetSocket() const { return m_Socket; } Socket* operator->() const { return GetSocket(); } SocketHandleWrapper& operator=(const SocketHandleWrapper& other) { Acquire(other.GetHandle()); return *this; } private: SocketHandle m_Handle; Socket* m_Socket; }; } }