2008-01-17 Rodrigo Kumpera <rkumpera@novell.com>
[mono.git] / mono / metadata / socket-io.c
index ecd0eaf7e3eae9630239c567e41e8e4acb1a42b6..72888dee513a4db4f760b96b7b9e38bcf5bb43c4 100644 (file)
@@ -6,7 +6,7 @@
  *     Gonzalo Paniagua Javier (gonzalo@ximian.com)
  *
  * (C) 2001 Ximian, Inc.
- * Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
+ * Copyright (c) 2005-2006 Novell, Inc. (http://www.novell.com)
  */
 
 #include <config.h>
@@ -14,7 +14,9 @@
 #include <glib.h>
 #include <string.h>
 #include <stdlib.h>
+#ifdef HAVE_UNISTD_H
 #include <unistd.h>
+#endif
 #include <errno.h>
 
 #include <mono/metadata/object.h>
 /* FIXME change this code to not mess so much with the internals */
 #include <mono/metadata/class-internals.h>
 #include <mono/metadata/threadpool-internals.h>
+#include <mono/metadata/domain-internals.h>
 
-#include <sys/time.h> 
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
 #ifdef HAVE_SYS_IOCTL_H
 #include <sys/ioctl.h>
 #endif
@@ -263,6 +268,45 @@ static gint32 convert_proto(MonoProtocolType mono_proto)
        return(proto);
 }
 
+/* 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;
+
+       if (sflags & SocketFlags_OutOfBand)
+               flags |= MSG_OOB;
+       if (sflags & SocketFlags_Peek)
+               flags |= MSG_PEEK;
+       if (sflags & SocketFlags_DontRoute)
+               flags |= MSG_DONTROUTE;
+       if (sflags & SocketFlags_Partial)
+#ifdef MSG_MORE
+               flags |= MSG_MORE;
+#else
+               return -1;      
+#endif
+       if (sflags & SocketFlags_MaxIOVectorLength)
+               /* FIXME: Don't know what to do for MaxIOVectorLength query */
+               return -1;      
+       
+       return (flags ? flags : -1);
+}
+
+/*
+ * 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,
@@ -406,7 +450,19 @@ static gint32 convert_sockopt_level_and_name(MonoSocketOptionLevel mono_level,
                        *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:
@@ -461,7 +517,9 @@ static gint32 convert_sockopt_level_and_name(MonoSocketOptionLevel mono_level,
                        *system_name = IPV6_LEAVE_GROUP;
                        break;
                case SocketOptionName_PacketInformation:
+#ifdef HAVE_IPV6_PKTINFO
                        *system_name = IPV6_PKTINFO;
+#endif
                        break;
                case SocketOptionName_HeaderIncluded:
                case SocketOptionName_IPOptions:
@@ -634,33 +692,6 @@ gpointer ves_icall_System_Net_Sockets_Socket_Socket_internal(MonoObject *this, g
        }
 #endif
 
-#ifndef PLATFORM_WIN32
-       /* .net seems to set this by default for SOCK_STREAM,
-        * not for SOCK_DGRAM (see bug #36322)
-        *
-        * It seems winsock has a rather different idea of what
-        * SO_REUSEADDR means.  If it's set, then a new socket can be
-        * bound over an existing listening socket.  There's a new
-        * windows-specific option called SO_EXCLUSIVEADDRUSE but
-        * using that means the socket MUST be closed properly, or a
-        * denial of service can occur.  Luckily for us, winsock
-        * behaves as though any other system would when SO_REUSEADDR
-        * is true, so we don't need to do anything else here.  See
-        * bug 53992.
-        */
-       {
-       int ret, true = 1;
-       
-       ret = _wapi_setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, &true, sizeof (true));
-       if(ret==SOCKET_ERROR) {
-               *error = WSAGetLastError ();
-               
-               closesocket(sock);
-               return(NULL);
-       }
-       }
-#endif
-       
        return(GUINT_TO_POINTER (sock));
 }
 
