Merge pull request #572 from jack-pappas/sockets-ipproto
[mono.git] / mono / io-layer / sockets.c
index 09afdd8300598f200fd8e5e32fb2e145308967de..a659d3763e80bdf39e8a7758d740738c36e937c1 100644 (file)
@@ -8,14 +8,21 @@
  */
 
 #include <config.h>
+
+#ifndef DISABLE_SOCKETS
+
 #include <glib.h>
 #include <pthread.h>
 #include <errno.h>
 #include <string.h>
 #include <sys/types.h>
 #include <sys/socket.h>
-#include <sys/ioctl.h>
-#include <sys/poll.h>
+#ifdef HAVE_SYS_UIO_H
+#  include <sys/uio.h>
+#endif
+#ifdef HAVE_SYS_IOCTL_H
+#  include <sys/ioctl.h>
+#endif
 #ifdef HAVE_SYS_FILIO_H
 #include <sys/filio.h>     /* defines FIONBIO and FIONREAD */
 #endif
 #include <mono/io-layer/socket-private.h>
 #include <mono/io-layer/handles-private.h>
 #include <mono/io-layer/socket-wrappers.h>
+#include <mono/utils/mono-poll.h>
 
 #include <netinet/in.h>
 #include <netinet/tcp.h>
 #include <netdb.h>
 #include <arpa/inet.h>
+#ifdef HAVE_SYS_SENDFILE_H
+#include <sys/sendfile.h>
+#endif
 
-#undef DEBUG
+#if 0
+#define DEBUG(...) g_message(__VA_ARGS__)
+#else
+#define DEBUG(...)
+#endif
 
 static guint32 startup_count=0;
+static guint32 in_cleanup = 0;
 
 static void socket_close (gpointer handle, gpointer data);
 
@@ -67,11 +83,9 @@ static void socket_close (gpointer handle, gpointer data)
        int ret;
        struct _WapiHandle_socket *socket_handle = (struct _WapiHandle_socket *)data;
 
-#ifdef DEBUG
-       g_message ("%s: closing socket handle %p", __func__, handle);
-#endif
+       DEBUG ("%s: closing socket handle %p", __func__, handle);
 
-       if (startup_count == 0) {
+       if (startup_count == 0 && !in_cleanup) {
                WSASetLastError (WSANOTINITIALISED);
                return;
        }
@@ -88,14 +102,14 @@ static void socket_close (gpointer handle, gpointer data)
        
        if (ret == -1) {
                gint errnum = errno;
-#ifdef DEBUG
-               g_message ("%s: close error: %s", __func__, strerror (errno));
-#endif
+               DEBUG ("%s: close error: %s", __func__, strerror (errno));
                errnum = errno_to_WSA (errnum, __func__);
-               WSASetLastError (errnum);
+               if (!in_cleanup)
+                       WSASetLastError (errnum);
        }
 
-       socket_handle->saved_error = 0;
+       if (!in_cleanup)
+               socket_handle->saved_error = 0;
 }
 
 int WSAStartup(guint32 requested, WapiWSAData *data)
@@ -117,9 +131,7 @@ int WSAStartup(guint32 requested, WapiWSAData *data)
        data->wVersion = requested < data->wHighVersion? requested:
                data->wHighVersion;
 
-#ifdef DEBUG
-       g_message ("%s: high version 0x%x", __func__, data->wHighVersion);
-#endif
+       DEBUG ("%s: high version 0x%x", __func__, data->wHighVersion);
        
        strncpy (data->szDescription, "WAPI", WSADESCRIPTION_LEN);
        strncpy (data->szSystemStatus, "groovy", WSASYS_STATUS_LEN);
@@ -136,16 +148,16 @@ cleanup_close (gpointer handle, gpointer data)
 
 int WSACleanup(void)
 {
-#ifdef DEBUG
-       g_message ("%s: cleaning up", __func__);
-#endif
+       DEBUG ("%s: cleaning up", __func__);
 
        if (--startup_count) {
                /* Do nothing */
                return(0);
        }
 
+       in_cleanup = 1;
        _wapi_handle_foreach (WAPI_HANDLE_SOCKET, cleanup_close, NULL);
+       in_cleanup = 0;
        return(0);
 }
 
