Merge pull request #3749 from BrzVlad/fix-mips-fix
[mono.git] / mono / metadata / socket-io.c
index c6374c00c450ad8846c2f45bbc5354d446f60d5e..3d59231813a4930c9467ccd3500ab869a1088154 100644 (file)
@@ -62,6 +62,7 @@
 #include <mono/utils/mono-threads.h>
 #include <mono/utils/mono-memory-model.h>
 #include <mono/utils/networking.h>
+#include <mono/utils/w32handle.h>
 
 #include <time.h>
 #ifdef HAVE_SYS_TIME_H
@@ -93,6 +94,9 @@
 #endif
 
 #include "mono/io-layer/socket-wrappers.h"
+#ifdef HOST_WIN32
+#include "mono/metadata/socket-io-windows-internals.h"
+#endif
 
 #define LOGDEBUG(...)  
 /* define LOGDEBUG(...) g_message(__VA_ARGS__)  */
@@ -587,61 +591,6 @@ get_socket_assembly (void)
        return domain->socket_assembly;
 }
 
-static gint32
-get_family_hint (MonoError *error)
-{
-       MonoDomain *domain = mono_domain_get ();
-
-       mono_error_init (error);
-
-       if (!domain->inet_family_hint) {
-               MonoImage *socket_assembly;
-               MonoClass *socket_class;
-               MonoClassField *ipv6_field, *ipv4_field;
-               gint32 ipv6_enabled = -1, ipv4_enabled = -1;
-               MonoVTable *vtable;
-
-               socket_assembly = get_socket_assembly ();
-               g_assert (socket_assembly);
-
-               socket_class = mono_class_load_from_name (socket_assembly, "System.Net.Sockets", "Socket");
-
-               ipv4_field = mono_class_get_field_from_name (socket_class, "ipv4_supported");
-               g_assert (ipv4_field);
-
-               ipv6_field = mono_class_get_field_from_name (socket_class, "ipv6_supported");
-               g_assert (ipv6_field);
-
-               vtable = mono_class_vtable (mono_domain_get (), socket_class);
-               g_assert (vtable);
-
-               mono_runtime_class_init_full (vtable, error);
-               return_val_if_nok (error, -1);
-
-               mono_field_static_get_value_checked (vtable, ipv4_field, &ipv4_enabled, error);
-               return_val_if_nok (error, -1);
-               mono_field_static_get_value_checked (vtable, ipv6_field, &ipv6_enabled, error);
-               return_val_if_nok (error, -1);
-
-               mono_domain_lock (domain);
-               if (ipv4_enabled == 1 && ipv6_enabled == 1) {
-                       domain->inet_family_hint = 1;
-               } else if (ipv4_enabled == 1) {
-                       domain->inet_family_hint = 2;
-               } else {
-                       domain->inet_family_hint = 3;
-               }
-               mono_domain_unlock (domain);
-       }
-       switch (domain->inet_family_hint) {
-       case 1: return PF_UNSPEC;
-       case 2: return PF_INET;
-       case 3: return PF_INET6;
-       default:
-               return PF_UNSPEC;
-       }
-}
-
 gpointer
 ves_icall_System_Net_Sockets_Socket_Socket_internal (MonoObject *this_obj, gint32 family, gint32 type, gint32 proto, gint32 *werror)
 {
@@ -695,9 +644,9 @@ ves_icall_System_Net_Sockets_Socket_Close_internal (SOCKET sock, gint32 *werror)
         * polling system does not notify when the socket is closed */
        mono_threadpool_ms_io_remove_socket (GPOINTER_TO_INT (sock));
 
-       MONO_PREPARE_BLOCKING;
+       MONO_ENTER_GC_SAFE;
        closesocket (sock);
-       MONO_FINISH_BLOCKING;
+       MONO_EXIT_GC_SAFE;
 }
 
 gint32
@@ -758,31 +707,30 @@ ves_icall_System_Net_Sockets_Socket_Accept_internal (SOCKET sock, gint32 *werror
                return NULL;
        }
 
-       MONO_PREPARE_BLOCKING;
+       MONO_ENTER_GC_SAFE;
 
 #ifdef HOST_WIN32
        {
                MonoInternalThread *curthread = mono_thread_internal_current ();
                curthread->interrupt_on_stop = (gpointer)TRUE;
-               newsock = _wapi_accept (sock, NULL, 0);
+               newsock = alertable_accept (sock, NULL, 0, blocking);
                curthread->interrupt_on_stop = (gpointer)FALSE;
        }
 #else
        newsock = _wapi_accept (sock, NULL, 0);
 #endif
 
-       MONO_FINISH_BLOCKING;
+       MONO_EXIT_GC_SAFE;
+
+       if (newsock == INVALID_SOCKET)
+               *werror = WSAGetLastError ();
 
        mono_thread_info_uninstall_interrupt (&interrupted);
-       if (interrupted) {
+       if (interrupted)
                *werror = WSAEINTR;
-               return NULL;
-       }
 