@@ -788,11 +819,22 @@ static MonoObject *create_object_from_sockaddr(struct sockaddr *saddr,
        field=mono_class_get_field_from_name(sockaddr_class, "data");
 
        /* Make sure there is space for the family and size bytes */
-       data=mono_array_new(domain, mono_get_byte_class (), sa_size+2);
+#ifdef HAVE_SYS_UN_H
+       if (saddr->sa_family == AF_UNIX) {
+               /* sa_len includes the entire sockaddr size, so we don't need the
+                * N bytes (sizeof (unsigned short)) of the family. */
+               data=mono_array_new(domain, mono_get_byte_class (), sa_size);
+       } else
+#endif
+       {
+               /* 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 */
+               data=mono_array_new(domain, mono_get_byte_class (), sa_size+2);
+       }
 
        /* The data buffer is laid out as follows:
-        * byte 0 is the address family
-        * byte 1 is the buffer length
+        * bytes 0 and 1 are the address family
         * bytes 2 and 3 are the port info
         * the rest is the address info
         */
@@ -804,7 +846,7 @@ static MonoObject *create_object_from_sockaddr(struct sockaddr *saddr,
        }
 
        mono_array_set(data, guint8, 0, family & 0x0FF);
-       mono_array_set(data, guint8, 1, ((family << 8) & 0x0FFFF));
+       mono_array_set(data, guint8, 1, (family >> 8) & 0x0FF);
        
        if(saddr->sa_family==AF_INET) {
                struct sockaddr_in *sa_in=(struct sockaddr_in *)saddr;
@@ -877,7 +919,7 @@ static MonoObject *create_object_from_sockaddr(struct sockaddr *saddr,
 extern MonoObject *ves_icall_System_Net_Sockets_Socket_LocalEndPoint_internal(SOCKET sock, gint32 *error)
 {
        gchar sa[32];   /* sockaddr in not big enough for sockaddr_in6 */
-       int salen;
+       socklen_t salen;
        int ret;
        
        MONO_ARCH_SAVE_REGS;
@@ -903,7 +945,7 @@ extern MonoObject *ves_icall_System_Net_Sockets_Socket_LocalEndPoint_internal(SO
 extern MonoObject *ves_icall_System_Net_Sockets_Socket_RemoteEndPoint_internal(SOCKET sock, gint32 *error)
 {
        gchar sa[32];   /* sockaddr in not big enough for sockaddr_in6 */
-       int salen;
+       socklen_t salen;
        int ret;
        
        MONO_ARCH_SAVE_REGS;
@@ -927,7 +969,7 @@ extern MonoObject *ves_icall_System_Net_Sockets_Socket_RemoteEndPoint_internal(S
 }
 
 static struct sockaddr *create_sockaddr_from_object(MonoObject *saddr_obj,
-                                                   int *sa_size,
+                                                   socklen_t *sa_size,
                                                    gint32 *error)
 {
        MonoClassField *field;
@@ -954,57 +996,81 @@ static struct sockaddr *create_sockaddr_from_object(MonoObject *saddr_obj,
        }
        
        family = convert_family (mono_array_get (data, guint8, 0) + (mono_array_get (data, guint8, 1) << 8));
-       if(family==AF_INET) {
-               struct sockaddr_in *sa=g_new0(struct sockaddr_in, 1);
-               guint16 port=(mono_array_get(data, guint8, 2) << 8) +
-                       mono_array_get(data, guint8, 3);
-               guint32 address=(mono_array_get(data, guint8, 4) << 24) +
-                       (mono_array_get(data, guint8, 5) << 16 ) +
-                       (mono_array_get(data, guint8, 6) << 8) +
-                       mono_array_get(data, guint8, 7);
+       if (family == AF_INET) {
+               struct sockaddr_in *sa;
+               guint16 port;
+               guint32 address;
                
-               sa->sin_family=family;
-               sa->sin_addr.s_addr=htonl(address);
-               sa->sin_port=htons(port);
+               if (len < 8) {
+                       mono_raise_exception (mono_exception_from_name (mono_get_corlib (), "System", "SystemException"));
+               }
+
+               sa = g_new0 (struct sockaddr_in, 1);
+               port = (mono_array_get (data, guint8, 2) << 8) +
+                       mono_array_get (data, guint8, 3);
+               address = (mono_array_get (data, guint8, 4) << 24) +
+                       (mono_array_get (data, guint8, 5) << 16 ) +
+                       (mono_array_get (data, guint8, 6) << 8) +
+                       mono_array_get (data, guint8, 7);
+
+               sa->sin_family = family;
+               sa->sin_addr.s_addr = htonl (address);
+               sa->sin_port = htons (port);
 
-               *sa_size=sizeof(struct sockaddr_in);
+               *sa_size = sizeof(struct sockaddr_in);
                return((struct sockaddr *)sa);
 
 #ifdef AF_INET6
        } else if (family == AF_INET6) {
-               struct sockaddr_in6 *sa=g_new0(struct sockaddr_in6, 1);
+               struct sockaddr_in6 *sa;
                int i;
+               guint16 port;
+               guint32 scopeid;
+               
+               if (len < 28) {
+                       mono_raise_exception (mono_exception_from_name (mono_get_corlib (), "System", "SystemException"));
+               }
 
-               guint16 port = mono_array_get(data, guint8, 3) + (mono_array_get(data, guint8, 2) << 8);
-               guint32 scopeid = mono_array_get(data, guint8, 24) + 
-                       (mono_array_get(data, guint8, 25)<<8) + 
-                       (mono_array_get(data, guint8, 26)<<16) + 
-                       (mono_array_get(data, guint8, 27)<<24);
+               sa = g_new0 (struct sockaddr_in6, 1);
+               port = mono_array_get (data, guint8, 3) +
+                       (mono_array_get (data, guint8, 2) << 8);
+               scopeid = mono_array_get (data, guint8, 24) + 
+                       (mono_array_get (data, guint8, 25) << 8) + 
+                       (mono_array_get (data, guint8, 26) << 16) + 
+                       (mono_array_get (data, guint8, 27) << 24);
 
-               sa->sin6_family=family;
-               sa->sin6_port=htons(port);
+               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] = mono_array_get(data, guint8, 8+i);
+               for(i=0; i<16; i++) {
+                       sa->sin6_addr.s6_addr[i] = mono_array_get (data, guint8, 8+i);
+               }
 
-               *sa_size=sizeof(struct sockaddr_in6);
+               *sa_size = sizeof(struct sockaddr_in6);
                return((struct sockaddr *)sa);
 #endif
 #ifdef HAVE_SYS_UN_H
        } else if (family == AF_UNIX) {
-               struct sockaddr_un *sock_un = g_new0 (struct sockaddr_un, 1);
+               struct sockaddr_un *sock_un;
                int i;
 
-               if (len - 2 > MONO_SIZEOF_SUNPATH)
+               /* Need a byte for the '\0' terminator/prefix, and the first
+                * two bytes hold the SocketAddress family
+                */
+               if (len - 2 >= MONO_SIZEOF_SUNPATH) {
                        mono_raise_exception (mono_get_exception_index_out_of_range ());
+               }
+               
+               sock_un = g_new0 (struct sockaddr_un, 1);
 
                sock_un->sun_family = family;
-               for (i = 0; i < len - 2; i++)
+               for (i = 0; i < len - 2; i++) {
                        sock_un->sun_path [i] = mono_array_get (data, guint8,
                                                                i + 2);
-               sock_un->sun_path [len - 2] = '\0';
-               *sa_size = sizeof (struct sockaddr_un);
+               }
+               
+               *sa_size = len;
 
                return (struct sockaddr *)sock_un;
 #endif
@@ -1017,7 +1083,7 @@ static struct sockaddr *create_sockaddr_from_object(MonoObject *saddr_obj,
 extern void ves_icall_System_Net_Sockets_Socket_Bind_internal(SOCKET sock, MonoObject *sockaddr, gint32 *error)
 {
        struct sockaddr *sa;
-       int sa_size;
+       socklen_t sa_size;
        int ret;
        
        MONO_ARCH_SAVE_REGS;
@@ -1051,58 +1117,81 @@ MonoBoolean
 ves_icall_System_Net_Sockets_Socket_Poll_internal (SOCKET sock, gint mode,
                                                   gint timeout, gint32 *error)
 {
-       fd_set fds;
-       int ret = 0;
-       struct timeval tv;
-       struct timeval *tvptr;
-       div_t divvy;
+       MonoThread *thread = NULL;
+       mono_pollfd *pfds;
+       int ret;
        time_t start;
+       
 
        MONO_ARCH_SAVE_REGS;
+       
+       pfds = g_new0 (mono_pollfd, 1);
+       pfds[0].fd = GPOINTER_TO_INT (sock);
+       pfds[0].events = (mode == SelectModeRead) ? MONO_POLLIN :
+               (mode == SelectModeWrite) ? MONO_POLLOUT :
+               (MONO_POLLERR | MONO_POLLHUP | MONO_POLLNVAL);
 
+       timeout = (timeout >= 0) ? (timeout / 1000) : -1;
        start = time (NULL);
        do {
                *error = 0;
-               FD_ZERO (&fds);
-               _wapi_FD_SET (sock, &fds);
-               if (timeout >= 0) {
-                       divvy = div (timeout, 1000000);
-                       tv.tv_sec = divvy.quot;
-                       tv.tv_usec = divvy.rem;
-                       tvptr = &tv;
-               } else {
-                       tvptr = NULL;
-               }
-
-               if (mode == SelectModeRead) {
-                       ret = _wapi_select (0, &fds, NULL, NULL, tvptr);
-               } else if (mode == SelectModeWrite) {
-                       ret = _wapi_select (0, NULL, &fds, NULL, tvptr);
-               } else if (mode == SelectModeError) {
-                       ret = _wapi_select (0, NULL, NULL, &fds, tvptr);
-               } else {
-                       g_assert_not_reached ();
-               }
-
+               
+               ret = mono_poll (pfds, 1, timeout);
                if (timeout > 0 && ret < 0) {
                        int err = errno;
                        int sec = time (NULL) - start;
-
+                       
                        timeout -= sec * 1000;
-                       if (timeout < 0)
+                       if (timeout < 0) {
                                timeout = 0;
+                       }
+                       
                        errno = err;
                }
+               
+               if (ret == -1 && errno == EINTR) {
+                       int leave = 0;
 
-       } while ((ret == SOCKET_ERROR) && (*error == WSAGetLastError ()) == WSAEINTR);
+                       if (thread == NULL) {
+                               thread = mono_thread_current ();
+                       }
+                       
+                       leave = mono_thread_test_state (thread, ThreadState_AbortRequested | ThreadState_StopRequested);
+                       
+                       if (leave != 0) {
+                               g_free (pfds);
+                               return(FALSE);
+                       } else {
+                               /* Suspend requested? */
+                               mono_thread_interruption_checkpoint ();
+                       }
+                       errno = EINTR;
+               }
+       } while (ret == -1 && errno == EINTR);
+
+       if (ret == -1) {
+#ifdef PLATFORM_WIN32
+               *error = WSAGetLastError ();
+#else
+               *error = errno_to_WSA (errno, __func__);
+#endif
+               g_free (pfds);
+               return(FALSE);
+       }
+       
+       g_free (pfds);
 
-       return (ret != SOCKET_ERROR && _wapi_FD_ISSET (sock, &fds));
+       if (ret == 0) {
+               return(FALSE);
+       } else {
+               return (TRUE);
+       }
 }
 
 extern void ves_icall_System_Net_Sockets_Socket_Connect_internal(SOCKET sock, MonoObject *sockaddr, gint32 *error)
 {
        struct sockaddr *sa;
-       int sa_size;
+       socklen_t sa_size;
        int ret;
        
        MONO_ARCH_SAVE_REGS;
@@ -1126,6 +1215,92 @@ extern void ves_icall_System_Net_Sockets_Socket_Connect_internal(SOCKET sock, Mo
        g_free(sa);
 }
 
+/* These #defines from mswsock.h from wine.  Defining them here allows
+ * us to build this file on a mingw box that doesn't know the magic
+ * numbers, but still run on a newer windows box that does.
+ */
+#ifndef WSAID_DISCONNECTEX
+#define WSAID_DISCONNECTEX {0x7fda2e11,0x8630,0x436f,{0xa0, 0x31, 0xf5, 0x36, 0xa6, 0xee, 0xc1, 0x57}}
+typedef BOOL (WINAPI *LPFN_DISCONNECTEX)(SOCKET, LPOVERLAPPED, DWORD, DWORD);
+#endif
+
+#ifndef WSAID_TRANSMITFILE
+#define WSAID_TRANSMITFILE {0xb5367df0,0xcbac,0x11cf,{0x95,0xca,0x00,0x80,0x5f,0x48,0xa1,0x92}}
+typedef BOOL (WINAPI *LPFN_TRANSMITFILE)(SOCKET, HANDLE, DWORD, DWORD, LPOVERLAPPED, LPTRANSMIT_FILE_BUFFERS, DWORD);
+#endif
+
+extern void ves_icall_System_Net_Sockets_Socket_Disconnect_internal(SOCKET sock, MonoBoolean reuse, gint32 *error)
+{
+       int ret;
+       glong output_bytes = 0;
+       GUID disco_guid = WSAID_DISCONNECTEX;
+       GUID trans_guid = WSAID_TRANSMITFILE;
+       LPFN_DISCONNECTEX _wapi_disconnectex = NULL;
+       LPFN_TRANSMITFILE _wapi_transmitfile = NULL;
+       gboolean bret;
+       
+       MONO_ARCH_SAVE_REGS;
+
+       *error = 0;
+       
+#ifdef DEBUG
+       g_message("%s: disconnecting from socket %p (reuse %d)", __func__,
+                 sock, reuse);
+#endif
+
+       /* I _think_ the extension function pointers need to be looked
+        * up for each socket.  FIXME: check the best way to store
+        * pointers to functions in managed objects that still works
+        * on 64bit platforms.
+        */
+       ret = WSAIoctl (sock, SIO_GET_EXTENSION_FUNCTION_POINTER,
+                       (void *)&disco_guid, sizeof(GUID),
+                       (void *)&_wapi_disconnectex, sizeof(void *),
+                       &output_bytes, NULL, NULL);
+       if (ret != 0) {
+               /* make sure that WSAIoctl didn't put crap in the
+                * output pointer
+                */
+               _wapi_disconnectex = NULL;
+
+               /* Look up the TransmitFile extension function pointer
+                * instead of calling TransmitFile() directly, because
+                * apparently "Several of the extension functions have
+                * been available since WinSock 1.1 and are exported
+                * from MSWsock.dll, however it's not advisable to
+                * link directly to this dll as this ties you to the
+                * Microsoft WinSock provider. A provider neutral way
+                * of accessing these extension functions is to load
+                * them dynamically via WSAIoctl using the
+                * SIO_GET_EXTENSION_FUNCTION_POINTER op code. This
+                * should, theoretically, allow you to access these
+                * functions from any provider that supports them..." 
+                * (http://www.codeproject.com/internet/jbsocketserver3.asp)
+                */
+               ret = WSAIoctl (sock, SIO_GET_EXTENSION_FUNCTION_POINTER,
+                               (void *)&trans_guid, sizeof(GUID),
+                               (void *)&_wapi_transmitfile, sizeof(void *),
+                               &output_bytes, NULL, NULL);
+               if (ret != 0) {
+                       _wapi_transmitfile = NULL;
+               }
+       }
+
+       if (_wapi_disconnectex != NULL) {
+               bret = _wapi_disconnectex (sock, NULL, TF_REUSE_SOCKET, 0);
+       } else if (_wapi_transmitfile != NULL) {
+               bret = _wapi_transmitfile (sock, NULL, 0, 0, NULL, NULL,
+                                          TF_DISCONNECT | TF_REUSE_SOCKET);
+       } else {
+               *error = ERROR_NOT_SUPPORTED;
+               return;
+       }
+
+       if (bret == FALSE) {
+               *error = WSAGetLastError ();
+       }
+}
+
 gint32 ves_icall_System_Net_Sockets_Socket_Receive_internal(SOCKET sock, MonoArray *buffer, gint32 offset, gint32 count, gint32 flags, gint32 *error)
 {
        int ret;
@@ -1137,13 +1312,19 @@ gint32 ves_icall_System_Net_Sockets_Socket_Receive_internal(SOCKET sock, MonoArr
 
        *error = 0;
        
-       alen=mono_array_length(buffer);
-       if(offset+count>alen) {
+       alen = mono_array_length (buffer);
+       if (offset > alen - count) {
                return(0);
        }
        
        buf=mono_array_addr(buffer, guchar, offset);
        
+       recvflags = convert_socketflags (flags);
+       if (recvflags == -1) {
+               *error = WSAEOPNOTSUPP;
+               return (0);
+       }
+               
        ret = _wapi_recv (sock, buf, count, recvflags);
        if(ret==SOCKET_ERROR) {
                *error = WSAGetLastError ();
@@ -1160,14 +1341,14 @@ gint32 ves_icall_System_Net_Sockets_Socket_RecvFrom_internal(SOCKET sock, MonoAr
        gint32 alen;
        int recvflags=0;
        struct sockaddr *sa;
-       int sa_size;
+       socklen_t sa_size;
        
        MONO_ARCH_SAVE_REGS;
 
        *error = 0;
        
-       alen=mono_array_length(buffer);
-       if(offset+count>alen) {
+       alen = mono_array_length (buffer);
+       if (offset > alen - count) {
                return(0);
        }
 
@@ -1178,6 +1359,12 @@ gint32 ves_icall_System_Net_Sockets_Socket_RecvFrom_internal(SOCKET sock, MonoAr
        
        buf=mono_array_addr(buffer, guchar, offset);
        
+       recvflags = convert_socketflags (flags);
+       if (recvflags == -1) {
+               *error = WSAEOPNOTSUPP;
+               return (0);
+       }
+
        ret = _wapi_recvfrom (sock, buf, count, recvflags, sa, &sa_size);
        if(ret==SOCKET_ERROR) {
                g_free(sa);
@@ -1210,8 +1397,8 @@ gint32 ves_icall_System_Net_Sockets_Socket_Send_internal(SOCKET sock, MonoArray
 
        *error = 0;
        
-       alen=mono_array_length(buffer);
-       if(offset+count>alen) {
+       alen = mono_array_length (buffer);
+       if (offset > alen - count) {
                return(0);
        }
 
@@ -1225,6 +1412,12 @@ gint32 ves_icall_System_Net_Sockets_Socket_Send_internal(SOCKET sock, MonoArray
        g_message(G_GNUC_PRETTY_FUNCTION ": Sending %d bytes", count);
 #endif
 
+       sendflags = convert_socketflags (flags);
+       if (sendflags == -1) {
+               *error = WSAEOPNOTSUPP;
+               return (0);
+       }
+
        ret = _wapi_send (sock, buf, count, sendflags);
        if(ret==SOCKET_ERROR) {
                *error = WSAGetLastError ();
@@ -1241,14 +1434,14 @@ gint32 ves_icall_System_Net_Sockets_Socket_SendTo_internal(SOCKET sock, MonoArra
        gint32 alen;
        int sendflags=0;
        struct sockaddr *sa;
-       int sa_size;
+       socklen_t sa_size;
        
        MONO_ARCH_SAVE_REGS;
 
        *error = 0;
        
-       alen=mono_array_length(buffer);
-       if(offset+count>alen) {
+       alen = mono_array_length (buffer);
+       if (offset > alen - count) {
                return(0);
        }
 
@@ -1267,6 +1460,12 @@ gint32 ves_icall_System_Net_Sockets_Socket_SendTo_internal(SOCKET sock, MonoArra
        g_message(G_GNUC_PRETTY_FUNCTION ": Sending %d bytes", count);
 #endif
 
+       sendflags = convert_socketflags (flags);
+       if (sendflags == -1) {
+               *error = WSAEOPNOTSUPP;
+               return (0);
+       }
+
        ret = _wapi_sendto (sock, buf, count, sendflags, sa, sa_size);
        if(ret==SOCKET_ERROR) {
                *error = WSAGetLastError ();
@@ -1316,6 +1515,13 @@ void ves_icall_System_Net_Sockets_Socket_Select_internal(MonoArray **sockets, gi
                        continue;
                }
 
+               if (idx >= nfds) {
+                       /* The socket array was bogus */
+                       g_free (pfds);
+                       *error = WSAEFAULT;
+                       return;
+               }
+
                pfds [idx].fd = GPOINTER_TO_INT (Socket_to_SOCKET (obj));
                pfds [idx].events = (mode == 0) ? MONO_POLLIN : (mode == 1) ? MONO_POLLOUT : POLL_ERRORS;
                idx++;
@@ -1341,10 +1547,8 @@ void ves_icall_System_Net_Sockets_Socket_Select_internal(MonoArray **sockets, gi
                        if (thread == NULL)
                                thread = mono_thread_current ();
 
-                       mono_monitor_enter (thread->synch_lock);
-                       leave = ((thread->state & ThreadState_AbortRequested) != 0 || 
-                                (thread->state & ThreadState_StopRequested) != 0);
-                       mono_monitor_exit (thread->synch_lock);
+                       leave = mono_thread_test_state (thread, ThreadState_AbortRequested | ThreadState_StopRequested);
+                       
                        if (leave != 0) {
                                g_free (pfds);
                                *sockets = NULL;
@@ -1375,7 +1579,7 @@ void ves_icall_System_Net_Sockets_Socket_Select_internal(MonoArray **sockets, gi
 
        sock_arr_class= ((MonoObject *)*sockets)->vtable->klass;
        ret += 3; /* space for the NULL delimiters */
-       socks = mono_array_new_full (mono_domain_get (), sock_arr_class, &ret, NULL);
+       socks = mono_array_new_full (mono_domain_get (), sock_arr_class, (guint32*)&ret, NULL);
        ret -= 3;
        mode = idx = 0;
        for (i = 0; i < count && ret > 0; i++) {
@@ -1394,11 +1598,11 @@ void ves_icall_System_Net_Sockets_Socket_Select_internal(MonoArray **sockets, gi
 
                ret--;
                if (mode == 0 && (pfd->revents & (MONO_POLLIN | POLL_ERRORS)) != 0) {
-                       mono_array_set (socks, MonoObject *, idx++, obj);
+                       mono_array_setref (socks, idx++, obj);
                } else if (mode == 1 && (pfd->revents & (MONO_POLLOUT | POLL_ERRORS)) != 0) {
-                       mono_array_set (socks, MonoObject *, idx++, obj);
+                       mono_array_setref (socks, idx++, obj);
                } else if ((pfd->revents & POLL_ERRORS) != 0) {
-                       mono_array_set (socks, MonoObject *, idx++, obj);
+                       mono_array_setref (socks, idx++, obj);
                }
        }
 
@@ -1418,14 +1622,14 @@ void ves_icall_System_Net_Sockets_Socket_GetSocketOption_obj_internal(SOCKET soc
        int system_name;
        int ret;
        int val;
-       int valsize=sizeof(val);
+       socklen_t valsize=sizeof(val);
        struct linger linger;
-       int lingersize=sizeof(linger);
+       socklen_t lingersize=sizeof(linger);
        int time_ms = 0;
-       int time_ms_size = sizeof (time_ms);
+       socklen_t time_ms_size = sizeof (time_ms);
 #ifdef SO_PEERCRED
        struct ucred cred;
-       int credsize = sizeof(cred);
+       socklen_t credsize = sizeof(cred);
 #endif
        MonoDomain *domain=mono_domain_get();
        MonoObject *obj;
@@ -1442,6 +1646,10 @@ void ves_icall_System_Net_Sockets_Socket_GetSocketOption_obj_internal(SOCKET soc
                *error = WSAENOPROTOOPT;
                return;
        }
+       if (ret == -2) {
+               *obj_val = int_to_object (domain, 0);
+               return;
+       }
        
        /* No need to deal with MulticastOption names here, because
         * you cant getsockopt AddMembership or DropMembership (the
@@ -1529,7 +1737,7 @@ void ves_icall_System_Net_Sockets_Socket_GetSocketOption_obj_internal(SOCKET soc
                
                obj_class = mono_class_from_name(mono_posix_image,
                                                 "Mono.Posix",
-                                                "PeerCred/PeerCredData");
+                                                "PeerCredData");
                obj = mono_object_new(domain, obj_class);
                cred_data = (MonoPeerCredData *)obj;
                cred_data->pid = cred.pid;
@@ -1552,7 +1760,7 @@ void ves_icall_System_Net_Sockets_Socket_GetSocketOption_arr_internal(SOCKET soc
        int system_name;
        int ret;
        guchar *buf;
-       int valsize;
+       socklen_t valsize;
        
        MONO_ARCH_SAVE_REGS;
 
@@ -1564,6 +1772,8 @@ void ves_icall_System_Net_Sockets_Socket_GetSocketOption_arr_internal(SOCKET soc
                *error = WSAENOPROTOOPT;
                return;
        }
+       if(ret==-2)
+               return;
 
        valsize=mono_array_length(*byte_val);
        buf=mono_array_addr(*byte_val, guchar, 0);
@@ -1580,14 +1790,13 @@ static struct in_addr ipaddress_to_struct_in_addr(MonoObject *ipaddr)
        struct in_addr inaddr;
        MonoClassField *field;
        
-       field=mono_class_get_field_from_name(ipaddr->vtable->klass, "address");
+       field=mono_class_get_field_from_name(ipaddr->vtable->klass, "m_Address");
 
        /* No idea why .net uses a 64bit type to hold a 32bit value...
         *
-        * Internal value of IPAddess is in Network Order, there is no need
-        * to call htonl here.
+        * Internal value of IPAddess is in little-endian order
         */
-       inaddr.s_addr=(guint32)*(guint64 *)(((char *)ipaddr)+field->offset);
+       inaddr.s_addr=GUINT_FROM_LE ((guint32)*(guint64 *)(((char *)ipaddr)+field->offset));
        
        return(inaddr);
 }
@@ -1601,7 +1810,7 @@ static struct in6_addr ipaddress_to_struct_in6_addr(MonoObject *ipaddr)
        MonoArray *data;
        int i;
 
-       field=mono_class_get_field_from_name(ipaddr->vtable->klass, "_numbers");
+       field=mono_class_get_field_from_name(ipaddr->vtable->klass, "m_Numbers");
        data=*(MonoArray **)(((char *)ipaddr) + field->offset);
 
 /* Solaris has only the 8 bit version. */
@@ -1659,6 +1868,9 @@ void ves_icall_System_Net_Sockets_Socket_SetSocketOption_internal(SOCKET sock, g
                *error = WSAENOPROTOOPT;
                return;
        }
+       if(ret==-2){
+               return;
+       }
 
        /* Only one of obj_val, byte_val or int_val has data */
        if(obj_val!=NULL) {
@@ -1772,7 +1984,21 @@ void ves_icall_System_Net_Sockets_Socket_SetSocketOption_internal(SOCKET sock, g
                }
        } else {
                /* ReceiveTimeout/SendTimeout get here */
-               ret = _wapi_setsockopt (sock, system_level, system_name, (char *) &int_val, sizeof (int_val));
+               switch(name) {
+               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 = _wapi_setsockopt (sock, system_level, system_name, (char *) &int_val, sizeof (int_val));
+               }
        }
 
        if(ret==SOCKET_ERROR) {
@@ -1804,7 +2030,7 @@ ves_icall_System_Net_Sockets_Socket_WSAIoctl (SOCKET sock, gint32 code,
                                              MonoArray *input,
                                              MonoArray *output, gint32 *error)
 {
-       gulong output_bytes = 0;
+       glong output_bytes = 0;
        gchar *i_buffer, *o_buffer;
        gint i_len, o_len;
        gint ret;
@@ -1977,7 +2203,7 @@ static gboolean hostent_to_IPHostEntry(struct hostent *he, MonoString **h_name,
                MonoString *alias;
                
                alias=mono_string_new(domain, he->h_aliases[i]);
-               mono_array_set(*h_aliases, MonoString *, i, alias);
+               mono_array_setref (*h_aliases, i, alias);
                i++;
        }
 
@@ -1997,7 +2223,7 @@ static gboolean hostent_to_IPHostEntry(struct hostent *he, MonoString **h_name,
                                         (unsigned char) ptr [3]);
                                
                                addr_string = mono_string_new (domain, addr);
-                               mono_array_set (*h_addr_list, MonoString *, i, addr_string);
+                               mono_array_setref (*h_addr_list, i, addr_string);
                                i++;
                        }
 
@@ -2024,13 +2250,27 @@ static gboolean hostent_to_IPHostEntry(struct hostent *he, MonoString **h_name,
                                 (unsigned char)he->h_addr_list[i][3]);
                        
                        addr_string=mono_string_new(domain, addr);
-                       mono_array_set(*h_addr_list, MonoString *, i, addr_string);
+                       mono_array_setref (*h_addr_list, i, addr_string);
                        i++;
                }
        }
 
        return(TRUE);
 }
+
+static gboolean ipaddr_to_IPHostEntry(const char *addr, MonoString **h_name,
+                                     MonoArray **h_aliases,
+                                     MonoArray **h_addr_list)
+{
+       MonoDomain *domain = mono_domain_get ();
+
+       *h_name=mono_string_new(domain, addr);
+       *h_aliases=mono_array_new(domain, mono_get_string_class (), 0);
+       *h_addr_list=mono_array_new(domain, mono_get_string_class (), 1);
+       mono_array_setref (*h_addr_list, 0, *h_name);
+
+       return(TRUE);
+}
 #endif
 
 #if defined(AF_INET6) && defined(HAVE_GETHOSTBYNAME2_R)
@@ -2082,10 +2322,11 @@ static gboolean hostent_to_IPHostEntry2(struct hostent *he1,struct hostent *he2,
                        MonoString *alias;
 
                        alias=mono_string_new (domain, he1->h_aliases[i]);
-                       mono_array_set (*h_aliases, MonoString *, i, alias);
+                       mono_array_setref (*h_aliases, i, alias);
                        i++;
                }
-       } else if (family_hint == PF_UNSPEC || family_hint == PF_INET6) {
+       } else if (he2 != NULL && (family_hint == PF_UNSPEC ||
+                                  family_hint == PF_INET6)) {
                *h_name=mono_string_new (domain, he2->h_name);
 
                i=0;
@@ -2100,9 +2341,11 @@ static gboolean hostent_to_IPHostEntry2(struct hostent *he1,struct hostent *he2,
                        MonoString *alias;
 
                        alias=mono_string_new (domain, he2->h_aliases[i]);
-                       mono_array_set (*h_aliases, MonoString *, i, alias);
+                       mono_array_setref (*h_aliases, i, alias);
                        i++;
                }
+       } else {
+               return(FALSE);
        }
 
        /*
@@ -2147,15 +2390,16 @@ static gboolean hostent_to_IPHostEntry2(struct hostent *he1,struct hostent *he2,
                                int n;
                                for (n = 0; n < nlocal_in6; n++) {
                                        MonoString *addr_string;
-                                       char addr[46]; /* INET6_ADDRSTRLEN == 46 */
+                                       const char *ret;
+                                       char addr[48]; /* INET6_ADDRSTRLEN == 46, but IPv6 addresses can be 48 bytes with the trailing NULL */
 
-                                       inet_ntop (AF_INET6, &local_in6 [n], addr,
-                                                  sizeof(addr));
+                                       ret = inet_ntop (AF_INET6, &local_in6 [n], addr, sizeof(addr));
 
-                                       addr_string = mono_string_new (domain, addr);
-                                       mono_array_set (*h_addr_list, MonoString *, host_index,
-                                                       addr_string);
-                                       host_index++;
+                                       if (ret != NULL) {
+                                               addr_string = mono_string_new (domain, addr);
+                                               mono_array_setref (*h_addr_list, host_index, addr_string);
+                                               host_index++;
+                                       }
                                }
                        }
 
@@ -2163,15 +2407,16 @@ static gboolean hostent_to_IPHostEntry2(struct hostent *he1,struct hostent *he2,
                                int n;
                                for (n = 0; n < nlocal_in; n++) {
                                        MonoString *addr_string;
+                                       const char *ret;
                                        char addr[16]; /* INET_ADDRSTRLEN == 16 */
 
-                                       inet_ntop (AF_INET, &local_in [n], addr,
-                                                  sizeof(addr));
+                                       ret = inet_ntop (AF_INET, &local_in [n], addr, sizeof(addr));
 
-                                       addr_string = mono_string_new (domain, addr);
-                                       mono_array_set (*h_addr_list, MonoString *, host_index,
-                                                       addr_string);
-                                       host_index++;
+                                       if (ret != NULL) {
+                                               addr_string = mono_string_new (domain, addr);
+                                               mono_array_setref (*h_addr_list, host_index, addr_string);
+                                               host_index++;
+                                       }
                                }
                        }
                        g_free (local_in);
@@ -2190,16 +2435,18 @@ static gboolean hostent_to_IPHostEntry2(struct hostent *he1,struct hostent *he2,
                i = 0;
                while(he2->h_addr_list[i] != NULL) {
                        MonoString *addr_string;
-                       char addr[46]; /* INET6_ADDRSTRLEN == 46 */
+                       const char *ret;
+                       char addr[48]; /* INET6_ADDRSTRLEN == 46, but IPv6 addresses can be 48 bytes long with the trailing NULL */
 
-                       inet_ntop (AF_INET6, he2->h_addr_list[i], addr,
-                                  sizeof(addr));
+                       ret = inet_ntop (AF_INET6, he2->h_addr_list[i], addr,
+                                        sizeof(addr));
 
-                       addr_string = mono_string_new (domain, addr);
-                       mono_array_set (*h_addr_list, MonoString *, host_index,
-                                       addr_string);
-                       i++;
-                       host_index++;
+                       if (ret != NULL) {
+                               addr_string = mono_string_new (domain, addr);
+                               mono_array_setref (*h_addr_list, host_index, addr_string);
+                               i++;
+                               host_index++;
+                       }
                }
        }
 
@@ -2208,16 +2455,18 @@ static gboolean hostent_to_IPHostEntry2(struct hostent *he1,struct hostent *he2,
                i=0;
                while(he1->h_addr_list[i] != NULL) {
                        MonoString *addr_string;
+                       const char *ret;
                        char addr[16]; /* INET_ADDRSTRLEN == 16 */
 
-                       inet_ntop (AF_INET, he1->h_addr_list[i], addr,
-                                  sizeof(addr));
+                       ret = inet_ntop (AF_INET, he1->h_addr_list[i], addr,
+                                        sizeof(addr));
 
-                       addr_string=mono_string_new (domain, addr);
-                       mono_array_set (*h_addr_list, MonoString *, host_index,
-                                       addr_string);
-                       i++;
-                       host_index++;
+                       if (ret != NULL) {
+                               addr_string=mono_string_new (domain, addr);
+                               mono_array_setref (*h_addr_list, host_index, addr_string);
+                               i++;
+                               host_index++;
+                       }
                }
        }
 
@@ -2246,7 +2495,7 @@ addrinfo_to_IPHostEntry(struct addrinfo *info, MonoString **h_name,
        *h_aliases=mono_array_new(domain, mono_get_string_class (), 0);
        if (add_local_ips) {
                local_in = (struct in_addr *) get_local_ips (AF_INET, &nlocal_in);
-               local_in6 = (struct in6_addr *) get_local_ips (AF_INET6, &nlocal_in);
+               local_in6 = (struct in6_addr *) get_local_ips (AF_INET6, &nlocal_in6);
                if (nlocal_in || nlocal_in6) {
                        *h_addr_list=mono_array_new(domain, mono_get_string_class (), nlocal_in + nlocal_in6);
                        if (nlocal_in) {
@@ -2257,21 +2506,24 @@ addrinfo_to_IPHostEntry(struct addrinfo *info, MonoString **h_name,
                                for (i = 0; i < nlocal_in; i++) {
                                        inet_ntop (AF_INET, &local_in [i], addr, sizeof (addr));
                                        addr_string = mono_string_new (domain, addr);
-                                       mono_array_set (*h_addr_list, MonoString *, addr_index, addr_string);
+                                       mono_array_setref (*h_addr_list, addr_index, addr_string);
                                        addr_index++;
                                }
                        }
 
                        if (nlocal_in6) {
                                MonoString *addr_string;
-                               char addr [46];
+                               const char *ret;
+                               char addr [48];
                                int i;
 
                                for (i = 0; i < nlocal_in6; i++) {
-                                       inet_ntop (AF_INET6, &local_in6 [i], addr, sizeof (addr));
-                                       addr_string = mono_string_new (domain, addr);
-                                       mono_array_set (*h_addr_list, MonoString *, addr_index, addr_string);
-                                       addr_index++;
+                                       ret = inet_ntop (AF_INET6, &local_in6 [i], addr, sizeof (addr));
+                                       if (ret != NULL) {
+                                               addr_string = mono_string_new (domain, addr);
+                                               mono_array_setref (*h_addr_list, addr_index, addr_string);
+                                               addr_index++;
+                                       }
                                }
                        }
 
@@ -2299,7 +2551,7 @@ addrinfo_to_IPHostEntry(struct addrinfo *info, MonoString **h_name,
        for (ai=info, i=0; ai!=NULL; ai=ai->ai_next) {
                MonoString *addr_string;
                const char *ret;
-               char buffer [46]; /* Max. size for IPv6 */
+               char buffer [48]; /* Max. size for IPv6 */
 
                if((ai->ai_family != PF_INET) && (ai->ai_family != PF_INET6)) {
                        continue;
@@ -2308,7 +2560,7 @@ addrinfo_to_IPHostEntry(struct addrinfo *info, MonoString **h_name,
                if(ai->ai_family == PF_INET) {
                        ret = inet_ntop(ai->ai_family, (void*)&(((struct sockaddr_in*)ai->ai_addr)->sin_addr), buffer, 16);
                } else {
-                       ret = inet_ntop(ai->ai_family, (void*)&(((struct sockaddr_in6*)ai->ai_addr)->sin6_addr), buffer, 46);
+                       ret = inet_ntop(ai->ai_family, (void*)&(((struct sockaddr_in6*)ai->ai_addr)->sin6_addr), buffer, 48);
                }
 
                if(ret) {
@@ -2317,7 +2569,7 @@ addrinfo_to_IPHostEntry(struct addrinfo *info, MonoString **h_name,
                        addr_string=mono_string_new(domain, "");
                }
 
-               mono_array_set(*h_addr_list, MonoString *, addr_index, addr_string);
+               mono_array_setref (*h_addr_list, addr_index, addr_string);
 
                if(!i && ai->ai_canonname != NULL) {
                        *h_name=mono_string_new(domain, ai->ai_canonname);
@@ -2339,7 +2591,7 @@ MonoBoolean ves_icall_System_Net_Dns_GetHostByName_internal(MonoString *host, Mo
 {
        gboolean add_local_ips = FALSE;
 #ifdef HAVE_SIOCGIFCONF
-       guchar this_hostname [256];
+       gchar this_hostname [256];
 #endif
 #if !defined(HAVE_GETHOSTBYNAME2_R)
        struct addrinfo *info = NULL, hints;
@@ -2425,7 +2677,7 @@ MonoBoolean ves_icall_System_Net_Dns_GetHostByName_internal(MonoString *host, Mo
        char *hostname;
        gboolean add_local_ips = FALSE;
 #ifdef HAVE_SIOCGIFCONF
-       guchar this_hostname [256];
+       gchar this_hostname [256];
 #endif
        
        MONO_ARCH_SAVE_REGS;
@@ -2491,20 +2743,27 @@ inet_pton (int family, const char *address, void *inaddrp)
 extern MonoBoolean ves_icall_System_Net_Dns_GetHostByAddr_internal(MonoString *addr, MonoString **h_name, MonoArray **h_aliases, MonoArray **h_addr_list)
 {
        char *address;
-
+       const char *version;
+       gboolean v1;
+       
 #ifdef AF_INET6
        struct sockaddr_in saddr;
        struct sockaddr_in6 saddr6;
        struct addrinfo *info = NULL, hints;
        gint32 family;
        char hostname[1024] = {0};
+       int flags = 0;
 #else
        struct in_addr inaddr;
        struct hostent *he;
+       gboolean ret;
 #endif
 
        MONO_ARCH_SAVE_REGS;
 
+       version = mono_get_runtime_info ()->framework_version;
+       v1 = (version[0] == '1');
+
        address = mono_string_to_utf8 (addr);
 
 #ifdef AF_INET6
@@ -2525,16 +2784,26 @@ extern MonoBoolean ves_icall_System_Net_Dns_GetHostByAddr_internal(MonoString *a
        }
        g_free(address);
 
+       if (v1) {
+               flags = NI_NAMEREQD;
+       }
+       
        if(family == AF_INET) {
+#if HAVE_SOCKADDR_IN_SIN_LEN
+               saddr.sin_len = sizeof (saddr);
+#endif
                if(getnameinfo ((struct sockaddr*)&saddr, sizeof(saddr),
                                hostname, sizeof(hostname), NULL, 0,
-                               NI_NAMEREQD) != 0) {
+                               flags) != 0) {
                        return(FALSE);
                }
        } else if(family == AF_INET6) {
+#if HAVE_SOCKADDR_IN6_SIN_LEN
+               saddr6.sin6_len = sizeof (saddr6);
+#endif
                if(getnameinfo ((struct sockaddr*)&saddr6, sizeof(saddr6),
                                hostname, sizeof(hostname), NULL, 0,
-                               NI_NAMEREQD) != 0) {
+                               flags) != 0) {
                        return(FALSE);
                }
        }
@@ -2554,19 +2823,27 @@ extern MonoBoolean ves_icall_System_Net_Dns_GetHostByAddr_internal(MonoString *a
                g_free (address);
                return(FALSE);
        }
-       g_free (address);
 
        if ((he = gethostbyaddr ((char *) &inaddr, sizeof (inaddr), AF_INET)) == NULL) {
-               return(FALSE);
+               if (v1) {
+                       ret = FALSE;
+               } else {
+                       ret = ipaddr_to_IPHostEntry (address, h_name,
+                                                    h_aliases, h_addr_list);
+               }
+       } else {
+               ret = hostent_to_IPHostEntry (he, h_name, h_aliases,
+                                             h_addr_list, FALSE);
        }
 
-       return(hostent_to_IPHostEntry (he, h_name, h_aliases, h_addr_list, FALSE));
+       g_free (address);
+       return(ret);
 #endif
 }
 
 extern MonoBoolean ves_icall_System_Net_Dns_GetHostName_internal(MonoString **h_name)
 {
-       guchar hostname[256];
+       gchar hostname[256];
        int ret;
        
        MONO_ARCH_SAVE_REGS;