#include "il2cpp-config.h" #include "icalls/System/System.Net.Sockets/Socket.h" #include "il2cpp-class-internals.h" #include "gc/WriteBarrier.h" #include "os/Socket.h" #include "os/Mutex.h" #include "os/Thread.h" #include "utils/StringUtils.h" #include "vm/Array.h" #include "vm/Assembly.h" #include "vm/Class.h" #include "vm/Exception.h" #include "vm/Field.h" #include "vm/Object.h" #include "vm/String.h" #include "vm/Thread.h" #include "Baselib.h" #include "Cpp/ReentrantLock.h" namespace il2cpp { namespace icalls { namespace System { namespace System { namespace Net { namespace Sockets { static os::AddressFamily convert_address_family(AddressFamily family) { switch (family) { case kAddressFamilyUnknown: case kAddressFamilyImpLink: case kAddressFamilyPup: case kAddressFamilyChaos: case kAddressFamilyIso: case kAddressFamilyEcma: case kAddressFamilyDataKit: case kAddressFamilyCcitt: case kAddressFamilyDataLink: case kAddressFamilyLat: case kAddressFamilyHyperChannel: case kAddressFamilyNetBios: case kAddressFamilyVoiceView: case kAddressFamilyFireFox: case kAddressFamilyBanyan: case kAddressFamilyAtm: case kAddressFamilyCluster: case kAddressFamilyIeee12844: case kAddressFamilyNetworkDesigners: // Unsupported return os::kAddressFamilyError; case kAddressFamilyUnspecified: return os::kAddressFamilyUnspecified; case kAddressFamilyUnix: return os::kAddressFamilyUnix; case kAddressFamilyInterNetwork: return os::kAddressFamilyInterNetwork; case kAddressFamilyIpx: return os::kAddressFamilyIpx; case kAddressFamilySna: return os::kAddressFamilySna; case kAddressFamilyDecNet: return os::kAddressFamilyDecNet; case kAddressFamilyAppleTalk: return os::kAddressFamilyAppleTalk; case kAddressFamilyInterNetworkV6: return os::kAddressFamilyInterNetworkV6; case kAddressFamilyIrda: return os::kAddressFamilyIrda; default: return os::kAddressFamilyError; } return os::kAddressFamilyError; } static os::SocketType convert_socket_type(SocketType type) { switch (type) { case kSocketTypeStream: return os::kSocketTypeStream; case kSocketTypeDgram: return os::kSocketTypeDgram; case kSocketTypeRaw: return os::kSocketTypeRaw; case kSocketTypeRdm: return os::kSocketTypeRdm; case kSocketTypeSeqpacket: return os::kSocketTypeSeqpacket; default: return os::kSocketTypeError; } return os::kSocketTypeError; } static os::ProtocolType 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: case kProtocolTypeIcmpV6: // In this case the enum values map exactly. return (os::ProtocolType)protocol; case kProtocolTypeND: case kProtocolTypeRaw: case kProtocolTypeIpx: case kProtocolTypeSpx: case kProtocolTypeSpxII: case kProtocolTypeUnknown: // Everything else in unsupported return os::kProtocolTypeUnknown; default: return os::kProtocolTypeUnknown; } return os::kProtocolTypeUnknown; } static AddressFamily convert_from_os_address_family(os::AddressFamily family) { switch (family) { case os::kAddressFamilyUnspecified: return kAddressFamilyUnspecified; case os::kAddressFamilyUnix: return kAddressFamilyUnix; case os::kAddressFamilyInterNetwork: return kAddressFamilyInterNetwork; // #ifdef AF_IPX case os::kAddressFamilyIpx: return kAddressFamilyIpx; // #endif case os::kAddressFamilySna: return kAddressFamilySna; case os::kAddressFamilyDecNet: return kAddressFamilyDecNet; case os::kAddressFamilyAppleTalk: return kAddressFamilyAppleTalk; // #ifdef AF_INET6 case os::kAddressFamilyInterNetworkV6: return kAddressFamilyInterNetworkV6; // #endif // #ifdef AF_IRDA case os::kAddressFamilyIrda: return kAddressFamilyIrda; // #endif default: break; } return kAddressFamilyUnknown; } static os::SocketFlags convert_socket_flags(SocketFlags flags) { return (os::SocketFlags)flags; } static Il2CppSocketAddress* end_point_info_to_socket_address(const os::EndPointInfo &info) { static Il2CppClass *System_Net_SocketAddress = NULL; Il2CppSocketAddress *socket_address = NULL; if (!System_Net_SocketAddress) { System_Net_SocketAddress = vm::Class::FromName( vm::Assembly::GetImage( vm::Assembly::Load("System.dll")), "System.Net", "SocketAddress"); } socket_address = (Il2CppSocketAddress*)vm::Object::New(System_Net_SocketAddress); const AddressFamily family = convert_from_os_address_family(info.family); if (info.family == os::kAddressFamilyInterNetwork) { socket_address->m_Size = 8; IL2CPP_OBJECT_SETREF(socket_address, data, vm::Array::New(il2cpp_defaults.byte_class, 8)); const uint16_t port = info.data.inet.port; const uint32_t address = info.data.inet.address; il2cpp_array_set(socket_address->data, uint8_t, 0, (family >> 0) & 0xFF); il2cpp_array_set(socket_address->data, uint8_t, 1, (family >> 8) & 0xFF); il2cpp_array_set(socket_address->data, uint8_t, 2, (port >> 8) & 0xFF); il2cpp_array_set(socket_address->data, uint8_t, 3, (port >> 0) & 0xFF); il2cpp_array_set(socket_address->data, uint8_t, 4, (address >> 24) & 0xFF); il2cpp_array_set(socket_address->data, uint8_t, 5, (address >> 16) & 0xFF); il2cpp_array_set(socket_address->data, uint8_t, 6, (address >> 8) & 0xFF); il2cpp_array_set(socket_address->data, uint8_t, 7, (address >> 0) & 0xFF); } else if (info.family == os::kAddressFamilyUnix) { const int32_t path_len = (int32_t)strlen(info.data.path); socket_address->m_Size = 3 + path_len; IL2CPP_OBJECT_SETREF(socket_address, data, vm::Array::New(il2cpp_defaults.byte_class, 3 + path_len)); il2cpp_array_set(socket_address->data, uint8_t, 0, (family >> 0) & 0xFF); il2cpp_array_set(socket_address->data, uint8_t, 1, (family >> 8) & 0xFF); for (int32_t i = 0; i <= path_len; i++) il2cpp_array_set(socket_address->data, uint8_t, i + 2, info.data.path[i]); il2cpp_array_set(socket_address->data, uint8_t, 2 + path_len, 0); } else if (info.family == os::kAddressFamilyInterNetworkV6) { socket_address->m_Size = 28; IL2CPP_OBJECT_SETREF(socket_address, data, vm::Array::New(il2cpp_defaults.byte_class, 28)); il2cpp_array_set(socket_address->data, uint8_t, 0, (family >> 0) & 0xFF); il2cpp_array_set(socket_address->data, uint8_t, 1, (family >> 8) & 0xFF); // Note that we start at the 3rd byte in both the managed array, where the first // two bytes are the family, set just above this. We also start at the third byte // in the info.data.raw array, as the first two bytes are unused and garbage data. for (int i = 2; i < 28; ++i) il2cpp_array_set(socket_address->data, uint8_t, i, info.data.raw[i]); } else { // Not supported return NULL; } return socket_address; } static bool check_thread_status() { static baselib::ReentrantLock _mutex; os::FastAutoLock lock(&_mutex); Il2CppThread *current_thread = vm::Thread::Current(); const vm::ThreadState state = vm::Thread::GetState(current_thread); if (state & vm::kThreadStateAbortRequested) return false; if (state & vm::kThreadStateSuspendRequested) { IL2CPP_ASSERT(0 && "kThreadStateSuspendRequested not supported yet!"); return true; } if (state & vm::kThreadStateStopRequested) return false; if (current_thread->GetInternalThread()->interruption_requested) { IL2CPP_ASSERT(0 && "thread->interruption_requested not supported yet!"); return false; } return true; } /// Acquire os::SocketHandle in IntPtr "socket" for the current scope. #define AUTO_ACQUIRE_SOCKET \ os::SocketHandleWrapper socketHandle (os::PointerToSocketHandle (reinterpret_cast(socket))) #define RETURN_IF_SOCKET_IS_INVALID(...) \ if (!socketHandle.IsValid ()) \ { \ *error = os::kErrorCodeInvalidHandle; \ return __VA_ARGS__; \ } intptr_t Socket::Accept_icall(intptr_t socket, int32_t* error, bool blocking) { *error = 0; os::Socket *new_sock = NULL; AUTO_ACQUIRE_SOCKET; if (socketHandle.IsValid()) { const os::WaitStatus status = socketHandle->Accept(&new_sock); if (status == kWaitStatusFailure) *error = socketHandle->GetLastError(); } else { *error = os::kErrorCodeInvalidHandle; } intptr_t ret; if (new_sock) ret = static_cast(os::CreateSocketHandle(new_sock)); else ret = static_cast(os::kInvalidSocketHandle); return ret; } bool Socket::SupportsPortReuse(int32_t proto) { IL2CPP_NOT_IMPLEMENTED_ICALL(Socket::SupportsPortReuse); return false; } int32_t Socket::Available_icall(intptr_t socket, int32_t* error) { *error = 0; AUTO_ACQUIRE_SOCKET; RETURN_IF_SOCKET_IS_INVALID(0); int32_t available = 0; const os::WaitStatus status = socketHandle->Available(&available); if (status == kWaitStatusFailure) { *error = socketHandle->GetLastError(); return 0; } return available; } void UnpackIPv6AddressFromBuffer(const uint8_t* buffer, int32_t length, uint16_t* port, uint8_t* address, uint32_t* scope) { if (length < 28) { vm::Exception::Raise(vm::Exception::GetSystemException()); return; } *port = ((buffer[2] << 8) | buffer[3]); for (int i = 0; i < ipv6AddressSize; i++) address[i] = buffer[i + 8]; *scope = (uint32_t)((buffer[27] << 24) + (buffer[26] << 16) + (buffer[25] << 8) + (buffer[24])); } void Socket::Bind_icall(intptr_t socket, Il2CppSocketAddress* socket_address, int32_t* error) { *error = 0; const int32_t length = ARRAY_LENGTH_AS_INT32(socket_address->data->max_length); const uint8_t *buffer = (uint8_t*)il2cpp::vm::Array::GetFirstElementAddress(socket_address->data); if (length < 2) { vm::Exception::Raise(vm::Exception::GetSystemException()); return; } const os::AddressFamily family = convert_address_family((AddressFamily)(buffer[0] | (buffer[1] << 8))); AUTO_ACQUIRE_SOCKET; RETURN_IF_SOCKET_IS_INVALID(); if (family == os::kAddressFamilyInterNetwork) { if (length < 8) { vm::Exception::Raise(vm::Exception::GetSystemException()); return; } const uint16_t port = ((buffer[2] << 8) | buffer[3]); const uint32_t address = ((buffer[4] << 24) | (buffer[5] << 16) | (buffer[6] << 8) | buffer[7]); const os::WaitStatus status = socketHandle->Bind(address, port); if (status == kWaitStatusFailure) *error = socketHandle->GetLastError(); } else if (family == os::kAddressFamilyUnix) { if (length - 2 >= END_POINT_MAX_PATH_LEN) { vm::Exception::Raise(vm::Exception::GetSystemException()); return; } char path[END_POINT_MAX_PATH_LEN] = {0}; for (int32_t i = 0; i < (length - 2); ++i) { path[i] = (char)buffer[i + 2]; } const os::WaitStatus status = socketHandle->Bind(path); if (status == kWaitStatusFailure) *error = socketHandle->GetLastError(); } else if (family == os::kAddressFamilyInterNetworkV6) { uint16_t port; uint8_t address[ipv6AddressSize] = {0}; uint32_t scope; UnpackIPv6AddressFromBuffer(buffer, length, &port, address, &scope); auto status = socketHandle->Bind(address, scope, port); vm::Exception::RaiseIfError(status.GetError()); if (status.Get() == kWaitStatusFailure) *error = socketHandle->GetLastError(); } else { *error = os::kWSAeafnosupport; return; } } void Socket::Blocking_icall(intptr_t socket, bool block, int32_t* error) { *error = 0; AUTO_ACQUIRE_SOCKET; RETURN_IF_SOCKET_IS_INVALID(); const os::WaitStatus status = socketHandle->SetBlocking(block); if (status == kWaitStatusFailure) *error = socketHandle->GetLastError(); } void Socket::Close_icall(intptr_t socket, int32_t* error) { *error = 0; // Socket::Close get invoked when running the finalizers; in case Socket_internal // didn't succeed, the socket has a NULL value and thus we don't need to do // anything here. if (os::PointerToSocketHandle(reinterpret_cast(socket)) == os::kInvalidSocketHandle) return; AUTO_ACQUIRE_SOCKET; RETURN_IF_SOCKET_IS_INVALID(); // There is an implicit acquisition happening when we create the socket which we undo // now that we have closed the socket. os::ReleaseSocketHandle(socketHandle.GetHandle(), socketHandle.GetSocket(), true); socketHandle->Close(); } void Socket::Connect_icall(intptr_t socket, Il2CppSocketAddress* socket_address, int32_t* error, bool blocking) { *error = 0; const int32_t length = ARRAY_LENGTH_AS_INT32(socket_address->data->max_length); const uint8_t *buffer = (uint8_t*)il2cpp::vm::Array::GetFirstElementAddress(socket_address->data); if (length < 2) { vm::Exception::Raise(vm::Exception::GetSystemException()); return; } const os::AddressFamily family = convert_address_family((AddressFamily)(buffer[0] | (buffer[1] << 8))); AUTO_ACQUIRE_SOCKET; RETURN_IF_SOCKET_IS_INVALID(); if (family == os::kAddressFamilyInterNetwork) { if (length < 8) { vm::Exception::Raise(vm::Exception::GetSystemException()); return; } const uint16_t port = ((buffer[2] << 8) | buffer[3]); const uint32_t address = ((buffer[4] << 24) | (buffer[5] << 16) | (buffer[6] << 8) | buffer[7]); const os::WaitStatus status = socketHandle->Connect(address, port); if (status == kWaitStatusFailure) *error = socketHandle->GetLastError(); } else if (family == os::kAddressFamilyUnix) { if (length - 2 >= END_POINT_MAX_PATH_LEN) { vm::Exception::Raise(vm::Exception::GetSystemException()); return; } char path[END_POINT_MAX_PATH_LEN] = {0}; for (int32_t i = 0; i < (length - 2); ++i) { path[i] = (char)buffer[i + 2]; } const os::WaitStatus status = socketHandle->Connect(path); if (status == kWaitStatusFailure) *error = socketHandle->GetLastError(); } else if (family == os::kAddressFamilyInterNetworkV6) { uint16_t port; uint8_t address[ipv6AddressSize] = {0}; uint32_t scope; UnpackIPv6AddressFromBuffer(buffer, length, &port, address, &scope); auto status = socketHandle->Connect(address, scope, port); vm::Exception::RaiseIfError(status.GetError()); if (status.Get() == kWaitStatusFailure) *error = socketHandle->GetLastError(); } else { *error = os::kWSAeafnosupport; return; } } void Socket::Disconnect_icall(intptr_t socket, bool reuse, int32_t* error) { *error = 0; AUTO_ACQUIRE_SOCKET; RETURN_IF_SOCKET_IS_INVALID(); const os::WaitStatus status = socketHandle->Disconnect(reuse); if (status == kWaitStatusFailure) *error = socketHandle->GetLastError(); } void Socket::GetSocketOption_arr_icall(intptr_t socket, SocketOptionLevel level, SocketOptionName name, Il2CppArray** byte_val, int32_t *error) { *error = 0; // Note: for now the options map one to one. const os::SocketOptionName system_name = (os::SocketOptionName)(name); const os::SocketOptionLevel system_level = (os::SocketOptionLevel)(level); int32_t length = ARRAY_LENGTH_AS_INT32((*byte_val)->max_length); uint8_t *buffer = (uint8_t*)il2cpp::vm::Array::GetFirstElementAddress((*byte_val)); AUTO_ACQUIRE_SOCKET; RETURN_IF_SOCKET_IS_INVALID(); const os::WaitStatus status = socketHandle->GetSocketOption(system_level, system_name, buffer, &length); if (status == kWaitStatusFailure) *error = socketHandle->GetLastError(); } void Socket::GetSocketOption_obj_icall(intptr_t socket, SocketOptionLevel level, SocketOptionName name, Il2CppObject** obj_val, int32_t *error) { *error = 0; // Note: for now the options map one to one. const os::SocketOptionName system_name = (os::SocketOptionName)(name); const os::SocketOptionLevel system_level = (os::SocketOptionLevel)(level); AUTO_ACQUIRE_SOCKET; RETURN_IF_SOCKET_IS_INVALID(); int32_t first = 0; int32_t second = 0; const os::WaitStatus status = socketHandle->GetSocketOptionFull(system_level, system_name, &first, &second); if (status == kWaitStatusFailure) { *error = socketHandle->GetLastError(); return; } switch (name) { case kSocketOptionNameLinger: { static Il2CppClass *System_Net_Sockets_LingerOption = NULL; if (!System_Net_Sockets_LingerOption) { System_Net_Sockets_LingerOption = vm::Class::FromName( vm::Assembly::GetImage( vm::Assembly::Load("System.dll")), "System.Net.Sockets", "LingerOption"); } *obj_val = vm::Object::New(System_Net_Sockets_LingerOption); const FieldInfo *enabled_field_info = vm::Class::GetFieldFromName(System_Net_Sockets_LingerOption, "enabled"); const FieldInfo *seconds_field_info = vm::Class::GetFieldFromName(System_Net_Sockets_LingerOption, "lingerTime"); *((bool*)((char*)(*obj_val) + enabled_field_info->offset)) = (first ? 1 : 0); *((int32_t*)((char*)(*obj_val) + seconds_field_info->offset)) = second; } break; case kSocketOptionNameDontLinger: case kSocketOptionNameSendTimeout: case kSocketOptionNameReceiveTimeout: default: *obj_val = vm::Object::Box(il2cpp_defaults.int32_class, &first); break; } } void Socket::Listen_icall(intptr_t socket, int32_t backlog, int32_t* error) { *error = 0; AUTO_ACQUIRE_SOCKET; RETURN_IF_SOCKET_IS_INVALID(); const os::WaitStatus status = socketHandle->Listen(backlog); if (status == kWaitStatusFailure) *error = socketHandle->GetLastError(); } static os::PollFlags select_mode_to_poll_flags(SelectMode mode) { switch (mode) { case kSelectModeSelectRead: return os::kPollFlagsIn; case kSelectModeSelectWrite: return os::kPollFlagsOut; case kSelectModeSelectError: return os::kPollFlagsErr; } return os::kPollFlagsNone; } bool Socket::Poll_icall(intptr_t socket, SelectMode mode, int32_t timeout, int32_t* error) { *error = 0; os::PollRequest request; AUTO_ACQUIRE_SOCKET; RETURN_IF_SOCKET_IS_INVALID(false); request.fd = socketHandle.GetSocket()->GetDescriptor(); request.events = select_mode_to_poll_flags(mode); request.revents = os::kPollFlagsNone; if (request.events == os::kPollFlagsNone) { *error = os::kWSAefault; return false; } std::vector requests; requests.push_back(request); // The timeout from managed code is in microseconds. Convert it to milliseconds // for the poll implementation. timeout = (timeout >= 0) ? (timeout / 1000) : -1; int32_t results = 0; const os::WaitStatus result = os::Socket::Poll(requests, timeout, &results, error); if (result == kWaitStatusFailure || results == 0) return false; return (requests[0].revents != os::kPollFlagsNone); } int32_t Socket::Receive_array_icall(intptr_t socket, os::WSABuf* bufarray, int32_t count, SocketFlags flags, int32_t *error, bool blocking) { *error = 0; AUTO_ACQUIRE_SOCKET; RETURN_IF_SOCKET_IS_INVALID(0); const os::SocketFlags c_flags = convert_socket_flags(flags); int32_t len = 0; const os::WaitStatus status = socketHandle->ReceiveArray(bufarray, count, &len, c_flags); if (status == kWaitStatusFailure) { *error = socketHandle->GetLastError(); return 0; } return len; } int32_t Socket::Receive_icall(intptr_t socket, uint8_t* buffer, int32_t count, SocketFlags flags, int32_t* error, bool blocking) { *error = 0; AUTO_ACQUIRE_SOCKET; RETURN_IF_SOCKET_IS_INVALID(0); const os::SocketFlags c_flags = convert_socket_flags(flags); int32_t len = 0; const os::WaitStatus status = socketHandle->Receive(buffer, count, c_flags, &len); if (status == kWaitStatusFailure) *error = socketHandle->GetLastError(); return len; } int32_t Socket::ReceiveFrom_icall(intptr_t socket, uint8_t* buffer, int32_t count, SocketFlags flags, Il2CppSocketAddress** socket_address, int32_t* error, bool blocking) { *error = 0; AUTO_ACQUIRE_SOCKET; RETURN_IF_SOCKET_IS_INVALID(0); const os::SocketFlags c_flags = convert_socket_flags(flags); int32_t len = 0; const int32_t length = ARRAY_LENGTH_AS_INT32((*socket_address)->data->max_length); const uint8_t *socket_buffer = (uint8_t*)il2cpp::vm::Array::GetFirstElementAddress((*socket_address)->data); if (length < 2) { vm::Exception::Raise(vm::Exception::GetSystemException()); return 0; } const os::AddressFamily family = convert_address_family((AddressFamily)(socket_buffer[0] | (socket_buffer[1] << 8))); os::EndPointInfo info; info.family = os::kAddressFamilyError; if (family == os::kAddressFamilyInterNetwork) { if (length < 8) { vm::Exception::Raise(vm::Exception::GetSystemException()); return 0; } const uint16_t port = ((socket_buffer[2] << 8) | socket_buffer[3]); const uint32_t address = ((socket_buffer[4] << 24) | (socket_buffer[5] << 16) | (socket_buffer[6] << 8) | socket_buffer[7]); const os::WaitStatus status = socketHandle->RecvFrom(address, port, buffer, count, c_flags, &len, info); if (status == kWaitStatusFailure) *error = socketHandle->GetLastError(); } else if (family == os::kAddressFamilyUnix) { if (length - 2 >= END_POINT_MAX_PATH_LEN) { vm::Exception::Raise(vm::Exception::GetSystemException()); return 0; } char path[END_POINT_MAX_PATH_LEN] = {0}; for (int32_t i = 0; i < (length - 2); ++i) { path[i] = (char)socket_buffer[i + 2]; } auto status = socketHandle->RecvFrom(path, buffer, count, c_flags, &len, info); vm::Exception::RaiseIfError(status.GetError()); if (status.Get() == kWaitStatusFailure) *error = socketHandle->GetLastError(); } else if (family == os::kAddressFamilyInterNetworkV6) { uint16_t port; uint8_t address[ipv6AddressSize] = {0}; uint32_t scope; UnpackIPv6AddressFromBuffer(socket_buffer, length, &port, address, &scope); auto status = socketHandle->RecvFrom(address, scope, port, buffer, count, c_flags, &len, info); vm::Exception::RaiseIfError(status.GetError()); if (status.Get() == kWaitStatusFailure) *error = socketHandle->GetLastError(); } else { *error = os::kWSAeafnosupport; return 0; } *socket_address = (info.family == os::kAddressFamilyError) ? NULL : end_point_info_to_socket_address(info); return len; } Il2CppSocketAddress* Socket::LocalEndPoint_icall(intptr_t socket, int32_t family, int32_t* error) { AUTO_ACQUIRE_SOCKET; RETURN_IF_SOCKET_IS_INVALID(NULL); os::EndPointInfo info; memset(&info, 0x00, sizeof(os::EndPointInfo)); const os::WaitStatus status = socketHandle->GetLocalEndPointInfo(info); if (status == kWaitStatusFailure) { *error = socketHandle->GetLastError(); return NULL; } Il2CppSocketAddress *socket_address = end_point_info_to_socket_address(info); if (socket_address == NULL) *error = os::kWSAeafnosupport; return socket_address; } Il2CppSocketAddress* Socket::RemoteEndPoint_icall(intptr_t socket, int32_t family, int32_t* error) { AUTO_ACQUIRE_SOCKET; RETURN_IF_SOCKET_IS_INVALID(NULL); os::EndPointInfo info; memset(&info, 0x00, sizeof(os::EndPointInfo)); const os::WaitStatus status = socketHandle->GetRemoteEndPointInfo(info); if (status == kWaitStatusFailure) { *error = socketHandle->GetLastError(); return NULL; } Il2CppSocketAddress *socket_address = end_point_info_to_socket_address(info); if (socket_address == NULL) *error = os::kWSAeafnosupport; return socket_address; } void Socket::Select_icall(Il2CppArray** sockets, int32_t timeout, int32_t* error) { *error = 0; // Layout: READ, null, WRITE, null, ERROR, null const uint32_t input_sockets_count = ARRAY_LENGTH_AS_INT32((*sockets)->max_length); std::vector requests; std::vector socketHandles; requests.reserve(input_sockets_count - 3); int32_t mode = 0; for (uint32_t i = 0; i < input_sockets_count; ++i) { Il2CppObject *obj = il2cpp_array_get(*sockets, Il2CppObject*, i); if (obj == NULL) { if (++mode > 3) { // Something very bad happened here (ie: the input array was wrong) // so we gracefully terminate the method. *error = os::kWSAefault; return; } continue; } FieldInfo *safe_handle_field_info = vm::Class::GetFieldFromName(obj->klass, "m_Handle"); const Il2CppObject* value = NULL; vm::Field::GetValue(obj, safe_handle_field_info, &value); const FieldInfo *handle_field_info = vm::Class::GetFieldFromName(value->klass, "handle"); intptr_t& intPtr = *((intptr_t*)((char*)value + handle_field_info->offset)); // Acquire socket. socketHandles.push_back(os::SocketHandleWrapper()); os::SocketHandleWrapper& socketHandle = socketHandles.back(); socketHandle.Acquire(os::PointerToSocketHandle(reinterpret_cast(intPtr))); os::PollRequest request; // May 'invalid socket' (-1); we want the error from Poll() in that case. request.fd = socketHandle.GetSocket() == NULL ? -1 : socketHandle.GetSocket()->GetDescriptor(); request.events = (mode == 0 ? os::kPollFlagsIn : (mode == 1 ? os::kPollFlagsOut : os::kPollFlagsErr)); request.revents = os::kPollFlagsNone; requests.push_back(request); } if (requests.size() == 0) return; int32_t results = 0; // The timeout from managed code is in microseconds. Convert it to milliseconds // for the poll implementation. timeout = (timeout >= 0) ? (timeout / 1000) : -1; const os::WaitStatus result = os::Socket::Poll(requests, timeout, &results, error); if (result == kWaitStatusFailure) { *sockets = NULL; return; } Il2CppArray *new_sockets = vm::Array::New(((Il2CppObject*)(*sockets))->klass->element_class, results + 3); if (results > 0) { mode = 0; uint32_t request_index = 0; // This odd loop is due to the layout of the sockets input array: // Layout: READ, null, WRITE, null, ERROR, null // We need to iterate each request and iterate the sockets array, skipping // the null entries. We try to avoid an infinite loop here as well. uint32_t add_index = 0; while (request_index < requests.size()) { const uint32_t input_sockets_index = (request_index + mode); if (input_sockets_index > input_sockets_count - 1) break; // We have exhausted the input sockets array, so exit. Il2CppObject *obj = il2cpp_array_get(*sockets, Il2CppObject*, input_sockets_index); if (obj == NULL) { ++mode; continue; // Here is a null entry, skip it without updated the next request index. } os::PollRequest &request = requests[request_index]; if (request.revents != os::kPollFlagsNone) { switch (mode) { case 0: if (request.revents & (os::kPollFlagsIn | os::kPollFlagsErr)) { il2cpp_array_setref(new_sockets, (add_index + mode), obj); add_index++; } break; case 1: if (request.revents & (os::kPollFlagsOut | os::kPollFlagsErr)) { il2cpp_array_setref(new_sockets, (add_index + mode), obj); add_index++; } break; default: if (request.revents & os::kPollFlagsErr) { il2cpp_array_setref(new_sockets, (add_index + mode), obj); add_index++; } break; } } ++request_index; } } *sockets = new_sockets; } bool Socket::SendFile_icall(intptr_t socket, Il2CppString* filename, Il2CppArray* pre_buffer, Il2CppArray* post_buffer, TransmitFileOptions flags, int32_t* error, bool blocking) { if (filename == NULL) return false; os::TransmitFileBuffers t_buffers = {0}; if (pre_buffer != NULL) { t_buffers.head = il2cpp_array_addr(pre_buffer, uint8_t, 0); t_buffers.head_length = ARRAY_LENGTH_AS_INT32(pre_buffer->max_length); } if (post_buffer != NULL) { t_buffers.tail = il2cpp_array_addr(post_buffer, uint8_t, 0); t_buffers.tail_length = ARRAY_LENGTH_AS_INT32(post_buffer->max_length); } AUTO_ACQUIRE_SOCKET; if (!socketHandle.IsValid()) return false; const Il2CppChar* ustr = utils::StringUtils::GetChars(filename); const std::string str = utils::StringUtils::Utf16ToUtf8(ustr); // Note: for now they map 1-1 const os::TransmitFileOptions o_flags = (os::TransmitFileOptions)flags; const os::WaitStatus status = socketHandle->SendFile(str.c_str(), &t_buffers, o_flags); if (status == kWaitStatusFailure) { // TODO: mono stores socketHandle->GetLastError into a threadlocal global variable // that can be retrieved later by other icalls. return false; } if ((flags & kTransmitFileOptionsDisconnect) == kTransmitFileOptionsDisconnect) socketHandle->Disconnect(true); return true; } int32_t Socket::SendTo_icall(intptr_t socket, uint8_t* buffer, int32_t count, SocketFlags flags, Il2CppSocketAddress* socket_address, int32_t* error, bool blocking) { *error = 0; const os::SocketFlags c_flags = convert_socket_flags(flags); int32_t len = 0; const int32_t length = ARRAY_LENGTH_AS_INT32(socket_address->data->max_length); const uint8_t *socket_buffer = (uint8_t*)il2cpp::vm::Array::GetFirstElementAddress(socket_address->data); if (length < 2) { vm::Exception::Raise(vm::Exception::GetSystemException()); return 0; } const os::AddressFamily family = convert_address_family((AddressFamily)(socket_buffer[0] | (socket_buffer[1] << 8))); AUTO_ACQUIRE_SOCKET; RETURN_IF_SOCKET_IS_INVALID(0); if (family == os::kAddressFamilyInterNetwork) { if (length < 8) { vm::Exception::Raise(vm::Exception::GetSystemException()); return 0; } const uint16_t port = ((socket_buffer[2] << 8) | socket_buffer[3]); const uint32_t address = ((socket_buffer[4] << 24) | (socket_buffer[5] << 16) | (socket_buffer[6] << 8) | socket_buffer[7]); const os::WaitStatus status = socketHandle->SendTo(address, port, buffer, count, c_flags, &len); if (status == kWaitStatusFailure) *error = socketHandle->GetLastError(); } else if (family == os::kAddressFamilyUnix) { if (length - 2 >= END_POINT_MAX_PATH_LEN) { vm::Exception::Raise(vm::Exception::GetSystemException()); return 0; } char path[END_POINT_MAX_PATH_LEN] = {0}; for (int32_t i = 0; i < (length - 2); ++i) { path[i] = (char)socket_buffer[i + 2]; } auto status = socketHandle->SendTo(path, buffer, count, c_flags, &len); vm::Exception::RaiseIfError(status.GetError()); if (status.Get() == kWaitStatusFailure) *error = socketHandle->GetLastError(); } else if (family == os::kAddressFamilyInterNetworkV6) { uint16_t port; uint8_t address[ipv6AddressSize] = {0}; uint32_t scope; UnpackIPv6AddressFromBuffer(socket_buffer, length, &port, address, &scope); auto status = socketHandle->SendTo(address, scope, port, buffer, count, c_flags, &len); vm::Exception::RaiseIfError(status.GetError()); if (status.Get() == kWaitStatusFailure) *error = socketHandle->GetLastError(); } else { *error = os::kWSAeafnosupport; return 0; } return len; } int32_t Socket::Send_array_icall(intptr_t socket, os::WSABuf* bufarray, int32_t count, SocketFlags flags, int32_t* error, bool blocking) { *error = 0; const os::SocketFlags c_flags = convert_socket_flags(flags); AUTO_ACQUIRE_SOCKET; RETURN_IF_SOCKET_IS_INVALID(0); int32_t sent = 0; const os::WaitStatus status = socketHandle->SendArray(bufarray, count, &sent, c_flags); if (status == kWaitStatusFailure) { *error = socketHandle->GetLastError(); return 0; } return sent; } int32_t Socket::Send_icall(intptr_t socket, uint8_t* buffer, int32_t count, SocketFlags flags, int32_t* error, bool blocking) { *error = 0; const os::SocketFlags c_flags = convert_socket_flags(flags); AUTO_ACQUIRE_SOCKET; RETURN_IF_SOCKET_IS_INVALID(0); int32_t len = 0; const os::WaitStatus status = socketHandle->Send(buffer, count, c_flags, &len); if (status == kWaitStatusFailure) *error = socketHandle->GetLastError(); return len; } #if IL2CPP_SUPPORT_IPV6 static os::IPv6Address ipaddress_to_ipv6_addr(Il2CppObject *ipaddr) { FieldInfo* numbersFieldInfo = vm::Class::GetFieldFromName(ipaddr->klass, "_numbers"); IL2CPP_ASSERT(numbersFieldInfo); Il2CppArray* data = (Il2CppArray*)vm::Field::GetValueObject(numbersFieldInfo, ipaddr); os::IPv6Address ipv6; for (int i = 0; i < 8; i++) { uint16_t s = il2cpp_array_get(data, uint16_t, i); ipv6.addr[2 * i] = (s >> 8) & 0xff; ipv6.addr[2 * i + 1] = s & 0xff; } return ipv6; } static void GetAddressAndInterfaceFromObject(Il2CppObject* object, const char* groupField, const char* interfaceField, os::IPv6Address& ipv6, uint64_t& interfaceOffset) { FieldInfo* groupFieldInfo = vm::Class::GetFieldFromName(object->klass, groupField); IL2CPP_ASSERT(groupFieldInfo); Il2CppObject* address = vm::Field::GetValueObject(groupFieldInfo, object); if (address) ipv6 = ipaddress_to_ipv6_addr(address); FieldInfo* interfaceFieldInfo = vm::Class::GetFieldFromName(object->klass, interfaceField); IL2CPP_ASSERT(interfaceFieldInfo); vm::Field::GetValue(object, interfaceFieldInfo, &interfaceOffset); } #endif // IL2CPP_SUPPORT_IPV6 void Socket::SetSocketOption_icall(intptr_t socket, SocketOptionLevel level, SocketOptionName name, Il2CppObject* obj_val, Il2CppArray* byte_val, int32_t int_val, int32_t* error) { *error = 0; // Note: for now the options map one to one. const os::SocketOptionName system_name = (os::SocketOptionName)(name); const os::SocketOptionLevel system_level = (os::SocketOptionLevel)(level); AUTO_ACQUIRE_SOCKET; RETURN_IF_SOCKET_IS_INVALID(); os::WaitStatus status = kWaitStatusFailure; if (byte_val != NULL) { const int32_t length = ARRAY_LENGTH_AS_INT32(byte_val->max_length); const uint8_t *buffer = (uint8_t*)il2cpp::vm::Array::GetFirstElementAddress(byte_val); status = socketHandle->SetSocketOptionArray(system_level, system_name, buffer, length); } else if (obj_val != NULL) { switch (name) { case kSocketOptionNameLinger: { const FieldInfo *enabled_field_info = vm::Class::GetFieldFromName(obj_val->klass, "enabled"); const FieldInfo *seconds_field_info = vm::Class::GetFieldFromName(obj_val->klass, "lingerTime"); const bool enabled = *((bool*)((char*)obj_val + enabled_field_info->offset)); const int32_t seconds = *((int32_t*)((char*)obj_val + seconds_field_info->offset)); status = socketHandle->SetSocketOptionLinger(system_level, system_name, enabled, seconds); } break; case kSocketOptionNameAddMembership: case kSocketOptionNameDropMembership: { #if IL2CPP_SUPPORT_IPV6 if (system_level == (os::SocketOptionLevel)kSocketOptionLevelIPv6) { os::IPv6Address ipv6 = {{0}}; uint64_t interfaceOffset; GetAddressAndInterfaceFromObject(obj_val, "m_Group", "m_Interface", ipv6, interfaceOffset); status = socketHandle->SetSocketOptionMembership(system_level, system_name, ipv6, interfaceOffset); } else if (system_level == (os::SocketOptionLevel)kSocketOptionLevelIP) #endif // IL2CPP_SUPPORT_IPV6 { FieldInfo *group_field_info = vm::Class::GetFieldFromName(obj_val->klass, "group"); Il2CppObject* group_obj = vm::Field::GetValueObject(group_field_info, obj_val); const FieldInfo *group_address_field_info = vm::Class::GetFieldFromName(group_obj->klass, "_addressOrScopeId"); IL2CPP_ASSERT(group_address_field_info != NULL); const uint32_t group_address = *((uint32_t*)(uint64_t*)((char*)group_obj + group_address_field_info->offset)); uint32_t local_address = 0; FieldInfo *local_field_info = vm::Class::GetFieldFromName(obj_val->klass, "localAddress"); if (local_field_info != NULL) { Il2CppObject* local_obj = vm::Field::GetValueObject(local_field_info, obj_val); if (local_obj != NULL) { const FieldInfo *local_address_field_info = vm::Class::GetFieldFromName(local_obj->klass, "_addressOrScopeId"); IL2CPP_ASSERT(local_address_field_info != NULL); local_address = *((uint32_t*)(uint64_t*)((char*)local_obj + local_address_field_info->offset)); } } status = socketHandle->SetSocketOptionMembership(system_level, system_name, group_address, local_address); } } break; default: *error = os::kWSAeinval; return; // early out } } else status = socketHandle->SetSocketOption(system_level, system_name, int_val); if (status == kWaitStatusFailure) *error = socketHandle->GetLastError(); } void Socket::Shutdown_icall(intptr_t socket, SocketShutdown how, int32_t* error) { *error = 0; AUTO_ACQUIRE_SOCKET; RETURN_IF_SOCKET_IS_INVALID(); const os::WaitStatus status = socketHandle->Shutdown(how); if (status == kWaitStatusFailure) *error = socketHandle->GetLastError(); } intptr_t Socket::Socket_icall(AddressFamily family, SocketType type, ProtocolType protocol, int32_t* error) { intptr_t socket = static_cast(os::kInvalidSocketHandle); *error = 0; os::AddressFamily n_family = convert_address_family(family); os::SocketType n_type = convert_socket_type(type); os::ProtocolType n_protocol = convert_socket_protocol(protocol); if (n_family == os::kAddressFamilyError) { *error = os::kWSAeafnosupport; return socket; } if (n_type == os::kSocketTypeError) { *error = os::kWSAesocktnosupport; return socket; } if (n_protocol == os::kProtocolTypeUnknown) { *error = os::kWSAeprotonosupport; return socket; } os::Socket *sock = new os::Socket(check_thread_status); const os::WaitStatus status = sock->Create(n_family, n_type, n_protocol); if (status == kWaitStatusFailure) { *error = sock->GetLastError(); // Okay to delete socket directly. We haven't created a handle yet. delete sock; return socket; } os::SocketHandle socketHandle = os::CreateSocketHandle(sock); socket = static_cast(socketHandle); return socket; } int32_t Socket::IOControl_icall(intptr_t socket, int32_t ioctl_code, Il2CppArray* input, Il2CppArray* output, int32_t* error) { *error = 0; AUTO_ACQUIRE_SOCKET; RETURN_IF_SOCKET_IS_INVALID(-1); if (ioctl_code == 0x5421 /* FIONBIO */) { // Invalid command. Must use Socket.Blocking return -1; } const int32_t in_length = (input ? ARRAY_LENGTH_AS_INT32(input->max_length) : 0); const uint8_t *in_buffer = (input ? (uint8_t*)il2cpp::vm::Array::GetFirstElementAddress(input) : NULL); const int32_t out_length = (output ? ARRAY_LENGTH_AS_INT32(output->max_length) : 0); uint8_t *out_buffer = (output ? (uint8_t*)il2cpp::vm::Array::GetFirstElementAddress(output) : NULL); int32_t output_bytes = 0; const os::WaitStatus status = socketHandle->Ioctl(ioctl_code, in_buffer, in_length, out_buffer, out_length, &output_bytes); if (status == kWaitStatusFailure) { *error = socketHandle->GetLastError(); return -1; } return output_bytes; } static void STDCALL abort_apc(void* param) { } void Socket::cancel_blocking_socket_operation(Il2CppObject* thread) { Il2CppThread* t = reinterpret_cast(thread); t->internal_thread->handle->QueueUserAPC(abort_apc, NULL); // IL2CPP_NOT_IMPLEMENTED_ICALL(Socket::cancel_blocking_socket_operation); //IL2CPP_UNREACHABLE; } bool Socket::Duplicate_icall(intptr_t handle, int32_t targetProcessId, intptr_t* duplicateHandle, int32_t* error) { IL2CPP_NOT_IMPLEMENTED_ICALL(Socket::Duplicate_icall); return false; } bool Socket::IsProtocolSupported_internal(int32_t networkInterface) { // The networkInterface argument is from the // System.Net.NetworkInformation.NetworkInterfaceComponent enum // 0 => IPv4 // 1 => IPv6 #if IL2CPP_SUPPORT_IPV6_SUPPORT_QUERY return networkInterface == 1 ? os::Socket::IsIPv6Supported() : true; #elif IL2CPP_SUPPORT_IPV6 // This platform supports both IPv6 and IPv4. return true; #else // This platform only supports IPv4. return networkInterface == 0; #endif } } /* namespace Sockets */ } /* namespace Net */ } /* namespace System */ } /* namespace System */ } /* namespace icalls */ } /* namespace il2cpp */