/** * \file * Unix specific socket code. * * Copyright 2016 Microsoft * Licensed under the MIT license. See LICENSE file in the project root for full license information. */ #include #include #include #include #include #include #include #ifdef HAVE_SYS_IOCTL_H #include #endif #include #include #ifdef HAVE_NETDB_H #include #endif #include #ifdef HAVE_UNISTD_H #include #endif #include #include #ifdef HAVE_SYS_UIO_H #include #endif #ifdef HAVE_SYS_IOCTL_H #include #endif #ifdef HAVE_SYS_FILIO_H #include /* defines FIONBIO and FIONREAD */ #endif #ifdef HAVE_SYS_SOCKIO_H #include /* defines SIOCATMARK */ #endif #ifndef HAVE_MSG_NOSIGNAL #include #endif #ifdef HAVE_SYS_SENDFILE_H #include #endif #include #include "w32socket.h" #include "w32socket-internals.h" #include "w32error.h" #include "fdhandle.h" #include "utils/mono-logger-internals.h" #include "utils/mono-poll.h" #include "utils/mono-compiler.h" typedef struct { MonoFDHandle fdhandle; gint domain; gint type; gint protocol; gint saved_error; gint still_readable; } SocketHandle; static SocketHandle* socket_data_create (MonoFDType type, gint fd) { SocketHandle *sockethandle; sockethandle = g_new0 (SocketHandle, 1); mono_fdhandle_init ((MonoFDHandle*) sockethandle, type, fd); return sockethandle; } static void socket_data_close (MonoFDHandle *fdhandle) { MonoThreadInfo *info; SocketHandle* sockethandle; gint ret; sockethandle = (SocketHandle*) fdhandle; g_assert (sockethandle); info = mono_thread_info_current (); mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: closing fd %d", __func__, ((MonoFDHandle*) sockethandle)->fd); /* Shutdown the socket for reading, to interrupt any potential * receives that may be blocking for data. See bug 75705. */ MONO_ENTER_GC_SAFE; shutdown (((MonoFDHandle*) sockethandle)->fd, SHUT_RD); MONO_EXIT_GC_SAFE; retry_close: MONO_ENTER_GC_SAFE; ret = close (((MonoFDHandle*) sockethandle)->fd); MONO_EXIT_GC_SAFE; if (ret == -1) { if (errno == EINTR && !mono_thread_info_is_interrupt_state (info)) goto retry_close; } sockethandle->saved_error = 0; } static void socket_data_destroy (MonoFDHandle *fdhandle) { SocketHandle *sockethandle; sockethandle = (SocketHandle*) fdhandle; g_assert (sockethandle); g_free (sockethandle); } void mono_w32socket_initialize (void) { MonoFDHandleCallback socket_data_callbacks = { .close = socket_data_close, .destroy = socket_data_destroy }; mono_fdhandle_register (MONO_FDTYPE_SOCKET, &socket_data_callbacks); } void mono_w32socket_cleanup (void) { } SOCKET mono_w32socket_accept (SOCKET sock, struct sockaddr *addr, socklen_t *addrlen, gboolean blocking) { SocketHandle *sockethandle, *accepted_socket_data; MonoThreadInfo *info; gint accepted_fd; if (addr != NULL && *addrlen < sizeof(struct sockaddr)) { mono_w32socket_set_last_error (WSAEFAULT); return INVALID_SOCKET; } if (!mono_fdhandle_lookup_and_ref(sock, (MonoFDHandle**) &sockethandle)) { mono_w32error_set_last (WSAENOTSOCK); return INVALID_SOCKET; } if (((MonoFDHandle*) sockethandle)->type != MONO_FDTYPE_SOCKET) { mono_fdhandle_unref ((MonoFDHandle*) sockethandle); mono_w32error_set_last (WSAENOTSOCK); return INVALID_SOCKET; } info = mono_thread_info_current (); do { MONO_ENTER_GC_SAFE; accepted_fd = accept (((MonoFDHandle*) sockethandle)->fd, addr, addrlen); MONO_EXIT_GC_SAFE; } while (accepted_fd == -1 && errno == EINTR && !mono_thread_info_is_interrupt_state (info)); if (accepted_fd == -1) { gint error = mono_w32socket_convert_error (errno); mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: accept error: %s", __func__, g_strerror(errno)); mono_w32socket_set_last_error (error); mono_fdhandle_unref ((MonoFDHandle*) sockethandle); return INVALID_SOCKET; } accepted_socket_data = socket_data_create (MONO_FDTYPE_SOCKET, accepted_fd); accepted_socket_data->domain = sockethandle->domain; accepted_socket_data->type = sockethandle->type; accepted_socket_data->protocol = sockethandle->protocol; accepted_socket_data->still_readable = 1; mono_fdhandle_insert ((MonoFDHandle*) accepted_socket_data); mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: returning accepted handle %p", __func__, GINT_TO_POINTER(((MonoFDHandle*) accepted_socket_data)->fd)); mono_fdhandle_unref ((MonoFDHandle*) sockethandle); return ((MonoFDHandle*) accepted_socket_data)->fd; } int mono_w32socket_connect (SOCKET sock, const struct sockaddr *addr, int addrlen, gboolean blocking) { SocketHandle *sockethandle; gint ret; if (!mono_fdhandle_lookup_and_ref(sock, (MonoFDHandle**) &sockethandle)) { mono_w32error_set_last (WSAENOTSOCK); return SOCKET_ERROR; } if (((MonoFDHandle*) sockethandle)->type != MONO_FDTYPE_SOCKET) { mono_fdhandle_unref ((MonoFDHandle*) sockethandle); mono_w32error_set_last (WSAENOTSOCK); return SOCKET_ERROR; } MONO_ENTER_GC_SAFE; ret = connect (((MonoFDHandle*) sockethandle)->fd, addr, addrlen); MONO_EXIT_GC_SAFE; if (ret == -1) { MonoThreadInfo *info; mono_pollfd fds; gint errnum, so_error; socklen_t len; errnum = errno; if (errno != EINTR) { mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: connect error: %s", __func__, g_strerror (errnum)); errnum = mono_w32socket_convert_error (errnum); if (errnum == WSAEINPROGRESS) errnum = WSAEWOULDBLOCK; /* see bug #73053 */ mono_w32socket_set_last_error (errnum); /* * On solaris x86 getsockopt (SO_ERROR) is not set after * connect () fails so we need to save this error. * * But don't do this for EWOULDBLOCK (bug 317315) */ if (errnum != WSAEWOULDBLOCK) { /* ECONNRESET means the socket was closed by another thread */ /* Async close on mac raises ECONNABORTED. */ sockethandle->saved_error = errnum; } mono_fdhandle_unref ((MonoFDHandle*) sockethandle); return SOCKET_ERROR; } info = mono_thread_info_current (); fds.fd = ((MonoFDHandle*) sockethandle)->fd; fds.events = MONO_POLLOUT; for (;;) { MONO_ENTER_GC_SAFE; ret = mono_poll (&fds, 1, -1); MONO_EXIT_GC_SAFE; if (ret != -1 || mono_thread_info_is_interrupt_state (info)) break; if (errno != EINTR) { gint errnum = errno; mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: connect poll error: %s", __func__, g_strerror (errno)); mono_w32socket_set_last_error (mono_w32socket_convert_error (errnum)); mono_fdhandle_unref ((MonoFDHandle*) sockethandle); return SOCKET_ERROR; } } len = sizeof(so_error); MONO_ENTER_GC_SAFE; ret = getsockopt (((MonoFDHandle*) sockethandle)->fd, SOL_SOCKET, SO_ERROR, &so_error, &len); MONO_EXIT_GC_SAFE; if (ret == -1) { gint errnum = errno; mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: connect getsockopt error: %s", __func__, g_strerror (errno)); mono_w32socket_set_last_error (mono_w32socket_convert_error (errnum)); mono_fdhandle_unref ((MonoFDHandle*) sockethandle); return SOCKET_ERROR; } if (so_error != 0) { gint errnum = mono_w32socket_convert_error (so_error); /* Need to save this socket error */ sockethandle->saved_error = errnum; mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: connect getsockopt returned error: %s", __func__, g_strerror (so_error)); mono_w32socket_set_last_error (errnum); mono_fdhandle_unref ((MonoFDHandle*) sockethandle); return SOCKET_ERROR; } } mono_fdhandle_unref ((MonoFDHandle*) sockethandle); return 0; } int mono_w32socket_recv (SOCKET sock, char *buf, int len, int flags, gboolean blocking) { return mono_w32socket_recvfrom (sock, buf, len, flags, NULL, 0, blocking); } int mono_w32socket_recvfrom (SOCKET sock, char *buf, int len, int flags, struct sockaddr *from, socklen_t *fromlen, gboolean blocking) { SocketHandle *sockethandle; int ret; MonoThreadInfo *info; if (!mono_fdhandle_lookup_and_ref(sock, (MonoFDHandle**) &sockethandle)) { mono_w32error_set_last (WSAENOTSOCK); return SOCKET_ERROR; } if (((MonoFDHandle*) sockethandle)->type != MONO_FDTYPE_SOCKET) { mono_fdhandle_unref ((MonoFDHandle*) sockethandle); mono_w32error_set_last (WSAENOTSOCK); return SOCKET_ERROR; } info = mono_thread_info_current (); do { MONO_ENTER_GC_SAFE; ret = recvfrom (((MonoFDHandle*) sockethandle)->fd, buf, len, flags, from, fromlen); MONO_EXIT_GC_SAFE; } while (ret == -1 && errno == EINTR && !mono_thread_info_is_interrupt_state (info)); if (ret == 0 && len > 0) { /* According to the Linux man page, recvfrom only * returns 0 when the socket has been shut down * cleanly. Turn this into an EINTR to simulate win32 * behaviour of returning EINTR when a socket is * closed while the recvfrom is blocking (we use a * shutdown() in socket_close() to trigger this.) See * bug 75705. */ /* Distinguish between the socket being shut down at * the local or remote ends, and reads that request 0 * bytes to be read */ /* If this returns FALSE, it means the socket has been * closed locally. If it returns TRUE, but * still_readable != 1 then shutdown * (SHUT_RD|SHUT_RDWR) has been called locally. */ if (sockethandle->still_readable != 1) { ret = -1; errno = EINTR; } } if (ret == -1) { gint errnum = errno; mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: recv error: %s", __func__, g_strerror(errno)); mono_w32socket_set_last_error (mono_w32socket_convert_error (errnum)); mono_fdhandle_unref ((MonoFDHandle*) sockethandle); return SOCKET_ERROR; } mono_fdhandle_unref ((MonoFDHandle*) sockethandle); return ret; } static void wsabuf_to_msghdr (WSABUF *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 mono_w32socket_recvbuffers (SOCKET sock, WSABUF *buffers, guint32 count, guint32 *received, guint32 *flags, gpointer overlapped, gpointer complete, gboolean blocking) { SocketHandle *sockethandle; MonoThreadInfo *info; gint ret; struct msghdr hdr; g_assert (overlapped == NULL); g_assert (complete == NULL); if (!mono_fdhandle_lookup_and_ref(sock, (MonoFDHandle**) &sockethandle)) { mono_w32error_set_last (WSAENOTSOCK); return SOCKET_ERROR; } if (((MonoFDHandle*) sockethandle)->type != MONO_FDTYPE_SOCKET) { mono_fdhandle_unref ((MonoFDHandle*) sockethandle); mono_w32error_set_last (WSAENOTSOCK); return SOCKET_ERROR; } info = mono_thread_info_current (); wsabuf_to_msghdr (buffers, count, &hdr); do { MONO_ENTER_GC_SAFE; ret = recvmsg (((MonoFDHandle*) sockethandle)->fd, &hdr, *flags); MONO_EXIT_GC_SAFE; } while (ret == -1 && errno == EINTR && !mono_thread_info_is_interrupt_state (info)); msghdr_iov_free (&hdr); if (ret == 0) { /* see mono_w32socket_recvfrom */ if (sockethandle->still_readable != 1) { ret = -1; errno = EINTR; } } if (ret == -1) { gint errnum = errno; mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: recvmsg error: %s", __func__, g_strerror(errno)); mono_w32socket_set_last_error (mono_w32socket_convert_error (errnum)); mono_fdhandle_unref ((MonoFDHandle*) sockethandle); return SOCKET_ERROR; } *received = ret; *flags = hdr.msg_flags; mono_fdhandle_unref ((MonoFDHandle*) sockethandle); return 0; } int mono_w32socket_send (SOCKET sock, char *buf, int len, int flags, gboolean blocking) { SocketHandle *sockethandle; int ret; MonoThreadInfo *info; if (!mono_fdhandle_lookup_and_ref(sock, (MonoFDHandle**) &sockethandle)) { mono_w32error_set_last (WSAENOTSOCK); return SOCKET_ERROR; } if (((MonoFDHandle*) sockethandle)->type != MONO_FDTYPE_SOCKET) { mono_fdhandle_unref ((MonoFDHandle*) sockethandle); mono_w32error_set_last (WSAENOTSOCK); return SOCKET_ERROR; } info = mono_thread_info_current (); do { MONO_ENTER_GC_SAFE; ret = send (((MonoFDHandle*) sockethandle)->fd, buf, len, flags); MONO_EXIT_GC_SAFE; } while (ret == -1 && errno == EINTR && !mono_thread_info_is_interrupt_state (info)); if (ret == -1) { gint errnum = errno; mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: send error: %s", __func__, g_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) { MONO_ENTER_GC_SAFE; ret = fcntl (((MonoFDHandle*) sockethandle)->fd, F_GETFL, 0); MONO_EXIT_GC_SAFE; if (ret != -1 && (ret & O_NONBLOCK) == 0) errnum = ETIMEDOUT; } #endif /* O_NONBLOCK */ mono_w32socket_set_last_error (mono_w32socket_convert_error (errnum)); mono_fdhandle_unref ((MonoFDHandle*) sockethandle); return SOCKET_ERROR; } mono_fdhandle_unref ((MonoFDHandle*) sockethandle); return ret; } int mono_w32socket_sendto (SOCKET sock, const char *buf, int len, int flags, const struct sockaddr *to, int tolen, gboolean blocking) { SocketHandle *sockethandle; int ret; MonoThreadInfo *info; if (!mono_fdhandle_lookup_and_ref(sock, (MonoFDHandle**) &sockethandle)) { mono_w32error_set_last (WSAENOTSOCK); return SOCKET_ERROR; } if (((MonoFDHandle*) sockethandle)->type != MONO_FDTYPE_SOCKET) { mono_fdhandle_unref ((MonoFDHandle*) sockethandle); mono_w32error_set_last (WSAENOTSOCK); return SOCKET_ERROR; } info = mono_thread_info_current (); do { MONO_ENTER_GC_SAFE; ret = sendto (((MonoFDHandle*) sockethandle)->fd, buf, len, flags, to, tolen); MONO_EXIT_GC_SAFE; } while (ret == -1 && errno == EINTR && !mono_thread_info_is_interrupt_state (info)); if (ret == -1) { gint errnum = errno; mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: send error: %s", __func__, g_strerror (errno)); mono_w32socket_set_last_error (mono_w32socket_convert_error (errnum)); mono_fdhandle_unref ((MonoFDHandle*) sockethandle); return SOCKET_ERROR; } mono_fdhandle_unref ((MonoFDHandle*) sockethandle); return ret; } int mono_w32socket_sendbuffers (SOCKET sock, WSABUF *buffers, guint32 count, guint32 *sent, guint32 flags, gpointer overlapped, gpointer complete, gboolean blocking) { struct msghdr hdr; MonoThreadInfo *info; SocketHandle *sockethandle; gint ret; g_assert (overlapped == NULL); g_assert (complete == NULL); if (!mono_fdhandle_lookup_and_ref(sock, (MonoFDHandle**) &sockethandle)) { mono_w32error_set_last (WSAENOTSOCK); return SOCKET_ERROR; } if (((MonoFDHandle*) sockethandle)->type != MONO_FDTYPE_SOCKET) { mono_fdhandle_unref ((MonoFDHandle*) sockethandle); mono_w32error_set_last (WSAENOTSOCK); return SOCKET_ERROR; } info = mono_thread_info_current (); wsabuf_to_msghdr (buffers, count, &hdr); do { MONO_ENTER_GC_SAFE; ret = sendmsg (((MonoFDHandle*) sockethandle)->fd, &hdr, flags); MONO_EXIT_GC_SAFE; } while (ret == -1 && errno == EINTR && !mono_thread_info_is_interrupt_state (info)); msghdr_iov_free (&hdr); if (ret == -1) { gint errnum = errno; mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: sendmsg error: %s", __func__, g_strerror (errno)); mono_w32socket_set_last_error (mono_w32socket_convert_error (errnum)); mono_fdhandle_unref ((MonoFDHandle*) sockethandle); return SOCKET_ERROR; } *sent = ret; mono_fdhandle_unref ((MonoFDHandle*) sockethandle); return 0; } #define SF_BUFFER_SIZE 16384 BOOL mono_w32socket_transmit_file (SOCKET sock, gpointer file_handle, TRANSMIT_FILE_BUFFERS *buffers, guint32 flags, gboolean blocking) { MonoThreadInfo *info; SocketHandle *sockethandle; gint file; gssize ret; #if defined(HAVE_SENDFILE) && (defined(__linux__) || defined(DARWIN)) struct stat statbuf; #else gchar *buffer; #endif if (!mono_fdhandle_lookup_and_ref(sock, (MonoFDHandle**) &sockethandle)) { mono_w32error_set_last (WSAENOTSOCK); return SOCKET_ERROR; } if (((MonoFDHandle*) sockethandle)->type != MONO_FDTYPE_SOCKET) { mono_fdhandle_unref ((MonoFDHandle*) sockethandle); mono_w32error_set_last (WSAENOTSOCK); return SOCKET_ERROR; } /* Write the header */ if (buffers != NULL && buffers->Head != NULL && buffers->HeadLength > 0) { ret = mono_w32socket_send (((MonoFDHandle*) sockethandle)->fd, buffers->Head, buffers->HeadLength, 0, FALSE); if (ret == SOCKET_ERROR) { mono_fdhandle_unref ((MonoFDHandle*) sockethandle); return FALSE; } } info = mono_thread_info_current (); file = GPOINTER_TO_INT (file_handle); #if defined(HAVE_SENDFILE) && (defined(__linux__) || defined(DARWIN)) MONO_ENTER_GC_SAFE; ret = fstat (file, &statbuf); MONO_EXIT_GC_SAFE; if (ret == -1) { gint errnum = errno; mono_w32socket_set_last_error (mono_w32socket_convert_error (errnum)); mono_fdhandle_unref ((MonoFDHandle*) sockethandle); return SOCKET_ERROR; } do { MONO_ENTER_GC_SAFE; #ifdef __linux__ ret = sendfile (((MonoFDHandle*) sockethandle)->fd, 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 */ ret = sendfile (file, ((MonoFDHandle*) sockethandle)->fd, 0, &statbuf.st_size, NULL, 0); #endif MONO_EXIT_GC_SAFE; } while (ret != -1 && errno == EINTR && !mono_thread_info_is_interrupt_state (info)); #else buffer = g_malloc (SF_BUFFER_SIZE); do { do { MONO_ENTER_GC_SAFE; ret = read (file, buffer, SF_BUFFER_SIZE); MONO_EXIT_GC_SAFE; } while (ret == -1 && errno == EINTR && !mono_thread_info_is_interrupt_state (info)); if (ret == -1 || ret == 0) break; do { MONO_ENTER_GC_SAFE; ret = send (((MonoFDHandle*) sockethandle)->fd, buffer, ret, 0); /* short sends? enclose this in a loop? */ MONO_EXIT_GC_SAFE; } while (ret == -1 && errno == EINTR && !mono_thread_info_is_interrupt_state (info)); } while (ret != -1 && errno == EINTR && !mono_thread_info_is_interrupt_state (info)); g_free (buffer); #endif if (ret == -1) { gint errnum = errno; mono_w32socket_set_last_error (mono_w32socket_convert_error (errnum)); mono_fdhandle_unref ((MonoFDHandle*) sockethandle); return FALSE; } /* Write the tail */ if (buffers != NULL && buffers->Tail != NULL && buffers->TailLength > 0) { ret = mono_w32socket_send (((MonoFDHandle*) sockethandle)->fd, buffers->Tail, buffers->TailLength, 0, FALSE); if (ret == SOCKET_ERROR) { mono_fdhandle_unref ((MonoFDHandle*) sockethandle); return FALSE; } } if ((flags & TF_DISCONNECT) == TF_DISCONNECT) mono_w32socket_close (((MonoFDHandle*) sockethandle)->fd); mono_fdhandle_unref ((MonoFDHandle*) sockethandle); return TRUE; } SOCKET mono_w32socket_socket (int domain, int type, int protocol) { SocketHandle *sockethandle; gint fd; retry_socket: MONO_ENTER_GC_SAFE; fd = socket (domain, type, protocol); MONO_EXIT_GC_SAFE; if (fd == -1) { if (domain == AF_INET && type == SOCK_RAW && protocol == 0) { /* Retry with protocol == 4 (see bug #54565) */ // https://bugzilla.novell.com/show_bug.cgi?id=MONO54565 protocol = 4; goto retry_socket; } gint errnum = errno; mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: socket error: %s", __func__, g_strerror (errno)); mono_w32socket_set_last_error (mono_w32socket_convert_error (errnum)); return INVALID_SOCKET; } sockethandle = socket_data_create(MONO_FDTYPE_SOCKET, fd); sockethandle->domain = domain; sockethandle->type = type; sockethandle->protocol = protocol; sockethandle->still_readable = 1; /* .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 * 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. * https://bugzilla.novell.com/show_bug.cgi?id=MONO53992 */ { int ret, true_ = 1; MONO_ENTER_GC_SAFE; ret = setsockopt (((MonoFDHandle*) sockethandle)->fd, SOL_SOCKET, SO_REUSEADDR, &true_, sizeof (true_)); MONO_EXIT_GC_SAFE; if (ret == -1) { gint errnum = errno; mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Error setting SO_REUSEADDR", __func__); mono_w32socket_set_last_error (mono_w32socket_convert_error (errnum)); MONO_ENTER_GC_SAFE; close (((MonoFDHandle*) sockethandle)->fd); MONO_EXIT_GC_SAFE; mono_fdhandle_unref ((MonoFDHandle*) sockethandle); return INVALID_SOCKET; } } mono_fdhandle_insert ((MonoFDHandle*) sockethandle); mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: returning socket handle %p", __func__, GINT_TO_POINTER(((MonoFDHandle*) sockethandle)->fd)); return ((MonoFDHandle*) sockethandle)->fd; } gint mono_w32socket_bind (SOCKET sock, struct sockaddr *addr, socklen_t addrlen) { SocketHandle *sockethandle; int ret; if (!mono_fdhandle_lookup_and_ref(sock, (MonoFDHandle**) &sockethandle)) { mono_w32error_set_last (WSAENOTSOCK); return SOCKET_ERROR; } if (((MonoFDHandle*) sockethandle)->type != MONO_FDTYPE_SOCKET) { mono_fdhandle_unref ((MonoFDHandle*) sockethandle); mono_w32error_set_last (WSAENOTSOCK); return SOCKET_ERROR; } MONO_ENTER_GC_SAFE; ret = bind (((MonoFDHandle*) sockethandle)->fd, addr, addrlen); MONO_EXIT_GC_SAFE; if (ret == -1) { gint errnum = errno; mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: bind error: %s", __func__, g_strerror(errno)); mono_w32socket_set_last_error (mono_w32socket_convert_error (errnum)); mono_fdhandle_unref ((MonoFDHandle*) sockethandle); return SOCKET_ERROR; } mono_fdhandle_unref ((MonoFDHandle*) sockethandle); return 0; } gint mono_w32socket_getpeername (SOCKET sock, struct sockaddr *name, socklen_t *namelen) { SocketHandle *sockethandle; gint ret; if (!mono_fdhandle_lookup_and_ref(sock, (MonoFDHandle**) &sockethandle)) { mono_w32error_set_last (WSAENOTSOCK); return SOCKET_ERROR; } if (((MonoFDHandle*) sockethandle)->type != MONO_FDTYPE_SOCKET) { mono_fdhandle_unref ((MonoFDHandle*) sockethandle); mono_w32error_set_last (WSAENOTSOCK); return SOCKET_ERROR; } MONO_ENTER_GC_SAFE; ret = getpeername (((MonoFDHandle*) sockethandle)->fd, name, namelen); MONO_EXIT_GC_SAFE; if (ret == -1) { gint errnum = errno; mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: getpeername error: %s", __func__, g_strerror (errno)); mono_w32socket_set_last_error (mono_w32socket_convert_error (errnum)); mono_fdhandle_unref ((MonoFDHandle*) sockethandle); return SOCKET_ERROR; } mono_fdhandle_unref ((MonoFDHandle*) sockethandle); return 0; } gint mono_w32socket_getsockname (SOCKET sock, struct sockaddr *name, socklen_t *namelen) { SocketHandle *sockethandle; gint ret; if (!mono_fdhandle_lookup_and_ref(sock, (MonoFDHandle**) &sockethandle)) { mono_w32error_set_last (WSAENOTSOCK); return SOCKET_ERROR; } if (((MonoFDHandle*) sockethandle)->type != MONO_FDTYPE_SOCKET) { mono_fdhandle_unref ((MonoFDHandle*) sockethandle); mono_w32error_set_last (WSAENOTSOCK); return SOCKET_ERROR; } MONO_ENTER_GC_SAFE; ret = getsockname (((MonoFDHandle*) sockethandle)->fd, name, namelen); MONO_EXIT_GC_SAFE; if (ret == -1) { gint errnum = errno; mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: getsockname error: %s", __func__, g_strerror (errno)); mono_w32socket_set_last_error (mono_w32socket_convert_error (errnum)); mono_fdhandle_unref ((MonoFDHandle*) sockethandle); return SOCKET_ERROR; } mono_fdhandle_unref ((MonoFDHandle*) sockethandle); return 0; } gint mono_w32socket_getsockopt (SOCKET sock, gint level, gint optname, gpointer optval, socklen_t *optlen) { SocketHandle *sockethandle; gint ret; struct timeval tv; gpointer tmp_val; if (!mono_fdhandle_lookup_and_ref(sock, (MonoFDHandle**) &sockethandle)) { mono_w32error_set_last (WSAENOTSOCK); return SOCKET_ERROR; } if (((MonoFDHandle*) sockethandle)->type != MONO_FDTYPE_SOCKET) { mono_fdhandle_unref ((MonoFDHandle*) sockethandle); mono_w32error_set_last (WSAENOTSOCK); return SOCKET_ERROR; } tmp_val = optval; if (level == SOL_SOCKET && (optname == SO_RCVTIMEO || optname == SO_SNDTIMEO)) { tmp_val = &tv; *optlen = sizeof (tv); } MONO_ENTER_GC_SAFE; ret = getsockopt (((MonoFDHandle*) sockethandle)->fd, level, optname, tmp_val, optlen); MONO_EXIT_GC_SAFE; if (ret == -1) { gint errnum = errno; mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: getsockopt error: %s", __func__, g_strerror (errno)); mono_w32socket_set_last_error (mono_w32socket_convert_error (errnum)); mono_fdhandle_unref ((MonoFDHandle*) sockethandle); return SOCKET_ERROR; } if (level == SOL_SOCKET && (optname == SO_RCVTIMEO || optname == SO_SNDTIMEO)) { *((int *) optval) = tv.tv_sec * 1000 + (tv.tv_usec / 1000); // milli from micro *optlen = sizeof (int); } if (optname == SO_ERROR) { if (*((int *)optval) != 0) { *((int *) optval) = mono_w32socket_convert_error (*((int *)optval)); sockethandle->saved_error = *((int *)optval); } else { *((int *)optval) = sockethandle->saved_error; } } mono_fdhandle_unref ((MonoFDHandle*) sockethandle); return 0; } gint mono_w32socket_setsockopt (SOCKET sock, gint level, gint optname, const gpointer optval, socklen_t optlen) { SocketHandle *sockethandle; gint ret; gpointer tmp_val; #if defined (__linux__) /* This has its address taken so it cannot be moved to the if block which uses it */ gint bufsize = 0; #endif struct timeval tv; if (!mono_fdhandle_lookup_and_ref(sock, (MonoFDHandle**) &sockethandle)) { mono_w32error_set_last (WSAENOTSOCK); return SOCKET_ERROR; } if (((MonoFDHandle*) sockethandle)->type != MONO_FDTYPE_SOCKET) { mono_fdhandle_unref ((MonoFDHandle*) sockethandle); mono_w32error_set_last (WSAENOTSOCK); return SOCKET_ERROR; } tmp_val = optval; if (level == SOL_SOCKET && (optname == SO_RCVTIMEO || optname == SO_SNDTIMEO)) { int ms = *((int *) optval); tv.tv_sec = ms / 1000; tv.tv_usec = (ms % 1000) * 1000; // micro from milli tmp_val = &tv; optlen = sizeof (tv); } #if defined (__linux__) else if (level == SOL_SOCKET && (optname == SO_SNDBUF || optname == SO_RCVBUF)) { /* According to socket(7) the Linux kernel doubles the * buffer sizes "to allow space for bookkeeping * overhead." */ bufsize = *((int *) optval); bufsize /= 2; tmp_val = &bufsize; } #endif MONO_ENTER_GC_SAFE; ret = setsockopt (((MonoFDHandle*) sockethandle)->fd, level, optname, tmp_val, optlen); MONO_EXIT_GC_SAFE; if (ret == -1) { gint errnum = errno; mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: setsockopt error: %s", __func__, g_strerror (errno)); mono_w32socket_set_last_error (mono_w32socket_convert_error (errnum)); mono_fdhandle_unref ((MonoFDHandle*) sockethandle); return SOCKET_ERROR; } #if defined (SO_REUSEPORT) /* 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; socklen_t type_len = sizeof (type); MONO_ENTER_GC_SAFE; ret = getsockopt (((MonoFDHandle*) sockethandle)->fd, level, SO_TYPE, &type, &type_len); MONO_EXIT_GC_SAFE; if (!ret) { if (type == SOCK_DGRAM || type == SOCK_STREAM) { MONO_ENTER_GC_SAFE; setsockopt (((MonoFDHandle*) sockethandle)->fd, level, SO_REUSEPORT, tmp_val, optlen); MONO_EXIT_GC_SAFE; } } } #endif mono_fdhandle_unref ((MonoFDHandle*) sockethandle); return ret; } gint mono_w32socket_listen (SOCKET sock, gint backlog) { SocketHandle *sockethandle; gint ret; if (!mono_fdhandle_lookup_and_ref(sock, (MonoFDHandle**) &sockethandle)) { mono_w32error_set_last (WSAENOTSOCK); return SOCKET_ERROR; } if (((MonoFDHandle*) sockethandle)->type != MONO_FDTYPE_SOCKET) { mono_fdhandle_unref ((MonoFDHandle*) sockethandle); mono_w32error_set_last (WSAENOTSOCK); return SOCKET_ERROR; } MONO_ENTER_GC_SAFE; ret = listen (((MonoFDHandle*) sockethandle)->fd, backlog); MONO_EXIT_GC_SAFE; if (ret == -1) { gint errnum = errno; mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: listen error: %s", __func__, g_strerror (errno)); mono_w32socket_set_last_error (mono_w32socket_convert_error (errnum)); mono_fdhandle_unref ((MonoFDHandle*) sockethandle); return SOCKET_ERROR; } mono_fdhandle_unref ((MonoFDHandle*) sockethandle); return 0; } gint mono_w32socket_shutdown (SOCKET sock, gint how) { SocketHandle *sockethandle; gint ret; if (!mono_fdhandle_lookup_and_ref(sock, (MonoFDHandle**) &sockethandle)) { mono_w32error_set_last (WSAENOTSOCK); return SOCKET_ERROR; } if (((MonoFDHandle*) sockethandle)->type != MONO_FDTYPE_SOCKET) { mono_fdhandle_unref ((MonoFDHandle*) sockethandle); mono_w32error_set_last (WSAENOTSOCK); return SOCKET_ERROR; } if (how == SHUT_RD || how == SHUT_RDWR) sockethandle->still_readable = 0; MONO_ENTER_GC_SAFE; ret = shutdown (((MonoFDHandle*) sockethandle)->fd, how); MONO_EXIT_GC_SAFE; if (ret == -1) { gint errnum = errno; mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: shutdown error: %s", __func__, g_strerror (errno)); mono_w32socket_set_last_error (mono_w32socket_convert_error (errnum)); mono_fdhandle_unref ((MonoFDHandle*) sockethandle); return SOCKET_ERROR; } mono_fdhandle_unref ((MonoFDHandle*) sockethandle); return ret; } gint mono_w32socket_disconnect (SOCKET sock, gboolean reuse) { SocketHandle *sockethandle; SOCKET newsock; gint ret; mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: called on socket %d!", __func__, sock); /* We could check the socket type here and fail unless its * SOCK_STREAM, SOCK_SEQPACKET or SOCK_RDM (according to msdn) * if we really wanted to */ if (!mono_fdhandle_lookup_and_ref(sock, (MonoFDHandle**) &sockethandle)) { mono_w32error_set_last (WSAENOTSOCK); return SOCKET_ERROR; } if (((MonoFDHandle*) sockethandle)->type != MONO_FDTYPE_SOCKET) { mono_fdhandle_unref ((MonoFDHandle*) sockethandle); mono_w32error_set_last (WSAENOTSOCK); return SOCKET_ERROR; } MONO_ENTER_GC_SAFE; newsock = socket (sockethandle->domain, sockethandle->type, sockethandle->protocol); MONO_EXIT_GC_SAFE; if (newsock == -1) { gint errnum = errno; mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: socket error: %s", __func__, g_strerror (errnum)); mono_w32socket_set_last_error (mono_w32socket_convert_error (errnum)); mono_fdhandle_unref ((MonoFDHandle*) sockethandle); return SOCKET_ERROR; } /* According to Stevens "Advanced Programming in the UNIX * Environment: UNIX File I/O" dup2() is atomic so there * should not be a race condition between the old fd being * closed and the new socket fd being copied over */ do { MONO_ENTER_GC_SAFE; ret = dup2 (newsock, ((MonoFDHandle*) sockethandle)->fd); MONO_EXIT_GC_SAFE; } while (ret == -1 && errno == EAGAIN); if (ret == -1) { gint errnum = errno; mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: dup2 error: %s", __func__, g_strerror (errnum)); mono_w32socket_set_last_error (mono_w32socket_convert_error (errnum)); mono_fdhandle_unref ((MonoFDHandle*) sockethandle); return SOCKET_ERROR; } MONO_ENTER_GC_SAFE; close (newsock); MONO_EXIT_GC_SAFE; mono_fdhandle_unref ((MonoFDHandle*) sockethandle); return 0; } static gboolean extension_disconect (SOCKET sock, OVERLAPPED *overlapped, guint32 flags, guint32 reserved) { gboolean ret; MONO_ENTER_GC_UNSAFE; ret = mono_w32socket_disconnect (sock, flags & TF_REUSE_SOCKET) == 0; MONO_EXIT_GC_UNSAFE; return ret; } static gboolean extension_transmit_file (SOCKET sock, gpointer file_handle, guint32 bytes_to_write, guint32 bytes_per_send, OVERLAPPED *ol, TRANSMIT_FILE_BUFFERS *buffers, guint32 flags) { gboolean ret; MONO_ENTER_GC_UNSAFE; ret = mono_w32socket_transmit_file (sock, file_handle, buffers, flags, FALSE); MONO_EXIT_GC_UNSAFE; return ret; } static struct { GUID guid; gpointer func; } extension_functions[] = { { {0x7fda2e11,0x8630,0x436f,{0xa0,0x31,0xf5,0x36,0xa6,0xee,0xc1,0x57}} /* WSAID_DISCONNECTEX */, extension_disconect }, { {0xb5367df0,0xcbac,0x11cf,{0x95,0xca,0x00,0x80,0x5f,0x48,0xa1,0x92}} /* WSAID_TRANSMITFILE */, extension_transmit_file }, { {0} , NULL }, }; gint mono_w32socket_ioctl (SOCKET sock, gint32 command, gchar *input, gint inputlen, gchar *output, gint outputlen, glong *written) { SocketHandle *sockethandle; gint ret; gchar *buffer; if (!mono_fdhandle_lookup_and_ref(sock, (MonoFDHandle**) &sockethandle)) { mono_w32error_set_last (WSAENOTSOCK); return SOCKET_ERROR; } if (((MonoFDHandle*) sockethandle)->type != MONO_FDTYPE_SOCKET) { mono_fdhandle_unref ((MonoFDHandle*) sockethandle); mono_w32error_set_last (WSAENOTSOCK); return SOCKET_ERROR; } if (command == 0xC8000006 /* SIO_GET_EXTENSION_FUNCTION_POINTER */) { gint i; GUID *guid; if (inputlen < sizeof(GUID)) { /* As far as I can tell, windows doesn't * actually set an error here... */ mono_w32socket_set_last_error (WSAEINVAL); mono_fdhandle_unref ((MonoFDHandle*) sockethandle); return SOCKET_ERROR; } if (outputlen < sizeof(gpointer)) { /* Or here... */ mono_w32socket_set_last_error (WSAEINVAL); mono_fdhandle_unref ((MonoFDHandle*) sockethandle); return SOCKET_ERROR; } if (output == NULL) { /* Or here */ mono_w32socket_set_last_error (WSAEINVAL); mono_fdhandle_unref ((MonoFDHandle*) sockethandle); return SOCKET_ERROR; } guid = (GUID*) input; for (i = 0; extension_functions[i].func; i++) { if (memcmp (guid, &extension_functions[i].guid, sizeof(GUID)) == 0) { memcpy (output, &extension_functions[i].func, sizeof(gpointer)); *written = sizeof(gpointer); mono_fdhandle_unref ((MonoFDHandle*) sockethandle); return 0; } } mono_w32socket_set_last_error (WSAEINVAL); mono_fdhandle_unref ((MonoFDHandle*) sockethandle); return SOCKET_ERROR; } if (command == 0x98000004 /* SIO_KEEPALIVE_VALS */) { guint32 onoff; if (inputlen < 3 * sizeof (guint32)) { mono_w32socket_set_last_error (WSAEINVAL); mono_fdhandle_unref ((MonoFDHandle*) sockethandle); return SOCKET_ERROR; } onoff = *((guint32*) input); MONO_ENTER_GC_SAFE; ret = setsockopt (((MonoFDHandle*) sockethandle)->fd, SOL_SOCKET, SO_KEEPALIVE, &onoff, sizeof (guint32)); MONO_EXIT_GC_SAFE; if (ret < 0) { mono_w32socket_set_last_error (mono_w32socket_convert_error (errno)); mono_fdhandle_unref ((MonoFDHandle*) sockethandle); return SOCKET_ERROR; } #if defined(TCP_KEEPIDLE) && defined(TCP_KEEPINTVL) if (onoff != 0) { /* Values are in ms, but we need s */ guint32 keepalivetime, keepaliveinterval, rem; keepalivetime = *(((guint32*) input) + 1); keepaliveinterval = *(((guint32*) input) + 2); /* keepalivetime and keepaliveinterval are > 0 (checked in managed code) */ rem = keepalivetime % 1000; keepalivetime /= 1000; if (keepalivetime == 0 || rem >= 500) keepalivetime++; MONO_ENTER_GC_SAFE; ret = setsockopt (((MonoFDHandle*) sockethandle)->fd, IPPROTO_TCP, TCP_KEEPIDLE, &keepalivetime, sizeof (guint32)); MONO_EXIT_GC_SAFE; if (ret == 0) { rem = keepaliveinterval % 1000; keepaliveinterval /= 1000; if (keepaliveinterval == 0 || rem >= 500) keepaliveinterval++; MONO_ENTER_GC_SAFE; ret = setsockopt (((MonoFDHandle*) sockethandle)->fd, IPPROTO_TCP, TCP_KEEPINTVL, &keepaliveinterval, sizeof (guint32)); MONO_EXIT_GC_SAFE; } if (ret != 0) { mono_w32socket_set_last_error (mono_w32socket_convert_error (errno)); mono_fdhandle_unref ((MonoFDHandle*) sockethandle); return SOCKET_ERROR; } mono_fdhandle_unref ((MonoFDHandle*) sockethandle); return 0; } #endif mono_fdhandle_unref ((MonoFDHandle*) sockethandle); return 0; } buffer = inputlen > 0 ? (gchar*) g_memdup (input, inputlen) : NULL; MONO_ENTER_GC_SAFE; ret = ioctl (((MonoFDHandle*) sockethandle)->fd, command, buffer); MONO_EXIT_GC_SAFE; if (ret == -1) { g_free (buffer); gint errnum = errno; mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: WSAIoctl error: %s", __func__, g_strerror (errno)); mono_w32socket_set_last_error (mono_w32socket_convert_error (errnum)); mono_fdhandle_unref ((MonoFDHandle*) sockethandle); return SOCKET_ERROR; } if (!buffer) { *written = 0; mono_fdhandle_unref ((MonoFDHandle*) sockethandle); return 0; } /* We just copy the buffer to the output. Some ioctls * don't even output any data, but, well... * * NB windows returns WSAEFAULT if outputlen is too small */ inputlen = (inputlen > outputlen) ? outputlen : inputlen; if (inputlen > 0 && output != NULL) memcpy (output, buffer, inputlen); g_free (buffer); *written = inputlen; mono_fdhandle_unref ((MonoFDHandle*) sockethandle); return 0; } gboolean mono_w32socket_close (SOCKET sock) { if (!mono_fdhandle_close (sock)) { mono_w32error_set_last (ERROR_INVALID_HANDLE); return FALSE; } return TRUE; } gint mono_w32socket_set_blocking (SOCKET sock, gboolean blocking) { #ifdef O_NONBLOCK SocketHandle *sockethandle; gint ret; if (!mono_fdhandle_lookup_and_ref(sock, (MonoFDHandle**) &sockethandle)) { mono_w32error_set_last (WSAENOTSOCK); return SOCKET_ERROR; } if (((MonoFDHandle*) sockethandle)->type != MONO_FDTYPE_SOCKET) { mono_fdhandle_unref ((MonoFDHandle*) sockethandle); mono_w32error_set_last (WSAENOTSOCK); return SOCKET_ERROR; } /* This works better than ioctl(...FIONBIO...) * on Linux (it causes connect to return * EINPROGRESS, but the ioctl doesn't seem to) */ MONO_ENTER_GC_SAFE; ret = fcntl (((MonoFDHandle*) sockethandle)->fd, F_GETFL, 0); MONO_EXIT_GC_SAFE; if (ret == -1) { gint errnum = mono_w32socket_convert_error (errno); mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: fcntl(F_GETFL) error: %s", __func__, g_strerror (errno)); mono_w32socket_set_last_error (errnum); mono_fdhandle_unref ((MonoFDHandle*) sockethandle); return SOCKET_ERROR; } MONO_ENTER_GC_SAFE; ret = fcntl (((MonoFDHandle*) sockethandle)->fd, F_SETFL, blocking ? (ret & (~O_NONBLOCK)) : (ret | (O_NONBLOCK))); MONO_EXIT_GC_SAFE; if (ret == -1) { gint errnum = mono_w32socket_convert_error (errno); mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: fcntl(F_SETFL) error: %s", __func__, g_strerror (errno)); mono_w32socket_set_last_error (errnum); mono_fdhandle_unref ((MonoFDHandle*) sockethandle); return SOCKET_ERROR; } mono_fdhandle_unref ((MonoFDHandle*) sockethandle); return 0; #else mono_w32socket_set_last_error (ERROR_NOT_SUPPORTED); return SOCKET_ERROR; #endif /* O_NONBLOCK */ } gint mono_w32socket_get_available (SOCKET sock, guint64 *amount) { SocketHandle *sockethandle; gint ret; if (!mono_fdhandle_lookup_and_ref(sock, (MonoFDHandle**) &sockethandle)) { mono_w32error_set_last (WSAENOTSOCK); return SOCKET_ERROR; } if (((MonoFDHandle*) sockethandle)->type != MONO_FDTYPE_SOCKET) { mono_fdhandle_unref ((MonoFDHandle*) sockethandle); mono_w32error_set_last (WSAENOTSOCK); return SOCKET_ERROR; } #if defined (HOST_DARWIN) // ioctl (socket, 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); MONO_ENTER_GC_SAFE; ret = getsockopt (((MonoFDHandle*) sockethandle)->fd, SOL_SOCKET, SO_NREAD, (gulong*) amount, &optlen); MONO_EXIT_GC_SAFE; if (ret == -1) { gint errnum = mono_w32socket_convert_error (errno); mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: getsockopt error: %s", __func__, g_strerror (errno)); mono_w32socket_set_last_error (errnum); mono_fdhandle_unref ((MonoFDHandle*) sockethandle); return SOCKET_ERROR; } #else MONO_ENTER_GC_SAFE; ret = ioctl (((MonoFDHandle*) sockethandle)->fd, FIONREAD, (gulong*) amount); MONO_EXIT_GC_SAFE; if (ret == -1) { gint errnum = mono_w32socket_convert_error (errno); mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: ioctl error: %s", __func__, g_strerror (errno)); mono_w32socket_set_last_error (errnum); mono_fdhandle_unref ((MonoFDHandle*) sockethandle); return SOCKET_ERROR; } #endif mono_fdhandle_unref ((MonoFDHandle*) sockethandle); return 0; } void mono_w32socket_set_last_error (gint32 error) { mono_w32error_set_last (error); } gint32 mono_w32socket_get_last_error (void) { return mono_w32error_get_last (); } gint32 mono_w32socket_convert_error (gint error) { switch (error) { case 0: return ERROR_SUCCESS; case EACCES: return WSAEACCES; #ifdef EADDRINUSE case EADDRINUSE: return WSAEADDRINUSE; #endif #ifdef EAFNOSUPPORT case EAFNOSUPPORT: return WSAEAFNOSUPPORT; #endif #if EAGAIN != EWOULDBLOCK case EAGAIN: return WSAEWOULDBLOCK; #endif #ifdef EALREADY case EALREADY: return WSAEALREADY; #endif case EBADF: return WSAENOTSOCK; #ifdef ECONNABORTED case ECONNABORTED: return WSAENETDOWN; #endif #ifdef ECONNREFUSED case ECONNREFUSED: return WSAECONNREFUSED; #endif #ifdef ECONNRESET case ECONNRESET: return WSAECONNRESET; #endif case EFAULT: return WSAEFAULT; #ifdef EHOSTUNREACH case EHOSTUNREACH: return WSAEHOSTUNREACH; #endif #ifdef EINPROGRESS case EINPROGRESS: return WSAEINPROGRESS; #endif case EINTR: return WSAEINTR; case EINVAL: return WSAEINVAL; /*FIXME: case EIO: return WSAE????; */ #ifdef EISCONN case EISCONN: return WSAEISCONN; #endif /* FIXME: case ELOOP: return WSA????; */ case EMFILE: return WSAEMFILE; #ifdef EMSGSIZE case EMSGSIZE: return WSAEMSGSIZE; #endif /* FIXME: case ENAMETOOLONG: return WSAEACCES; */ #ifdef ENETUNREACH case ENETUNREACH: return WSAENETUNREACH; #endif #ifdef ENOBUFS case ENOBUFS: return WSAENOBUFS; /* not documented */ #endif /* case ENOENT: return WSAE????; */ case ENOMEM: return WSAENOBUFS; #ifdef ENOPROTOOPT case ENOPROTOOPT: return WSAENOPROTOOPT; #endif #ifdef ENOSR case ENOSR: return WSAENETDOWN; #endif #ifdef ENOTCONN case ENOTCONN: return WSAENOTCONN; #endif /*FIXME: case ENOTDIR: return WSAE????; */ #ifdef ENOTSOCK case ENOTSOCK: return WSAENOTSOCK; #endif case ENOTTY: return WSAENOTSOCK; #ifdef EOPNOTSUPP case EOPNOTSUPP: return WSAEOPNOTSUPP; #endif case EPERM: return WSAEACCES; case EPIPE: return WSAESHUTDOWN; #ifdef EPROTONOSUPPORT case EPROTONOSUPPORT: return WSAEPROTONOSUPPORT; #endif #if ERESTARTSYS case ERESTARTSYS: return WSAENETDOWN; #endif /*FIXME: case EROFS: return WSAE????; */ #ifdef ESOCKTNOSUPPORT case ESOCKTNOSUPPORT: return WSAESOCKTNOSUPPORT; #endif #ifdef ETIMEDOUT case ETIMEDOUT: return WSAETIMEDOUT; #endif #ifdef EWOULDBLOCK case EWOULDBLOCK: return WSAEWOULDBLOCK; #endif #ifdef EADDRNOTAVAIL case EADDRNOTAVAIL: return WSAEADDRNOTAVAIL; #endif /* This might happen with unix sockets */ case ENOENT: return WSAECONNREFUSED; #ifdef EDESTADDRREQ case EDESTADDRREQ: return WSAEDESTADDRREQ; #endif #ifdef EHOSTDOWN case EHOSTDOWN: return WSAEHOSTDOWN; #endif #ifdef ENETDOWN case ENETDOWN: return WSAENETDOWN; #endif case ENODEV: return WSAENETDOWN; #ifdef EPROTOTYPE case EPROTOTYPE: return WSAEPROTOTYPE; #endif #ifdef ENXIO case ENXIO: return WSAENXIO; #endif default: g_error ("%s: no translation into winsock error for (%d) \"%s\"", __func__, error, g_strerror (error)); } } gboolean ves_icall_System_Net_Sockets_Socket_SupportPortReuse (MonoProtocolType proto, MonoError *error) { error_init (error); #if defined (SO_REUSEPORT) return TRUE; #else #ifdef __linux__ /* Linux always supports double binding for UDP, even on older kernels. */ if (proto == ProtocolType_Udp) return TRUE; #endif return FALSE; #endif } gboolean mono_w32socket_duplicate (gpointer handle, gint32 targetProcessId, gpointer *duplicate_handle) { SocketHandle *sockethandle; if (!mono_fdhandle_lookup_and_ref (GPOINTER_TO_INT(handle), (MonoFDHandle**) &sockethandle)) { mono_w32error_set_last (ERROR_INVALID_HANDLE); return FALSE; } if (((MonoFDHandle*) sockethandle)->type != MONO_FDTYPE_SOCKET) { mono_fdhandle_unref ((MonoFDHandle*) sockethandle); mono_w32error_set_last (ERROR_INVALID_HANDLE); return FALSE; } *duplicate_handle = handle; return TRUE; }