@@ -212,9 +224,7 @@ guint32 _wapi_accept(guint32 fd, struct sockaddr *addr, socklen_t *addrlen)
 
        if (new_fd == -1) {
                gint errnum = errno;
-#ifdef DEBUG
-               g_message ("%s: accept error: %s", __func__, strerror(errno));
-#endif
+               DEBUG ("%s: accept error: %s", __func__, strerror(errno));
 
                errnum = errno_to_WSA (errnum, __func__);
                WSASetLastError (errnum);
@@ -223,9 +233,7 @@ guint32 _wapi_accept(guint32 fd, struct sockaddr *addr, socklen_t *addrlen)
        }
 
        if (new_fd >= _wapi_fd_reserve) {
-#ifdef DEBUG
-               g_message ("%s: File descriptor is too big", __func__);
-#endif
+               DEBUG ("%s: File descriptor is too big", __func__);
 
                WSASetLastError (WSASYSCALLFAILURE);
                
@@ -247,10 +255,8 @@ guint32 _wapi_accept(guint32 fd, struct sockaddr *addr, socklen_t *addrlen)
                return(INVALID_SOCKET);
        }
 
-#ifdef DEBUG
-       g_message ("%s: returning newly accepted socket handle %p with",
+       DEBUG ("%s: returning newly accepted socket handle %p with",
                   __func__, new_handle);
-#endif
        
        return(new_fd);
 }
