/**
|
* \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 <config.h>
|
|
#ifndef DISABLE_SOCKETS
|
|
#if defined(__APPLE__) || defined(__FreeBSD__)
|
#define __APPLE_USE_RFC_3542
|
#endif
|
|
#include <glib.h>
|
#include <string.h>
|
#include <stdlib.h>
|
#ifdef HOST_WIN32
|
#include <ws2tcpip.h>
|
#else
|
#include <sys/socket.h>
|
#ifdef HAVE_SYS_IOCTL_H
|
#include <sys/ioctl.h>
|
#endif
|
#include <netinet/in.h>
|
#include <netinet/tcp.h>
|
#ifdef HAVE_NETDB_H
|
#include <netdb.h>
|
#endif
|
#include <arpa/inet.h>
|
#endif
|
#ifdef HAVE_UNISTD_H
|
#include <unistd.h>
|
#endif
|
#include <errno.h>
|
|
#include <sys/types.h>
|
|
#include <mono/metadata/object.h>
|
#include <mono/metadata/exception.h>
|
#include <mono/metadata/assembly-internals.h>
|
#include <mono/metadata/appdomain.h>
|
#include <mono/metadata/w32file.h>
|
#include <mono/metadata/threads.h>
|
#include <mono/metadata/threads-types.h>
|
#include <mono/metadata/threadpool-io.h>
|
#include <mono/utils/mono-poll.h>
|
/* FIXME change this code to not mess so much with the internals */
|
#include <mono/metadata/class-internals.h>
|
#include <mono/metadata/domain-internals.h>
|
#include <mono/utils/mono-threads.h>
|
#include <mono/utils/mono-memory-model.h>
|
#include <mono/utils/networking.h>
|
#include <mono/metadata/w32handle.h>
|
#include <mono/metadata/w32socket.h>
|
#include <mono/metadata/w32socket-internals.h>
|
#include <mono/metadata/w32error.h>
|
|
#include <time.h>
|
#ifdef HAVE_SYS_TIME_H
|
#include <sys/time.h>
|
#endif
|
#ifdef HAVE_SYS_IOCTL_H
|
#include <sys/ioctl.h>
|
#endif
|
#ifdef HAVE_NET_IF_H
|
#include <net/if.h>
|
#endif
|
|
#ifdef HAVE_NETDB_H
|
#include <netdb.h>
|
#endif
|
#ifdef HAVE_SYS_FILIO_H
|
#include <sys/filio.h> /* defines FIONBIO and FIONREAD */
|
#endif
|
#ifdef HAVE_SYS_SOCKIO_H
|
#include <sys/sockio.h> /* defines SIOCATMARK */
|
#endif
|
#ifdef HAVE_SYS_UN_H
|
#include <sys/un.h>
|
#endif
|
|
#ifdef HAVE_GETIFADDRS
|
// <net/if.h> must be included before <ifaddrs.h>
|
#include <ifaddrs.h>
|
#endif
|
|
#if defined(_MSC_VER) && G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT | HAVE_UWP_WINAPI_SUPPORT)
|
#include <MSWSock.h>
|
#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 ();
|
|
#ifdef HAVE_IP_MTU_DISCOVER
|
if (system_name == IP_MTU_DISCOVER) {
|
switch (system_level) {
|
case IP_PMTUDISC_DONT:
|
case IP_PMTUDISC_WANT:
|
case IP_PMTUDISC_DO:
|
#ifdef IP_PMTUDISC_PROBE
|
case IP_PMTUDISC_PROBE:
|
#endif
|
#ifdef IP_PMTUDISC_INTERFACE
|
case IP_PMTUDISC_INTERFACE:
|
#endif
|
#ifdef IP_PMTUDISC_OMIT
|
case IP_PMTUDISC_OMIT:
|
#endif
|
/*
|
* This happens if HAVE_IP_MTU_DISCOVER is set but the OS
|
* doesn't actually understand it. The only OS that this is
|
* known to happen on currently is Windows Subsystem for Linux
|
* (newer versions have been fixed to recognize it). Just
|
* pretend everything is fine.
|
*/
|
ret = 0;
|
*werror = 0;
|
break;
|
default:
|
break;
|
}
|
}
|
#endif
|
}
|
}
|
|
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;
|
}
|
|
MonoBoolean
|
ves_icall_System_Net_Sockets_Socket_IsProtocolSupported_internal (gint32 networkInterface)
|
{
|
return TRUE;
|
}
|
|
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);
|
goto_if_nok (error, 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));
|
goto_if_nok (error, 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));
|
goto_if_nok (error, leave);
|
if (add_local_ips) {
|
int addr_index = addrinfo_add_local_ips (domain, h_addr_list, error);
|
goto_if_nok (error, 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));
|
goto_if_nok (error, 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));
|
goto_if_nok (error, 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 */
|