/** * \file * Socket IO internal calls * * Authors: * Dick Porter (dick@ximian.com) * Gonzalo Paniagua Javier (gonzalo@ximian.com) * * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com) * Copyright 2004-2009 Novell, Inc (http://www.novell.com) * * This file has been re-licensed under the MIT License: * http://opensource.org/licenses/MIT * Licensed under the MIT license. See LICENSE file in the project root for full license information. */ #include #ifndef DISABLE_SOCKETS #if defined(__APPLE__) || defined(__FreeBSD__) #define __APPLE_USE_RFC_3542 #endif #include #include #include #ifdef HOST_WIN32 #include #else #include #ifdef HAVE_SYS_IOCTL_H #include #endif #include #include #ifdef HAVE_NETDB_H #include #endif #include #endif #ifdef HAVE_UNISTD_H #include #endif #include #include #include #include #include #include #include #include #include #include #include /* FIXME change this code to not mess so much with the internals */ #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_SYS_TIME_H #include #endif #ifdef HAVE_SYS_IOCTL_H #include #endif #ifdef HAVE_NET_IF_H #include #endif #ifdef HAVE_NETDB_H #include #endif #ifdef HAVE_SYS_FILIO_H #include /* defines FIONBIO and FIONREAD */ #endif #ifdef HAVE_SYS_SOCKIO_H #include /* defines SIOCATMARK */ #endif #ifdef HAVE_SYS_UN_H #include #endif #ifdef HAVE_GETIFADDRS // must be included before #include #endif #if defined(_MSC_VER) && G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT | HAVE_UWP_WINAPI_SUPPORT) #include #endif #define LOGDEBUG(...) /* define LOGDEBUG(...) g_message(__VA_ARGS__) */ static gboolean addrinfo_to_IPHostEntry_handles (MonoAddressInfo *info, MonoStringHandleOut h_name, MonoArrayHandleOut h_aliases, MonoArrayHandleOut h_addr_list, gboolean add_local_ips, MonoError *error); static MonoObjectHandle create_object_handle_from_sockaddr (struct sockaddr *saddr, int sa_size, gint32 *werror, MonoError *error); static struct sockaddr* create_sockaddr_from_handle (MonoObjectHandle saddr_obj, socklen_t *sa_size, gint32 *werror, MonoError *error); #ifdef HOST_WIN32 static SOCKET mono_w32socket_socket (int domain, int type, int protocol) { SOCKET ret; MONO_ENTER_GC_SAFE; ret = WSASocket (domain, type, protocol, NULL, 0, WSA_FLAG_OVERLAPPED); MONO_EXIT_GC_SAFE; return ret; } static gint mono_w32socket_bind (SOCKET sock, struct sockaddr *addr, socklen_t addrlen) { gint ret; MONO_ENTER_GC_SAFE; ret = bind (sock, addr, addrlen); MONO_EXIT_GC_SAFE; return ret; } static gint mono_w32socket_getpeername (SOCKET sock, struct sockaddr *name, socklen_t *namelen) { gint ret; MONO_ENTER_GC_SAFE; ret = getpeername (sock, name, namelen); MONO_EXIT_GC_SAFE; return ret; } static gint mono_w32socket_getsockname (SOCKET sock, struct sockaddr *name, socklen_t *namelen) { gint ret; MONO_ENTER_GC_SAFE; ret = getsockname (sock, name, namelen); MONO_EXIT_GC_SAFE; return ret; } static gint mono_w32socket_getsockopt (SOCKET sock, gint level, gint optname, gpointer optval, socklen_t *optlen) { gint ret; MONO_ENTER_GC_SAFE; ret = getsockopt (sock, level, optname, optval, optlen); MONO_EXIT_GC_SAFE; return ret; } static gint mono_w32socket_setsockopt (SOCKET sock, gint level, gint optname, const gpointer optval, socklen_t optlen) { gint ret; MONO_ENTER_GC_SAFE; ret = setsockopt (sock, level, optname, optval, optlen); MONO_EXIT_GC_SAFE; return ret; } static gint mono_w32socket_listen (SOCKET sock, gint backlog) { gint ret; MONO_ENTER_GC_SAFE; ret = listen (sock, backlog); MONO_EXIT_GC_SAFE; return ret; } static gint mono_w32socket_shutdown (SOCKET sock, gint how) { gint ret; MONO_ENTER_GC_SAFE; ret = shutdown (sock, how); MONO_EXIT_GC_SAFE; return ret; } static gint mono_w32socket_ioctl (SOCKET sock, gint32 command, gchar *input, gint inputlen, gchar *output, gint outputlen, glong *written) { gint ret; MONO_ENTER_GC_SAFE; ret = WSAIoctl (sock, command, input, inputlen, output, outputlen, written, NULL, NULL); MONO_EXIT_GC_SAFE; return ret; } static gboolean mono_w32socket_close (SOCKET sock) { gboolean ret; MONO_ENTER_GC_SAFE; ret = closesocket (sock); MONO_EXIT_GC_SAFE; return ret; } #endif /* HOST_WIN32 */ static void abort_syscall (gpointer data) { mono_thread_info_abort_socket_syscall_for_close ((MonoNativeThreadId) (gsize) data); } static gint32 convert_family (MonoAddressFamily mono_family) { switch (mono_family) { case AddressFamily_Unknown: case AddressFamily_ImpLink: case AddressFamily_Pup: case AddressFamily_Chaos: case AddressFamily_Iso: case AddressFamily_Ecma: case AddressFamily_DataKit: case AddressFamily_Ccitt: case AddressFamily_DataLink: case AddressFamily_Lat: case AddressFamily_HyperChannel: case AddressFamily_NetBios: case AddressFamily_VoiceView: case AddressFamily_FireFox: case AddressFamily_Banyan: case AddressFamily_Atm: case AddressFamily_Cluster: case AddressFamily_Ieee12844: case AddressFamily_NetworkDesigners: g_warning ("System.Net.Sockets.AddressFamily has unsupported value 0x%x", mono_family); return -1; case AddressFamily_Unspecified: return AF_UNSPEC; case AddressFamily_Unix: return AF_UNIX; case AddressFamily_InterNetwork: return AF_INET; case AddressFamily_AppleTalk: #ifdef AF_APPLETALK return AF_APPLETALK; #else return -1; #endif case AddressFamily_InterNetworkV6: #ifdef HAVE_STRUCT_SOCKADDR_IN6 return AF_INET6; #else return -1; #endif case AddressFamily_DecNet: #ifdef AF_DECnet return AF_DECnet; #else return -1; #endif case AddressFamily_Ipx: #ifdef AF_IPX return AF_IPX; #else return -1; #endif case AddressFamily_Sna: #ifdef AF_SNA return AF_SNA; #else return -1; #endif case AddressFamily_Irda: #ifdef AF_IRDA return AF_IRDA; #else return -1; #endif default: g_warning ("System.Net.Sockets.AddressFamily has unknown value 0x%x", mono_family); return -1; } } static MonoAddressFamily convert_to_mono_family (guint16 af_family) { switch (af_family) { case AF_UNSPEC: return AddressFamily_Unspecified; case AF_UNIX: return AddressFamily_Unix; case AF_INET: return AddressFamily_InterNetwork; #ifdef AF_IPX case AF_IPX: return AddressFamily_Ipx; #endif #ifdef AF_SNA case AF_SNA: return AddressFamily_Sna; #endif #ifdef AF_DECnet case AF_DECnet: return AddressFamily_DecNet; #endif #ifdef AF_APPLETALK case AF_APPLETALK: return AddressFamily_AppleTalk; #endif #ifdef HAVE_STRUCT_SOCKADDR_IN6 case AF_INET6: return AddressFamily_InterNetworkV6; #endif #ifdef AF_IRDA case AF_IRDA: return AddressFamily_Irda; #endif default: g_warning ("unknown address family 0x%x", af_family); return AddressFamily_Unknown; } } static gint32 convert_type (MonoSocketType mono_type) { switch (mono_type) { case SocketType_Stream: return SOCK_STREAM; case SocketType_Dgram: return SOCK_DGRAM; case SocketType_Raw: return SOCK_RAW; case SocketType_Rdm: #ifdef SOCK_RDM return SOCK_RDM; #else return -1; #endif case SocketType_Seqpacket: #ifdef SOCK_SEQPACKET return SOCK_SEQPACKET; #else return -1; #endif case SocketType_Unknown: g_warning ("System.Net.Sockets.SocketType has unsupported value 0x%x", mono_type); return -1; default: g_warning ("System.Net.Sockets.SocketType has unknown value 0x%x", mono_type); return -1; } } static gint32 convert_proto (MonoProtocolType mono_proto) { switch (mono_proto) { case ProtocolType_IP: case ProtocolType_IPv6: case ProtocolType_Icmp: case ProtocolType_Igmp: case ProtocolType_Ggp: case ProtocolType_Tcp: case ProtocolType_Pup: case ProtocolType_Udp: case ProtocolType_Idp: /* These protocols are known (on my system at least) */ return mono_proto; case ProtocolType_ND: case ProtocolType_Raw: case ProtocolType_Ipx: case ProtocolType_Spx: case ProtocolType_SpxII: case ProtocolType_Unknown: /* These protocols arent */ g_warning ("System.Net.Sockets.ProtocolType has unsupported value 0x%x", mono_proto); return -1; default: return -1; } } /* Convert MonoSocketFlags */ static gint32 convert_socketflags (gint32 sflags) { gint32 flags = 0; if (!sflags) /* SocketFlags.None */ return 0; if (sflags & ~(SocketFlags_OutOfBand | SocketFlags_MaxIOVectorLength | SocketFlags_Peek | SocketFlags_DontRoute | SocketFlags_Partial)) /* Contains invalid flag values */ return -1; #ifdef MSG_OOB if (sflags & SocketFlags_OutOfBand) flags |= MSG_OOB; #endif if (sflags & SocketFlags_Peek) flags |= MSG_PEEK; if (sflags & SocketFlags_DontRoute) 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 (sflags & SocketFlags_Partial) flags |= MSG_MORE; #endif #if 0 /* Don't do anything for MaxIOVectorLength */ if (sflags & SocketFlags_MaxIOVectorLength) return -1; #endif return flags; } /* * Returns: * 0 on success (mapped mono_level and mono_name to system_level and system_name * -1 on error * -2 on non-fatal error (ie, must ignore) */ static gint32 convert_sockopt_level_and_name (MonoSocketOptionLevel mono_level, MonoSocketOptionName mono_name, int *system_level, int *system_name) { switch (mono_level) { case SocketOptionLevel_Socket: *system_level = SOL_SOCKET; switch (mono_name) { case SocketOptionName_DontLinger: /* This is SO_LINGER, because the setsockopt * internal call maps DontLinger to SO_LINGER * with l_onoff=0 */ *system_name = SO_LINGER; break; #ifdef SO_DEBUG case SocketOptionName_Debug: *system_name = SO_DEBUG; break; #endif #ifdef SO_ACCEPTCONN case SocketOptionName_AcceptConnection: *system_name = SO_ACCEPTCONN; break; #endif case SocketOptionName_ReuseAddress: *system_name = SO_REUSEADDR; break; case SocketOptionName_KeepAlive: *system_name = SO_KEEPALIVE; break; #ifdef SO_DONTROUTE case SocketOptionName_DontRoute: *system_name = SO_DONTROUTE; break; #endif case SocketOptionName_Broadcast: *system_name = SO_BROADCAST; break; case SocketOptionName_Linger: *system_name = SO_LINGER; break; #ifdef SO_OOBINLINE case SocketOptionName_OutOfBandInline: *system_name = SO_OOBINLINE; break; #endif case SocketOptionName_SendBuffer: *system_name = SO_SNDBUF; break; case SocketOptionName_ReceiveBuffer: *system_name = SO_RCVBUF; break; case SocketOptionName_SendLowWater: *system_name = SO_SNDLOWAT; break; case SocketOptionName_ReceiveLowWater: *system_name = SO_RCVLOWAT; break; case SocketOptionName_SendTimeout: *system_name = SO_SNDTIMEO; break; case SocketOptionName_ReceiveTimeout: *system_name = SO_RCVTIMEO; break; case SocketOptionName_Error: *system_name = SO_ERROR; break; case SocketOptionName_Type: *system_name = SO_TYPE; break; #ifdef SO_PEERCRED case SocketOptionName_PeerCred: *system_name = SO_PEERCRED; break; #endif case SocketOptionName_ExclusiveAddressUse: #ifdef SO_EXCLUSIVEADDRUSE *system_name = SO_EXCLUSIVEADDRUSE; break; #endif case SocketOptionName_UseLoopback: #ifdef SO_USELOOPBACK *system_name = SO_USELOOPBACK; break; #endif case SocketOptionName_MaxConnections: #ifdef SO_MAXCONN *system_name = SO_MAXCONN; break; #elif defined(SOMAXCONN) *system_name = SOMAXCONN; break; #endif default: g_warning ("System.Net.Sockets.SocketOptionName 0x%x is not supported at Socket level", mono_name); return -1; } break; case SocketOptionLevel_IP: *system_level = mono_networking_get_ip_protocol (); switch (mono_name) { #ifdef IP_OPTIONS case SocketOptionName_IPOptions: *system_name = IP_OPTIONS; break; #endif #ifdef IP_HDRINCL case SocketOptionName_HeaderIncluded: *system_name = IP_HDRINCL; break; #endif #ifdef IP_TOS case SocketOptionName_TypeOfService: *system_name = IP_TOS; break; #endif #ifdef IP_TTL case SocketOptionName_IpTimeToLive: *system_name = IP_TTL; break; #endif case SocketOptionName_MulticastInterface: *system_name = IP_MULTICAST_IF; break; case SocketOptionName_MulticastTimeToLive: *system_name = IP_MULTICAST_TTL; break; case SocketOptionName_MulticastLoopback: *system_name = IP_MULTICAST_LOOP; break; case SocketOptionName_AddMembership: *system_name = IP_ADD_MEMBERSHIP; break; case SocketOptionName_DropMembership: *system_name = IP_DROP_MEMBERSHIP; break; #ifdef HAVE_IP_PKTINFO case SocketOptionName_PacketInformation: *system_name = IP_PKTINFO; break; #endif /* HAVE_IP_PKTINFO */ case SocketOptionName_DontFragment: #ifdef HAVE_IP_DONTFRAGMENT *system_name = IP_DONTFRAGMENT; break; #elif defined HAVE_IP_MTU_DISCOVER /* Not quite the same */ *system_name = IP_MTU_DISCOVER; break; #else /* If the flag is not available on this system, we can ignore this error */ return -2; #endif /* HAVE_IP_DONTFRAGMENT */ case SocketOptionName_AddSourceMembership: case SocketOptionName_DropSourceMembership: case SocketOptionName_BlockSource: case SocketOptionName_UnblockSource: /* Can't figure out how to map these, so fall * through */ default: g_warning ("System.Net.Sockets.SocketOptionName 0x%x is not supported at IP level", mono_name); return -1; } break; case SocketOptionLevel_IPv6: *system_level = mono_networking_get_ipv6_protocol (); switch (mono_name) { case SocketOptionName_IpTimeToLive: case SocketOptionName_HopLimit: *system_name = IPV6_UNICAST_HOPS; break; case SocketOptionName_MulticastInterface: *system_name = IPV6_MULTICAST_IF; break; case SocketOptionName_MulticastTimeToLive: *system_name = IPV6_MULTICAST_HOPS; break; case SocketOptionName_MulticastLoopback: *system_name = IPV6_MULTICAST_LOOP; break; case SocketOptionName_AddMembership: *system_name = IPV6_JOIN_GROUP; break; case SocketOptionName_DropMembership: *system_name = IPV6_LEAVE_GROUP; break; case SocketOptionName_IPv6Only: #ifdef IPV6_V6ONLY *system_name = IPV6_V6ONLY; #else return -1; #endif break; case SocketOptionName_PacketInformation: #ifdef HAVE_IPV6_PKTINFO *system_name = IPV6_PKTINFO; #endif break; case SocketOptionName_HeaderIncluded: case SocketOptionName_IPOptions: case SocketOptionName_TypeOfService: case SocketOptionName_DontFragment: case SocketOptionName_AddSourceMembership: case SocketOptionName_DropSourceMembership: case SocketOptionName_BlockSource: case SocketOptionName_UnblockSource: /* Can't figure out how to map these, so fall * through */ default: g_warning ("System.Net.Sockets.SocketOptionName 0x%x is not supported at IPv6 level", mono_name); return -1; } break; /* SocketOptionLevel_IPv6 */ case SocketOptionLevel_Tcp: *system_level = mono_networking_get_tcp_protocol (); switch (mono_name) { case SocketOptionName_NoDelay: *system_name = TCP_NODELAY; break; #if 0 /* The documentation is talking complete * bollocks here: rfc-1222 is titled * 'Advancing the NSFNET Routing Architecture' * and doesn't mention either of the words * "expedite" or "urgent". */ case SocketOptionName_BsdUrgent: case SocketOptionName_Expedited: #endif default: g_warning ("System.Net.Sockets.SocketOptionName 0x%x is not supported at TCP level", mono_name); return -1; } break; case SocketOptionLevel_Udp: g_warning ("System.Net.Sockets.SocketOptionLevel has unsupported value 0x%x", mono_level); switch(mono_name) { case SocketOptionName_NoChecksum: case SocketOptionName_ChecksumCoverage: default: g_warning ("System.Net.Sockets.SocketOptionName 0x%x is not supported at UDP level", mono_name); return -1; } return -1; break; default: g_warning ("System.Net.Sockets.SocketOptionLevel has unknown value 0x%x", mono_level); return -1; } return 0; } static MonoImage* get_socket_assembly (void) { MonoDomain *domain = mono_domain_get (); if (domain->socket_assembly == NULL) { MonoImage *socket_assembly; socket_assembly = mono_image_loaded ("System"); if (!socket_assembly) { MonoAssembly *sa = mono_assembly_open_predicate ("System.dll", FALSE, FALSE, NULL, NULL, NULL); if (!sa) { g_assert_not_reached (); } else { socket_assembly = mono_assembly_get_image (sa); } } mono_atomic_store_release (&domain->socket_assembly, socket_assembly); } return domain->socket_assembly; } gpointer ves_icall_System_Net_Sockets_Socket_Socket_internal (MonoObjectHandle this_obj, gint32 family, gint32 type, gint32 proto, gint32 *werror, MonoError *error) { SOCKET sock; gint32 sock_family; gint32 sock_proto; gint32 sock_type; error_init (error); *werror = 0; sock_family = convert_family ((MonoAddressFamily)family); if (sock_family == -1) { *werror = WSAEAFNOSUPPORT; return NULL; } sock_proto = convert_proto ((MonoProtocolType)proto); if (sock_proto == -1) { *werror = WSAEPROTONOSUPPORT; return NULL; } sock_type = convert_type ((MonoSocketType)type); if (sock_type == -1) { *werror = WSAESOCKTNOSUPPORT; return NULL; } sock = mono_w32socket_socket (sock_family, sock_type, sock_proto); if (sock == INVALID_SOCKET) { *werror = mono_w32socket_get_last_error (); return NULL; } return GUINT_TO_POINTER (sock); } /* FIXME: the SOCKET parameter (here and in other functions in this * file) is really an IntPtr which needs to be converted to a guint32. */ void ves_icall_System_Net_Sockets_Socket_Close_internal (gsize sock, gint32 *werror, MonoError *error) { LOGDEBUG (g_message ("%s: closing 0x%x", __func__, sock)); error_init (error); *werror = 0; /* Clear any pending work item from this socket if the underlying * polling system does not notify when the socket is closed */ mono_threadpool_io_remove_socket (GPOINTER_TO_INT (sock)); mono_w32socket_close ((SOCKET) sock); } gint32 ves_icall_System_Net_Sockets_SocketException_WSAGetLastError_internal (void) { LOGDEBUG (g_message("%s: returning %d", __func__, mono_w32socket_get_last_error ())); return mono_w32socket_get_last_error (); } gint32 ves_icall_System_Net_Sockets_Socket_Available_internal (gsize sock, gint32 *werror, MonoError *error) { int ret; guint64 amount; error_init (error); *werror = 0; /* FIXME: this might require amount to be unsigned long. */ ret = mono_w32socket_get_available (sock, &amount); if (ret == SOCKET_ERROR) { *werror = mono_w32socket_get_last_error (); return 0; } return amount; } void ves_icall_System_Net_Sockets_Socket_Blocking_internal (gsize sock, gboolean block, gint32 *werror, MonoError *error) { int ret; error_init (error); *werror = 0; ret = mono_w32socket_set_blocking (sock, block); if (ret == SOCKET_ERROR) *werror = mono_w32socket_get_last_error (); } gpointer ves_icall_System_Net_Sockets_Socket_Accept_internal (gsize sock, gint32 *werror, gboolean blocking, MonoError *error) { gboolean interrupted; SOCKET newsock; error_init (error); *werror = 0; mono_thread_info_install_interrupt (abort_syscall, (gpointer) (gsize) mono_native_thread_id_get (), &interrupted); if (interrupted) { *werror = WSAEINTR; return NULL; } newsock = mono_w32socket_accept (sock, NULL, 0, blocking); if (newsock == INVALID_SOCKET) *werror = mono_w32socket_get_last_error (); mono_thread_info_uninstall_interrupt (&interrupted); if (interrupted) *werror = WSAEINTR; if (*werror) return NULL; return GUINT_TO_POINTER (newsock); } void ves_icall_System_Net_Sockets_Socket_Listen_internal(gsize sock, guint32 backlog, gint32 *werror, MonoError *error) { int ret; error_init (error); *werror = 0; ret = mono_w32socket_listen (sock, backlog); if (ret == SOCKET_ERROR) *werror = mono_w32socket_get_last_error (); } #ifdef HAVE_STRUCT_SOCKADDR_IN6 // Check whether it's ::ffff::0:0. static gboolean is_ipv4_mapped_any (const struct in6_addr *addr) { int i; for (i = 0; i < 10; i++) { if (addr->s6_addr [i]) return FALSE; } if ((addr->s6_addr [10] != 0xff) || (addr->s6_addr [11] != 0xff)) return FALSE; for (i = 12; i < 16; i++) { if (addr->s6_addr [i]) return FALSE; } return TRUE; } #endif static MonoObjectHandle create_object_handle_from_sockaddr (struct sockaddr *saddr, int sa_size, gint32 *werror, MonoError *error) { MonoDomain *domain = mono_domain_get (); MonoAddressFamily family; error_init (error); /* Build a System.Net.SocketAddress object instance */ if (!domain->sockaddr_class) domain->sockaddr_class = mono_class_load_from_name (get_socket_assembly (), "System.Net", "SocketAddress"); MonoObjectHandle sockaddr_obj = MONO_HANDLE_NEW (MonoObject, mono_object_new_checked (domain, domain->sockaddr_class, error)); return_val_if_nok (error, MONO_HANDLE_NEW (MonoObject, NULL)); /* Locate the SocketAddress data buffer in the object */ if (!domain->sockaddr_data_field) { domain->sockaddr_data_field = mono_class_get_field_from_name (domain->sockaddr_class, "m_Buffer"); g_assert (domain->sockaddr_data_field); } /* Locate the SocketAddress data buffer length in the object */ if (!domain->sockaddr_data_length_field) { domain->sockaddr_data_length_field = mono_class_get_field_from_name (domain->sockaddr_class, "m_Size"); g_assert (domain->sockaddr_data_length_field); } /* May be the +2 here is too conservative, as sa_len returns * the length of the entire sockaddr_in/in6, including * sizeof (unsigned short) of the family */ /* We can't really avoid the +2 as all code below depends on this size - INCLUDING unix domain sockets.*/ MonoArrayHandle data = mono_array_new_handle (domain, mono_get_byte_class (), sa_size + 2, error); return_val_if_nok (error, MONO_HANDLE_NEW (MonoObject, NULL)); /* The data buffer is laid out as follows: * bytes 0 and 1 are the address family * bytes 2 and 3 are the port info * the rest is the address info */ family = convert_to_mono_family (saddr->sa_family); if (family == AddressFamily_Unknown) { *werror = WSAEAFNOSUPPORT; return MONO_HANDLE_NEW (MonoObject, NULL); } MONO_HANDLE_ARRAY_SETVAL (data, guint8, 0, family & 0x0FF); MONO_HANDLE_ARRAY_SETVAL (data, guint8, 1, (family >> 8) & 0x0FF); if (saddr->sa_family == AF_INET) { struct sockaddr_in *sa_in = (struct sockaddr_in *)saddr; guint16 port = ntohs (sa_in->sin_port); guint32 address = ntohl (sa_in->sin_addr.s_addr); int buffer_size = 8; if (sa_size < buffer_size) { mono_error_set_generic_error (error, "System", "SystemException", ""); return MONO_HANDLE_NEW (MonoObject, NULL); } MONO_HANDLE_ARRAY_SETVAL (data, guint8, 2, (port>>8) & 0xff); MONO_HANDLE_ARRAY_SETVAL (data, guint8, 3, (port) & 0xff); MONO_HANDLE_ARRAY_SETVAL (data, guint8, 4, (address>>24) & 0xff); MONO_HANDLE_ARRAY_SETVAL (data, guint8, 5, (address>>16) & 0xff); MONO_HANDLE_ARRAY_SETVAL (data, guint8, 6, (address>>8) & 0xff); MONO_HANDLE_ARRAY_SETVAL (data, guint8, 7, (address) & 0xff); mono_field_set_value (MONO_HANDLE_RAW (sockaddr_obj), domain->sockaddr_data_field, MONO_HANDLE_RAW (data)); /* FIXME: use handles for mono_field_set_value */ mono_field_set_value (MONO_HANDLE_RAW (sockaddr_obj), domain->sockaddr_data_length_field, &buffer_size); /* FIXME: use handles for mono_field_set_value */ return sockaddr_obj; } #ifdef HAVE_STRUCT_SOCKADDR_IN6 else if (saddr->sa_family == AF_INET6) { struct sockaddr_in6 *sa_in = (struct sockaddr_in6 *)saddr; int i; int buffer_size = 28; guint16 port = ntohs (sa_in->sin6_port); if (sa_size < buffer_size) { mono_error_set_generic_error (error, "System", "SystemException", ""); return MONO_HANDLE_NEW (MonoObject, NULL); } MONO_HANDLE_ARRAY_SETVAL (data, guint8, 2, (port>>8) & 0xff); MONO_HANDLE_ARRAY_SETVAL (data, guint8, 3, (port) & 0xff); if (is_ipv4_mapped_any (&sa_in->sin6_addr)) { // Map ::ffff:0:0 to :: (bug #5502) for (i = 0; i < 16; i++) MONO_HANDLE_ARRAY_SETVAL (data, guint8, 8 + i, 0); } else { for (i = 0; i < 16; i++) { MONO_HANDLE_ARRAY_SETVAL (data, guint8, 8 + i, sa_in->sin6_addr.s6_addr [i]); } } MONO_HANDLE_ARRAY_SETVAL (data, guint8, 24, sa_in->sin6_scope_id & 0xff); MONO_HANDLE_ARRAY_SETVAL (data, guint8, 25, (sa_in->sin6_scope_id >> 8) & 0xff); MONO_HANDLE_ARRAY_SETVAL (data, guint8, 26, (sa_in->sin6_scope_id >> 16) & 0xff); MONO_HANDLE_ARRAY_SETVAL (data, guint8, 27, (sa_in->sin6_scope_id >> 24) & 0xff); mono_field_set_value (MONO_HANDLE_RAW (sockaddr_obj), domain->sockaddr_data_field, MONO_HANDLE_RAW (data)); /* FIXME: use handles for mono_field_set_value */ mono_field_set_value (MONO_HANDLE_RAW (sockaddr_obj), domain->sockaddr_data_length_field, &buffer_size); /* FIXME: use handles for mono_field_set_value */ return sockaddr_obj; } #endif #ifdef HAVE_SYS_UN_H else if (saddr->sa_family == AF_UNIX) { int i; int buffer_size = sa_size + 2; for (i = 0; i < sa_size; i++) MONO_HANDLE_ARRAY_SETVAL (data, guint8, i + 2, saddr->sa_data [i]); mono_field_set_value (MONO_HANDLE_RAW (sockaddr_obj), domain->sockaddr_data_field, MONO_HANDLE_RAW (data)); /* FIXME: use handles for mono_field_set_value */ mono_field_set_value (MONO_HANDLE_RAW (sockaddr_obj), domain->sockaddr_data_length_field, &buffer_size); /* FIXME: use handles for mono_field_set_value */ return sockaddr_obj; } #endif else { *werror = WSAEAFNOSUPPORT; return MONO_HANDLE_NEW (MonoObject, NULL); } } static int get_sockaddr_size (int family) { int size; size = 0; if (family == AF_INET) { size = sizeof (struct sockaddr_in); } #ifdef HAVE_STRUCT_SOCKADDR_IN6 else if (family == AF_INET6) { size = sizeof (struct sockaddr_in6); } #endif #ifdef HAVE_SYS_UN_H else if (family == AF_UNIX) { size = sizeof (struct sockaddr_un); } #endif return size; } MonoObjectHandle ves_icall_System_Net_Sockets_Socket_LocalEndPoint_internal (gsize sock, gint32 af, gint32 *werror, MonoError *error) { gchar *sa; socklen_t salen; int ret; *werror = 0; salen = get_sockaddr_size (convert_family ((MonoAddressFamily)af)); if (salen == 0) { *werror = WSAEAFNOSUPPORT; return NULL; } sa = (salen <= 128) ? (gchar *)alloca (salen) : (gchar *)g_malloc0 (salen); ret = mono_w32socket_getsockname (sock, (struct sockaddr *)sa, &salen); if (ret == SOCKET_ERROR) { *werror = mono_w32socket_get_last_error (); if (salen > 128) g_free (sa); return NULL_HANDLE; } LOGDEBUG (g_message("%s: bound to %s port %d", __func__, inet_ntoa (((struct sockaddr_in *)&sa)->sin_addr), ntohs (((struct sockaddr_in *)&sa)->sin_port))); MonoObjectHandle result = create_object_handle_from_sockaddr ((struct sockaddr *)sa, salen, werror, error); if (salen > 128) g_free (sa); return result; } MonoObjectHandle ves_icall_System_Net_Sockets_Socket_RemoteEndPoint_internal (gsize sock, gint32 af, gint32 *werror, MonoError *error) { gchar *sa; socklen_t salen; int ret; error_init (error); *werror = 0; salen = get_sockaddr_size (convert_family ((MonoAddressFamily)af)); if (salen == 0) { *werror = WSAEAFNOSUPPORT; return MONO_HANDLE_NEW (MonoObject, NULL); } sa = (salen <= 128) ? (gchar *)alloca (salen) : (gchar *)g_malloc0 (salen); /* Note: linux returns just 2 for AF_UNIX. Always. */ ret = mono_w32socket_getpeername (sock, (struct sockaddr *)sa, &salen); if (ret == SOCKET_ERROR) { *werror = mono_w32socket_get_last_error (); if (salen > 128) g_free (sa); return MONO_HANDLE_NEW (MonoObject, NULL); } LOGDEBUG (g_message("%s: connected to %s port %d", __func__, inet_ntoa (((struct sockaddr_in *)&sa)->sin_addr), ntohs (((struct sockaddr_in *)&sa)->sin_port))); MonoObjectHandle result = create_object_handle_from_sockaddr ((struct sockaddr *)sa, salen, werror, error); if (salen > 128) g_free (sa); return result; } static struct sockaddr* create_sockaddr_from_handle (MonoObjectHandle saddr_obj, socklen_t *sa_size, gint32 *werror, MonoError *error) { MonoDomain *domain = mono_domain_get (); gint32 family; int len; error_init (error); if (!domain->sockaddr_class) domain->sockaddr_class = mono_class_load_from_name (get_socket_assembly (), "System.Net", "SocketAddress"); /* Locate the SocketAddress data buffer in the object */ if (!domain->sockaddr_data_field) { domain->sockaddr_data_field = mono_class_get_field_from_name (domain->sockaddr_class, "m_Buffer"); g_assert (domain->sockaddr_data_field); } /* Locate the SocketAddress data buffer length in the object */ if (!domain->sockaddr_data_length_field) { domain->sockaddr_data_length_field = mono_class_get_field_from_name (domain->sockaddr_class, "m_Size"); g_assert (domain->sockaddr_data_length_field); } MonoArrayHandle data = MONO_HANDLE_NEW_GET_FIELD (saddr_obj, MonoArray, domain->sockaddr_data_field); /* The data buffer is laid out as follows: * byte 0 is the address family low byte * byte 1 is the address family high byte * INET: * bytes 2 and 3 are the port info * the rest is the address info * UNIX: * the rest is the file name */ len = MONO_HANDLE_GET_FIELD_VAL (saddr_obj, int, domain->sockaddr_data_length_field); g_assert (len >= 2); uint32_t gchandle; guint8 *buf = MONO_ARRAY_HANDLE_PIN (data, guint8, 0, &gchandle); family = convert_family ((MonoAddressFamily)(buf[0] + (buf[1] << 8))); if (family == AF_INET) { struct sockaddr_in *sa; guint16 port; guint32 address; if (len < 8) { mono_error_set_generic_error (error, "System", "SystemException", ""); mono_gchandle_free (gchandle); return NULL; } sa = g_new0 (struct sockaddr_in, 1); port = (buf[2] << 8) + buf[3]; address = (buf[4] << 24) + (buf[5] << 16) + (buf[6] << 8) + buf[7]; sa->sin_family = family; sa->sin_addr.s_addr = htonl (address); sa->sin_port = htons (port); *sa_size = sizeof (struct sockaddr_in); mono_gchandle_free (gchandle); return (struct sockaddr *)sa; } #ifdef HAVE_STRUCT_SOCKADDR_IN6 else if (family == AF_INET6) { struct sockaddr_in6 *sa; int i; guint16 port; guint32 scopeid; if (len < 28) { mono_error_set_generic_error (error, "System", "SystemException", ""); mono_gchandle_free (gchandle); return NULL; } sa = g_new0 (struct sockaddr_in6, 1); port = buf[3] + (buf[2] << 8); scopeid = buf[24] + (buf[25] << 8) + (buf[26] << 16) + (buf[27] << 24); sa->sin6_family = family; sa->sin6_port = htons (port); sa->sin6_scope_id = scopeid; for (i = 0; i < 16; i++) sa->sin6_addr.s6_addr [i] = buf[8 + i]; *sa_size = sizeof (struct sockaddr_in6); mono_gchandle_free (gchandle); return (struct sockaddr *)sa; } #endif #ifdef HAVE_SYS_UN_H else if (family == AF_UNIX) { struct sockaddr_un *sock_un; int i; /* Need a byte for the '\0' terminator/prefix, and the first * two bytes hold the SocketAddress family */ if (len - 2 >= sizeof (sock_un->sun_path)) { mono_error_set_exception_instance (error, mono_get_exception_index_out_of_range ()); mono_gchandle_free (gchandle); return NULL; } sock_un = g_new0 (struct sockaddr_un, 1); sock_un->sun_family = family; for (i = 0; i < len - 2; i++) sock_un->sun_path [i] = buf[i + 2]; *sa_size = len; mono_gchandle_free (gchandle); return (struct sockaddr *)sock_un; } #endif else { *werror = WSAEAFNOSUPPORT; mono_gchandle_free (gchandle); return 0; } } void ves_icall_System_Net_Sockets_Socket_Bind_internal (gsize sock, MonoObjectHandle sockaddr, gint32 *werror, MonoError *error) { struct sockaddr *sa; socklen_t sa_size; int ret; error_init (error); *werror = 0; sa = create_sockaddr_from_handle (sockaddr, &sa_size, werror, error); if (*werror != 0) return; return_if_nok (error); LOGDEBUG (g_message("%s: binding to %s port %d", __func__, inet_ntoa (((struct sockaddr_in *)sa)->sin_addr), ntohs (((struct sockaddr_in *)sa)->sin_port))); ret = mono_w32socket_bind (sock, sa, sa_size); if (ret == SOCKET_ERROR) *werror = mono_w32socket_get_last_error (); g_free (sa); } enum { SelectModeRead, SelectModeWrite, SelectModeError }; MonoBoolean ves_icall_System_Net_Sockets_Socket_Poll_internal (gsize sock, gint mode, gint timeout, gint32 *werror, MonoError *error) { MonoInternalThread *thread = mono_thread_internal_current (); mono_pollfd *pfds; int ret; gboolean interrupted; time_t start; error_init (error); *werror = 0; pfds = g_new0 (mono_pollfd, 1); pfds->fd = GPOINTER_TO_INT (sock); switch (mode) { case SelectModeRead: pfds->events = MONO_POLLIN; break; case SelectModeWrite: pfds->events = MONO_POLLOUT; break; default: pfds->events = MONO_POLLERR | MONO_POLLHUP | MONO_POLLNVAL; break; } timeout = (timeout >= 0) ? (timeout / 1000) : -1; start = time (NULL); do { mono_thread_info_install_interrupt (abort_syscall, (gpointer) (gsize) mono_native_thread_id_get (), &interrupted); if (interrupted) { g_free (pfds); *werror = WSAEINTR; return FALSE; } MONO_ENTER_GC_SAFE; ret = mono_poll (pfds, 1, timeout); MONO_EXIT_GC_SAFE; mono_thread_info_uninstall_interrupt (&interrupted); if (interrupted) { g_free (pfds); *werror = WSAEINTR; return FALSE; } if (timeout > 0 && ret < 0) { int err = errno; int sec = time (NULL) - start; timeout -= sec * 1000; if (timeout < 0) { timeout = 0; } errno = err; } if (ret == -1 && errno == EINTR) { if (mono_thread_test_state (thread, ThreadState_AbortRequested)) { g_free (pfds); return FALSE; } /* Suspend requested? */ mono_thread_interruption_checkpoint (); errno = EINTR; } } while (ret == -1 && errno == EINTR); if (ret == -1) { *werror = mono_w32socket_convert_error (errno); g_free (pfds); return FALSE; } g_free (pfds); return ret != 0; } void ves_icall_System_Net_Sockets_Socket_Connect_internal (gsize sock, MonoObjectHandle sockaddr, gint32 *werror, gboolean blocking, MonoError *error) { struct sockaddr *sa; socklen_t sa_size; int ret; gboolean interrupted; error_init (error); *werror = 0; sa = create_sockaddr_from_handle (sockaddr, &sa_size, werror, error); if (*werror != 0) return; return_if_nok (error); LOGDEBUG (g_message("%s: connecting to %s port %d", __func__, inet_ntoa (((struct sockaddr_in *)sa)->sin_addr), ntohs (((struct sockaddr_in *)sa)->sin_port))); mono_thread_info_install_interrupt (abort_syscall, (gpointer) (gsize) mono_native_thread_id_get (), &interrupted); if (interrupted) { *werror = WSAEINTR; return; } ret = mono_w32socket_connect (sock, sa, sa_size, blocking); if (ret == SOCKET_ERROR) *werror = mono_w32socket_get_last_error (); mono_thread_info_uninstall_interrupt (&interrupted); if (interrupted) *werror = WSAEINTR; g_free (sa); } #if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT | HAVE_UWP_WINAPI_SUPPORT) void ves_icall_System_Net_Sockets_Socket_Disconnect_internal (gsize sock, MonoBoolean reuse, gint32 *werror, MonoError *error) { gboolean interrupted; error_init (error); LOGDEBUG (g_message("%s: disconnecting from socket %p (reuse %d)", __func__, sock, reuse)); mono_thread_info_install_interrupt (abort_syscall, (gpointer) (gsize) mono_native_thread_id_get (), &interrupted); if (interrupted) { *werror = WSAEINTR; return; } *werror = mono_w32socket_disconnect (sock, reuse); mono_thread_info_uninstall_interrupt (&interrupted); if (interrupted) *werror = WSAEINTR; } #endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT | HAVE_UWP_WINAPI_SUPPORT) */ MonoBoolean ves_icall_System_Net_Sockets_Socket_Duplicate_internal (gpointer handle, gint32 targetProcessId, gpointer *duplicate_handle, gint32 *werror, MonoError *error) { error_init (error); *werror = 0; if (!mono_w32socket_duplicate (handle, targetProcessId, duplicate_handle)) { *werror = mono_w32error_get_last (); return FALSE; } return TRUE; } gint32 ves_icall_System_Net_Sockets_Socket_Receive_internal (gsize sock, gchar *buffer, gint32 count, gint32 flags, gint32 *werror, gboolean blocking, MonoError *error) { int ret; int recvflags = 0; gboolean interrupted; error_init (error); *werror = 0; recvflags = convert_socketflags (flags); if (recvflags == -1) { *werror = WSAEOPNOTSUPP; return 0; } mono_thread_info_install_interrupt (abort_syscall, (gpointer) (gsize) mono_native_thread_id_get (), &interrupted); if (interrupted) return 0; ret = mono_w32socket_recv (sock, buffer, count, recvflags, blocking); if (ret == SOCKET_ERROR) *werror = mono_w32socket_get_last_error (); mono_thread_info_uninstall_interrupt (&interrupted); if (interrupted) *werror = WSAEINTR; if (*werror) return 0; return ret; } gint32 ves_icall_System_Net_Sockets_Socket_Receive_array_internal (gsize sock, WSABUF *buffers, gint32 count, gint32 flags, gint32 *werror, gboolean blocking, MonoError *error) { int ret; gboolean interrupted; guint32 recv; guint32 recvflags = 0; error_init (error); *werror = 0; recvflags = convert_socketflags (flags); if (recvflags == -1) { *werror = WSAEOPNOTSUPP; return 0; } mono_thread_info_install_interrupt (abort_syscall, (gpointer) (gsize) mono_native_thread_id_get (), &interrupted); if (interrupted) { *werror = WSAEINTR; return 0; } ret = mono_w32socket_recvbuffers (sock, buffers, count, &recv, &recvflags, NULL, NULL, blocking); if (ret == SOCKET_ERROR) *werror = mono_w32socket_get_last_error (); mono_thread_info_uninstall_interrupt (&interrupted); if (interrupted) *werror = WSAEINTR; if (*werror) return 0; return recv; } gint32 ves_icall_System_Net_Sockets_Socket_ReceiveFrom_internal (gsize sock, gchar *buffer, gint32 count, gint32 flags, MonoObjectHandle sockaddr, gint32 *werror, gboolean blocking, MonoError *error) { int ret; int recvflags = 0; struct sockaddr *sa; socklen_t sa_size; gboolean interrupted; error_init (error); *werror = 0; sa = create_sockaddr_from_handle (sockaddr, &sa_size, werror, error); if (*werror != 0) return 0; if (!is_ok (error)) return 0; recvflags = convert_socketflags (flags); if (recvflags == -1) { *werror = WSAEOPNOTSUPP; return 0; } mono_thread_info_install_interrupt (abort_syscall, (gpointer) (gsize) mono_native_thread_id_get (), &interrupted); if (interrupted) { g_free (sa); *werror = WSAEINTR; return 0; } ret = mono_w32socket_recvfrom (sock, buffer, count, recvflags, sa, &sa_size, blocking); if (ret == SOCKET_ERROR) *werror = mono_w32socket_get_last_error (); mono_thread_info_uninstall_interrupt (&interrupted); if (interrupted) *werror = WSAEINTR; if (*werror) { g_free(sa); return 0; } /* If we didn't get a socket size, then we're probably a * connected connection-oriented socket and the stack hasn't * returned the remote address. All we can do is return null. */ if (sa_size) { MONO_HANDLE_ASSIGN (sockaddr, create_object_handle_from_sockaddr (sa, sa_size, werror, error)); if (!is_ok (error)) { g_free (sa); return 0; } } else { MONO_HANDLE_ASSIGN (sockaddr, MONO_HANDLE_NEW (MonoObject, NULL)); } g_free (sa); return ret; } gint32 ves_icall_System_Net_Sockets_Socket_Send_internal (gsize sock, gchar *buffer, gint32 count, gint32 flags, gint32 *werror, gboolean blocking, MonoError *error) { int ret; int sendflags = 0; gboolean interrupted; error_init (error); *werror = 0; LOGDEBUG (g_message("%s: Sending %d bytes", __func__, count)); sendflags = convert_socketflags (flags); if (sendflags == -1) { *werror = WSAEOPNOTSUPP; return 0; } mono_thread_info_install_interrupt (abort_syscall, (gpointer) (gsize) mono_native_thread_id_get (), &interrupted); if (interrupted) { *werror = WSAEINTR; return 0; } ret = mono_w32socket_send (sock, buffer, count, sendflags, blocking); if (ret == SOCKET_ERROR) *werror = mono_w32socket_get_last_error (); mono_thread_info_uninstall_interrupt (&interrupted); if (interrupted) *werror = WSAEINTR; if (*werror) return 0; return ret; } gint32 ves_icall_System_Net_Sockets_Socket_Send_array_internal (gsize sock, WSABUF *buffers, gint32 count, gint32 flags, gint32 *werror, gboolean blocking, MonoError *error) { int ret; guint32 sent; guint32 sendflags = 0; gboolean interrupted; error_init (error); *werror = 0; sendflags = convert_socketflags (flags); if (sendflags == -1) { *werror = WSAEOPNOTSUPP; return 0; } mono_thread_info_install_interrupt (abort_syscall, (gpointer) (gsize) mono_native_thread_id_get (), &interrupted); if (interrupted) { *werror = WSAEINTR; return 0; } ret = mono_w32socket_sendbuffers (sock, buffers, count, &sent, sendflags, NULL, NULL, blocking); if (ret == SOCKET_ERROR) *werror = mono_w32socket_get_last_error (); mono_thread_info_uninstall_interrupt (&interrupted); if (interrupted) *werror = WSAEINTR; if (*werror) return 0; return sent; } gint32 ves_icall_System_Net_Sockets_Socket_SendTo_internal (gsize sock, gchar *buffer, gint32 count, gint32 flags, MonoObjectHandle sockaddr, gint32 *werror, gboolean blocking, MonoError *error) { int ret; int sendflags = 0; struct sockaddr *sa; socklen_t sa_size; gboolean interrupted; *werror = 0; sa = create_sockaddr_from_handle (sockaddr, &sa_size, werror, error); if (*werror != 0) return 0; return_val_if_nok (error, 0); LOGDEBUG (g_message("%s: Sending %d bytes", __func__, count)); sendflags = convert_socketflags (flags); if (sendflags == -1) { g_free (sa); *werror = WSAEOPNOTSUPP; return 0; } mono_thread_info_install_interrupt (abort_syscall, (gpointer) (gsize) mono_native_thread_id_get (), &interrupted); if (interrupted) { g_free (sa); *werror = WSAEINTR; return 0; } ret = mono_w32socket_sendto (sock, buffer, count, sendflags, sa, sa_size, blocking); if (ret == SOCKET_ERROR) *werror = mono_w32socket_get_last_error (); mono_thread_info_uninstall_interrupt (&interrupted); if (interrupted) *werror = WSAEINTR; g_free(sa); if (*werror) return 0; return ret; } static SOCKET Socket_to_SOCKET (MonoObjectHandle sockobj) { MonoClassField *field; field = mono_class_get_field_from_name (mono_handle_class (sockobj), "m_Handle"); MonoSafeHandleHandle safe_handle = MONO_HANDLE_NEW_GET_FIELD(sockobj, MonoSafeHandle, field); if (MONO_HANDLE_IS_NULL (safe_handle)) return -1; return (SOCKET)MONO_HANDLE_GETVAL (safe_handle, handle); } #define POLL_ERRORS (MONO_POLLERR | MONO_POLLHUP | MONO_POLLNVAL) static gboolean collect_pollfds_from_array (MonoArrayHandle sockets, int i, int nfds, mono_pollfd *pfds, int *idx, int *mode) { HANDLE_FUNCTION_ENTER (); gboolean result = TRUE; MonoObjectHandle obj = MONO_HANDLE_NEW (MonoObject, NULL); MONO_HANDLE_ARRAY_GETREF (obj, sockets, i); if (MONO_HANDLE_IS_NULL (obj)) { (*mode)++; goto leave; } if (*idx >= nfds) { result = FALSE; goto leave; } pfds [*idx].fd = Socket_to_SOCKET (obj); pfds [*idx].events = (*mode == 0) ? MONO_POLLIN : (*mode == 1) ? MONO_POLLOUT : POLL_ERRORS; (*idx)++; leave: HANDLE_FUNCTION_RETURN_VAL (result); } static void set_socks_array_from_pollfds (MonoArrayHandle sockets, int i, mono_pollfd *pfds, int *ret, int *mode, MonoArrayHandle socks, int *idx) { HANDLE_FUNCTION_ENTER (); mono_pollfd *pfd; MonoObjectHandle obj = MONO_HANDLE_NEW (MonoObject, NULL); MONO_HANDLE_ARRAY_GETREF (obj, sockets, i); if (MONO_HANDLE_IS_NULL (obj)) { (*mode)++; (*idx)++; goto leave; } pfd = &pfds [i - *mode]; if (pfd->revents == 0) goto leave; (*ret)--; if (((*mode == 0 && (pfd->revents & (MONO_POLLIN | POLL_ERRORS)) != 0)) || ((*mode == 1 && (pfd->revents & (MONO_POLLOUT | POLL_ERRORS)) != 0)) || ((pfd->revents & POLL_ERRORS) != 0)) { MONO_HANDLE_ARRAY_SETREF (socks, *idx, obj); (*idx)++; } leave: HANDLE_FUNCTION_RETURN (); } void ves_icall_System_Net_Sockets_Socket_Select_internal (MonoArrayHandle sockets, gint32 timeout, gint32 *werror, MonoError *error) { MonoInternalThread *thread = mono_thread_internal_current (); mono_pollfd *pfds; int nfds, idx; int ret; int i, count; int mode; MonoClass *sock_arr_class; time_t start; uintptr_t socks_size; gboolean interrupted; error_init (error); *werror = 0; /* *sockets -> READ, null, WRITE, null, ERROR, null */ count = mono_array_handle_length (sockets); nfds = count - 3; /* NULL separators */ pfds = g_new0 (mono_pollfd, nfds); mode = idx = 0; for (i = 0; i < count; i++) { if (!collect_pollfds_from_array (sockets, i, nfds, pfds, &idx, &mode)) { /* The socket array was bogus */ g_free (pfds); *werror = WSAEFAULT; return; } } timeout = (timeout >= 0) ? (timeout / 1000) : -1; start = time (NULL); do { mono_thread_info_install_interrupt (abort_syscall, (gpointer) (gsize) mono_native_thread_id_get (), &interrupted); if (interrupted) { g_free (pfds); *werror = WSAEINTR; return; } MONO_ENTER_GC_SAFE; ret = mono_poll (pfds, nfds, timeout); MONO_EXIT_GC_SAFE; mono_thread_info_uninstall_interrupt (&interrupted); if (interrupted) { g_free (pfds); *werror = WSAEINTR; return; } if (timeout > 0 && ret < 0) { int err = errno; int sec = time (NULL) - start; timeout -= sec * 1000; if (timeout < 0) timeout = 0; errno = err; } if (ret == -1 && errno == EINTR) { if (mono_thread_test_state (thread, ThreadState_AbortRequested)) { g_free (pfds); MONO_HANDLE_ASSIGN (sockets, MONO_HANDLE_NEW (MonoObject, NULL)); return; } /* Suspend requested? */ mono_thread_interruption_checkpoint (); errno = EINTR; } } while (ret == -1 && errno == EINTR); if (ret == -1) { *werror = mono_w32socket_convert_error (errno); g_free (pfds); return; } if (ret == 0) { g_free (pfds); MONO_HANDLE_ASSIGN (sockets, MONO_HANDLE_NEW (MonoObject, NULL)); return; } sock_arr_class = mono_handle_class (sockets); socks_size = ((uintptr_t)ret) + 3; /* space for the NULL delimiters */ MonoArrayHandle socks = MONO_HANDLE_NEW (MonoArray, mono_array_new_full_checked (mono_domain_get (), sock_arr_class, &socks_size, NULL, error)); if (!is_ok (error)) { g_free (pfds); return; } mode = idx = 0; for (i = 0; i < count && ret > 0; i++) { set_socks_array_from_pollfds (sockets, i, pfds, &ret, &mode, socks, &idx); } MONO_HANDLE_ASSIGN (sockets, socks); g_free (pfds); } static MonoObjectHandle int_to_object_handle (MonoDomain *domain, int val, MonoError *error) { return MONO_HANDLE_NEW (MonoObject, mono_value_box_checked (domain, mono_get_int32_class (), &val, error)); } void ves_icall_System_Net_Sockets_Socket_GetSocketOption_obj_internal (gsize sock, gint32 level, gint32 name, MonoObjectHandle obj_val, gint32 *werror, MonoError *error) { int system_level = 0; int system_name = 0; int ret; int val = 0; socklen_t valsize = sizeof (val); struct linger linger; socklen_t lingersize = sizeof (linger); int time_ms = 0; socklen_t time_ms_size = sizeof (time_ms); #ifdef SO_PEERCRED # if defined(__OpenBSD__) struct sockpeercred cred; # else struct ucred cred; # endif socklen_t credsize = sizeof (cred); #endif MonoDomain *domain = mono_domain_get (); MonoClass *obj_class; MonoClassField *field; error_init (error); *werror = 0; #if !defined(SO_EXCLUSIVEADDRUSE) && defined(SO_REUSEADDR) if (level == SocketOptionLevel_Socket && name == SocketOptionName_ExclusiveAddressUse) { system_level = SOL_SOCKET; system_name = SO_REUSEADDR; ret = 0; } else #endif { ret = convert_sockopt_level_and_name ((MonoSocketOptionLevel)level, (MonoSocketOptionName)name, &system_level, &system_name); } if (ret == -1) { *werror = WSAENOPROTOOPT; return; } if (ret == -2) { MONO_HANDLE_ASSIGN (obj_val, int_to_object_handle (domain, 0, error)); return; } /* No need to deal with MulticastOption names here, because * you cant getsockopt AddMembership or DropMembership (the * int getsockopt will error, causing an exception) */ switch (name) { case SocketOptionName_Linger: case SocketOptionName_DontLinger: ret = mono_w32socket_getsockopt (sock, system_level, system_name, &linger, &lingersize); break; case SocketOptionName_SendTimeout: case SocketOptionName_ReceiveTimeout: ret = mono_w32socket_getsockopt (sock, system_level, system_name, (char *)&time_ms, &time_ms_size); break; #ifdef SO_PEERCRED case SocketOptionName_PeerCred: ret = mono_w32socket_getsockopt (sock, system_level, system_name, &cred, &credsize); break; #endif default: ret = mono_w32socket_getsockopt (sock, system_level, system_name, &val, &valsize); } if (ret == SOCKET_ERROR) { *werror = mono_w32socket_get_last_error (); return; } switch (name) { case SocketOptionName_Linger: { /* build a System.Net.Sockets.LingerOption */ obj_class = mono_class_load_from_name (get_socket_assembly (), "System.Net.Sockets", "LingerOption"); MonoObjectHandle obj = MONO_HANDLE_NEW (MonoObject, mono_object_new_checked (domain, obj_class, error)); return_if_nok (error); /* Locate and set the fields "bool enabled" and "int * lingerTime" */ field = mono_class_get_field_from_name(obj_class, "enabled"); MONO_HANDLE_SET_FIELD_VAL (obj, guint8, field, linger.l_onoff); field = mono_class_get_field_from_name(obj_class, "lingerTime"); MONO_HANDLE_SET_FIELD_VAL (obj, guint32, field, linger.l_linger); MONO_HANDLE_ASSIGN (obj_val, obj); break; } case SocketOptionName_DontLinger: { /* construct a bool int in val - true if linger is off */ MonoObjectHandle obj = int_to_object_handle (domain, !linger.l_onoff, error); return_if_nok (error); MONO_HANDLE_ASSIGN (obj_val, obj); break; } case SocketOptionName_SendTimeout: case SocketOptionName_ReceiveTimeout: { MonoObjectHandle obj = int_to_object_handle (domain, time_ms, error); return_if_nok (error); MONO_HANDLE_ASSIGN (obj_val, obj); break; } #ifdef SO_PEERCRED case SocketOptionName_PeerCred: { /* * build a Mono.Posix.PeerCred+PeerCredData if * possible */ static MonoImage *mono_posix_image = NULL; if (mono_posix_image == NULL) { mono_posix_image = mono_image_loaded ("Mono.Posix"); if (!mono_posix_image) { MonoAssembly *sa = mono_assembly_open_predicate ("Mono.Posix.dll", FALSE, FALSE, NULL, NULL, NULL); if (!sa) { *werror = WSAENOPROTOOPT; return; } else { mono_posix_image = mono_assembly_get_image (sa); } } } obj_class = mono_class_load_from_name (mono_posix_image, "Mono.Posix", "PeerCredData"); MonoPeerCredDataHandle cred_data = MONO_HANDLE_NEW (MonoPeerCredData, mono_object_new_checked (domain, obj_class, error)); return_if_nok (error); MONO_HANDLE_SETVAL (cred_data, pid, gint, cred.pid); MONO_HANDLE_SETVAL (cred_data, uid, gint, cred.uid); MONO_HANDLE_SETVAL (cred_data, gid, gint, cred.gid); MONO_HANDLE_ASSIGN (obj_val, cred_data); break; } #endif default: { #if !defined(SO_EXCLUSIVEADDRUSE) && defined(SO_REUSEADDR) if (level == SocketOptionLevel_Socket && name == SocketOptionName_ExclusiveAddressUse) val = val ? 0 : 1; #endif MonoObjectHandle obj = int_to_object_handle (domain, val, error); return_if_nok (error); MONO_HANDLE_ASSIGN (obj_val, obj); } } } void ves_icall_System_Net_Sockets_Socket_GetSocketOption_arr_internal (gsize sock, gint32 level, gint32 name, MonoArrayHandle byte_val, gint32 *werror, MonoError *error) { int system_level = 0; int system_name = 0; int ret; socklen_t valsize; error_init (error); *werror = 0; ret = convert_sockopt_level_and_name((MonoSocketOptionLevel)level, (MonoSocketOptionName)name, &system_level, &system_name); if (ret == -1) { *werror = WSAENOPROTOOPT; return; } if (ret == -2) return; valsize = mono_array_handle_length (byte_val); uint32_t gchandle; guchar *buf = MONO_ARRAY_HANDLE_PIN (byte_val, guchar, 0, &gchandle); ret = mono_w32socket_getsockopt (sock, system_level, system_name, buf, &valsize); mono_gchandle_free (gchandle); if (ret == SOCKET_ERROR) *werror = mono_w32socket_get_last_error (); } #if defined(HAVE_STRUCT_IP_MREQN) || defined(HAVE_STRUCT_IP_MREQ) static struct in_addr ipaddress_handle_to_struct_in_addr (MonoObjectHandle ipaddr) { struct in_addr inaddr; MonoClassField *field; field = mono_class_get_field_from_name (mono_handle_class (ipaddr), "m_Address"); g_assert (field); /* No idea why .net uses a 64bit type to hold a 32bit value... * * Internal value of IPAddess is in little-endian order */ inaddr.s_addr = GUINT_FROM_LE ((guint32)MONO_HANDLE_GET_FIELD_VAL (ipaddr, guint64, field)); return inaddr; } #ifdef HAVE_STRUCT_SOCKADDR_IN6 static struct in6_addr ipaddress_handle_to_struct_in6_addr (MonoObjectHandle ipaddr) { struct in6_addr in6addr; MonoClassField *field; int i; field = mono_class_get_field_from_name (mono_handle_class (ipaddr), "m_Numbers"); g_assert (field); MonoArrayHandle data = MONO_HANDLE_NEW_GET_FIELD (ipaddr, MonoArray, field); for (i = 0; i < 8; i++) { guint16 v; MONO_HANDLE_ARRAY_GETVAL (v, data, guint16, i); const guint16 s = GUINT16_TO_BE (v); /* Solaris/MacOS have only the 8 bit version. */ #ifndef s6_addr16 in6addr.s6_addr[2 * i + 1] = (s >> 8) & 0xff; in6addr.s6_addr[2 * i] = s & 0xff; #else in6addr.s6_addr16[i] = s; #endif } return in6addr; } #endif #endif #if defined(__APPLE__) || defined(__FreeBSD__) static int get_local_interface_id (int family) { #if !defined(HAVE_GETIFADDRS) || !defined(HAVE_IF_NAMETOINDEX) return 0; #else 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 } #endif /* defined(__APPLE__) || defined(__FreeBSD__) */ void ves_icall_System_Net_Sockets_Socket_SetSocketOption_internal (gsize sock, gint32 level, gint32 name, MonoObjectHandle obj_val, MonoArrayHandle byte_val, gint32 int_val, gint32 *werror, MonoError *error) { struct linger linger; int system_level = 0; int system_name = 0; int ret; int sol_ip; int sol_ipv6; error_init (error); *werror = 0; sol_ipv6 = mono_networking_get_ipv6_protocol (); sol_ip = mono_networking_get_ip_protocol (); ret = convert_sockopt_level_and_name ((MonoSocketOptionLevel)level, (MonoSocketOptionName)name, &system_level, &system_name); #if !defined(SO_EXCLUSIVEADDRUSE) && defined(SO_REUSEADDR) if (level == SocketOptionLevel_Socket && name == SocketOptionName_ExclusiveAddressUse) { system_name = SO_REUSEADDR; int_val = int_val ? 0 : 1; ret = 0; } #endif if (ret == -1) { *werror = WSAENOPROTOOPT; return; } if (ret == -2) return; /* Only one of obj_val, byte_val or int_val has data */ if (!MONO_HANDLE_IS_NULL (obj_val)) { MonoClass *obj_class = mono_handle_class (obj_val); MonoClassField *field; int valsize; switch (name) { case SocketOptionName_Linger: /* Dig out "bool enabled" and "int lingerTime" * fields */ field = mono_class_get_field_from_name (obj_class, "enabled"); linger.l_onoff = MONO_HANDLE_GET_FIELD_VAL (obj_val, guint8, field); field = mono_class_get_field_from_name (obj_class, "lingerTime"); linger.l_linger = MONO_HANDLE_GET_FIELD_VAL (obj_val, guint32, field); valsize = sizeof (linger); ret = mono_w32socket_setsockopt (sock, system_level, system_name, &linger, valsize); break; case SocketOptionName_AddMembership: case SocketOptionName_DropMembership: #if defined(HAVE_STRUCT_IP_MREQN) || defined(HAVE_STRUCT_IP_MREQ) { MonoObjectHandle address = MONO_HANDLE_NEW (MonoObject, NULL); #ifdef HAVE_STRUCT_SOCKADDR_IN6 if (system_level == sol_ipv6) { struct ipv6_mreq mreq6; /* * Get group address */ field = mono_class_get_field_from_name (obj_class, "m_Group"); g_assert (field); MONO_HANDLE_ASSIGN (address, MONO_HANDLE_NEW_GET_FIELD (obj_val, MonoObject, field)); if (!MONO_HANDLE_IS_NULL (address)) mreq6.ipv6mr_multiaddr = ipaddress_handle_to_struct_in6_addr (address); field = mono_class_get_field_from_name (obj_class, "m_Interface"); mreq6.ipv6mr_interface = MONO_HANDLE_GET_FIELD_VAL (obj_val, guint64, field); #if defined(__APPLE__) || defined(__FreeBSD__) /* * Bug #5504: * * Mac OS Lion doesn't allow ipv6mr_interface = 0. * * Tests on Windows and Linux show that the multicast group is only * joined on one NIC when interface = 0, so we simply use the interface * id from the first non-loopback interface (this is also what * Dns.GetHostName (string.Empty) would return). */ if (!mreq6.ipv6mr_interface) mreq6.ipv6mr_interface = get_local_interface_id (AF_INET6); #endif ret = mono_w32socket_setsockopt (sock, system_level, system_name, &mreq6, sizeof (mreq6)); break; // Don't check sol_ip } #endif if (system_level == sol_ip) { #ifdef HAVE_STRUCT_IP_MREQN struct ip_mreqn mreq = {{0}}; #else struct ip_mreq mreq = {{0}}; #endif /* HAVE_STRUCT_IP_MREQN */ /* * pain! MulticastOption holds two IPAddress * members, so I have to dig the value out of * those :-( */ field = mono_class_get_field_from_name (obj_class, "group"); MONO_HANDLE_ASSIGN (address, MONO_HANDLE_NEW_GET_FIELD (obj_val, MonoObject, field)); /* address might not be defined and if so, set the address to ADDR_ANY. */ if (!MONO_HANDLE_IS_NULL (address)) mreq.imr_multiaddr = ipaddress_handle_to_struct_in_addr (address); field = mono_class_get_field_from_name (obj_class, "localAddress"); MONO_HANDLE_ASSIGN (address, MONO_HANDLE_NEW_GET_FIELD (obj_val, MonoObject, field)); #ifdef HAVE_STRUCT_IP_MREQN if (!MONO_HANDLE_IS_NULL (address)) mreq.imr_address = ipaddress_handle_to_struct_in_addr (address); field = mono_class_get_field_from_name (obj_class, "ifIndex"); mreq.imr_ifindex = MONO_HANDLE_GET_FIELD_VAL (obj_val, gint32, field); #else if (!MONO_HANDLE_IS_NULL (address)) mreq.imr_interface = ipaddress_handle_to_struct_in_addr (address); #endif /* HAVE_STRUCT_IP_MREQN */ ret = mono_w32socket_setsockopt (sock, system_level, system_name, &mreq, sizeof (mreq)); } break; } #endif /* HAVE_STRUCT_IP_MREQN || HAVE_STRUCT_IP_MREQ */ default: /* Cause an exception to be thrown */ *werror = WSAEINVAL; return; } } else if (!MONO_HANDLE_IS_NULL (byte_val)) { int valsize = mono_array_handle_length (byte_val); uint32_t gchandle; guchar *buf = MONO_ARRAY_HANDLE_PIN (byte_val, guchar, 0, &gchandle); switch(name) { case SocketOptionName_DontLinger: if (valsize == 1) { linger.l_onoff = (*buf) ? 0 : 1; linger.l_linger = 0; ret = mono_w32socket_setsockopt (sock, system_level, system_name, &linger, sizeof (linger)); } else { *werror = WSAEINVAL; } break; default: ret = mono_w32socket_setsockopt (sock, system_level, system_name, buf, valsize); break; } mono_gchandle_free (gchandle); } else { /* ReceiveTimeout/SendTimeout get here */ switch (name) { case SocketOptionName_DontLinger: linger.l_onoff = !int_val; linger.l_linger = 0; ret = mono_w32socket_setsockopt (sock, system_level, system_name, &linger, sizeof (linger)); break; case SocketOptionName_MulticastInterface: #ifndef HOST_WIN32 #ifdef HAVE_STRUCT_IP_MREQN int_val = GUINT32_FROM_BE (int_val); if ((int_val & 0xff000000) == 0) { /* int_val is interface index */ struct ip_mreqn mreq = {{0}}; mreq.imr_ifindex = int_val; ret = mono_w32socket_setsockopt (sock, system_level, system_name, (char *) &mreq, sizeof (mreq)); break; } int_val = GUINT32_TO_BE (int_val); #endif /* HAVE_STRUCT_IP_MREQN */ #endif /* HOST_WIN32 */ /* int_val is in_addr */ ret = mono_w32socket_setsockopt (sock, system_level, system_name, (char *) &int_val, sizeof (int_val)); break; case SocketOptionName_DontFragment: #ifdef HAVE_IP_MTU_DISCOVER /* Fiddle with the value slightly if we're * turning DF on */ if (int_val == 1) int_val = IP_PMTUDISC_DO; /* Fall through */ #endif default: ret = mono_w32socket_setsockopt (sock, system_level, system_name, (char *) &int_val, sizeof (int_val)); } } if (ret == SOCKET_ERROR) *werror = mono_w32socket_get_last_error (); } void ves_icall_System_Net_Sockets_Socket_Shutdown_internal (gsize sock, gint32 how, gint32 *werror, MonoError *error) { int ret; gboolean interrupted; error_init (error); *werror = 0; mono_thread_info_install_interrupt (abort_syscall, (gpointer) (gsize) mono_native_thread_id_get (), &interrupted); if (interrupted) { *werror = WSAEINTR; return; } /* Currently, the values for how (recv=0, send=1, both=2) match the BSD API */ ret = mono_w32socket_shutdown (sock, how); if (ret == SOCKET_ERROR) *werror = mono_w32socket_get_last_error (); mono_thread_info_uninstall_interrupt (&interrupted); if (interrupted) { *werror = WSAEINTR; } } gint ves_icall_System_Net_Sockets_Socket_IOControl_internal (gsize sock, gint32 code, MonoArrayHandle input, MonoArrayHandle output, gint32 *werror, MonoError *error) { glong output_bytes = 0; gchar *i_buffer, *o_buffer; gint i_len, o_len; uint32_t i_gchandle, o_gchandle; gint ret; error_init (error); *werror = 0; if ((guint32)code == FIONBIO) /* Invalid command. Must use Socket.Blocking */ return -1; if (MONO_HANDLE_IS_NULL (input)) { i_buffer = NULL; i_len = 0; i_gchandle = 0; } else { i_len = mono_array_handle_length (input); i_buffer = MONO_ARRAY_HANDLE_PIN (input, gchar, 0, &i_gchandle); } if (MONO_HANDLE_IS_NULL (output)) { o_buffer = NULL; o_len = 0; o_gchandle = 0; } else { o_len = mono_array_handle_length (output); o_buffer = MONO_ARRAY_HANDLE_PIN (output, gchar, 0, &o_gchandle); } ret = mono_w32socket_ioctl (sock, code, i_buffer, i_len, o_buffer, o_len, &output_bytes); if (i_gchandle) mono_gchandle_free (i_gchandle); if (o_gchandle) mono_gchandle_free (o_gchandle); if (ret == SOCKET_ERROR) { *werror = mono_w32socket_get_last_error (); return -1; } return (gint)output_bytes; } static gboolean addrinfo_add_string (MonoDomain *domain, const char *s, MonoArrayHandle arr, int index, MonoError *error) { HANDLE_FUNCTION_ENTER (); error_init (error); MonoStringHandle str = mono_string_new_handle (domain, s, error); if (!is_ok (error)) goto leave; MONO_HANDLE_ARRAY_SETREF (arr, index, str); leave: HANDLE_FUNCTION_RETURN_VAL (is_ok (error)); } static int addrinfo_add_local_ips (MonoDomain *domain, MonoArrayHandleOut h_addr_list, MonoError *error) { HANDLE_FUNCTION_ENTER (); struct in_addr *local_in = NULL; int nlocal_in = 0; struct in6_addr *local_in6 = NULL; int nlocal_in6 = 0; int addr_index = 0; error_init (error); local_in = (struct in_addr *) mono_get_local_interfaces (AF_INET, &nlocal_in); local_in6 = (struct in6_addr *) mono_get_local_interfaces (AF_INET6, &nlocal_in6); if (nlocal_in || nlocal_in6) { char addr [INET6_ADDRSTRLEN]; MONO_HANDLE_ASSIGN (h_addr_list, mono_array_new_handle (domain, mono_get_string_class (), nlocal_in + nlocal_in6, error)); if (!is_ok (error)) goto leave; if (nlocal_in) { int i; for (i = 0; i < nlocal_in; i++) { MonoAddress maddr; mono_address_init (&maddr, AF_INET, &local_in [i]); if (mono_networking_addr_to_str (&maddr, addr, sizeof (addr))) { if (!addrinfo_add_string (domain, addr, h_addr_list, addr_index, error)) goto leave; addr_index++; } } } #ifdef HAVE_STRUCT_SOCKADDR_IN6 if (nlocal_in6) { int i; for (i = 0; i < nlocal_in6; i++) { MonoAddress maddr; mono_address_init (&maddr, AF_INET6, &local_in6 [i]); if (mono_networking_addr_to_str (&maddr, addr, sizeof (addr))) { if (!addrinfo_add_string (domain, addr, h_addr_list, addr_index, error)) goto leave; addr_index++; } } } #endif } leave: g_free (local_in); g_free (local_in6); HANDLE_FUNCTION_RETURN_VAL (addr_index); } static gboolean addrinfo_to_IPHostEntry_handles (MonoAddressInfo *info, MonoStringHandleOut h_name, MonoArrayHandleOut h_aliases, MonoArrayHandleOut h_addr_list, gboolean add_local_ips, MonoError *error) { HANDLE_FUNCTION_ENTER (); MonoAddressEntry *ai = NULL; MonoDomain *domain = mono_domain_get (); error_init (error); MONO_HANDLE_ASSIGN (h_aliases, mono_array_new_handle (domain, mono_get_string_class (), 0, error)); if (!is_ok (error)) goto leave; if (add_local_ips) { int addr_index = addrinfo_add_local_ips (domain, h_addr_list, error); if (!is_ok (error)) goto leave; if (addr_index > 0) goto leave; } gint32 count = 0; for (ai = info->entries; ai != NULL; ai = ai->next) { if (ai->family != AF_INET && ai->family != AF_INET6) continue; count++; } int addr_index = 0; MONO_HANDLE_ASSIGN (h_addr_list, mono_array_new_handle (domain, mono_get_string_class (), count, error)); if (!is_ok (error)) goto leave; gboolean name_assigned = FALSE; for (ai = info->entries; ai != NULL; ai = ai->next) { MonoAddress maddr; char buffer [INET6_ADDRSTRLEN]; /* Max. size for IPv6 */ if ((ai->family != PF_INET) && (ai->family != PF_INET6)) continue; mono_address_init (&maddr, ai->family, &ai->address); const char *addr = NULL; if (mono_networking_addr_to_str (&maddr, buffer, sizeof (buffer))) addr = buffer; else addr = ""; if (!addrinfo_add_string (domain, addr, h_addr_list, addr_index, error)) goto leave; if (!name_assigned) { name_assigned = TRUE; const char *name = ai->canonical_name != NULL ? ai->canonical_name : buffer; MONO_HANDLE_ASSIGN (h_name, mono_string_new_handle (domain, name, error)); if (!is_ok (error)) goto leave; } addr_index++; } leave: if (info) mono_free_address_info (info); HANDLE_FUNCTION_RETURN_VAL (is_ok (error)); } MonoBoolean ves_icall_System_Net_Dns_GetHostByName_internal (MonoStringHandle host, MonoStringHandleOut h_name, MonoArrayHandleOut h_aliases, MonoArrayHandleOut h_addr_list, gint32 hint, MonoError *error) { gboolean add_local_ips = FALSE, add_info_ok = TRUE; gchar this_hostname [256]; MonoAddressInfo *info = NULL; error_init (error); char *hostname = mono_string_handle_to_utf8 (host, error); return_val_if_nok (error, FALSE); if (*hostname == '\0') { add_local_ips = TRUE; MONO_HANDLE_ASSIGN (h_name, host); } if (!add_local_ips && gethostname (this_hostname, sizeof (this_hostname)) != -1) { if (!strcmp (hostname, this_hostname)) { add_local_ips = TRUE; MONO_HANDLE_ASSIGN (h_name, host); } } #ifdef HOST_WIN32 // Win32 APIs already returns local interface addresses for empty hostname ("") // so we never want to add them manually. add_local_ips = FALSE; if (mono_get_address_info(hostname, 0, MONO_HINT_CANONICAL_NAME | hint, &info)) add_info_ok = FALSE; #else if (*hostname && mono_get_address_info (hostname, 0, MONO_HINT_CANONICAL_NAME | hint, &info)) add_info_ok = FALSE; #endif g_free(hostname); if (add_info_ok) { MonoBoolean result = addrinfo_to_IPHostEntry_handles (info, h_name, h_aliases, h_addr_list, add_local_ips, error); return result; } return FALSE; } MonoBoolean ves_icall_System_Net_Dns_GetHostByAddr_internal (MonoStringHandle addr, MonoStringHandleOut h_name, MonoArrayHandleOut h_aliases, MonoArrayHandleOut h_addr_list, gint32 hint, MonoError *error) { char *address; struct sockaddr_in saddr; #ifdef HAVE_STRUCT_SOCKADDR_IN6 struct sockaddr_in6 saddr6; #endif MonoAddressInfo *info = NULL; gint32 family; gchar hostname [NI_MAXHOST] = { 0 }; gboolean ret; error_init (error); address = mono_string_handle_to_utf8 (addr, error); return_val_if_nok (error, FALSE); if (inet_pton (AF_INET, address, &saddr.sin_addr ) == 1) { family = AF_INET; saddr.sin_family = AF_INET; } #ifdef HAVE_STRUCT_SOCKADDR_IN6 else if (inet_pton (AF_INET6, address, &saddr6.sin6_addr) == 1) { family = AF_INET6; saddr6.sin6_family = AF_INET6; } #endif else { g_free (address); return FALSE; } g_free (address); switch (family) { case AF_INET: { #if HAVE_SOCKADDR_IN_SIN_LEN saddr.sin_len = sizeof (saddr); #endif MONO_ENTER_GC_SAFE; ret = getnameinfo ((struct sockaddr*)&saddr, sizeof (saddr), hostname, sizeof (hostname), NULL, 0, 0) == 0; MONO_EXIT_GC_SAFE; break; } #ifdef HAVE_STRUCT_SOCKADDR_IN6 case AF_INET6: { #if HAVE_SOCKADDR_IN6_SIN_LEN saddr6.sin6_len = sizeof (saddr6); #endif MONO_ENTER_GC_SAFE; ret = getnameinfo ((struct sockaddr*)&saddr6, sizeof (saddr6), hostname, sizeof (hostname), NULL, 0, 0) == 0; MONO_EXIT_GC_SAFE; break; } #endif default: g_assert_not_reached (); } if (!ret) return FALSE; if (mono_get_address_info (hostname, 0, hint | MONO_HINT_CANONICAL_NAME | MONO_HINT_CONFIGURED_ONLY, &info) != 0) return FALSE; MonoBoolean result = addrinfo_to_IPHostEntry_handles (info, h_name, h_aliases, h_addr_list, FALSE, error); return result; } MonoBoolean ves_icall_System_Net_Dns_GetHostName_internal (MonoStringHandleOut h_name, MonoError *error) { gchar hostname [NI_MAXHOST] = { 0 }; int ret; error_init (error); MONO_ENTER_GC_SAFE; ret = gethostname (hostname, sizeof (hostname)); MONO_EXIT_GC_SAFE; if (ret == -1) return FALSE; MONO_HANDLE_ASSIGN (h_name, mono_string_new_handle (mono_domain_get (), hostname, error)); return TRUE; } #if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT | HAVE_UWP_WINAPI_SUPPORT) gboolean ves_icall_System_Net_Sockets_Socket_SendFile_internal (gsize sock, MonoStringHandle filename, MonoArrayHandle pre_buffer, MonoArrayHandle post_buffer, gint flags, gint32 *werror, gboolean blocking, MonoError *error) { HANDLE file; gboolean ret; gboolean interrupted; TRANSMIT_FILE_BUFFERS buffers; uint32_t pre_buffer_gchandle = 0; uint32_t post_buffer_gchandle = 0; error_init (error); *werror = 0; if (MONO_HANDLE_IS_NULL (filename)) return FALSE; /* FIXME: replace file by a proper fd that we can call open and close on, as they are interruptible */ uint32_t filename_gchandle; gunichar2 *filename_chars = mono_string_handle_pin_chars (filename, &filename_gchandle); file = mono_w32file_create (filename_chars, GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING, 0); mono_gchandle_free (filename_gchandle); if (file == INVALID_HANDLE_VALUE) { *werror = mono_w32error_get_last (); return FALSE; } mono_thread_info_install_interrupt (abort_syscall, (gpointer) (gsize) mono_native_thread_id_get (), &interrupted); if (interrupted) { mono_w32file_close (file); mono_w32error_set_last (WSAEINTR); return FALSE; } memset (&buffers, 0, sizeof (buffers)); if (!MONO_HANDLE_IS_NULL (pre_buffer)) { buffers.Head = MONO_ARRAY_HANDLE_PIN (pre_buffer, guchar, 0, &pre_buffer_gchandle); buffers.HeadLength = mono_array_handle_length (pre_buffer); } if (!MONO_HANDLE_IS_NULL (post_buffer)) { buffers.Tail = MONO_ARRAY_HANDLE_PIN (post_buffer, guchar, 0, &post_buffer_gchandle); buffers.TailLength = mono_array_handle_length (post_buffer); } ret = mono_w32socket_transmit_file (sock, file, &buffers, flags, blocking); if (pre_buffer_gchandle) mono_gchandle_free (pre_buffer_gchandle); if (post_buffer_gchandle) mono_gchandle_free (post_buffer_gchandle); if (!ret) *werror = mono_w32socket_get_last_error (); mono_thread_info_uninstall_interrupt (&interrupted); if (interrupted) { mono_w32file_close (file); *werror = WSAEINTR; return FALSE; } mono_w32file_close (file); if (*werror) return FALSE; return ret; } #endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT | HAVE_UWP_WINAPI_SUPPORT) */ void mono_network_init (void) { mono_networking_init (); mono_w32socket_initialize (); } void mono_network_cleanup (void) { mono_w32socket_cleanup (); mono_networking_shutdown (); } void icall_cancel_blocking_socket_operation (MonoThreadObjectHandle thread, MonoError *error) { error_init (error); MonoInternalThreadHandle internal = MONO_HANDLE_NEW_GET (MonoInternalThread, thread, internal_thread); g_assert (!MONO_HANDLE_IS_NULL (internal)); guint64 tid = mono_internal_thread_handle_ptr (internal)->tid; mono_thread_info_abort_socket_syscall_for_close (MONO_UINT_TO_NATIVE_THREAD_ID (tid)); } #endif /* #ifndef DISABLE_SOCKETS */