@@ -273,9 +279,7 @@ int _wapi_bind(guint32 fd, struct sockaddr *my_addr, socklen_t addrlen)
        ret = bind (fd, my_addr, addrlen);
        if (ret == -1) {
                gint errnum = errno;
-#ifdef DEBUG
-               g_message ("%s: bind error: %s", __func__, strerror(errno));
-#endif
+               DEBUG ("%s: bind error: %s", __func__, strerror(errno));
                errnum = errno_to_WSA (errnum, __func__);
                WSASetLastError (errnum);
                
@@ -303,17 +307,15 @@ int _wapi_connect(guint32 fd, const struct sockaddr *serv_addr,
        }
        
        if (connect (fd, serv_addr, addrlen) == -1) {
-               struct pollfd fds;
+               mono_pollfd fds;
                int so_error;
                socklen_t len;
                
                errnum = errno;
                
                if (errno != EINTR) {
-#ifdef DEBUG
-                       g_message ("%s: connect error: %s", __func__,
+                       DEBUG ("%s: connect error: %s", __func__,
                                   strerror (errnum));
-#endif
 
                        errnum = errno_to_WSA (errnum, __func__);
                        if (errnum == WSAEINPROGRESS)
@@ -332,7 +334,10 @@ int _wapi_connect(guint32 fd, const struct sockaddr *serv_addr,
                                                          WAPI_HANDLE_SOCKET,
                                                          (gpointer *)&socket_handle);
                                if (ok == FALSE) {
-                                       g_warning ("%s: error looking up socket handle %p", __func__, handle);
+                                       /* ECONNRESET means the socket was closed by another thread */
+                                       /* Async close on mac raises ECONNABORTED. */
+                                       if (errnum != WSAECONNRESET && errnum != WSAENETDOWN)
+                                               g_warning ("%s: error looking up socket handle %p (error %d)", __func__, handle, errnum);
                                } else {
                                        socket_handle->saved_error = errnum;
                                }
@@ -342,15 +347,13 @@ int _wapi_connect(guint32 fd, const struct sockaddr *serv_addr,
 
                fds.fd = fd;
                fds.events = POLLOUT;
-               while (poll (&fds, 1, -1) == -1 &&
+               while (mono_poll (&fds, 1, -1) == -1 &&
                       !_wapi_thread_cur_apc_pending ()) {
                        if (errno != EINTR) {
                                errnum = errno_to_WSA (errno, __func__);
 
-#ifdef DEBUG
-                               g_message ("%s: connect poll error: %s",
+                               DEBUG ("%s: connect poll error: %s",
                                           __func__, strerror (errno));
-#endif
 
                                WSASetLastError (errnum);
                                return(SOCKET_ERROR);
@@ -362,10 +365,8 @@ int _wapi_connect(guint32 fd, const struct sockaddr *serv_addr,
                                &len) == -1) {
                        errnum = errno_to_WSA (errno, __func__);
 
-#ifdef DEBUG
-                       g_message ("%s: connect getsockopt error: %s",
+                       DEBUG ("%s: connect getsockopt error: %s",
                                   __func__, strerror (errno));
-#endif
 
                        WSASetLastError (errnum);
                        return(SOCKET_ERROR);
@@ -383,10 +384,8 @@ int _wapi_connect(guint32 fd, const struct sockaddr *serv_addr,
                                socket_handle->saved_error = errnum;
                        }
                        
-#ifdef DEBUG
-                       g_message ("%s: connect getsockopt returned error: %s",
+                       DEBUG ("%s: connect getsockopt returned error: %s",
                                   __func__, strerror (so_error));
-#endif
 
                        WSASetLastError (errnum);
                        return(SOCKET_ERROR);
@@ -414,10 +413,8 @@ int _wapi_getpeername(guint32 fd, struct sockaddr *name, socklen_t *namelen)
        ret = getpeername (fd, name, namelen);
        if (ret == -1) {
                gint errnum = errno;
-#ifdef DEBUG
-               g_message ("%s: getpeername error: %s", __func__,
+               DEBUG ("%s: getpeername error: %s", __func__,
                           strerror (errno));
-#endif
 
                errnum = errno_to_WSA (errnum, __func__);
                WSASetLastError (errnum);
@@ -446,10 +443,8 @@ int _wapi_getsockname(guint32 fd, struct sockaddr *name, socklen_t *namelen)
        ret = getsockname (fd, name, namelen);
        if (ret == -1) {
                gint errnum = errno;
-#ifdef DEBUG
-               g_message ("%s: getsockname error: %s", __func__,
+               DEBUG ("%s: getsockname error: %s", __func__,
                           strerror (errno));
-#endif
 
                errnum = errno_to_WSA (errnum, __func__);
                WSASetLastError (errnum);
@@ -490,10 +485,8 @@ int _wapi_getsockopt(guint32 fd, int level, int optname, void *optval,
        ret = getsockopt (fd, level, optname, tmp_val, optlen);
        if (ret == -1) {
                gint errnum = errno;
-#ifdef DEBUG
-               g_message ("%s: getsockopt error: %s", __func__,
+               DEBUG ("%s: getsockopt error: %s", __func__,
                           strerror (errno));
-#endif
 
                errnum = errno_to_WSA (errnum, __func__);
                WSASetLastError (errnum);
@@ -549,9 +542,7 @@ int _wapi_listen(guint32 fd, int backlog)
        ret = listen (fd, backlog);
        if (ret == -1) {
                gint errnum = errno;
-#ifdef DEBUG
-               g_message ("%s: listen error: %s", __func__, strerror (errno));
-#endif
+               DEBUG ("%s: listen error: %s", __func__, strerror (errno));
 
                errnum = errno_to_WSA (errnum, __func__);
                WSASetLastError (errnum);
@@ -619,9 +610,52 @@ int _wapi_recvfrom(guint32 fd, void *buf, size_t len, int recv_flags,
        
        if (ret == -1) {
                gint errnum = errno;
-#ifdef DEBUG
-               g_message ("%s: recv error: %s", __func__, strerror(errno));
-#endif
+               DEBUG ("%s: recv error: %s", __func__, strerror(errno));
+
+               errnum = errno_to_WSA (errnum, __func__);
+               WSASetLastError (errnum);
+               
+               return(SOCKET_ERROR);
+       }
+       return(ret);
+}
+
+static int
+_wapi_recvmsg(guint32 fd, struct msghdr *msg, int recv_flags)
+{
+       gpointer handle = GUINT_TO_POINTER (fd);
+       struct _WapiHandle_socket *socket_handle;
+       gboolean ok;
+       int ret;
+       
+       if (startup_count == 0) {
+               WSASetLastError (WSANOTINITIALISED);
+               return(SOCKET_ERROR);
+       }
+       
+       if (_wapi_handle_type (handle) != WAPI_HANDLE_SOCKET) {
+               WSASetLastError (WSAENOTSOCK);
+               return(SOCKET_ERROR);
+       }
+       
+       do {
+               ret = recvmsg (fd, msg, recv_flags);
+       } while (ret == -1 && errno == EINTR &&
+                !_wapi_thread_cur_apc_pending ());
+
+       if (ret == 0) {
+               /* see _wapi_recvfrom */
+               ok = _wapi_lookup_handle (handle, WAPI_HANDLE_SOCKET,
+                                         (gpointer *)&socket_handle);
+               if (ok == FALSE || socket_handle->still_readable != 1) {
+                       ret = -1;
+                       errno = EINTR;
+               }
+       }
+       
+       if (ret == -1) {
+               gint errnum = errno;
+               DEBUG ("%s: recvmsg error: %s", __func__, strerror(errno));
 
                errnum = errno_to_WSA (errnum, __func__);
                WSASetLastError (errnum);
@@ -653,10 +687,17 @@ int _wapi_send(guint32 fd, const void *msg, size_t len, int send_flags)
 
        if (ret == -1) {
                gint errnum = errno;
-#ifdef DEBUG
-               g_message ("%s: send error: %s", __func__, strerror (errno));
-#endif
+               DEBUG ("%s: send error: %s", __func__, strerror (errno));
 
+#ifdef O_NONBLOCK
+               /* At least linux returns EAGAIN/EWOULDBLOCK when the timeout has been set on
+                * a blocking socket. See bug #599488 */
+               if (errnum == EAGAIN) {
+                       ret = fcntl (fd, F_GETFL, 0);
+                       if (ret != -1 && (ret & O_NONBLOCK) == 0)
+                               errnum = ETIMEDOUT;
+               }
+#endif /* O_NONBLOCK */
                errnum = errno_to_WSA (errnum, __func__);
                WSASetLastError (errnum);
                
@@ -688,9 +729,40 @@ int _wapi_sendto(guint32 fd, const void *msg, size_t len, int send_flags,
 
        if (ret == -1) {
                gint errnum = errno;
-#ifdef DEBUG
-               g_message ("%s: send error: %s", __func__, strerror (errno));
-#endif
+               DEBUG ("%s: send error: %s", __func__, strerror (errno));
+
+               errnum = errno_to_WSA (errnum, __func__);
+               WSASetLastError (errnum);
+               
+               return(SOCKET_ERROR);
+       }
+       return(ret);
+}
+
+static int
+_wapi_sendmsg(guint32 fd,  const struct msghdr *msg, int send_flags)
+{
+       gpointer handle = GUINT_TO_POINTER (fd);
+       int ret;
+       
+       if (startup_count == 0) {
+               WSASetLastError (WSANOTINITIALISED);
+               return(SOCKET_ERROR);
+       }
+       
+       if (_wapi_handle_type (handle) != WAPI_HANDLE_SOCKET) {
+               WSASetLastError (WSAENOTSOCK);
+               return(SOCKET_ERROR);
+       }
+       
+       do {
+               ret = sendmsg (fd, msg, send_flags);
+       } while (ret == -1 && errno == EINTR &&
+                !_wapi_thread_cur_apc_pending ());
+
+       if (ret == -1) {
+               gint errnum = errno;
+               DEBUG ("%s: sendmsg error: %s", __func__, strerror (errno));
 
                errnum = errno_to_WSA (errnum, __func__);
                WSASetLastError (errnum);
@@ -743,10 +815,8 @@ int _wapi_setsockopt(guint32 fd, int level, int optname,
        ret = setsockopt (fd, level, optname, tmp_val, optlen);
        if (ret == -1) {
                gint errnum = errno;
-#ifdef DEBUG
-               g_message ("%s: setsockopt error: %s", __func__,
+               DEBUG ("%s: setsockopt error: %s", __func__,
                           strerror (errno));
-#endif
 
                errnum = errno_to_WSA (errnum, __func__);
                WSASetLastError (errnum);
@@ -758,7 +828,7 @@ int _wapi_setsockopt(guint32 fd, int level, int optname,
        /* BSD's and MacOS X multicast sockets also need SO_REUSEPORT when SO_REUSEADDR is requested.  */
        if (level == SOL_SOCKET && optname == SO_REUSEADDR) {
                int type;
-               int type_len = sizeof (type);
+               socklen_t type_len = sizeof (type);
 
                if (!getsockopt (fd, level, SO_TYPE, &type, &type_len)) {
                        if (type == SOCK_DGRAM)
@@ -804,10 +874,8 @@ int _wapi_shutdown(guint32 fd, int how)
        ret = shutdown (fd, how);
        if (ret == -1) {
                gint errnum = errno;
-#ifdef DEBUG
-               g_message ("%s: shutdown error: %s", __func__,
+               DEBUG ("%s: shutdown error: %s", __func__,
                           strerror (errno));
-#endif
 
                errnum = errno_to_WSA (errnum, __func__);
                WSASetLastError (errnum);
@@ -834,15 +902,14 @@ guint32 _wapi_socket(int domain, int type, int protocol, void *unused,
        if (fd == -1 && domain == AF_INET && type == SOCK_RAW &&
            protocol == 0) {
                /* Retry with protocol == 4 (see bug #54565) */
+               // https://bugzilla.novell.com/show_bug.cgi?id=MONO54565
                socket_handle.protocol = 4;
                fd = socket (AF_INET, SOCK_RAW, 4);
        }
        
        if (fd == -1) {
                gint errnum = errno;
-#ifdef DEBUG
-               g_message ("%s: socket error: %s", __func__, strerror (errno));
-#endif
+               DEBUG ("%s: socket error: %s", __func__, strerror (errno));
                errnum = errno_to_WSA (errnum, __func__);
                WSASetLastError (errnum);
 
@@ -850,10 +917,8 @@ guint32 _wapi_socket(int domain, int type, int protocol, void *unused,
        }
 
        if (fd >= _wapi_fd_reserve) {
-#ifdef DEBUG
-               g_message ("%s: File descriptor is too big (%d >= %d)",
+               DEBUG ("%s: File descriptor is too big (%d >= %d)",
                           __func__, fd, _wapi_fd_reserve);
-#endif
 
                WSASetLastError (WSASYSCALLFAILURE);
                close (fd);
@@ -863,6 +928,7 @@ guint32 _wapi_socket(int domain, int type, int protocol, void *unused,
 
        /* .net seems to set this by default for SOCK_STREAM, not for
         * SOCK_DGRAM (see bug #36322)
+        * https://bugzilla.novell.com/show_bug.cgi?id=MONO36322
         *
         * It seems winsock has a rather different idea of what
         * SO_REUSEADDR means.  If it's set, then a new socket can be
@@ -873,6 +939,7 @@ guint32 _wapi_socket(int domain, int type, int protocol, void *unused,
         * 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.
+        * https://bugzilla.novell.com/show_bug.cgi?id=MONO53992
         */
        {
                int ret, true = 1;
@@ -882,9 +949,7 @@ guint32 _wapi_socket(int domain, int type, int protocol, void *unused,
                if (ret == -1) {
                        int errnum = errno;
 
-#ifdef DEBUG
-                       g_message ("%s: Error setting SO_REUSEADDR", __func__);
-#endif
+                       DEBUG ("%s: Error setting SO_REUSEADDR", __func__);
                        
                        errnum = errno_to_WSA (errnum, __func__);
                        WSASetLastError (errnum);
@@ -906,9 +971,7 @@ guint32 _wapi_socket(int domain, int type, int protocol, void *unused,
                return(INVALID_SOCKET);
        }
 
-#ifdef DEBUG
-       g_message ("%s: returning socket handle %p", __func__, handle);
-#endif
+       DEBUG ("%s: returning socket handle %p", __func__, handle);
 
        return(fd);
 }
@@ -924,10 +987,8 @@ struct hostent *_wapi_gethostbyname(const char *hostname)
 
        he = gethostbyname (hostname);
        if (he == NULL) {
-#ifdef DEBUG
-               g_message ("%s: gethostbyname error: %s", __func__,
+               DEBUG ("%s: gethostbyname error: %s", __func__,
                           strerror (h_errno));
-#endif
 
                switch(h_errno) {
                case HOST_NOT_FOUND:
@@ -975,9 +1036,7 @@ static gboolean socket_disconnect (guint32 fd)
        if (newsock == -1) {
                gint errnum = errno;
 
-#ifdef DEBUG
-               g_message ("%s: socket error: %s", __func__, strerror (errno));
-#endif
+               DEBUG ("%s: socket error: %s", __func__, strerror (errno));
 
                errnum = errno_to_WSA (errnum, __func__);
                WSASetLastError (errnum);
@@ -997,9 +1056,7 @@ static gboolean socket_disconnect (guint32 fd)
        if (ret == -1) {
                gint errnum = errno;
                
-#ifdef DEBUG
-               g_message ("%s: dup2 error: %s", __func__, strerror (errno));
-#endif
+               DEBUG ("%s: dup2 error: %s", __func__, strerror (errno));
 
                errnum = errno_to_WSA (errnum, __func__);
                WSASetLastError (errnum);
@@ -1015,9 +1072,7 @@ static gboolean socket_disconnect (guint32 fd)
 static gboolean wapi_disconnectex (guint32 fd, WapiOverlapped *overlapped,
                                   guint32 flags, guint32 reserved)
 {
-#ifdef DEBUG
-       g_message ("%s: called on socket %d!", __func__, fd);
-#endif
+       DEBUG ("%s: called on socket %d!", __func__, fd);
        
        if (reserved != 0) {
                WSASetLastError (WSAEINVAL);
@@ -1032,29 +1087,112 @@ static gboolean wapi_disconnectex (guint32 fd, WapiOverlapped *overlapped,
        return(socket_disconnect (fd));
 }
 
-/* NB only supports NULL file handle, NULL buffers and
- * TF_DISCONNECT|TF_REUSE_SOCKET flags to disconnect the socket fd.
- * Shouldn't actually ever need to be called anyway though, because we
- * have DisconnectEx ().
- */
-static gboolean wapi_transmitfile (guint32 fd, gpointer file,
-                                  guint32 num_write, guint32 num_per_send,
-                                  WapiOverlapped *overlapped,
-                                  WapiTransmitFileBuffers *buffers,
-                                  WapiTransmitFileFlags flags)
+#define SF_BUFFER_SIZE 16384
+static gint
+wapi_sendfile (guint32 socket, gpointer fd, guint32 bytes_to_write, guint32 bytes_per_send, guint32 flags)
 {
-#ifdef DEBUG
-       g_message ("%s: called on socket %d!", __func__, fd);
+#if defined(HAVE_SENDFILE) && (defined(__linux__) || defined(DARWIN))
+       gint file = GPOINTER_TO_INT (fd);
+       gint n;
+       gint errnum;
+       gssize res;
+       struct stat statbuf;
+
+       n = fstat (file, &statbuf);
+       if (n == -1) {
+               errnum = errno;
+               errnum = errno_to_WSA (errnum, __func__);
+               WSASetLastError (errnum);
+               return SOCKET_ERROR;
+       }
+       do {
+#ifdef __linux__
+               res = sendfile (socket, file, NULL, statbuf.st_size);
+#elif defined(DARWIN)
+               /* TODO: header/tail could be sent in the 5th argument */
+               /* TODO: Might not send the entire file for non-blocking sockets */
+               res = sendfile (file, socket, 0, &statbuf.st_size, NULL, 0);
 #endif
+       } while (res != -1 && (errno == EINTR || errno == EAGAIN) && !_wapi_thread_cur_apc_pending ());
+       if (res == -1) {
+               errnum = errno;
+               errnum = errno_to_WSA (errnum, __func__);
+               WSASetLastError (errnum);
+               return SOCKET_ERROR;
+       }
+#else
+       /* Default implementation */
+       gint file = GPOINTER_TO_INT (fd);
+       gchar *buffer;
+       gint n;
+
+       buffer = g_malloc (SF_BUFFER_SIZE);
+       do {
+               do {
+                       n = read (file, buffer, SF_BUFFER_SIZE);
+               } while (n == -1 && errno == EINTR && !_wapi_thread_cur_apc_pending ());
+               if (n == -1)
+                       break;
+               if (n == 0) {
+                       g_free (buffer);
+                       return 0; /* We're done reading */
+               }
+               do {
+                       n = send (socket, buffer, n, 0); /* short sends? enclose this in a loop? */
+               } while (n == -1 && errno == EINTR && !_wapi_thread_cur_apc_pending ());
+       } while (n != -1);
+
+       if (n == -1) {
+               gint errnum = errno;
+               errnum = errno_to_WSA (errnum, __func__);
+               WSASetLastError (errnum);
+               g_free (buffer);
+               return SOCKET_ERROR;
+       }
+       g_free (buffer);
+#endif
+       return 0;
+}
+
+gboolean
+TransmitFile (guint32 socket, gpointer file, guint32 bytes_to_write, guint32 bytes_per_send, WapiOverlapped *ol,
+               WapiTransmitFileBuffers *buffers, guint32 flags)
+{
+       gpointer sock = GUINT_TO_POINTER (socket);
+       gint ret;
        
-       g_assert (file == NULL);
-       g_assert (overlapped == NULL);
-       g_assert (buffers == NULL);
-       g_assert (num_write == 0);
-       g_assert (num_per_send == 0);
-       g_assert (flags == (TF_DISCONNECT | TF_REUSE_SOCKET));
+       if (startup_count == 0) {
+               WSASetLastError (WSANOTINITIALISED);
+               return FALSE;
+       }
+       
+       if (_wapi_handle_type (sock) != WAPI_HANDLE_SOCKET) {
+               WSASetLastError (WSAENOTSOCK);
+               return FALSE;
+       }
 
-       return(socket_disconnect (fd));
+       /* Write the header */
+       if (buffers != NULL && buffers->Head != NULL && buffers->HeadLength > 0) {
+               ret = _wapi_send (socket, buffers->Head, buffers->HeadLength, 0);
+               if (ret == SOCKET_ERROR)
+                       return FALSE;
+       }
+
+       ret = wapi_sendfile (socket, file, bytes_to_write, bytes_per_send, flags);
+       if (ret == SOCKET_ERROR)
+               return FALSE;
+
+       /* Write the tail */
+       if (buffers != NULL && buffers->Tail != NULL && buffers->TailLength > 0) {
+               ret = _wapi_send (socket, buffers->Tail, buffers->TailLength, 0);
+               if (ret == SOCKET_ERROR)
+                       return FALSE;
+       }
+
+       if ((flags & TF_DISCONNECT) == TF_DISCONNECT)
+               closesocket (socket);
+
+       return TRUE;
 }
 
 static struct 
@@ -1063,7 +1201,7 @@ static struct
        gpointer func;
 } extension_functions[] = {
        {WSAID_DISCONNECTEX, wapi_disconnectex},
-       {WSAID_TRANSMITFILE, wapi_transmitfile},
+       {WSAID_TRANSMITFILE, TransmitFile},
        {{0}, NULL},
 };
 
@@ -1127,6 +1265,55 @@ WSAIoctl (guint32 fd, gint32 command,
                return(SOCKET_ERROR);
        }
 
+       if (command == SIO_KEEPALIVE_VALS) {
+               uint32_t onoff;
+               uint32_t keepalivetime;
+               uint32_t keepaliveinterval;
+
+               if (i_len < (3 * sizeof (uint32_t))) {
+                       WSASetLastError (WSAEINVAL);
+                       return SOCKET_ERROR;
+               }
+               memcpy (&onoff, input, sizeof (uint32_t));
+               memcpy (&keepalivetime, input + sizeof (uint32_t), sizeof (uint32_t));
+               memcpy (&keepaliveinterval, input + 2 * sizeof (uint32_t), sizeof (uint32_t));
+               ret = setsockopt (fd, SOL_SOCKET, SO_KEEPALIVE, &onoff, sizeof (uint32_t));
+               if (ret < 0) {
+                       gint errnum = errno;
+                       errnum = errno_to_WSA (errnum, __func__);
+                       WSASetLastError (errnum);
+                       return SOCKET_ERROR;
+               }
+               if (onoff != 0) {
+#if defined(TCP_KEEPIDLE) && defined(TCP_KEEPINTVL)
+                       /* Values are in ms, but we need s */
+                       uint32_t rem;
+
+                       /* keepalivetime and keepaliveinterval are > 0 (checked in managed code) */
+                       rem = keepalivetime % 1000;
+                       keepalivetime /= 1000;
+                       if (keepalivetime == 0 || rem >= 500)
+                               keepalivetime++;
+                       ret = setsockopt (fd, IPPROTO_TCP, TCP_KEEPIDLE, &keepalivetime, sizeof (uint32_t));
+                       if (ret == 0) {
+                               rem = keepaliveinterval % 1000;
+                               keepaliveinterval /= 1000;
+                               if (keepaliveinterval == 0 || rem >= 500)
+                                       keepaliveinterval++;
+                               ret = setsockopt (fd, IPPROTO_TCP, TCP_KEEPINTVL, &keepaliveinterval, sizeof (uint32_t));
+                       }
+                       if (ret != 0) {
+                               gint errnum = errno;
+                               errnum = errno_to_WSA (errnum, __func__);
+                               WSASetLastError (errnum);
+                               return SOCKET_ERROR;
+                       }
+                       return 0;
+#endif
+               }
+               return 0;
+       }
+
        if (i_len > 0) {
                buffer = g_memdup (input, i_len);
        }
@@ -1134,10 +1321,8 @@ WSAIoctl (guint32 fd, gint32 command,
        ret = ioctl (fd, command, buffer);
        if (ret == -1) {
                gint errnum = errno;
-#ifdef DEBUG
-               g_message("%s: WSAIoctl error: %s", __func__,
+               DEBUG("%s: WSAIoctl error: %s", __func__,
                          strerror (errno));
-#endif
 
                errnum = errno_to_WSA (errnum, __func__);
                WSASetLastError (errnum);
@@ -1167,6 +1352,7 @@ WSAIoctl (guint32 fd, gint32 command,
        return(0);
 }
 
+#ifndef PLATFORM_PORT_PROVIDES_IOCTLSOCKET
 int ioctlsocket(guint32 fd, gint32 command, gpointer arg)
 {
        gpointer handle = GUINT_TO_POINTER (fd);
@@ -1200,10 +1386,32 @@ int ioctlsocket(guint32 fd, gint32 command, gpointer arg)
                        }
                        break;
 #endif /* O_NONBLOCK */
-               case FIONREAD:
+                       /* Unused in Mono */
                case SIOCATMARK:
                        ret = ioctl (fd, command, arg);
                        break;
+                       
+               case FIONREAD:
+               {
+#if defined (PLATFORM_MACOSX)
+                       
+                       // ioctl (fd, FIONREAD, XXX) returns the size of
+                       // the UDP header as well on
+                       // Darwin.
+                       //
+                       // Use getsockopt SO_NREAD instead to get the
+                       // right values for TCP and UDP.
+                       // 
+                       // ai_canonname can be null in some cases on darwin, where the runtime assumes it will
+                       // be the value of the ip buffer.
+
+                       socklen_t optlen = sizeof (int);
+                       ret = getsockopt (fd, SOL_SOCKET, SO_NREAD, arg, &optlen);
+#else
+                       ret = ioctl (fd, command, arg);
+#endif
+                       break;
+               }
                default:
                        WSASetLastError (WSAEINVAL);
                        return(SOCKET_ERROR);
@@ -1211,9 +1419,7 @@ int ioctlsocket(guint32 fd, gint32 command, gpointer arg)
 
        if (ret == -1) {
                gint errnum = errno;
-#ifdef DEBUG
-               g_message ("%s: ioctl error: %s", __func__, strerror (errno));
-#endif
+               DEBUG ("%s: ioctl error: %s", __func__, strerror (errno));
 
                errnum = errno_to_WSA (errnum, __func__);
                WSASetLastError (errnum);
@@ -1255,9 +1461,7 @@ int _wapi_select(int nfds G_GNUC_UNUSED, fd_set *readfds, fd_set *writefds,
 
        if (ret == -1) {
                gint errnum = errno;
-#ifdef DEBUG
-               g_message ("%s: select error: %s", __func__, strerror (errno));
-#endif
+               DEBUG ("%s: select error: %s", __func__, strerror (errno));
                errnum = errno_to_WSA (errnum, __func__);
                WSASetLastError (errnum);
                
@@ -1317,39 +1521,49 @@ void _wapi_FD_SET(guint32 fd, fd_set *set)
 
        FD_SET (fd, set);
 }
+#endif
+
+static void
+wsabuf_to_msghdr (WapiWSABuf *buffers, guint32 count, struct msghdr *hdr)
+{
+       guint32 i;
+
+       memset (hdr, 0, sizeof (struct msghdr));
+       hdr->msg_iovlen = count;
+       hdr->msg_iov = g_new0 (struct iovec, count);
+       for (i = 0; i < count; i++) {
+               hdr->msg_iov [i].iov_base = buffers [i].buf;
+               hdr->msg_iov [i].iov_len  = buffers [i].len;
+       }
+}
+
+static void
+msghdr_iov_free (struct msghdr *hdr)
+{
+       g_free (hdr->msg_iov);
+}
 
 int WSARecv (guint32 fd, WapiWSABuf *buffers, guint32 count, guint32 *received,
             guint32 *flags, WapiOverlapped *overlapped,
             WapiOverlappedCB *complete)
 {
-       int ret, i, buflen = 0, offset = 0, copylen;
-       gint8 *recvbuf;
+       int ret;
+       struct msghdr hdr;
 
        g_assert (overlapped == NULL);
        g_assert (complete == NULL);
-       
-       for(i = 0; i < count; i++) {
-               buflen += buffers[i].len;
-       }
 
-       recvbuf = g_new0 (gint8, buflen);
-       ret = _wapi_recvfrom (fd, recvbuf, buflen, *flags, NULL, 0);
+       wsabuf_to_msghdr (buffers, count, &hdr);
+       ret = _wapi_recvmsg (fd, &hdr, *flags);
+       msghdr_iov_free (&hdr);
+       
        if(ret == SOCKET_ERROR) {
-               g_free (recvbuf);
                return(ret);
        }
        
-       for(i = 0; i < count; i++) {
-               copylen = buffers[i].len > (buflen - offset)? (buflen - offset):buffers[i].len;
-               memcpy (buffers[i].buf, recvbuf + offset, copylen);
-
-               if (copylen != buffers[i].len) {
-                       break;
-               }
-               offset += copylen;
-       }
-       
        *received = ret;
+       *flags = hdr.msg_flags;
+
        return(0);
 }
 
@@ -1357,30 +1571,15 @@ int WSASend (guint32 fd, WapiWSABuf *buffers, guint32 count, guint32 *sent,
             guint32 flags, WapiOverlapped *overlapped,
             WapiOverlappedCB *complete)
 {
-       int ret, i, buflen = 0, offset = 0, copylen;
-       gint8 *sendbuf;
+       int ret;
+       struct msghdr hdr;
 
        g_assert (overlapped == NULL);
        g_assert (complete == NULL);
 
-       if (count == 1)
-               ret = _wapi_sendto (fd, buffers [0].buf, buffers [0].len, flags, NULL, 0);
-       else {
-               for(i = 0; i < count; i++) {
-                       buflen += buffers[i].len;
-               }
-               
-               sendbuf = g_new0 (gint8, buflen);
-               for(i = 0; i < count; i++) {
-                       copylen = buffers[i].len;
-                       memcpy (sendbuf + offset, buffers[i].buf, copylen);
-                       offset += copylen;
-               }
-               
-               ret = _wapi_sendto (fd, sendbuf, buflen, flags, NULL, 0);
-               g_free (sendbuf);
-               
-       }
+       wsabuf_to_msghdr (buffers, count, &hdr);
+       ret = _wapi_sendmsg (fd, &hdr, flags);
+       msghdr_iov_free (&hdr);
        
        if(ret == SOCKET_ERROR) 
                return ret;
@@ -1388,3 +1587,5 @@ int WSASend (guint32 fd, WapiWSABuf *buffers, guint32 count, guint32 *sent,
        *sent = ret;
        return 0;
 }
+
+#endif /* ifndef DISABLE_SOCKETS */