-       if (newsock == INVALID_SOCKET) {
-               *werror = WSAGetLastError ();
+       if (*werror)
                return NULL;
-       }
        
        return GUINT_TO_POINTER (newsock);
 }
@@ -794,11 +742,11 @@ ves_icall_System_Net_Sockets_Socket_Listen_internal(SOCKET sock, guint32 backlog
        
        *werror = 0;
 
-       MONO_PREPARE_BLOCKING;
+       MONO_ENTER_GC_SAFE;
 
        ret = _wapi_listen (sock, backlog);
 
-       MONO_FINISH_BLOCKING;
+       MONO_EXIT_GC_SAFE;
 
        if (ret == SOCKET_ERROR)
                *werror = WSAGetLastError ();
@@ -841,10 +789,16 @@ create_object_from_sockaddr (struct sockaddr *saddr, int sa_size, gint32 *werror
        
        /* 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, "data");
+               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 */
@@ -871,8 +825,9 @@ create_object_from_sockaddr (struct sockaddr *saddr, int sa_size, gint32 *werror
                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 < 8) {
+               if (sa_size < buffer_size) {
                        mono_error_set_exception_instance (error, mono_exception_from_name (mono_get_corlib (), "System", "SystemException"));
                        return NULL;
                }
@@ -885,15 +840,17 @@ create_object_from_sockaddr (struct sockaddr *saddr, int sa_size, gint32 *werror
                mono_array_set (data, guint8, 7, (address) & 0xff);
        
                mono_field_set_value (sockaddr_obj, domain->sockaddr_data_field, data);
+               mono_field_set_value (sockaddr_obj, domain->sockaddr_data_length_field, &buffer_size);
 
                return sockaddr_obj;
        } 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 < 28) {
+               if (sa_size < buffer_size) {
                        mono_error_set_exception_instance (error, mono_exception_from_name (mono_get_corlib (), "System", "SystemException"));
                        return NULL;
                }
@@ -921,17 +878,20 @@ create_object_from_sockaddr (struct sockaddr *saddr, int sa_size, gint32 *werror
                                                (sa_in->sin6_scope_id >> 24) & 0xff);
 
                mono_field_set_value (sockaddr_obj, domain->sockaddr_data_field, data);
+               mono_field_set_value (sockaddr_obj, domain->sockaddr_data_length_field, &buffer_size);
 
                return sockaddr_obj;
        }
 #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_array_set (data, guint8, i + 2, saddr->sa_data [i]);
                
                mono_field_set_value (sockaddr_obj, domain->sockaddr_data_field, data);
+               mono_field_set_value (sockaddr_obj, domain->sockaddr_data_length_field, &buffer_size);
 
                return sockaddr_obj;
        }
@@ -979,11 +939,11 @@ ves_icall_System_Net_Sockets_Socket_LocalEndPoint_internal (SOCKET sock, gint32
        }
        sa = (salen <= 128) ? (gchar *)alloca (salen) : (gchar *)g_malloc0 (salen);
 
-       MONO_PREPARE_BLOCKING;
+       MONO_ENTER_GC_SAFE;
 
        ret = _wapi_getsockname (sock, (struct sockaddr *)sa, &salen);
 
-       MONO_FINISH_BLOCKING;
+       MONO_EXIT_GC_SAFE;
        
        if (ret == SOCKET_ERROR) {
                *werror = WSAGetLastError ();
@@ -1021,11 +981,11 @@ ves_icall_System_Net_Sockets_Socket_RemoteEndPoint_internal (SOCKET sock, gint32
        sa = (salen <= 128) ? (gchar *)alloca (salen) : (gchar *)g_malloc0 (salen);
        /* Note: linux returns just 2 for AF_UNIX. Always. */
 
-       MONO_PREPARE_BLOCKING;
+       MONO_ENTER_GC_SAFE;
 
        ret = _wapi_getpeername (sock, (struct sockaddr *)sa, &salen);
 
-       MONO_FINISH_BLOCKING;
+       MONO_EXIT_GC_SAFE;
 
        if (ret == SOCKET_ERROR) {
                *werror = WSAGetLastError ();
@@ -1047,16 +1007,29 @@ ves_icall_System_Net_Sockets_Socket_RemoteEndPoint_internal (SOCKET sock, gint32
 static struct sockaddr*
 create_sockaddr_from_object (MonoObject *saddr_obj, socklen_t *sa_size, gint32 *werror, MonoError *error)
 {
-       MonoClassField *field;
+       MonoDomain *domain = mono_domain_get ();
        MonoArray *data;
        gint32 family;
        int len;
 
        mono_error_init (error);
 
-       /* Dig the SocketAddress data buffer out of the object */
-       field = mono_class_get_field_from_name (saddr_obj->vtable->klass, "data");
-       data = *(MonoArray **)(((char *)saddr_obj) + field->offset);
+       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);
+       }
+
+       data = *(MonoArray **)(((char *)saddr_obj) + domain->sockaddr_data_field->offset);
 
        /* The data buffer is laid out as follows:
         * byte 0 is the address family low byte
@@ -1067,12 +1040,9 @@ create_sockaddr_from_object (MonoObject *saddr_obj, socklen_t *sa_size, gint32 *
         * UNIX:
         *      the rest is the file name
         */
-       len = mono_array_length (data);
-       if (len < 2) {
-               mono_error_set_exception_instance (error, mono_exception_from_name (mono_get_corlib (), "System", "SystemException"));
-               return NULL;
-       }
-       
+       len = *(int *)(((char *)saddr_obj) + domain->sockaddr_data_length_field->offset);
+       g_assert (len >= 2);
+
        family = convert_family ((MonoAddressFamily)(mono_array_get (data, guint8, 0) + (mono_array_get (data, guint8, 1) << 8)));
        if (family == AF_INET) {
                struct sockaddr_in *sa;
@@ -1228,11 +1198,11 @@ ves_icall_System_Net_Sockets_Socket_Poll_internal (SOCKET sock, gint mode,
                        return FALSE;
                }
 
-               MONO_PREPARE_BLOCKING;
+               MONO_ENTER_GC_SAFE;
 
                ret = mono_poll (pfds, 1, timeout);
 
-               MONO_FINISH_BLOCKING;
+               MONO_EXIT_GC_SAFE;
 
                mono_thread_info_uninstall_interrupt (&interrupted);
                if (interrupted) {
@@ -1281,7 +1251,7 @@ ves_icall_System_Net_Sockets_Socket_Poll_internal (SOCKET sock, gint mode,
 }
 
 void
-ves_icall_System_Net_Sockets_Socket_Connect_internal (SOCKET sock, MonoObject *sockaddr, gint32 *werror)
+ves_icall_System_Net_Sockets_Socket_Connect_internal (SOCKET sock, MonoObject *sockaddr, gint32 *werror, gboolean blocking)
 {
        MonoError error;
        struct sockaddr *sa;
@@ -1307,21 +1277,23 @@ ves_icall_System_Net_Sockets_Socket_Connect_internal (SOCKET sock, MonoObject *s
                return;
        }
 
-       MONO_PREPARE_BLOCKING;
+       MONO_ENTER_GC_SAFE;
 
+#ifdef HOST_WIN32
+       ret = alertable_connect (sock, sa, sa_size, blocking);
+#else
        ret = _wapi_connect (sock, sa, sa_size);
+#endif
 
-       MONO_FINISH_BLOCKING;
-
-       mono_thread_info_uninstall_interrupt (&interrupted);
-       if (interrupted) {
-               *werror = WSAEINTR;
-               return;
-       }
+       MONO_EXIT_GC_SAFE;
 
        if (ret == SOCKET_ERROR)
                *werror = WSAGetLastError ();
 
+       mono_thread_info_uninstall_interrupt (&interrupted);
+       if (interrupted)
+               *werror = WSAEINTR;
+
        g_free (sa);
 }
 
@@ -1354,7 +1326,7 @@ ves_icall_System_Net_Sockets_Socket_Disconnect_internal (SOCKET sock, MonoBoolea
 
        LOGDEBUG (g_message("%s: disconnecting from socket %p (reuse %d)", __func__, sock, reuse));
 
-       MONO_PREPARE_BLOCKING;
+       MONO_ENTER_GC_SAFE;
 
        /* I _think_ the extension function pointers need to be looked
         * up for each socket.  FIXME: check the best way to store
@@ -1364,7 +1336,7 @@ ves_icall_System_Net_Sockets_Socket_Disconnect_internal (SOCKET sock, MonoBoolea
        ret = WSAIoctl (sock, SIO_GET_EXTENSION_FUNCTION_POINTER, (gchar *)&disco_guid, sizeof (GUID),
                                        (gchar *)&_wapi_disconnectex, sizeof (void *), &output_bytes, NULL, NULL);
 
-       MONO_FINISH_BLOCKING;
+       MONO_EXIT_GC_SAFE;
 
        if (ret != 0) {
                /* make sure that WSAIoctl didn't put crap in the
@@ -1372,7 +1344,7 @@ ves_icall_System_Net_Sockets_Socket_Disconnect_internal (SOCKET sock, MonoBoolea
                 */
                _wapi_disconnectex = NULL;
 
-               MONO_PREPARE_BLOCKING;
+               MONO_ENTER_GC_SAFE;
 
                /*
                 * Use the SIO_GET_EXTENSION_FUNCTION_POINTER to
@@ -1385,7 +1357,7 @@ ves_icall_System_Net_Sockets_Socket_Disconnect_internal (SOCKET sock, MonoBoolea
                ret = WSAIoctl (sock, SIO_GET_EXTENSION_FUNCTION_POINTER, (gchar *)&trans_guid, sizeof(GUID),
                                (gchar *)&_wapi_transmitfile, sizeof(void *), &output_bytes, NULL, NULL);
 
-               MONO_FINISH_BLOCKING;
+               MONO_EXIT_GC_SAFE;
 
                if (ret != 0)
                        _wapi_transmitfile = NULL;
@@ -1397,19 +1369,19 @@ ves_icall_System_Net_Sockets_Socket_Disconnect_internal (SOCKET sock, MonoBoolea
                return;
        }
 
-       MONO_PREPARE_BLOCKING;
+       MONO_ENTER_GC_SAFE;
 
        if (_wapi_disconnectex != NULL) {
-               if (!_wapi_disconnectex (sock, NULL, TF_REUSE_SOCKET, 0))
+               if (!_wapi_disconnectex (sock, NULL, reuse ? TF_REUSE_SOCKET : 0, 0))
                        *werror = WSAGetLastError ();
        } else if (_wapi_transmitfile != NULL) {
-               if (!_wapi_transmitfile (sock, NULL, 0, 0, NULL, NULL, TF_DISCONNECT | TF_REUSE_SOCKET))
+               if (!_wapi_transmitfile (sock, NULL, 0, 0, NULL, NULL, TF_DISCONNECT | (reuse ? TF_REUSE_SOCKET : 0)))
                        *werror = WSAGetLastError ();
        } else {
                *werror = ERROR_NOT_SUPPORTED;
        }
 
-       MONO_FINISH_BLOCKING;
+       MONO_EXIT_GC_SAFE;
 
        mono_thread_info_uninstall_interrupt (&interrupted);
        if (interrupted)
@@ -1417,7 +1389,7 @@ ves_icall_System_Net_Sockets_Socket_Disconnect_internal (SOCKET sock, MonoBoolea
 }
 
 gint32
-ves_icall_System_Net_Sockets_Socket_Receive_internal (SOCKET sock, MonoArray *buffer, gint32 offset, gint32 count, gint32 flags, gint32 *werror)
+ves_icall_System_Net_Sockets_Socket_Receive_internal (SOCKET sock, MonoArray *buffer, gint32 offset, gint32 count, gint32 flags, gint32 *werror, gboolean blocking)
 {
        int ret;
        guchar *buf;
@@ -1444,36 +1416,35 @@ ves_icall_System_Net_Sockets_Socket_Receive_internal (SOCKET sock, MonoArray *bu
        if (interrupted)
                return 0;
 
-       MONO_PREPARE_BLOCKING;
+       MONO_ENTER_GC_SAFE;
 
 #ifdef HOST_WIN32
        {
                curthread->interrupt_on_stop = (gpointer)TRUE;
-               ret = _wapi_recv (sock, buf, count, recvflags);
+               ret = alertable_recv (sock, buf, count, recvflags, blocking);
                curthread->interrupt_on_stop = (gpointer)FALSE;
        }
 #else
        ret = _wapi_recv (sock, buf, count, recvflags);
 #endif
 
-       MONO_FINISH_BLOCKING;
+       MONO_EXIT_GC_SAFE;
+
+       if (ret == SOCKET_ERROR)
+               *werror = WSAGetLastError ();
 
        mono_thread_info_uninstall_interrupt (&interrupted);
-       if (interrupted) {
+       if (interrupted)
                *werror = WSAEINTR;
-               return 0;
-       }
 
-       if (ret == SOCKET_ERROR) {
-               *werror = WSAGetLastError ();
+       if (*werror)
                return 0;
-       }
 
        return ret;
 }
 
 gint32
-ves_icall_System_Net_Sockets_Socket_Receive_array_internal (SOCKET sock, MonoArray *buffers, gint32 flags, gint32 *werror)
+ves_icall_System_Net_Sockets_Socket_Receive_array_internal (SOCKET sock, MonoArray *buffers, gint32 flags, gint32 *werror, gboolean blocking)
 {
        int ret, count;
        gboolean interrupted;
@@ -1498,28 +1469,31 @@ ves_icall_System_Net_Sockets_Socket_Receive_array_internal (SOCKET sock, MonoArr
                return 0;
        }
 
-       MONO_PREPARE_BLOCKING;
+       MONO_ENTER_GC_SAFE;
 
+#ifdef HOST_WIN32
+       ret = alertable_WSARecv (sock, wsabufs, count, &recv, &recvflags, NULL, NULL, blocking);
+#else
        ret = WSARecv (sock, wsabufs, count, &recv, &recvflags, NULL, NULL);
+#endif
+
+       MONO_EXIT_GC_SAFE;
 
-       MONO_FINISH_BLOCKING;
+       if (ret == SOCKET_ERROR)
+               *werror = WSAGetLastError ();
 
        mono_thread_info_uninstall_interrupt (&interrupted);
-       if (interrupted) {
+       if (interrupted)
                *werror = WSAEINTR;
-               return 0;
-       }
 
-       if (ret == SOCKET_ERROR) {
-               *werror = WSAGetLastError ();
+       if (*werror)
                return 0;
-       }
 
        return recv;
 }
 
 gint32
-ves_icall_System_Net_Sockets_Socket_ReceiveFrom_internal (SOCKET sock, MonoArray *buffer, gint32 offset, gint32 count, gint32 flags, MonoObject **sockaddr, gint32 *werror)
+ves_icall_System_Net_Sockets_Socket_ReceiveFrom_internal (SOCKET sock, MonoArray *buffer, gint32 offset, gint32 count, gint32 flags, MonoObject **sockaddr, gint32 *werror, gboolean blocking)
 {
        MonoError error;
        int ret;
@@ -1559,22 +1533,26 @@ ves_icall_System_Net_Sockets_Socket_ReceiveFrom_internal (SOCKET sock, MonoArray
                return 0;
        }
 
-       MONO_PREPARE_BLOCKING;
+       MONO_ENTER_GC_SAFE;
 
+#ifdef HOST_WIN32
+       ret = alertable_recvfrom (sock, buf, count, recvflags, sa, &sa_size, blocking);
+#else
        ret = _wapi_recvfrom (sock, buf, count, recvflags, sa, &sa_size);
+#endif
 
-       MONO_FINISH_BLOCKING;
+       MONO_EXIT_GC_SAFE;
+
+       if (ret == SOCKET_ERROR)
+               *werror = WSAGetLastError ();
 
        mono_thread_info_uninstall_interrupt (&interrupted);
-       if (interrupted) {
-               g_free (sa);
+
+       if (interrupted)
                *werror = WSAEINTR;
-               return 0;
-       }
 
-       if (ret==SOCKET_ERROR) {
-               g_free (sa);
-               *werror = WSAGetLastError ();
+       if (*werror) {
+               g_free(sa);
                return 0;
        }
 
@@ -1599,7 +1577,7 @@ ves_icall_System_Net_Sockets_Socket_ReceiveFrom_internal (SOCKET sock, MonoArray
 }
 
 gint32
-ves_icall_System_Net_Sockets_Socket_Send_internal (SOCKET sock, MonoArray *buffer, gint32 offset, gint32 count, gint32 flags, gint32 *werror)
+ves_icall_System_Net_Sockets_Socket_Send_internal (SOCKET sock, MonoArray *buffer, gint32 offset, gint32 count, gint32 flags, gint32 *werror, gboolean blocking)
 {
        int ret;
        guchar *buf;
@@ -1631,28 +1609,31 @@ ves_icall_System_Net_Sockets_Socket_Send_internal (SOCKET sock, MonoArray *buffe
                return 0;
        }
 
-       MONO_PREPARE_BLOCKING;
+       MONO_ENTER_GC_SAFE;
 
+#ifdef HOST_WIN32
+       ret = alertable_send (sock, buf, count, sendflags, blocking);
+#else
        ret = _wapi_send (sock, buf, count, sendflags);
+#endif
 
-       MONO_FINISH_BLOCKING;
+       MONO_EXIT_GC_SAFE;
+
+       if (ret == SOCKET_ERROR)
+               *werror = WSAGetLastError ();
 
        mono_thread_info_uninstall_interrupt (&interrupted);
-       if (interrupted) {
+       if (interrupted)
                *werror = WSAEINTR;
-               return 0;
-       }
 
-       if (ret == SOCKET_ERROR) {
-               *werror = WSAGetLastError ();
+       if (*werror)
                return 0;
-       }
 
        return ret;
 }
 
 gint32
-ves_icall_System_Net_Sockets_Socket_Send_array_internal (SOCKET sock, MonoArray *buffers, gint32 flags, gint32 *werror)
+ves_icall_System_Net_Sockets_Socket_Send_array_internal (SOCKET sock, MonoArray *buffers, gint32 flags, gint32 *werror, gboolean blocking)
 {
        int ret, count;
        DWORD sent;
@@ -1677,28 +1658,31 @@ ves_icall_System_Net_Sockets_Socket_Send_array_internal (SOCKET sock, MonoArray
                return 0;
        }
 
-       MONO_PREPARE_BLOCKING;
+       MONO_ENTER_GC_SAFE;
 
+#ifdef HOST_WIN32
+       ret = alertable_WSASend (sock, wsabufs, count, &sent, sendflags, NULL, NULL, blocking);
+#else
        ret = WSASend (sock, wsabufs, count, &sent, sendflags, NULL, NULL);
+#endif
+
+       MONO_EXIT_GC_SAFE;
 
-       MONO_FINISH_BLOCKING;
+       if (ret == SOCKET_ERROR)
+               *werror = WSAGetLastError ();
 
        mono_thread_info_uninstall_interrupt (&interrupted);
-       if (interrupted) {
+       if (interrupted)
                *werror = WSAEINTR;
-               return 0;
-       }
 
-       if (ret == SOCKET_ERROR) {
-               *werror = WSAGetLastError ();
+       if (*werror)
                return 0;
-       }
-       
+
        return sent;
 }
 
 gint32
-ves_icall_System_Net_Sockets_Socket_SendTo_internal (SOCKET sock, MonoArray *buffer, gint32 offset, gint32 count, gint32 flags, MonoObject *sockaddr, gint32 *werror)
+ves_icall_System_Net_Sockets_Socket_SendTo_internal (SOCKET sock, MonoArray *buffer, gint32 offset, gint32 count, gint32 flags, MonoObject *sockaddr, gint32 *werror, gboolean blocking)
 {
        MonoError error;
        int ret;
@@ -1744,24 +1728,28 @@ ves_icall_System_Net_Sockets_Socket_SendTo_internal (SOCKET sock, MonoArray *buf
                return 0;
        }
 
-       MONO_PREPARE_BLOCKING;
+       MONO_ENTER_GC_SAFE;
 
+#ifdef HOST_WIN32
+       ret = alertable_sendto (sock, buf, count, sendflags, sa, sa_size, blocking);
+#else
        ret = _wapi_sendto (sock, buf, count, sendflags, sa, sa_size);
+#endif
+
+       MONO_EXIT_GC_SAFE;
 
-       MONO_FINISH_BLOCKING;
+       if (ret == SOCKET_ERROR)
+               *werror = WSAGetLastError ();
 
        mono_thread_info_uninstall_interrupt (&interrupted);
-       if (interrupted) {
-               g_free (sa);
+       if (interrupted)
                *werror = WSAEINTR;
-               return 0;
-       }
 
-       if (ret == SOCKET_ERROR)
-               *werror = WSAGetLastError ();
+       g_free(sa);
+
+       if (*werror)
+               return 0;
 
-       g_free (sa);
-       
        return ret;
 }
 
@@ -1771,7 +1759,7 @@ Socket_to_SOCKET (MonoObject *sockobj)
        MonoSafeHandle *safe_handle;
        MonoClassField *field;
        
-       field = mono_class_get_field_from_name (sockobj->vtable->klass, "safe_handle");
+       field = mono_class_get_field_from_name (sockobj->vtable->klass, "m_Handle");
        safe_handle = ((MonoSafeHandle *)(*(gpointer *)(((char *)sockobj) + field->offset)));
 
        if (safe_handle == NULL)
@@ -1835,11 +1823,11 @@ ves_icall_System_Net_Sockets_Socket_Select_internal (MonoArray **sockets, gint32
                        return;
                }
 
-               MONO_PREPARE_BLOCKING;
+               MONO_ENTER_GC_SAFE;
 
                ret = mono_poll (pfds, nfds, timeout);
 
-               MONO_FINISH_BLOCKING;
+               MONO_EXIT_GC_SAFE;
 
                mono_thread_info_uninstall_interrupt (&interrupted);
                if (interrupted) {
@@ -1938,7 +1926,7 @@ ves_icall_System_Net_Sockets_Socket_GetSocketOption_obj_internal (SOCKET sock, g
        int system_level = 0;
        int system_name = 0;
        int ret;
-       int val;
+       int val = 0;
        socklen_t valsize = sizeof (val);
        struct linger linger;
        socklen_t lingersize = sizeof (linger);
@@ -1981,7 +1969,7 @@ ves_icall_System_Net_Sockets_Socket_GetSocketOption_obj_internal (SOCKET sock, g
                return;
        }
 
-       MONO_PREPARE_BLOCKING;
+       MONO_ENTER_GC_SAFE;
 
        /* No need to deal with MulticastOption names here, because
         * you cant getsockopt AddMembership or DropMembership (the
@@ -2008,7 +1996,7 @@ ves_icall_System_Net_Sockets_Socket_GetSocketOption_obj_internal (SOCKET sock, g
                ret = _wapi_getsockopt (sock, system_level, system_name, &val, &valsize);
        }
 
-       MONO_FINISH_BLOCKING;
+       MONO_EXIT_GC_SAFE;
 
        if (ret == SOCKET_ERROR) {
                *werror = WSAGetLastError ();
@@ -2120,11 +2108,11 @@ ves_icall_System_Net_Sockets_Socket_GetSocketOption_arr_internal (SOCKET sock, g
        valsize = mono_array_length (*byte_val);
        buf = mono_array_addr (*byte_val, guchar, 0);
 
-       MONO_PREPARE_BLOCKING;
+       MONO_ENTER_GC_SAFE;
 
        ret = _wapi_getsockopt (sock, system_level, system_name, buf, &valsize);
 
-       MONO_FINISH_BLOCKING;
+       MONO_EXIT_GC_SAFE;
 
        if (ret == SOCKET_ERROR)
                *werror = WSAGetLastError ();
@@ -2157,19 +2145,20 @@ ipaddress_to_struct_in6_addr (MonoObject *ipaddr)
        int i;
 
        field = mono_class_get_field_from_name (ipaddr->vtable->klass, "m_Numbers");
+       g_assert (field);
        data = *(MonoArray **)(((char *)ipaddr) + field->offset);
 
-/* Solaris has only the 8 bit version. */
-#ifndef s6_addr16
        for (i = 0; i < 8; i++) {
-               guint16 s = mono_array_get (data, guint16, i);
+               const guint16 s = GUINT16_TO_BE (mono_array_get (data, guint16, i));
+
+/* 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
-       for (i = 0; i < 8; i++)
-               in6addr.s6_addr16[i] = mono_array_get (data, guint16, i);
+               in6addr.s6_addr16[i] = s;
 #endif
+       }
        return in6addr;
 }
 #endif
@@ -2274,6 +2263,7 @@ ves_icall_System_Net_Sockets_Socket_SetSocketOption_internal (SOCKET sock, gint3
                                 *      Get group address
                                 */
                                field = mono_class_get_field_from_name (obj_val->vtable->klass, "m_Group");
+                               g_assert (field);
                                address = *(MonoObject **)(((char *)obj_val) + field->offset);
                                
                                if (address)
@@ -2422,21 +2412,21 @@ ves_icall_System_Net_Sockets_Socket_Shutdown_internal (SOCKET sock, gint32 how,
                return;
        }
 
-       MONO_PREPARE_BLOCKING;
+       MONO_ENTER_GC_SAFE;
 
        /* Currently, the values for how (recv=0, send=1, both=2) match the BSD API */
        ret = _wapi_shutdown (sock, how);
 
-       MONO_FINISH_BLOCKING;
+       MONO_EXIT_GC_SAFE;
+
+       if (ret == SOCKET_ERROR)
+               *werror = WSAGetLastError ();
 
        mono_thread_info_uninstall_interrupt (&interrupted);
        if (interrupted) {
                *werror = WSAEINTR;
-               return;
        }
 
-       if (ret == SOCKET_ERROR)
-               *werror = WSAGetLastError ();
 }
 
 gint
@@ -2469,11 +2459,11 @@ ves_icall_System_Net_Sockets_Socket_IOControl_internal (SOCKET sock, gint32 code
                o_len = mono_array_length (output);
        }
 
-       MONO_PREPARE_BLOCKING;
+       MONO_ENTER_GC_SAFE;
 
        ret = WSAIoctl (sock, code, i_buffer, i_len, o_buffer, o_len, &output_bytes, NULL, NULL);
 
-       MONO_FINISH_BLOCKING;
+       MONO_EXIT_GC_SAFE;
 
        if (ret == SOCKET_ERROR) {
                *werror = WSAGetLastError ();
@@ -2595,46 +2585,17 @@ leave2:
        return is_ok (error);
 }
 
-static int
-get_addrinfo_family_hint (MonoError *error)
-{
-       int hint;
-
-       mono_error_init (error);
-
-       hint = get_family_hint (error);
-       return_val_if_nok (error, 0);
-
-       switch (hint) {
-       case PF_UNSPEC:
-               return MONO_HINT_UNSPECIFIED;
-       case PF_INET:
-               return MONO_HINT_IPV4;
-#ifdef PF_INET6
-       case PF_INET6:
-               return MONO_HINT_IPV6;
-#endif
-       default:
-               g_error ("invalid hint");
-               return 0;
-       }
-}
-
 MonoBoolean
-ves_icall_System_Net_Dns_GetHostByName_internal (MonoString *host, MonoString **h_name, MonoArray **h_aliases, MonoArray **h_addr_list)
+ves_icall_System_Net_Dns_GetHostByName_internal (MonoString *host, MonoString **h_name, MonoArray **h_aliases, MonoArray **h_addr_list, gint32 hint)
 {
+       MonoError error;
        gboolean add_local_ips = FALSE, add_info_ok = TRUE;
        gchar this_hostname [256];
        MonoAddressInfo *info = NULL;
-       char *hostname = mono_string_to_utf8 (host);
-       MonoError error;
-       int hint;
 
-       hint = get_addrinfo_family_hint (&error);
-       if (!mono_error_ok (&error)) {
-               mono_error_set_pending_exception (&error);
+       char *hostname = mono_string_to_utf8_checked (host, &error);
+       if (mono_error_set_pending_exception (&error))
                return FALSE;
-       }
 
        if (*hostname == '\0') {
                add_local_ips = TRUE;
@@ -2648,8 +2609,16 @@ ves_icall_System_Net_Dns_GetHostByName_internal (MonoString *host, MonoString **
                }
        }
 
+#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);
 
@@ -2662,18 +2631,20 @@ ves_icall_System_Net_Dns_GetHostByName_internal (MonoString *host, MonoString **
 }
 
 MonoBoolean
-ves_icall_System_Net_Dns_GetHostByAddr_internal (MonoString *addr, MonoString **h_name, MonoArray **h_aliases, MonoArray **h_addr_list)
+ves_icall_System_Net_Dns_GetHostByAddr_internal (MonoString *addr, MonoString **h_name, MonoArray **h_aliases, MonoArray **h_addr_list, gint32 hint)
 {
        char *address;
        struct sockaddr_in saddr;
        struct sockaddr_in6 saddr6;
        MonoAddressInfo *info = NULL;
        MonoError error;
-       gint32 family, hint;
+       gint32 family;
        gchar hostname [NI_MAXHOST] = { 0 };
        gboolean ret;
 
-       address = mono_string_to_utf8 (addr);
+       address = mono_string_to_utf8_checked (addr, &error);
+       if (mono_error_set_pending_exception (&error))
+               return FALSE;
 
        if (inet_pton (AF_INET, address, &saddr.sin_addr ) == 1) {
                family = AF_INET;
@@ -2688,7 +2659,7 @@ ves_icall_System_Net_Dns_GetHostByAddr_internal (MonoString *addr, MonoString **
 
        g_free (address);
 
-       MONO_PREPARE_BLOCKING;
+       MONO_ENTER_GC_SAFE;
 
        switch (family) {
        case AF_INET: {
@@ -2709,16 +2680,11 @@ ves_icall_System_Net_Dns_GetHostByAddr_internal (MonoString *addr, MonoString **
                g_assert_not_reached ();
        }
 
-       MONO_FINISH_BLOCKING;
+       MONO_EXIT_GC_SAFE;
 
        if (!ret)
                return FALSE;
 
-       hint = get_addrinfo_family_hint (&error);
-       if (!mono_error_ok (&error)) {
-               mono_error_set_pending_exception (&error);
-               return FALSE;
-       }
        if (mono_get_address_info (hostname, 0, hint | MONO_HINT_CANONICAL_NAME | MONO_HINT_CONFIGURED_ONLY, &info) != 0)
                return FALSE;
 
@@ -2743,10 +2709,9 @@ ves_icall_System_Net_Dns_GetHostName_internal (MonoString **h_name)
 }
 
 gboolean
-ves_icall_System_Net_Sockets_Socket_SendFile_internal (SOCKET sock, MonoString *filename, MonoArray *pre_buffer, MonoArray *post_buffer, gint flags)
+ves_icall_System_Net_Sockets_Socket_SendFile_internal (SOCKET sock, MonoString *filename, MonoArray *pre_buffer, MonoArray *post_buffer, gint flags, gint32 *werror, gboolean blocking)
 {
        HANDLE file;
-       gint32 werror;
        gboolean ret;
        gboolean interrupted;
        TRANSMIT_FILE_BUFFERS buffers;
@@ -2756,10 +2721,10 @@ ves_icall_System_Net_Sockets_Socket_SendFile_internal (SOCKET sock, MonoString *
 
        /* FIXME: replace file by a proper fd that we can call open and close on, as they are interruptible */
 
-       file = ves_icall_System_IO_MonoIO_Open (filename, FileMode_Open, FileAccess_Read, FileShare_Read, 0, &werror);
+       file = ves_icall_System_IO_MonoIO_Open (filename, FileMode_Open, FileAccess_Read, FileShare_Read, 0, werror);
 
        if (file == INVALID_HANDLE_VALUE) {
-               SetLastError (werror);
+               SetLastError (*werror);
                return FALSE;
        }
 
@@ -2780,35 +2745,50 @@ ves_icall_System_Net_Sockets_Socket_SendFile_internal (SOCKET sock, MonoString *
                return FALSE;
        }
 
-       MONO_PREPARE_BLOCKING;
+       MONO_ENTER_GC_SAFE;
 
+#ifdef HOST_WIN32
+       ret = alertable_TransmitFile (sock, file, 0, 0, NULL, &buffers, flags, blocking);
+#else
        ret = TransmitFile (sock, file, 0, 0, NULL, &buffers, flags);
+#endif
+
+       MONO_EXIT_GC_SAFE;
 
-       MONO_FINISH_BLOCKING;
+       if (!ret)
+               *werror = WSAGetLastError ();
 
        mono_thread_info_uninstall_interrupt (&interrupted);
        if (interrupted) {
                CloseHandle (file);
-               SetLastError (WSAEINTR);
+               *werror = WSAEINTR;
                return FALSE;
        }
 
-       MONO_PREPARE_BLOCKING;
+       MONO_ENTER_GC_SAFE;
 
        CloseHandle (file);
 
-       MONO_FINISH_BLOCKING;
+       MONO_EXIT_GC_SAFE;
+
+       if (*werror)
+               return FALSE;
 
        return ret;
 }
 
 gboolean
-ves_icall_System_Net_Sockets_Socket_SupportPortReuse (void)
+ves_icall_System_Net_Sockets_Socket_SupportPortReuse (MonoProtocolType proto)
 {
 #if defined (SO_REUSEPORT) || defined (HOST_WIN32)
-    return TRUE;
+       return TRUE;
 #else
-    return FALSE;
+#ifdef __linux__
+       /* Linux always supports double binding for UDP, even on older kernels. */
+       if (proto == ProtocolType_Udp)
+               return TRUE;
+#endif
+       return FALSE;
 #endif
 }