From: Ludovic Henry Date: Thu, 19 Jan 2017 14:25:55 +0000 (-0500) Subject: [io-layer] Extract socket (#4241) X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=commitdiff_plain;h=e343ad0cfd404c92761cd6e0f683ccd46402897b;hp=6220d1140479b2a313c59501ff958e967b5e7fb5;p=mono.git [io-layer] Extract socket (#4241) * [socket] Extract win32 and unix specific code from w32socket.c * [socket] Extract WSAGetLastError and WSASetLastError * [socket] Extract closesocket * [socket] Extract iocltsocket * [socket] Extract socket-wrappers.h functions * [socket] Extract w32handle operations * [socket] Extract WSAIoctl * [socket] Do not use mono_w32socket_ioctl for disconnect * [socket] Extract errno_to_WSA * [socket] Remove redundant mono_w32handle_get_type/mono_w32handle_lookup * [socket] Inline _wapi_* functions * [socket] Make disconnect not rely on mono_w32socket_ioctl * [socket] Remove unused parameters to mono_w32socket_transmit_file * [socket] Refactor calls to mono_w32socket_convert_error * [socket] Remove dead code --- diff --git a/mono/io-layer/Makefile.am b/mono/io-layer/Makefile.am index c0f5119c006..2247a51a71c 100644 --- a/mono/io-layer/Makefile.am +++ b/mono/io-layer/Makefile.am @@ -16,7 +16,6 @@ OTHER_H = \ io-trace.h \ io-layer.h \ io-portability.h \ - sockets.h \ uglify.h \ wapi.h \ wapi-remap.h @@ -32,10 +31,6 @@ OTHER_SRC = \ io-layer.h \ locking.c \ posix.c \ - sockets.c \ - sockets.h \ - socket-private.h \ - socket-wrappers.h \ uglify.h \ wapi_glob.h \ wapi_glob.c \ diff --git a/mono/io-layer/error.c b/mono/io-layer/error.c index bc884c3d8d9..5b1b22d6dcd 100644 --- a/mono/io-layer/error.c +++ b/mono/io-layer/error.c @@ -81,131 +81,6 @@ void SetLastError(guint32 code) g_assert (ret == 0); } -guint32 -errno_to_WSA (guint32 code, const gchar *function_name) -{ - gint result = -1; - char *sys_error; - gchar *msg; - - switch (code) { - case 0: result = ERROR_SUCCESS; break; - case EACCES: result = WSAEACCES; break; -#ifdef EADDRINUSE - case EADDRINUSE: result = WSAEADDRINUSE; break; -#endif -#ifdef EAFNOSUPPORT - case EAFNOSUPPORT: result = WSAEAFNOSUPPORT; break; -#endif -#if EAGAIN != EWOULDBLOCK - case EAGAIN: result = WSAEWOULDBLOCK; break; -#endif -#ifdef EALREADY - case EALREADY: result = WSAEALREADY; break; -#endif - case EBADF: result = WSAENOTSOCK; break; -#ifdef ECONNABORTED - case ECONNABORTED: result = WSAENETDOWN; break; -#endif -#ifdef ECONNREFUSED - case ECONNREFUSED: result = WSAECONNREFUSED; break; -#endif -#ifdef ECONNRESET - case ECONNRESET: result = WSAECONNRESET; break; -#endif - case EFAULT: result = WSAEFAULT; break; -#ifdef EHOSTUNREACH - case EHOSTUNREACH: result = WSAEHOSTUNREACH; break; -#endif -#ifdef EINPROGRESS - case EINPROGRESS: result = WSAEINPROGRESS; break; -#endif - case EINTR: result = WSAEINTR; break; - case EINVAL: result = WSAEINVAL; break; - /*FIXME: case EIO: result = WSAE????; break; */ -#ifdef EISCONN - case EISCONN: result = WSAEISCONN; break; -#endif - /* FIXME: case ELOOP: result = WSA????; break; */ - case EMFILE: result = WSAEMFILE; break; -#ifdef EMSGSIZE - case EMSGSIZE: result = WSAEMSGSIZE; break; -#endif - /* FIXME: case ENAMETOOLONG: result = WSAEACCES; break; */ -#ifdef ENETUNREACH - case ENETUNREACH: result = WSAENETUNREACH; break; -#endif -#ifdef ENOBUFS - case ENOBUFS: result = WSAENOBUFS; break; /* not documented */ -#endif - /* case ENOENT: result = WSAE????; break; */ - case ENOMEM: result = WSAENOBUFS; break; -#ifdef ENOPROTOOPT - case ENOPROTOOPT: result = WSAENOPROTOOPT; break; -#endif -#ifdef ENOSR - case ENOSR: result = WSAENETDOWN; break; -#endif -#ifdef ENOTCONN - case ENOTCONN: result = WSAENOTCONN; break; -#endif - /*FIXME: case ENOTDIR: result = WSAE????; break; */ -#ifdef ENOTSOCK - case ENOTSOCK: result = WSAENOTSOCK; break; -#endif - case ENOTTY: result = WSAENOTSOCK; break; -#ifdef EOPNOTSUPP - case EOPNOTSUPP: result = WSAEOPNOTSUPP; break; -#endif - case EPERM: result = WSAEACCES; break; - case EPIPE: result = WSAESHUTDOWN; break; -#ifdef EPROTONOSUPPORT - case EPROTONOSUPPORT: result = WSAEPROTONOSUPPORT; break; -#endif -#if ERESTARTSYS - case ERESTARTSYS: result = WSAENETDOWN; break; -#endif - /*FIXME: case EROFS: result = WSAE????; break; */ -#ifdef ESOCKTNOSUPPORT - case ESOCKTNOSUPPORT: result = WSAESOCKTNOSUPPORT; break; -#endif -#ifdef ETIMEDOUT - case ETIMEDOUT: result = WSAETIMEDOUT; break; -#endif -#ifdef EWOULDBLOCK - case EWOULDBLOCK: result = WSAEWOULDBLOCK; break; -#endif -#ifdef EADDRNOTAVAIL - case EADDRNOTAVAIL: result = WSAEADDRNOTAVAIL; break; -#endif - /* This might happen with unix sockets */ - case ENOENT: result = WSAECONNREFUSED; break; -#ifdef EDESTADDRREQ - case EDESTADDRREQ: result = WSAEDESTADDRREQ; break; -#endif -#ifdef EHOSTDOWN - case EHOSTDOWN: result = WSAEHOSTDOWN; break; -#endif -#ifdef ENETDOWN - case ENETDOWN: result = WSAENETDOWN; break; -#endif - case ENODEV: result = WSAENETDOWN; break; - default: - sys_error = strerror (code); - msg = g_locale_to_utf8 (sys_error, strlen (sys_error), NULL, NULL, NULL); - if (function_name == NULL) - function_name = __func__; - - g_warning ("%s: Need to translate %d [%s] into winsock error", - function_name, code, msg); - - g_free (msg); - result = WSASYSCALLFAILURE; - } - - return result; -} - gint _wapi_get_win32_file_error (gint err) { diff --git a/mono/io-layer/error.h b/mono/io-layer/error.h index 7271d71bb1c..d4ffe1e9235 100644 --- a/mono/io-layer/error.h +++ b/mono/io-layer/error.h @@ -81,7 +81,6 @@ G_BEGIN_DECLS guint32 GetLastError (void); void SetLastError (guint32 code); -guint32 errno_to_WSA (guint32 code, const char *function_name); gint _wapi_get_win32_file_error (gint err); void _wapi_error_cleanup (void); diff --git a/mono/io-layer/socket-private.h b/mono/io-layer/socket-private.h deleted file mode 100644 index 31bbfe33c89..00000000000 --- a/mono/io-layer/socket-private.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * socket-private.h: Private definitions for socket handles - * - * Author: - * Dick Porter (dick@ximian.com) - * - * (C) 2002 Ximian, Inc. - */ - -#ifndef _WAPI_SOCKET_PRIVATE_H_ -#define _WAPI_SOCKET_PRIVATE_H_ - -#include -#include - -#include "wapi-private.h" - -struct _WapiHandle_socket -{ - int domain; - int type; - int protocol; - int saved_error; - int still_readable; -}; - -void -_wapi_socket_init (void); - -#endif /* _WAPI_SOCKET_PRIVATE_H_ */ diff --git a/mono/io-layer/socket-wrappers.h b/mono/io-layer/socket-wrappers.h deleted file mode 100644 index a532ce9f3ba..00000000000 --- a/mono/io-layer/socket-wrappers.h +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Special header file to be included only in selected C files. - * We need to use the _wapi_ equivalents of the socket API when - * working with io-layer handles. On windows we define the wrappers to use - * the normal win32 functions. - */ - -#include -#ifdef HAVE_SYS_SELECT_H -# include -#endif -#ifdef HAVE_SYS_TIME_H -# include -#endif -#ifdef HAVE_SYS_SOCKET_H -# include -#endif - -#ifndef HAVE_SOCKLEN_T -#define socklen_t int -#endif - -#ifdef HOST_WIN32 -#define _wapi_accept accept -#define _wapi_bind bind -#define _wapi_connect connect -#define _wapi_getpeername getpeername -#define _wapi_getsockname getsockname -#define _wapi_getsockopt getsockopt -#define _wapi_listen listen -#define _wapi_recv recv -#define _wapi_recvfrom recvfrom -#define _wapi_send send -#define _wapi_sendto sendto -#define _wapi_setsockopt setsockopt -#define _wapi_shutdown shutdown -#define _wapi_socket WSASocket -#define _wapi_select select - -/* No need to wrap FD_ZERO because it doesnt involve file - * descriptors -*/ -#define _wapi_FD_CLR FD_CLR -#define _wapi_FD_ISSET FD_ISSET -#define _wapi_FD_SET FD_SET - -#define _wapi_cleanup_networking() ; -#else - -#define WSA_FLAG_OVERLAPPED 0x01 - -extern guint32 _wapi_accept(guint32 handle, struct sockaddr *addr, - socklen_t *addrlen); -extern int _wapi_bind(guint32 handle, struct sockaddr *my_addr, - socklen_t addrlen); -extern int _wapi_connect(guint32 handle, const struct sockaddr *serv_addr, - socklen_t addrlen); -extern int _wapi_getpeername(guint32 handle, struct sockaddr *name, - socklen_t *namelen); -extern int _wapi_getsockname(guint32 handle, struct sockaddr *name, - socklen_t *namelen); -extern int _wapi_getsockopt(guint32 handle, int level, int optname, - void *optval, socklen_t *optlen); -extern int _wapi_listen(guint32 handle, int backlog); -extern int _wapi_recv(guint32 handle, void *buf, size_t len, int recv_flags); -extern int _wapi_recvfrom(guint32 handle, void *buf, size_t len, - int recv_flags, struct sockaddr *from, - socklen_t *fromlen); -extern int _wapi_send(guint32 handle, const void *msg, size_t len, - int send_flags); -extern int _wapi_sendto(guint32 handle, const void *msg, size_t len, - int send_flags, const struct sockaddr *to, - socklen_t tolen); -extern int _wapi_setsockopt(guint32 handle, int level, int optname, - const void *optval, socklen_t optlen); -extern int _wapi_shutdown(guint32 handle, int how); -extern guint32 _wapi_socket(int domain, int type, int protocol, void *unused, - guint32 unused2, guint32 flags); - -#ifdef HAVE_SYS_SELECT_H -extern int _wapi_select(int nfds, fd_set *readfds, fd_set *writefds, - fd_set *exceptfds, struct timeval *timeout); - -extern void _wapi_FD_CLR(guint32 handle, fd_set *set); -extern int _wapi_FD_ISSET(guint32 handle, fd_set *set); -extern void _wapi_FD_SET(guint32 handle, fd_set *set); -#endif - -extern void _wapi_cleanup_networking (void); -#endif /* HOST_WIN32 */ - diff --git a/mono/io-layer/sockets.c b/mono/io-layer/sockets.c deleted file mode 100644 index d98ac677a2e..00000000000 --- a/mono/io-layer/sockets.c +++ /dev/null @@ -1,1457 +0,0 @@ -/* - * sockets.c: Socket handles - * - * Author: - * Dick Porter (dick@ximian.com) - * - * (C) 2002 Ximian, Inc. - */ - -#include - -#ifndef DISABLE_SOCKETS - -#include -#include -#include -#include -#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 -#include -#include - -#ifndef HAVE_MSG_NOSIGNAL -#include -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#ifdef HAVE_SYS_SENDFILE_H -#include -#endif -#ifdef HAVE_NETDB_H -#include -#endif - -static guint32 in_cleanup = 0; - -static void socket_close (gpointer handle, gpointer data); -static void socket_details (gpointer data); -static const gchar* socket_typename (void); -static gsize socket_typesize (void); - -static MonoW32HandleOps _wapi_socket_ops = { - socket_close, /* close */ - NULL, /* signal */ - NULL, /* own */ - NULL, /* is_owned */ - NULL, /* special_wait */ - NULL, /* prewait */ - socket_details, /* details */ - socket_typename, /* typename */ - socket_typesize, /* typesize */ -}; - -void -_wapi_socket_init (void) -{ - mono_w32handle_register_ops (MONO_W32HANDLE_SOCKET, &_wapi_socket_ops); -} - -static void socket_close (gpointer handle, gpointer data) -{ - int ret; - struct _WapiHandle_socket *socket_handle = (struct _WapiHandle_socket *)data; - MonoThreadInfo *info = mono_thread_info_current (); - - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: closing socket handle %p", __func__, handle); - - /* Shutdown the socket for reading, to interrupt any potential - * receives that may be blocking for data. See bug 75705. - */ - shutdown (GPOINTER_TO_UINT (handle), SHUT_RD); - - do { - ret = close (GPOINTER_TO_UINT(handle)); - } 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: close error: %s", __func__, strerror (errno)); - errnum = errno_to_WSA (errnum, __func__); - if (!in_cleanup) - WSASetLastError (errnum); - } - - if (!in_cleanup) - socket_handle->saved_error = 0; -} - -static void socket_details (gpointer data) -{ - /* FIXME: do something */ -} - -static const gchar* socket_typename (void) -{ - return "Socket"; -} - -static gsize socket_typesize (void) -{ - return sizeof (struct _WapiHandle_socket); -} - -static gboolean -cleanup_close (gpointer handle, gpointer data, gpointer user_data) -{ - if (mono_w32handle_get_type (handle) == MONO_W32HANDLE_SOCKET) - mono_w32handle_force_close (handle, data); - - return FALSE; -} - -void _wapi_cleanup_networking(void) -{ - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: cleaning up", __func__); - - in_cleanup = 1; - mono_w32handle_foreach (cleanup_close, NULL); - in_cleanup = 0; -} - -void WSASetLastError(int error) -{ - SetLastError (error); -} - -int WSAGetLastError(void) -{ - return(GetLastError ()); -} - -int closesocket(guint32 fd) -{ - gpointer handle = GUINT_TO_POINTER (fd); - - if (mono_w32handle_get_type (handle) != MONO_W32HANDLE_SOCKET) { - WSASetLastError (WSAENOTSOCK); - return(0); - } - - mono_w32handle_unref (handle); - return(0); -} - -guint32 _wapi_accept(guint32 fd, struct sockaddr *addr, socklen_t *addrlen) -{ - gpointer handle = GUINT_TO_POINTER (fd); - gpointer new_handle; - struct _WapiHandle_socket *socket_handle; - struct _WapiHandle_socket new_socket_handle = {0}; - gboolean ok; - int new_fd; - MonoThreadInfo *info = mono_thread_info_current (); - - if (addr != NULL && *addrlen < sizeof(struct sockaddr)) { - WSASetLastError (WSAEFAULT); - return(INVALID_SOCKET); - } - - if (mono_w32handle_get_type (handle) != MONO_W32HANDLE_SOCKET) { - WSASetLastError (WSAENOTSOCK); - return(INVALID_SOCKET); - } - - ok = mono_w32handle_lookup (handle, MONO_W32HANDLE_SOCKET, - (gpointer *)&socket_handle); - if (ok == FALSE) { - g_warning ("%s: error looking up socket handle %p", - __func__, handle); - WSASetLastError (WSAENOTSOCK); - return(INVALID_SOCKET); - } - - do { - new_fd = accept (fd, addr, addrlen); - } while (new_fd == -1 && errno == EINTR && - !mono_thread_info_is_interrupt_state (info)); - - if (new_fd == -1) { - gint errnum = errno; - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: accept error: %s", __func__, strerror(errno)); - - errnum = errno_to_WSA (errnum, __func__); - WSASetLastError (errnum); - - return(INVALID_SOCKET); - } - - if (new_fd >= mono_w32handle_fd_reserve) { - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: File descriptor is too big", __func__); - - WSASetLastError (WSASYSCALLFAILURE); - - close (new_fd); - - return(INVALID_SOCKET); - } - - new_socket_handle.domain = socket_handle->domain; - new_socket_handle.type = socket_handle->type; - new_socket_handle.protocol = socket_handle->protocol; - new_socket_handle.still_readable = 1; - - new_handle = mono_w32handle_new_fd (MONO_W32HANDLE_SOCKET, new_fd, - &new_socket_handle); - if(new_handle == INVALID_HANDLE_VALUE) { - g_warning ("%s: error creating socket handle", __func__); - WSASetLastError (ERROR_GEN_FAILURE); - return(INVALID_SOCKET); - } - - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: returning newly accepted socket handle %p with", - __func__, new_handle); - - return(new_fd); -} - -int _wapi_bind(guint32 fd, struct sockaddr *my_addr, socklen_t addrlen) -{ - gpointer handle = GUINT_TO_POINTER (fd); - int ret; - - if (mono_w32handle_get_type (handle) != MONO_W32HANDLE_SOCKET) { - WSASetLastError (WSAENOTSOCK); - return(SOCKET_ERROR); - } - - ret = bind (fd, my_addr, addrlen); - if (ret == -1) { - gint errnum = errno; - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: bind error: %s", __func__, strerror(errno)); - errnum = errno_to_WSA (errnum, __func__); - WSASetLastError (errnum); - - return(SOCKET_ERROR); - } - return(ret); -} - -int _wapi_connect(guint32 fd, const struct sockaddr *serv_addr, - socklen_t addrlen) -{ - gpointer handle = GUINT_TO_POINTER (fd); - struct _WapiHandle_socket *socket_handle; - gboolean ok; - gint errnum; - MonoThreadInfo *info = mono_thread_info_current (); - - if (mono_w32handle_get_type (handle) != MONO_W32HANDLE_SOCKET) { - WSASetLastError (WSAENOTSOCK); - return(SOCKET_ERROR); - } - - if (connect (fd, serv_addr, addrlen) == -1) { - mono_pollfd fds; - int 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__, - strerror (errnum)); - - errnum = errno_to_WSA (errnum, __func__); - if (errnum == WSAEINPROGRESS) - errnum = WSAEWOULDBLOCK; /* see bug #73053 */ - - WSASetLastError (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) { - ok = mono_w32handle_lookup (handle, - MONO_W32HANDLE_SOCKET, - (gpointer *)&socket_handle); - if (ok == FALSE) { - /* 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; - } - } - return(SOCKET_ERROR); - } - - fds.fd = fd; - fds.events = MONO_POLLOUT; - while (mono_poll (&fds, 1, -1) == -1 && - !mono_thread_info_is_interrupt_state (info)) { - if (errno != EINTR) { - errnum = errno_to_WSA (errno, __func__); - - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: connect poll error: %s", - __func__, strerror (errno)); - - WSASetLastError (errnum); - return(SOCKET_ERROR); - } - } - - len = sizeof(so_error); - if (getsockopt (fd, SOL_SOCKET, SO_ERROR, &so_error, - &len) == -1) { - errnum = errno_to_WSA (errno, __func__); - - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: connect getsockopt error: %s", - __func__, strerror (errno)); - - WSASetLastError (errnum); - return(SOCKET_ERROR); - } - - if (so_error != 0) { - errnum = errno_to_WSA (so_error, __func__); - - /* Need to save this socket error */ - ok = mono_w32handle_lookup (handle, MONO_W32HANDLE_SOCKET, - (gpointer *)&socket_handle); - if (ok == FALSE) { - g_warning ("%s: error looking up socket handle %p", __func__, handle); - } else { - socket_handle->saved_error = errnum; - } - - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: connect getsockopt returned error: %s", - __func__, strerror (so_error)); - - WSASetLastError (errnum); - return(SOCKET_ERROR); - } - } - - return(0); -} - -int _wapi_getpeername(guint32 fd, struct sockaddr *name, socklen_t *namelen) -{ - gpointer handle = GUINT_TO_POINTER (fd); - int ret; - - if (mono_w32handle_get_type (handle) != MONO_W32HANDLE_SOCKET) { - WSASetLastError (WSAENOTSOCK); - return(SOCKET_ERROR); - } - - ret = getpeername (fd, name, namelen); - if (ret == -1) { - gint errnum = errno; - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: getpeername error: %s", __func__, - strerror (errno)); - - errnum = errno_to_WSA (errnum, __func__); - WSASetLastError (errnum); - - return(SOCKET_ERROR); - } - - return(ret); -} - -int _wapi_getsockname(guint32 fd, struct sockaddr *name, socklen_t *namelen) -{ - gpointer handle = GUINT_TO_POINTER (fd); - int ret; - - if (mono_w32handle_get_type (handle) != MONO_W32HANDLE_SOCKET) { - WSASetLastError (WSAENOTSOCK); - return(SOCKET_ERROR); - } - - ret = getsockname (fd, name, namelen); - if (ret == -1) { - gint errnum = errno; - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: getsockname error: %s", __func__, - strerror (errno)); - - errnum = errno_to_WSA (errnum, __func__); - WSASetLastError (errnum); - - return(SOCKET_ERROR); - } - - return(ret); -} - -int _wapi_getsockopt(guint32 fd, int level, int optname, void *optval, - socklen_t *optlen) -{ - gpointer handle = GUINT_TO_POINTER (fd); - int ret; - struct timeval tv; - void *tmp_val; - struct _WapiHandle_socket *socket_handle; - gboolean ok; - - if (mono_w32handle_get_type (handle) != MONO_W32HANDLE_SOCKET) { - WSASetLastError (WSAENOTSOCK); - return(SOCKET_ERROR); - } - - tmp_val = optval; - if (level == SOL_SOCKET && - (optname == SO_RCVTIMEO || optname == SO_SNDTIMEO)) { - tmp_val = &tv; - *optlen = sizeof (tv); - } - - ret = getsockopt (fd, level, optname, tmp_val, optlen); - if (ret == -1) { - gint errnum = errno; - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: getsockopt error: %s", __func__, - strerror (errno)); - - errnum = errno_to_WSA (errnum, __func__); - WSASetLastError (errnum); - - 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) { - ok = mono_w32handle_lookup (handle, MONO_W32HANDLE_SOCKET, - (gpointer *)&socket_handle); - if (ok == FALSE) { - g_warning ("%s: error looking up socket handle %p", - __func__, handle); - - /* can't extract the last error */ - *((int *) optval) = errno_to_WSA (*((int *)optval), - __func__); - } else { - if (*((int *)optval) != 0) { - *((int *) optval) = errno_to_WSA (*((int *)optval), - __func__); - socket_handle->saved_error = *((int *)optval); - } else { - *((int *)optval) = socket_handle->saved_error; - } - } - } - - return(ret); -} - -int _wapi_listen(guint32 fd, int backlog) -{ - gpointer handle = GUINT_TO_POINTER (fd); - int ret; - - if (mono_w32handle_get_type (handle) != MONO_W32HANDLE_SOCKET) { - WSASetLastError (WSAENOTSOCK); - return(SOCKET_ERROR); - } - - ret = listen (fd, backlog); - if (ret == -1) { - gint errnum = errno; - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: listen error: %s", __func__, strerror (errno)); - - errnum = errno_to_WSA (errnum, __func__); - WSASetLastError (errnum); - - return(SOCKET_ERROR); - } - - return(0); -} - -int _wapi_recv(guint32 fd, void *buf, size_t len, int recv_flags) -{ - return(_wapi_recvfrom (fd, buf, len, recv_flags, NULL, 0)); -} - -int _wapi_recvfrom(guint32 fd, void *buf, size_t len, int recv_flags, - struct sockaddr *from, socklen_t *fromlen) -{ - gpointer handle = GUINT_TO_POINTER (fd); - struct _WapiHandle_socket *socket_handle; - gboolean ok; - int ret; - MonoThreadInfo *info = mono_thread_info_current (); - - if (mono_w32handle_get_type (handle) != MONO_W32HANDLE_SOCKET) { - WSASetLastError (WSAENOTSOCK); - return(SOCKET_ERROR); - } - - do { - ret = recvfrom (fd, buf, len, recv_flags, from, fromlen); - } 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. - */ - ok = mono_w32handle_lookup (handle, MONO_W32HANDLE_SOCKET, - (gpointer *)&socket_handle); - if (ok == FALSE || socket_handle->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__, 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; - MonoThreadInfo *info = mono_thread_info_current (); - - if (mono_w32handle_get_type (handle) != MONO_W32HANDLE_SOCKET) { - WSASetLastError (WSAENOTSOCK); - return(SOCKET_ERROR); - } - - do { - ret = recvmsg (fd, msg, recv_flags); - } while (ret == -1 && errno == EINTR && - !mono_thread_info_is_interrupt_state (info)); - - if (ret == 0) { - /* see _wapi_recvfrom */ - ok = mono_w32handle_lookup (handle, MONO_W32HANDLE_SOCKET, - (gpointer *)&socket_handle); - if (ok == FALSE || socket_handle->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__, strerror(errno)); - - errnum = errno_to_WSA (errnum, __func__); - WSASetLastError (errnum); - - return(SOCKET_ERROR); - } - return(ret); -} - -int _wapi_send(guint32 fd, const void *msg, size_t len, int send_flags) -{ - gpointer handle = GUINT_TO_POINTER (fd); - int ret; - MonoThreadInfo *info = mono_thread_info_current (); - - if (mono_w32handle_get_type (handle) != MONO_W32HANDLE_SOCKET) { - WSASetLastError (WSAENOTSOCK); - return(SOCKET_ERROR); - } - - do { - ret = send (fd, msg, len, send_flags); - } 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__, 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); - - return(SOCKET_ERROR); - } - return(ret); -} - -int _wapi_sendto(guint32 fd, const void *msg, size_t len, int send_flags, - const struct sockaddr *to, socklen_t tolen) -{ - gpointer handle = GUINT_TO_POINTER (fd); - int ret; - MonoThreadInfo *info = mono_thread_info_current (); - - if (mono_w32handle_get_type (handle) != MONO_W32HANDLE_SOCKET) { - WSASetLastError (WSAENOTSOCK); - return(SOCKET_ERROR); - } - - do { - ret = sendto (fd, msg, len, send_flags, to, tolen); - } 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__, 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; - MonoThreadInfo *info = mono_thread_info_current (); - - if (mono_w32handle_get_type (handle) != MONO_W32HANDLE_SOCKET) { - WSASetLastError (WSAENOTSOCK); - return(SOCKET_ERROR); - } - - do { - ret = sendmsg (fd, msg, send_flags); - } 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: sendmsg error: %s", __func__, strerror (errno)); - - errnum = errno_to_WSA (errnum, __func__); - WSASetLastError (errnum); - - return(SOCKET_ERROR); - } - return(ret); -} - -int _wapi_setsockopt(guint32 fd, int level, int optname, - const void *optval, socklen_t optlen) -{ - gpointer handle = GUINT_TO_POINTER (fd); - int ret; - const void *tmp_val; -#if defined (__linux__) - /* This has its address taken so it cannot be moved to the if block which uses it */ - int bufsize = 0; -#endif - struct timeval tv; - - if (mono_w32handle_get_type (handle) != MONO_W32HANDLE_SOCKET) { - WSASetLastError (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 - - ret = setsockopt (fd, level, optname, tmp_val, optlen); - if (ret == -1) { - gint errnum = errno; - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: setsockopt error: %s", __func__, - strerror (errno)); - - errnum = errno_to_WSA (errnum, __func__); - WSASetLastError (errnum); - - 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); - - if (!getsockopt (fd, level, SO_TYPE, &type, &type_len)) { - if (type == SOCK_DGRAM || type == SOCK_STREAM) - setsockopt (fd, level, SO_REUSEPORT, tmp_val, optlen); - } - } -#endif - - return(ret); -} - -int _wapi_shutdown(guint32 fd, int how) -{ - struct _WapiHandle_socket *socket_handle; - gboolean ok; - gpointer handle = GUINT_TO_POINTER (fd); - int ret; - - if (mono_w32handle_get_type (handle) != MONO_W32HANDLE_SOCKET) { - WSASetLastError (WSAENOTSOCK); - return(SOCKET_ERROR); - } - - if (how == SHUT_RD || - how == SHUT_RDWR) { - ok = mono_w32handle_lookup (handle, MONO_W32HANDLE_SOCKET, - (gpointer *)&socket_handle); - if (ok == FALSE) { - g_warning ("%s: error looking up socket handle %p", - __func__, handle); - WSASetLastError (WSAENOTSOCK); - return(SOCKET_ERROR); - } - - socket_handle->still_readable = 0; - } - - ret = shutdown (fd, how); - if (ret == -1) { - gint errnum = errno; - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: shutdown error: %s", __func__, - strerror (errno)); - - errnum = errno_to_WSA (errnum, __func__); - WSASetLastError (errnum); - - return(SOCKET_ERROR); - } - - return(ret); -} - -guint32 _wapi_socket(int domain, int type, int protocol, void *unused, - guint32 unused2, guint32 unused3) -{ - struct _WapiHandle_socket socket_handle = {0}; - gpointer handle; - int fd; - - socket_handle.domain = domain; - socket_handle.type = type; - socket_handle.protocol = protocol; - socket_handle.still_readable = 1; - - fd = socket (domain, type, protocol); - 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; - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: socket error: %s", __func__, strerror (errno)); - errnum = errno_to_WSA (errnum, __func__); - WSASetLastError (errnum); - - return(INVALID_SOCKET); - } - - if (fd >= mono_w32handle_fd_reserve) { - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: File descriptor is too big (%d >= %d)", - __func__, fd, mono_w32handle_fd_reserve); - - WSASetLastError (WSASYSCALLFAILURE); - close (fd); - - return(INVALID_SOCKET); - } - - /* .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; - - ret = setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, &true_, - sizeof (true_)); - if (ret == -1) { - int errnum = errno; - - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Error setting SO_REUSEADDR", __func__); - - errnum = errno_to_WSA (errnum, __func__); - WSASetLastError (errnum); - - close (fd); - - return(INVALID_SOCKET); - } - } - - - handle = mono_w32handle_new_fd (MONO_W32HANDLE_SOCKET, fd, &socket_handle); - if (handle == INVALID_HANDLE_VALUE) { - g_warning ("%s: error creating socket handle", __func__); - WSASetLastError (WSASYSCALLFAILURE); - close (fd); - return(INVALID_SOCKET); - } - - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: returning socket handle %p", __func__, handle); - - return(fd); -} - -static gboolean socket_disconnect (guint32 fd) -{ - struct _WapiHandle_socket *socket_handle; - gboolean ok; - gpointer handle = GUINT_TO_POINTER (fd); - int newsock, ret; - - ok = mono_w32handle_lookup (handle, MONO_W32HANDLE_SOCKET, - (gpointer *)&socket_handle); - if (ok == FALSE) { - g_warning ("%s: error looking up socket handle %p", __func__, - handle); - WSASetLastError (WSAENOTSOCK); - return(FALSE); - } - - newsock = socket (socket_handle->domain, socket_handle->type, - socket_handle->protocol); - if (newsock == -1) { - gint errnum = errno; - - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: socket error: %s", __func__, strerror (errno)); - - errnum = errno_to_WSA (errnum, __func__); - WSASetLastError (errnum); - - return(FALSE); - } - - /* 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 { - ret = dup2 (newsock, fd); - } 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__, strerror (errno)); - - errnum = errno_to_WSA (errnum, __func__); - WSASetLastError (errnum); - - return(FALSE); - } - - close (newsock); - - return(TRUE); -} - -static gboolean wapi_disconnectex (guint32 fd, WapiOverlapped *overlapped, - guint32 flags, guint32 reserved) -{ - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: called on socket %d!", __func__, fd); - - if (reserved != 0) { - WSASetLastError (WSAEINVAL); - return(FALSE); - } - - /* 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 - */ - - return(socket_disconnect (fd)); -} - -#define SF_BUFFER_SIZE 16384 -static gint -wapi_sendfile (guint32 socket, gpointer fd, guint32 bytes_to_write, guint32 bytes_per_send, guint32 flags) -{ - MonoThreadInfo *info = mono_thread_info_current (); -#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 && !mono_thread_info_is_interrupt_state (info)); - 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 && !mono_thread_info_is_interrupt_state (info)); - 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 && !mono_thread_info_is_interrupt_state (info)); - } while (n != -1 && errno == EINTR && !mono_thread_info_is_interrupt_state (info)); - - 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; - - if (mono_w32handle_get_type (sock) != MONO_W32HANDLE_SOCKET) { - WSASetLastError (WSAENOTSOCK); - return FALSE; - } - - /* 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 -{ - WapiGuid guid; - gpointer func; -} extension_functions[] = { - {WSAID_DISCONNECTEX, wapi_disconnectex}, - {WSAID_TRANSMITFILE, TransmitFile}, - {{0}, NULL}, -}; - -int -WSAIoctl (guint32 fd, gint32 command, - gchar *input, gint i_len, - gchar *output, gint o_len, glong *written, - void *unused1, void *unused2) -{ - gpointer handle = GUINT_TO_POINTER (fd); - int ret; - gchar *buffer = NULL; - - if (mono_w32handle_get_type (handle) != MONO_W32HANDLE_SOCKET) { - WSASetLastError (WSAENOTSOCK); - return SOCKET_ERROR; - } - - if (command == SIO_GET_EXTENSION_FUNCTION_POINTER) { - int i = 0; - WapiGuid *guid = (WapiGuid *)input; - - if (i_len < sizeof(WapiGuid)) { - /* As far as I can tell, windows doesn't - * actually set an error here... - */ - WSASetLastError (WSAEINVAL); - return(SOCKET_ERROR); - } - - if (o_len < sizeof(gpointer)) { - /* Or here... */ - WSASetLastError (WSAEINVAL); - return(SOCKET_ERROR); - } - - if (output == NULL) { - /* Or here */ - WSASetLastError (WSAEINVAL); - return(SOCKET_ERROR); - } - - while(extension_functions[i].func != NULL) { - if (!memcmp (guid, &extension_functions[i].guid, - sizeof(WapiGuid))) { - memcpy (output, &extension_functions[i].func, - sizeof(gpointer)); - *written = sizeof(gpointer); - return(0); - } - - i++; - } - - WSASetLastError (WSAEINVAL); - 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 = (char *)g_memdup (input, i_len); - } - - ret = ioctl (fd, command, buffer); - if (ret == -1) { - gint errnum = errno; - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: WSAIoctl error: %s", __func__, - strerror (errno)); - - errnum = errno_to_WSA (errnum, __func__); - WSASetLastError (errnum); - g_free (buffer); - - return(SOCKET_ERROR); - } - - if (buffer == NULL) { - *written = 0; - } else { - /* We just copy the buffer to the output. Some ioctls - * don't even output any data, but, well... - * - * NB windows returns WSAEFAULT if o_len is too small - */ - i_len = (i_len > o_len) ? o_len : i_len; - - if (i_len > 0 && output != NULL) { - memcpy (output, buffer, i_len); - } - - g_free (buffer); - *written = i_len; - } - - return(0); -} - -#ifndef PLATFORM_PORT_PROVIDES_IOCTLSOCKET -int ioctlsocket(guint32 fd, unsigned long command, gpointer arg) -{ - gpointer handle = GUINT_TO_POINTER (fd); - int ret; - - if (mono_w32handle_get_type (handle) != MONO_W32HANDLE_SOCKET) { - WSASetLastError (WSAENOTSOCK); - return(SOCKET_ERROR); - } - - switch(command){ - case FIONBIO: -#ifdef O_NONBLOCK - /* This works better than ioctl(...FIONBIO...) - * on Linux (it causes connect to return - * EINPROGRESS, but the ioctl doesn't seem to) - */ - ret = fcntl(fd, F_GETFL, 0); - if (ret != -1) { - if (*(gboolean *)arg) { - ret |= O_NONBLOCK; - } else { - ret &= ~O_NONBLOCK; - } - ret = fcntl(fd, F_SETFL, ret); - } - break; -#endif /* O_NONBLOCK */ - /* 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); - } - - if (ret == -1) { - gint errnum = errno; - MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: ioctl error: %s", __func__, strerror (errno)); - - errnum = errno_to_WSA (errnum, __func__); - WSASetLastError (errnum); - - return(SOCKET_ERROR); - } - - return(0); -} - -int _wapi_select(int nfds G_GNUC_UNUSED, fd_set *readfds, fd_set *writefds, - fd_set *exceptfds, struct timeval *timeout) -{ - int ret, maxfd; - MonoThreadInfo *info = mono_thread_info_current (); - - for (maxfd = FD_SETSIZE-1; maxfd >= 0; maxfd--) { - if ((readfds && FD_ISSET (maxfd, readfds)) || - (writefds && FD_ISSET (maxfd, writefds)) || - (exceptfds && FD_ISSET (maxfd, exceptfds))) { - break; - } - } - - if (maxfd == -1) { - WSASetLastError (WSAEINVAL); - return(SOCKET_ERROR); - } - - do { - ret = select(maxfd + 1, readfds, writefds, exceptfds, - timeout); - } 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: select error: %s", __func__, strerror (errno)); - errnum = errno_to_WSA (errnum, __func__); - WSASetLastError (errnum); - - return(SOCKET_ERROR); - } - - return(ret); -} - -void _wapi_FD_CLR(guint32 fd, fd_set *set) -{ - gpointer handle = GUINT_TO_POINTER (fd); - - if (fd >= FD_SETSIZE) { - WSASetLastError (WSAEINVAL); - return; - } - - if (mono_w32handle_get_type (handle) != MONO_W32HANDLE_SOCKET) { - WSASetLastError (WSAENOTSOCK); - return; - } - - FD_CLR (fd, set); -} - -int _wapi_FD_ISSET(guint32 fd, fd_set *set) -{ - gpointer handle = GUINT_TO_POINTER (fd); - - if (fd >= FD_SETSIZE) { - WSASetLastError (WSAEINVAL); - return(0); - } - - if (mono_w32handle_get_type (handle) != MONO_W32HANDLE_SOCKET) { - WSASetLastError (WSAENOTSOCK); - return(0); - } - - return(FD_ISSET (fd, set)); -} - -void _wapi_FD_SET(guint32 fd, fd_set *set) -{ - gpointer handle = GUINT_TO_POINTER (fd); - - if (fd >= FD_SETSIZE) { - WSASetLastError (WSAEINVAL); - return; - } - - if (mono_w32handle_get_type (handle) != MONO_W32HANDLE_SOCKET) { - WSASetLastError (WSAENOTSOCK); - return; - } - - 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; - struct msghdr hdr; - - g_assert (overlapped == NULL); - g_assert (complete == NULL); - - wsabuf_to_msghdr (buffers, count, &hdr); - ret = _wapi_recvmsg (fd, &hdr, *flags); - msghdr_iov_free (&hdr); - - if(ret == SOCKET_ERROR) { - return(ret); - } - - *received = ret; - *flags = hdr.msg_flags; - - return(0); -} - -int WSASend (guint32 fd, WapiWSABuf *buffers, guint32 count, guint32 *sent, - guint32 flags, WapiOverlapped *overlapped, - WapiOverlappedCB *complete) -{ - int ret; - struct msghdr hdr; - - g_assert (overlapped == NULL); - g_assert (complete == NULL); - - wsabuf_to_msghdr (buffers, count, &hdr); - ret = _wapi_sendmsg (fd, &hdr, flags); - msghdr_iov_free (&hdr); - - if(ret == SOCKET_ERROR) - return ret; - - *sent = ret; - return 0; -} - -#endif /* ifndef DISABLE_SOCKETS */ diff --git a/mono/io-layer/sockets.h b/mono/io-layer/sockets.h deleted file mode 100644 index d634ae956ec..00000000000 --- a/mono/io-layer/sockets.h +++ /dev/null @@ -1,100 +0,0 @@ -/* - * sockets.h: Socket handles - * - * Author: - * Dick Porter (dick@ximian.com) - * - * (C) 2002 Ximian, Inc. - */ - -#ifndef _WAPI_SOCKETS_H_ -#define _WAPI_SOCKETS_H_ - -#include "mono/io-layer/wapi.h" - -G_BEGIN_DECLS - -#define WSADESCRIPTION_LEN 256 -#define WSASYS_STATUS_LEN 128 - -typedef struct -{ - guint16 wVersion; - guint16 wHighVersion; - char szDescription[WSADESCRIPTION_LEN+1]; - char szSystemStatus[WSASYS_STATUS_LEN+1]; - guint16 iMaxSockets; - guint16 iMaxUdpDg; - guchar *lpVendorInfo; -} WapiWSAData; - -#define INVALID_SOCKET (guint32)(~0) -#define SOCKET_ERROR -1 - -#define WSAID_DISCONNECTEX {0x7fda2e11,0x8630,0x436f,{0xa0, 0x31, 0xf5, 0x36, 0xa6, 0xee, 0xc1, 0x57}} -#define WSAID_TRANSMITFILE {0xb5367df0,0xcbac,0x11cf,{0x95,0xca,0x00,0x80,0x5f,0x48,0xa1,0x92}} - -typedef struct -{ - guint32 Data1; - guint16 Data2; - guint16 Data3; - guint8 Data4[8]; -} WapiGuid; - -typedef struct -{ - gpointer Head; - guint32 HeadLength; - gpointer Tail; - guint32 TailLength; -} WapiTransmitFileBuffers; - -typedef enum { - TF_USE_DEFAULT_WORKER = 0, - TF_DISCONNECT = 0x01, - TF_REUSE_SOCKET = 0x02, - TF_WRITE_BEHIND = 0x04, - TF_USE_SYSTEM_THREAD = 0x10, - TF_USE_KERNEL_APC = 0x20 -} WapiTransmitFileFlags; - -typedef struct -{ - guint32 len; - gpointer buf; -} WapiWSABuf; - -/* If we need to support more WSAIoctl commands then define these - * using the bitfield flags method - */ -#define SIO_GET_EXTENSION_FUNCTION_POINTER 0xC8000006 -#define SIO_KEEPALIVE_VALS 0x98000004 - -typedef gboolean (*WapiDisconnectExFn)(guint32, WapiOverlapped *, guint32, - guint32); -typedef gboolean (*WapiTransmitFileFn)(guint32, gpointer, guint32, guint32, - WapiOverlapped *, - WapiTransmitFileBuffers *, - WapiTransmitFileFlags); - -extern void WSASetLastError(int error); -extern int WSAGetLastError(void); -extern int closesocket(guint32 handle); - -extern int ioctlsocket(guint32 handle, unsigned long command, gpointer arg); -extern int WSAIoctl (guint32 handle, gint32 command, - gchar *input, gint i_len, - gchar *output, gint o_len, glong *written, - void *unused1, void *unused2); -extern int WSARecv (guint32 handle, WapiWSABuf *buffers, guint32 count, - guint32 *received, guint32 *flags, - WapiOverlapped *overlapped, WapiOverlappedCB *complete); -extern int WSASend (guint32 handle, WapiWSABuf *buffers, guint32 count, - guint32 *sent, guint32 flags, - WapiOverlapped *overlapped, WapiOverlappedCB *complete); - -gboolean TransmitFile (guint32 socket, gpointer file, guint32 bytes_to_write, guint32 bytes_per_send, WapiOverlapped *ol, - WapiTransmitFileBuffers *tb, guint32 flags); -G_END_DECLS -#endif /* _WAPI_SOCKETS_H_ */ diff --git a/mono/io-layer/uglify.h b/mono/io-layer/uglify.h index fdadebc1207..0335d0a7200 100644 --- a/mono/io-layer/uglify.h +++ b/mono/io-layer/uglify.h @@ -40,7 +40,6 @@ typedef gconstpointer LPCVOID; typedef gpointer HANDLE; typedef gpointer *LPHANDLE; -typedef guint32 SOCKET; typedef gpointer HMODULE; typedef gpointer HINSTANCE; typedef gpointer HWND; @@ -54,21 +53,10 @@ typedef WapiFileTime FILETIME; typedef WapiFileTime *LPFILETIME; typedef WapiSystemTime SYSTEMTIME; typedef WapiSystemTime *LPSYSTEMTIME; -typedef WapiWSAData WSADATA; -typedef WapiWSAData *LDWSADATA; -typedef WapiWSABuf WSABUF; -typedef WapiWSABuf *LPWSABUF; typedef WapiFindData WIN32_FIND_DATA; typedef WapiFindData *LPWIN32_FIND_DATA; typedef WapiFileAttributesData WIN32_FILE_ATTRIBUTE_DATA; typedef WapiGetFileExInfoLevels GET_FILEEX_INFO_LEVELS; -typedef WapiTransmitFileBuffers TRANSMIT_FILE_BUFFERS; -typedef WapiTransmitFileBuffers *PTRANSMIT_FILE_BUFFERS; -typedef WapiTransmitFileBuffers *LPTRANSMIT_FILE_BUFFERS; -typedef WapiDisconnectExFn LPFN_DISCONNECTEX; -typedef WapiTransmitFileFn LPFN_TRANSMITFILE; -typedef WapiGuid GUID; -typedef WapiGuid *LPGUID; #define CONST const #define VOID void diff --git a/mono/io-layer/wapi-private.h b/mono/io-layer/wapi-private.h index 9be764c1521..f39d4e4fab4 100644 --- a/mono/io-layer/wapi-private.h +++ b/mono/io-layer/wapi-private.h @@ -25,7 +25,6 @@ extern gboolean _wapi_has_shut_down; #include -#include #include struct _WapiHandle_shared_ref diff --git a/mono/io-layer/wapi-remap.h b/mono/io-layer/wapi-remap.h index e8426c4da83..7cc3b89ff38 100644 --- a/mono/io-layer/wapi-remap.h +++ b/mono/io-layer/wapi-remap.h @@ -14,7 +14,6 @@ #define GetLastError wapi_GetLastError #define SetLastError wapi_SetLastError -#define TransmitFile wapi_TransmitFile #define CloseHandle wapi_CloseHandle #define CreateFile wapi_CreateFile #define DeleteFile wapi_DeleteFile @@ -51,11 +50,6 @@ #define GetVolumeInformation wapi_GetVolumeInformation #define ImpersonateLoggedOnUser wapi_ImpersonateLoggedOnUser #define RevertToSelf wapi_RevertToSelf -#define WSASetLastError wapi_WSASetLastError -#define WSAGetLastError wapi_WSAGetLastError -#define WSAIoctl wapi_WSAIoctl -#define WSARecv wapi_WSARecv -#define WSASend wapi_WSASend #define GetSystemInfo wapi_GetSystemInfo #endif /* __WAPI_REMAP_H__ */ diff --git a/mono/io-layer/wapi.c b/mono/io-layer/wapi.c index fc9c00ccd90..d4da229603e 100644 --- a/mono/io-layer/wapi.c +++ b/mono/io-layer/wapi.c @@ -3,7 +3,6 @@ #include "io-trace.h" #include "io.h" -#include "socket-private.h" #include "mono/utils/mono-lazy-init.h" #include "mono/metadata/w32handle.h" @@ -14,7 +13,6 @@ void wapi_init (void) { _wapi_io_init (); - _wapi_socket_init (); } void diff --git a/mono/io-layer/wapi.h b/mono/io-layer/wapi.h index 65c98a4a584..fb0bf4fbce9 100644 --- a/mono/io-layer/wapi.h +++ b/mono/io-layer/wapi.h @@ -18,7 +18,6 @@ #include #include #include -#include G_BEGIN_DECLS diff --git a/mono/metadata/Makefile.am b/mono/metadata/Makefile.am index 92b99cd28bc..5deda477e2d 100644 --- a/mono/metadata/Makefile.am +++ b/mono/metadata/Makefile.am @@ -16,7 +16,7 @@ win32_sources = \ w32event-win32.c \ w32process-win32.c \ w32process-win32-internals.h \ - socket-io-windows.c + w32socket-win32.c platform_sources = $(win32_sources) @@ -48,7 +48,8 @@ unix_sources = \ w32process-unix-osx.c \ w32process-unix-bsd.c \ w32process-unix-haiku.c \ - w32process-unix-default.c + w32process-unix-default.c \ + w32socket-unix.c platform_sources = $(unix_sources) endif @@ -205,8 +206,9 @@ common_sources = \ opcodes.c \ property-bag.h \ property-bag.c \ - socket-io.c \ - socket-io.h \ + w32socket.c \ + w32socket.h \ + w32socket-internals.h \ w32process.c \ w32process.h \ w32process-internals.h \ diff --git a/mono/metadata/appdomain.c b/mono/metadata/appdomain.c index cd8df1cb063..74323ab37b3 100644 --- a/mono/metadata/appdomain.c +++ b/mono/metadata/appdomain.c @@ -43,7 +43,6 @@ #include #include #include -#include #include #include #include @@ -60,6 +59,7 @@ #include #include #include +#include #include #include #include diff --git a/mono/metadata/icall.c b/mono/metadata/icall.c index 3d772e9a092..322f77805ab 100644 --- a/mono/metadata/icall.c +++ b/mono/metadata/icall.c @@ -49,7 +49,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/mono/metadata/socket-io-windows-internals.h b/mono/metadata/socket-io-windows-internals.h deleted file mode 100644 index b76f9cbd43e..00000000000 --- a/mono/metadata/socket-io-windows-internals.h +++ /dev/null @@ -1,27 +0,0 @@ -/* -* socket-io-windows-internals.h: Windows specific socket code. -* -* Copyright 2016 Microsoft -* Licensed under the MIT license. See LICENSE file in the project root for full license information. -*/ -#ifndef __MONO_METADATA_SOCKET_IO_WINDOWS_INTERNALS_H__ -#define __MONO_METADATA_SOCKET_IO_WINDOWS_INTERNALS_H__ - -#include -#include -#include - -SOCKET alertable_accept (SOCKET s, struct sockaddr *addr, int *addrlen, gboolean blocking); -int alertable_connect (SOCKET s, const struct sockaddr *name, int namelen, gboolean blocking); -int alertable_recv (SOCKET s, char *buf, int len, int flags, gboolean blocking); -int alertable_recvfrom (SOCKET s, char *buf, int len, int flags, struct sockaddr *from, int *fromlen, gboolean blocking); -int alertable_WSARecv (SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount, LPDWORD lpNumberOfBytesRecvd, LPDWORD lpFlags, LPWSAOVERLAPPED lpOverlapped, LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine, gboolean blocking); -int alertable_send (SOCKET s, char *buf, int len, int flags, gboolean blocking); -int alertable_sendto (SOCKET s, const char *buf, int len, int flags, const struct sockaddr *to, int tolen, gboolean blocking); -int alertable_WSASend (SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount, LPDWORD lpNumberOfBytesRecvd, DWORD lpFlags, LPWSAOVERLAPPED lpOverlapped, LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine, gboolean blocking); - -#if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT | HAVE_UWP_WINAPI_SUPPORT) -BOOL alertable_TransmitFile (SOCKET hSocket, HANDLE hFile, DWORD nNumberOfBytesToWrite, DWORD nNumberOfBytesPerSend, LPOVERLAPPED lpOverlapped, LPTRANSMIT_FILE_BUFFERS lpTransmitBuffers, DWORD dwReserved, gboolean blocking); -#endif - -#endif // __MONO_METADATA_SOCKET_IO_WINDOWS_INTERNALS_H__ diff --git a/mono/metadata/socket-io-windows.c b/mono/metadata/socket-io-windows.c deleted file mode 100644 index 125dbeb9c4d..00000000000 --- a/mono/metadata/socket-io-windows.c +++ /dev/null @@ -1,209 +0,0 @@ -/* -* socket-io-windows.c: Windows 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 "mono/metadata/socket-io-windows-internals.h" - -#define LOGDEBUG(...) - -static gboolean set_blocking (SOCKET sock, gboolean block) -{ - u_long non_block = block ? 0 : 1; - return ioctlsocket (sock, FIONBIO, &non_block) != SOCKET_ERROR; -} - -static DWORD get_socket_timeout (SOCKET sock, int optname) -{ - DWORD timeout = 0; - int optlen = sizeof (DWORD); - if (getsockopt (sock, SOL_SOCKET, optname, (char *)&timeout, &optlen) == SOCKET_ERROR) { - WSASetLastError (0); - return WSA_INFINITE; - } - if (timeout == 0) - timeout = WSA_INFINITE; // 0 means infinite - return timeout; -} - -/* -* Performs an alertable wait for the specified event (FD_ACCEPT_BIT, -* FD_CONNECT_BIT, FD_READ_BIT, FD_WRITE_BIT) on the specified socket. -* Returns TRUE if the event is fired without errors. Calls WSASetLastError() -* with WSAEINTR and returns FALSE if the thread is alerted. If the event is -* fired but with an error WSASetLastError() is called to set the error and the -* function returns FALSE. -*/ -static gboolean alertable_socket_wait (SOCKET sock, int event_bit) -{ - static char *EVENT_NAMES[] = { "FD_READ", "FD_WRITE", NULL /*FD_OOB*/, "FD_ACCEPT", "FD_CONNECT", "FD_CLOSE" }; - gboolean success = FALSE; - int error = -1; - DWORD timeout = WSA_INFINITE; - if (event_bit == FD_READ_BIT || event_bit == FD_WRITE_BIT) { - timeout = get_socket_timeout (sock, event_bit == FD_READ_BIT ? SO_RCVTIMEO : SO_SNDTIMEO); - } - WSASetLastError (0); - WSAEVENT event = WSACreateEvent (); - if (event != WSA_INVALID_EVENT) { - if (WSAEventSelect (sock, event, (1 << event_bit) | FD_CLOSE) != SOCKET_ERROR) { - LOGDEBUG (g_message ("%06d - Calling WSAWaitForMultipleEvents () on socket %d", GetCurrentThreadId (), sock)); - DWORD ret = WSAWaitForMultipleEvents (1, &event, TRUE, timeout, TRUE); - if (ret == WSA_WAIT_IO_COMPLETION) { - LOGDEBUG (g_message ("%06d - WSAWaitForMultipleEvents () returned WSA_WAIT_IO_COMPLETION for socket %d", GetCurrentThreadId (), sock)); - error = WSAEINTR; - } else if (ret == WSA_WAIT_TIMEOUT) { - error = WSAETIMEDOUT; - } else { - g_assert (ret == WSA_WAIT_EVENT_0); - WSANETWORKEVENTS ne = { 0 }; - if (WSAEnumNetworkEvents (sock, event, &ne) != SOCKET_ERROR) { - if (ne.lNetworkEvents & (1 << event_bit) && ne.iErrorCode[event_bit]) { - LOGDEBUG (g_message ("%06d - %s error %d on socket %d", GetCurrentThreadId (), EVENT_NAMES[event_bit], ne.iErrorCode[event_bit], sock)); - error = ne.iErrorCode[event_bit]; - } else if (ne.lNetworkEvents & FD_CLOSE_BIT && ne.iErrorCode[FD_CLOSE_BIT]) { - LOGDEBUG (g_message ("%06d - FD_CLOSE error %d on socket %d", GetCurrentThreadId (), ne.iErrorCode[FD_CLOSE_BIT], sock)); - error = ne.iErrorCode[FD_CLOSE_BIT]; - } else { - LOGDEBUG (g_message ("%06d - WSAEnumNetworkEvents () finished successfully on socket %d", GetCurrentThreadId (), sock)); - success = TRUE; - error = 0; - } - } - } - WSAEventSelect (sock, NULL, 0); - } - WSACloseEvent (event); - } - if (error != -1) { - WSASetLastError (error); - } - return success; -} - -#define ALERTABLE_SOCKET_CALL(event_bit, blocking, repeat, ret, op, sock, ...) \ - LOGDEBUG (g_message ("%06d - Performing %s " #op " () on socket %d", GetCurrentThreadId (), blocking ? "blocking" : "non-blocking", sock)); \ - if (blocking) { \ - if (set_blocking(sock, FALSE)) { \ - while (-1 == (int) (ret = op (sock, __VA_ARGS__))) { \ - int _error = WSAGetLastError ();\ - if (_error != WSAEWOULDBLOCK && _error != WSA_IO_PENDING) \ - break; \ - if (!alertable_socket_wait (sock, event_bit) || !repeat) \ - break; \ - } \ - int _saved_error = WSAGetLastError (); \ - set_blocking (sock, TRUE); \ - WSASetLastError (_saved_error); \ - } \ - } else { \ - ret = op (sock, __VA_ARGS__); \ - } \ - int _saved_error = WSAGetLastError (); \ - LOGDEBUG (g_message ("%06d - Finished %s " #op " () on socket %d (ret = %d, WSAGetLastError() = %d)", GetCurrentThreadId (), \ - blocking ? "blocking" : "non-blocking", sock, ret, _saved_error)); \ - WSASetLastError (_saved_error); - -SOCKET alertable_accept (SOCKET s, struct sockaddr *addr, int *addrlen, gboolean blocking) -{ - SOCKET newsock = INVALID_SOCKET; - ALERTABLE_SOCKET_CALL (FD_ACCEPT_BIT, blocking, TRUE, newsock, accept, s, addr, addrlen); - return newsock; -} - -int alertable_connect (SOCKET s, const struct sockaddr *name, int namelen, gboolean blocking) -{ - int ret = SOCKET_ERROR; - ALERTABLE_SOCKET_CALL (FD_CONNECT_BIT, blocking, FALSE, ret, connect, s, name, namelen); - ret = WSAGetLastError () != 0 ? SOCKET_ERROR : 0; - return ret; -} - -int alertable_recv (SOCKET s, char *buf, int len, int flags, gboolean blocking) -{ - int ret = SOCKET_ERROR; - ALERTABLE_SOCKET_CALL (FD_READ_BIT, blocking, TRUE, ret, recv, s, buf, len, flags); - return ret; -} - -int alertable_recvfrom (SOCKET s, char *buf, int len, int flags, struct sockaddr *from, int *fromlen, gboolean blocking) -{ - int ret = SOCKET_ERROR; - ALERTABLE_SOCKET_CALL (FD_READ_BIT, blocking, TRUE, ret, recvfrom, s, buf, len, flags, from, fromlen); - return ret; -} - -int alertable_WSARecv (SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount, LPDWORD lpNumberOfBytesRecvd, LPDWORD lpFlags, LPWSAOVERLAPPED lpOverlapped, LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine, gboolean blocking) -{ - int ret = SOCKET_ERROR; - ALERTABLE_SOCKET_CALL (FD_READ_BIT, blocking, TRUE, ret, WSARecv, s, lpBuffers, dwBufferCount, lpNumberOfBytesRecvd, lpFlags, lpOverlapped, lpCompletionRoutine); - return ret; -} - -int alertable_send (SOCKET s, char *buf, int len, int flags, gboolean blocking) -{ - int ret = SOCKET_ERROR; - ALERTABLE_SOCKET_CALL (FD_WRITE_BIT, blocking, FALSE, ret, send, s, buf, len, flags); - return ret; -} - -int alertable_sendto (SOCKET s, const char *buf, int len, int flags, const struct sockaddr *to, int tolen, gboolean blocking) -{ - int ret = SOCKET_ERROR; - ALERTABLE_SOCKET_CALL (FD_WRITE_BIT, blocking, FALSE, ret, sendto, s, buf, len, flags, to, tolen); - return ret; -} - -int alertable_WSASend (SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount, LPDWORD lpNumberOfBytesRecvd, DWORD lpFlags, LPWSAOVERLAPPED lpOverlapped, LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine, gboolean blocking) -{ - int ret = SOCKET_ERROR; - ALERTABLE_SOCKET_CALL (FD_WRITE_BIT, blocking, FALSE, ret, WSASend, s, lpBuffers, dwBufferCount, lpNumberOfBytesRecvd, lpFlags, lpOverlapped, lpCompletionRoutine); - return ret; -} - -#if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT | HAVE_UWP_WINAPI_SUPPORT) -BOOL alertable_TransmitFile (SOCKET hSocket, HANDLE hFile, DWORD nNumberOfBytesToWrite, DWORD nNumberOfBytesPerSend, LPOVERLAPPED lpOverlapped, LPTRANSMIT_FILE_BUFFERS lpTransmitBuffers, DWORD dwReserved, gboolean blocking) -{ - LOGDEBUG (g_message ("%06d - Performing %s TransmitFile () on socket %d", GetCurrentThreadId (), blocking ? "blocking" : "non-blocking", hSocket)); - - int error = 0; - if (blocking) { - g_assert (lpOverlapped == NULL); - OVERLAPPED overlapped = { 0 }; - overlapped.hEvent = WSACreateEvent (); - if (overlapped.hEvent == WSA_INVALID_EVENT) - return FALSE; - if (!TransmitFile (hSocket, hFile, nNumberOfBytesToWrite, nNumberOfBytesPerSend, &overlapped, lpTransmitBuffers, dwReserved)) { - error = WSAGetLastError (); - if (error == WSA_IO_PENDING) { - error = 0; - // NOTE: .NET's Socket.SendFile() doesn't honor the Socket's SendTimeout so we shouldn't either - DWORD ret = WaitForSingleObjectEx (overlapped.hEvent, INFINITE, TRUE); - if (ret == WAIT_IO_COMPLETION) { - LOGDEBUG (g_message ("%06d - WaitForSingleObjectEx () returned WSA_WAIT_IO_COMPLETION for socket %d", GetCurrentThreadId (), hSocket)); - error = WSAEINTR; - } else if (ret == WAIT_TIMEOUT) { - error = WSAETIMEDOUT; - } else if (ret != WAIT_OBJECT_0) { - error = GetLastError (); - } - } - } - WSACloseEvent (overlapped.hEvent); - } else { - if (!TransmitFile (hSocket, hFile, nNumberOfBytesToWrite, nNumberOfBytesPerSend, lpOverlapped, lpTransmitBuffers, dwReserved)) { - error = WSAGetLastError (); - } - } - - LOGDEBUG (g_message ("%06d - Finished %s TransmitFile () on socket %d (ret = %d, WSAGetLastError() = %d)", GetCurrentThreadId (), \ - blocking ? "blocking" : "non-blocking", hSocket, error == 0, error)); - WSASetLastError (error); - - return error == 0; -} -#endif /* #if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT | HAVE_UWP_WINAPI_SUPPORT) */ diff --git a/mono/metadata/socket-io.c b/mono/metadata/socket-io.c deleted file mode 100644 index 606eff3692d..00000000000 --- a/mono/metadata/socket-io.c +++ /dev/null @@ -1,2827 +0,0 @@ -/* - * socket-io.c: Socket IO internal calls - * - * Authors: - * Dick Porter (dick@ximian.com) - * Gonzalo Paniagua Javier (gonzalo@ximian.com) - * - * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com) - * Copyright 2004-2009 Novell, Inc (http://www.novell.com) - * - * This file has been re-licensed under the MIT License: - * http://opensource.org/licenses/MIT - * Licensed under the MIT license. See LICENSE file in the project root for full license information. - */ - -#include - -#ifndef DISABLE_SOCKETS - -#if defined(__APPLE__) || defined(__FreeBSD__) -#define __APPLE_USE_RFC_3542 -#endif - -#include -#include -#include -#ifdef HOST_WIN32 -#include -#else -#include -#ifdef HAVE_SYS_IOCTL_H -#include -#endif -#include -#include -#ifdef HAVE_NETDB_H -#include -#endif -#include -#endif -#ifdef HAVE_UNISTD_H -#include -#endif -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -/* FIXME change this code to not mess so much with the internals */ -#include -#include -#include -#include -#include -#include - -#include -#ifdef HAVE_SYS_TIME_H -#include -#endif -#ifdef HAVE_SYS_IOCTL_H -#include -#endif -#ifdef HAVE_NET_IF_H -#include -#endif - -#ifdef HAVE_NETDB_H -#include -#endif -#ifdef HAVE_SYS_FILIO_H -#include /* defines FIONBIO and FIONREAD */ -#endif -#ifdef HAVE_SYS_SOCKIO_H -#include /* defines SIOCATMARK */ -#endif -#ifdef HAVE_SYS_UN_H -#include -#endif - -#ifdef HAVE_GETIFADDRS -// must be included before -#include -#endif - -#if defined(_MSC_VER) && G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT | HAVE_UWP_WINAPI_SUPPORT) -#include -#endif - -#include "mono/io-layer/socket-wrappers.h" -#ifdef HOST_WIN32 -#include "mono/metadata/socket-io-windows-internals.h" -#endif - -#define LOGDEBUG(...) -/* define LOGDEBUG(...) g_message(__VA_ARGS__) */ - -static void -abort_syscall (gpointer data) -{ - mono_thread_info_abort_socket_syscall_for_close ((MonoNativeThreadId) (gsize) data); -} - -static gint32 -convert_family (MonoAddressFamily mono_family) -{ - switch (mono_family) { - case AddressFamily_Unknown: - case AddressFamily_ImpLink: - case AddressFamily_Pup: - case AddressFamily_Chaos: - case AddressFamily_Iso: - case AddressFamily_Ecma: - case AddressFamily_DataKit: - case AddressFamily_Ccitt: - case AddressFamily_DataLink: - case AddressFamily_Lat: - case AddressFamily_HyperChannel: - case AddressFamily_NetBios: - case AddressFamily_VoiceView: - case AddressFamily_FireFox: - case AddressFamily_Banyan: - case AddressFamily_Atm: - case AddressFamily_Cluster: - case AddressFamily_Ieee12844: - case AddressFamily_NetworkDesigners: - g_warning ("System.Net.Sockets.AddressFamily has unsupported value 0x%x", mono_family); - return -1; - case AddressFamily_Unspecified: - return AF_UNSPEC; - case AddressFamily_Unix: - return AF_UNIX; - case AddressFamily_InterNetwork: - return AF_INET; - case AddressFamily_AppleTalk: - return AF_APPLETALK; - case AddressFamily_InterNetworkV6: - return AF_INET6; - case AddressFamily_DecNet: -#ifdef AF_DECnet - return AF_DECnet; -#else - return -1; -#endif - case AddressFamily_Ipx: -#ifdef AF_IPX - return AF_IPX; -#else - return -1; -#endif - case AddressFamily_Sna: -#ifdef AF_SNA - return AF_SNA; -#else - return -1; -#endif - case AddressFamily_Irda: -#ifdef AF_IRDA - return AF_IRDA; -#else - return -1; -#endif - default: - g_warning ("System.Net.Sockets.AddressFamily has unknown value 0x%x", mono_family); - return -1; - } -} - -static MonoAddressFamily -convert_to_mono_family (guint16 af_family) -{ - switch (af_family) { - case AF_UNSPEC: - return AddressFamily_Unspecified; - case AF_UNIX: - return AddressFamily_Unix; - case AF_INET: - return AddressFamily_InterNetwork; -#ifdef AF_IPX - case AF_IPX: - return AddressFamily_Ipx; -#endif -#ifdef AF_SNA - case AF_SNA: - return AddressFamily_Sna; -#endif -#ifdef AF_DECnet - case AF_DECnet: - return AddressFamily_DecNet; -#endif - case AF_APPLETALK: - return AddressFamily_AppleTalk; - case AF_INET6: - return AddressFamily_InterNetworkV6; -#ifdef AF_IRDA - case AF_IRDA: - return AddressFamily_Irda; -#endif - default: - g_warning ("unknown address family 0x%x", af_family); - return AddressFamily_Unknown; - } -} - -static gint32 -convert_type (MonoSocketType mono_type) -{ - switch (mono_type) { - case SocketType_Stream: - return SOCK_STREAM; - case SocketType_Dgram: - return SOCK_DGRAM; - case SocketType_Raw: - return SOCK_RAW; - case SocketType_Rdm: -#ifdef SOCK_RDM - return SOCK_RDM; -#else - return -1; -#endif - case SocketType_Seqpacket: - return SOCK_SEQPACKET; - case SocketType_Unknown: - g_warning ("System.Net.Sockets.SocketType has unsupported value 0x%x", mono_type); - return -1; - default: - g_warning ("System.Net.Sockets.SocketType has unknown value 0x%x", mono_type); - return -1; - } -} - -static gint32 -convert_proto (MonoProtocolType mono_proto) -{ - switch (mono_proto) { - case ProtocolType_IP: - case ProtocolType_IPv6: - case ProtocolType_Icmp: - case ProtocolType_Igmp: - case ProtocolType_Ggp: - case ProtocolType_Tcp: - case ProtocolType_Pup: - case ProtocolType_Udp: - case ProtocolType_Idp: - /* These protocols are known (on my system at least) */ - return mono_proto; - case ProtocolType_ND: - case ProtocolType_Raw: - case ProtocolType_Ipx: - case ProtocolType_Spx: - case ProtocolType_SpxII: - case ProtocolType_Unknown: - /* These protocols arent */ - g_warning ("System.Net.Sockets.ProtocolType has unsupported value 0x%x", mono_proto); - return -1; - default: - return -1; - } -} - -/* Convert MonoSocketFlags */ -static gint32 -convert_socketflags (gint32 sflags) -{ - gint32 flags = 0; - - if (!sflags) - /* SocketFlags.None */ - return 0; - - if (sflags & ~(SocketFlags_OutOfBand | SocketFlags_MaxIOVectorLength | SocketFlags_Peek | - SocketFlags_DontRoute | SocketFlags_Partial)) - /* Contains invalid flag values */ - return -1; - - if (sflags & SocketFlags_OutOfBand) - flags |= MSG_OOB; - if (sflags & SocketFlags_Peek) - flags |= MSG_PEEK; - if (sflags & SocketFlags_DontRoute) - flags |= MSG_DONTROUTE; - - /* Ignore Partial - see bug 349688. Don't return -1, because - * according to the comment in that bug ms runtime doesn't for - * UDP sockets (this means we will silently ignore it for TCP - * too) - */ -#ifdef MSG_MORE - if (sflags & SocketFlags_Partial) - flags |= MSG_MORE; -#endif -#if 0 - /* Don't do anything for MaxIOVectorLength */ - if (sflags & SocketFlags_MaxIOVectorLength) - return -1; -#endif - return flags; -} - -/* - * Returns: - * 0 on success (mapped mono_level and mono_name to system_level and system_name - * -1 on error - * -2 on non-fatal error (ie, must ignore) - */ -static gint32 -convert_sockopt_level_and_name (MonoSocketOptionLevel mono_level, MonoSocketOptionName mono_name, int *system_level, int *system_name) -{ - switch (mono_level) { - case SocketOptionLevel_Socket: - *system_level = SOL_SOCKET; - - switch (mono_name) { - case SocketOptionName_DontLinger: - /* This is SO_LINGER, because the setsockopt - * internal call maps DontLinger to SO_LINGER - * with l_onoff=0 - */ - *system_name = SO_LINGER; - break; - case SocketOptionName_Debug: - *system_name = SO_DEBUG; - break; -#ifdef SO_ACCEPTCONN - case SocketOptionName_AcceptConnection: - *system_name = SO_ACCEPTCONN; - break; -#endif - case SocketOptionName_ReuseAddress: - *system_name = SO_REUSEADDR; - break; - case SocketOptionName_KeepAlive: - *system_name = SO_KEEPALIVE; - break; - case SocketOptionName_DontRoute: - *system_name = SO_DONTROUTE; - break; - case SocketOptionName_Broadcast: - *system_name = SO_BROADCAST; - break; - case SocketOptionName_Linger: - *system_name = SO_LINGER; - break; - case SocketOptionName_OutOfBandInline: - *system_name = SO_OOBINLINE; - break; - case SocketOptionName_SendBuffer: - *system_name = SO_SNDBUF; - break; - case SocketOptionName_ReceiveBuffer: - *system_name = SO_RCVBUF; - break; - case SocketOptionName_SendLowWater: - *system_name = SO_SNDLOWAT; - break; - case SocketOptionName_ReceiveLowWater: - *system_name = SO_RCVLOWAT; - break; - case SocketOptionName_SendTimeout: - *system_name = SO_SNDTIMEO; - break; - case SocketOptionName_ReceiveTimeout: - *system_name = SO_RCVTIMEO; - break; - case SocketOptionName_Error: - *system_name = SO_ERROR; - break; - case SocketOptionName_Type: - *system_name = SO_TYPE; - break; -#ifdef SO_PEERCRED - case SocketOptionName_PeerCred: - *system_name = SO_PEERCRED; - break; -#endif - case SocketOptionName_ExclusiveAddressUse: -#ifdef SO_EXCLUSIVEADDRUSE - *system_name = SO_EXCLUSIVEADDRUSE; - break; -#endif - case SocketOptionName_UseLoopback: -#ifdef SO_USELOOPBACK - *system_name = SO_USELOOPBACK; - break; -#endif - case SocketOptionName_MaxConnections: -#ifdef SO_MAXCONN - *system_name = SO_MAXCONN; - break; -#elif defined(SOMAXCONN) - *system_name = SOMAXCONN; - break; -#endif - default: - g_warning ("System.Net.Sockets.SocketOptionName 0x%x is not supported at Socket level", mono_name); - return -1; - } - break; - - case SocketOptionLevel_IP: - *system_level = mono_networking_get_ip_protocol (); - - switch (mono_name) { - case SocketOptionName_IPOptions: - *system_name = IP_OPTIONS; - break; -#ifdef IP_HDRINCL - case SocketOptionName_HeaderIncluded: - *system_name = IP_HDRINCL; - break; -#endif -#ifdef IP_TOS - case SocketOptionName_TypeOfService: - *system_name = IP_TOS; - break; -#endif -#ifdef IP_TTL - case SocketOptionName_IpTimeToLive: - *system_name = IP_TTL; - break; -#endif - case SocketOptionName_MulticastInterface: - *system_name = IP_MULTICAST_IF; - break; - case SocketOptionName_MulticastTimeToLive: - *system_name = IP_MULTICAST_TTL; - break; - case SocketOptionName_MulticastLoopback: - *system_name = IP_MULTICAST_LOOP; - break; - case SocketOptionName_AddMembership: - *system_name = IP_ADD_MEMBERSHIP; - break; - case SocketOptionName_DropMembership: - *system_name = IP_DROP_MEMBERSHIP; - break; -#ifdef HAVE_IP_PKTINFO - case SocketOptionName_PacketInformation: - *system_name = IP_PKTINFO; - break; -#endif /* HAVE_IP_PKTINFO */ - - case SocketOptionName_DontFragment: -#ifdef HAVE_IP_DONTFRAGMENT - *system_name = IP_DONTFRAGMENT; - break; -#elif defined HAVE_IP_MTU_DISCOVER - /* Not quite the same */ - *system_name = IP_MTU_DISCOVER; - break; -#else - /* If the flag is not available on this system, we can ignore this error */ - return -2; -#endif /* HAVE_IP_DONTFRAGMENT */ - case SocketOptionName_AddSourceMembership: - case SocketOptionName_DropSourceMembership: - case SocketOptionName_BlockSource: - case SocketOptionName_UnblockSource: - /* Can't figure out how to map these, so fall - * through - */ - default: - g_warning ("System.Net.Sockets.SocketOptionName 0x%x is not supported at IP level", mono_name); - return -1; - } - break; - - case SocketOptionLevel_IPv6: - *system_level = mono_networking_get_ipv6_protocol (); - - switch (mono_name) { - case SocketOptionName_IpTimeToLive: - case SocketOptionName_HopLimit: - *system_name = IPV6_UNICAST_HOPS; - break; - case SocketOptionName_MulticastInterface: - *system_name = IPV6_MULTICAST_IF; - break; - case SocketOptionName_MulticastTimeToLive: - *system_name = IPV6_MULTICAST_HOPS; - break; - case SocketOptionName_MulticastLoopback: - *system_name = IPV6_MULTICAST_LOOP; - break; - case SocketOptionName_AddMembership: - *system_name = IPV6_JOIN_GROUP; - break; - case SocketOptionName_DropMembership: - *system_name = IPV6_LEAVE_GROUP; - break; - case SocketOptionName_IPv6Only: -#ifdef IPV6_V6ONLY - *system_name = IPV6_V6ONLY; -#else - return -1; -#endif - break; - case SocketOptionName_PacketInformation: -#ifdef HAVE_IPV6_PKTINFO - *system_name = IPV6_PKTINFO; -#endif - break; - case SocketOptionName_HeaderIncluded: - case SocketOptionName_IPOptions: - case SocketOptionName_TypeOfService: - case SocketOptionName_DontFragment: - case SocketOptionName_AddSourceMembership: - case SocketOptionName_DropSourceMembership: - case SocketOptionName_BlockSource: - case SocketOptionName_UnblockSource: - /* Can't figure out how to map these, so fall - * through - */ - default: - g_warning ("System.Net.Sockets.SocketOptionName 0x%x is not supported at IPv6 level", mono_name); - return -1; - } - break; /* SocketOptionLevel_IPv6 */ - - case SocketOptionLevel_Tcp: - *system_level = mono_networking_get_tcp_protocol (); - - switch (mono_name) { - case SocketOptionName_NoDelay: - *system_name = TCP_NODELAY; - break; -#if 0 - /* The documentation is talking complete - * bollocks here: rfc-1222 is titled - * 'Advancing the NSFNET Routing Architecture' - * and doesn't mention either of the words - * "expedite" or "urgent". - */ - case SocketOptionName_BsdUrgent: - case SocketOptionName_Expedited: -#endif - default: - g_warning ("System.Net.Sockets.SocketOptionName 0x%x is not supported at TCP level", mono_name); - return -1; - } - break; - - case SocketOptionLevel_Udp: - g_warning ("System.Net.Sockets.SocketOptionLevel has unsupported value 0x%x", mono_level); - - switch(mono_name) { - case SocketOptionName_NoChecksum: - case SocketOptionName_ChecksumCoverage: - default: - g_warning ("System.Net.Sockets.SocketOptionName 0x%x is not supported at UDP level", mono_name); - return -1; - } - return -1; - break; - - default: - g_warning ("System.Net.Sockets.SocketOptionLevel has unknown value 0x%x", mono_level); - return -1; - } - - return 0; -} - -static MonoImage* -get_socket_assembly (void) -{ - MonoDomain *domain = mono_domain_get (); - - if (domain->socket_assembly == NULL) { - MonoImage *socket_assembly; - - socket_assembly = mono_image_loaded ("System"); - if (!socket_assembly) { - MonoAssembly *sa = mono_assembly_open ("System.dll", NULL); - - if (!sa) { - g_assert_not_reached (); - } else { - socket_assembly = mono_assembly_get_image (sa); - } - } - mono_atomic_store_release (&domain->socket_assembly, socket_assembly); - } - - return domain->socket_assembly; -} - -gpointer -ves_icall_System_Net_Sockets_Socket_Socket_internal (MonoObject *this_obj, gint32 family, gint32 type, gint32 proto, gint32 *werror) -{ - SOCKET sock; - gint32 sock_family; - gint32 sock_proto; - gint32 sock_type; - - *werror = 0; - - sock_family = convert_family ((MonoAddressFamily)family); - if (sock_family == -1) { - *werror = WSAEAFNOSUPPORT; - return NULL; - } - - sock_proto = convert_proto ((MonoProtocolType)proto); - if (sock_proto == -1) { - *werror = WSAEPROTONOSUPPORT; - return NULL; - } - - sock_type = convert_type ((MonoSocketType)type); - if (sock_type == -1) { - *werror = WSAESOCKTNOSUPPORT; - return NULL; - } - - sock = _wapi_socket (sock_family, sock_type, sock_proto, - NULL, 0, WSA_FLAG_OVERLAPPED); - - if (sock == INVALID_SOCKET) { - *werror = WSAGetLastError (); - return NULL; - } - - return GUINT_TO_POINTER (sock); -} - -/* FIXME: the SOCKET parameter (here and in other functions in this - * file) is really an IntPtr which needs to be converted to a guint32. - */ -void -ves_icall_System_Net_Sockets_Socket_Close_internal (SOCKET sock, gint32 *werror) -{ - LOGDEBUG (g_message ("%s: closing 0x%x", __func__, sock)); - - *werror = 0; - - /* Clear any pending work item from this socket if the underlying - * polling system does not notify when the socket is closed */ - mono_threadpool_io_remove_socket (GPOINTER_TO_INT (sock)); - - MONO_ENTER_GC_SAFE; - closesocket (sock); - MONO_EXIT_GC_SAFE; -} - -gint32 -ves_icall_System_Net_Sockets_SocketException_WSAGetLastError_internal (void) -{ - LOGDEBUG (g_message("%s: returning %d", __func__, WSAGetLastError ())); - - return WSAGetLastError (); -} - -gint32 -ves_icall_System_Net_Sockets_Socket_Available_internal (SOCKET sock, gint32 *werror) -{ - int ret; - int amount; - - *werror = 0; - - /* FIXME: this might require amount to be unsigned long. */ - ret = ioctlsocket (sock, FIONREAD, &amount); - if (ret == SOCKET_ERROR) { - *werror = WSAGetLastError (); - return 0; - } - - return amount; -} - -void -ves_icall_System_Net_Sockets_Socket_Blocking_internal (SOCKET sock, gboolean block, gint32 *werror) -{ - int ret; - - *werror = 0; - - /* - * block == TRUE/FALSE means we will block/not block. - * But the ioctlsocket call takes TRUE/FALSE for non-block/block - */ - block = !block; - - ret = ioctlsocket (sock, FIONBIO, (gulong *)&block); - if (ret == SOCKET_ERROR) - *werror = WSAGetLastError (); -} - -gpointer -ves_icall_System_Net_Sockets_Socket_Accept_internal (SOCKET sock, gint32 *werror, gboolean blocking) -{ - gboolean interrupted; - SOCKET newsock; - - *werror = 0; - - mono_thread_info_install_interrupt (abort_syscall, (gpointer) (gsize) mono_native_thread_id_get (), &interrupted); - if (interrupted) { - *werror = WSAEINTR; - return NULL; - } - - MONO_ENTER_GC_SAFE; - -#ifdef HOST_WIN32 - { - MonoInternalThread *curthread = mono_thread_internal_current (); - curthread->interrupt_on_stop = (gpointer)TRUE; - newsock = alertable_accept (sock, NULL, 0, blocking); - curthread->interrupt_on_stop = (gpointer)FALSE; - } -#else - newsock = _wapi_accept (sock, NULL, 0); -#endif - - MONO_EXIT_GC_SAFE; - - if (newsock == INVALID_SOCKET) - *werror = WSAGetLastError (); - - mono_thread_info_uninstall_interrupt (&interrupted); - if (interrupted) - *werror = WSAEINTR; - - if (*werror) - return NULL; - - return GUINT_TO_POINTER (newsock); -} - -void -ves_icall_System_Net_Sockets_Socket_Listen_internal(SOCKET sock, guint32 backlog, gint32 *werror) -{ - int ret; - - *werror = 0; - - MONO_ENTER_GC_SAFE; - - ret = _wapi_listen (sock, backlog); - - MONO_EXIT_GC_SAFE; - - if (ret == SOCKET_ERROR) - *werror = WSAGetLastError (); -} - -// Check whether it's ::ffff::0:0. -static gboolean -is_ipv4_mapped_any (const struct in6_addr *addr) -{ - int i; - - for (i = 0; i < 10; i++) { - if (addr->s6_addr [i]) - return FALSE; - } - if ((addr->s6_addr [10] != 0xff) || (addr->s6_addr [11] != 0xff)) - return FALSE; - for (i = 12; i < 16; i++) { - if (addr->s6_addr [i]) - return FALSE; - } - return TRUE; -} - -static MonoObject* -create_object_from_sockaddr (struct sockaddr *saddr, int sa_size, gint32 *werror, MonoError *error) -{ - MonoDomain *domain = mono_domain_get (); - MonoObject *sockaddr_obj; - MonoArray *data; - MonoAddressFamily family; - - mono_error_init (error); - - /* Build a System.Net.SocketAddress object instance */ - if (!domain->sockaddr_class) - domain->sockaddr_class = mono_class_load_from_name (get_socket_assembly (), "System.Net", "SocketAddress"); - sockaddr_obj = mono_object_new_checked (domain, domain->sockaddr_class, error); - return_val_if_nok (error, NULL); - - /* Locate the SocketAddress data buffer in the object */ - if (!domain->sockaddr_data_field) { - domain->sockaddr_data_field = mono_class_get_field_from_name (domain->sockaddr_class, "m_Buffer"); - g_assert (domain->sockaddr_data_field); - } - - /* Locate the SocketAddress data buffer length in the object */ - if (!domain->sockaddr_data_length_field) { - domain->sockaddr_data_length_field = mono_class_get_field_from_name (domain->sockaddr_class, "m_Size"); - g_assert (domain->sockaddr_data_length_field); - } - - /* May be the +2 here is too conservative, as sa_len returns - * the length of the entire sockaddr_in/in6, including - * sizeof (unsigned short) of the family */ - /* We can't really avoid the +2 as all code below depends on this size - INCLUDING unix domain sockets.*/ - data = mono_array_new_cached (domain, mono_get_byte_class (), sa_size + 2, error); - return_val_if_nok (error, NULL); - - /* The data buffer is laid out as follows: - * bytes 0 and 1 are the address family - * bytes 2 and 3 are the port info - * the rest is the address info - */ - - family = convert_to_mono_family (saddr->sa_family); - if (family == AddressFamily_Unknown) { - *werror = WSAEAFNOSUPPORT; - return NULL; - } - - mono_array_set (data, guint8, 0, family & 0x0FF); - mono_array_set (data, guint8, 1, (family >> 8) & 0x0FF); - - if (saddr->sa_family == AF_INET) { - struct sockaddr_in *sa_in = (struct sockaddr_in *)saddr; - guint16 port = ntohs (sa_in->sin_port); - guint32 address = ntohl (sa_in->sin_addr.s_addr); - int buffer_size = 8; - - if (sa_size < buffer_size) { - mono_error_set_exception_instance (error, mono_exception_from_name (mono_get_corlib (), "System", "SystemException")); - return NULL; - } - - mono_array_set (data, guint8, 2, (port>>8) & 0xff); - mono_array_set (data, guint8, 3, (port) & 0xff); - mono_array_set (data, guint8, 4, (address>>24) & 0xff); - mono_array_set (data, guint8, 5, (address>>16) & 0xff); - mono_array_set (data, guint8, 6, (address>>8) & 0xff); - mono_array_set (data, guint8, 7, (address) & 0xff); - - mono_field_set_value (sockaddr_obj, domain->sockaddr_data_field, data); - mono_field_set_value (sockaddr_obj, domain->sockaddr_data_length_field, &buffer_size); - - return sockaddr_obj; - } else if (saddr->sa_family == AF_INET6) { - struct sockaddr_in6 *sa_in = (struct sockaddr_in6 *)saddr; - int i; - int buffer_size = 28; - - guint16 port = ntohs (sa_in->sin6_port); - - if (sa_size < buffer_size) { - mono_error_set_exception_instance (error, mono_exception_from_name (mono_get_corlib (), "System", "SystemException")); - return NULL; - } - - mono_array_set (data, guint8, 2, (port>>8) & 0xff); - mono_array_set (data, guint8, 3, (port) & 0xff); - - if (is_ipv4_mapped_any (&sa_in->sin6_addr)) { - // Map ::ffff:0:0 to :: (bug #5502) - for (i = 0; i < 16; i++) - mono_array_set (data, guint8, 8 + i, 0); - } else { - for (i = 0; i < 16; i++) { - mono_array_set (data, guint8, 8 + i, - sa_in->sin6_addr.s6_addr [i]); - } - } - - mono_array_set (data, guint8, 24, sa_in->sin6_scope_id & 0xff); - mono_array_set (data, guint8, 25, - (sa_in->sin6_scope_id >> 8) & 0xff); - mono_array_set (data, guint8, 26, - (sa_in->sin6_scope_id >> 16) & 0xff); - mono_array_set (data, guint8, 27, - (sa_in->sin6_scope_id >> 24) & 0xff); - - mono_field_set_value (sockaddr_obj, domain->sockaddr_data_field, data); - mono_field_set_value (sockaddr_obj, domain->sockaddr_data_length_field, &buffer_size); - - return sockaddr_obj; - } -#ifdef HAVE_SYS_UN_H - else if (saddr->sa_family == AF_UNIX) { - int i; - int buffer_size = sa_size + 2; - - for (i = 0; i < sa_size; i++) - mono_array_set (data, guint8, i + 2, saddr->sa_data [i]); - - mono_field_set_value (sockaddr_obj, domain->sockaddr_data_field, data); - mono_field_set_value (sockaddr_obj, domain->sockaddr_data_length_field, &buffer_size); - - return sockaddr_obj; - } -#endif - else { - *werror = WSAEAFNOSUPPORT; - return NULL; - } -} - -static int -get_sockaddr_size (int family) -{ - int size; - - size = 0; - if (family == AF_INET) { - size = sizeof (struct sockaddr_in); - } else if (family == AF_INET6) { - size = sizeof (struct sockaddr_in6); - } -#ifdef HAVE_SYS_UN_H - else if (family == AF_UNIX) { - size = sizeof (struct sockaddr_un); - } -#endif - return size; -} - -MonoObject* -ves_icall_System_Net_Sockets_Socket_LocalEndPoint_internal (SOCKET sock, gint32 af, gint32 *werror) -{ - gchar *sa; - socklen_t salen; - int ret; - MonoObject *result; - MonoError error; - - *werror = 0; - - salen = get_sockaddr_size (convert_family ((MonoAddressFamily)af)); - if (salen == 0) { - *werror = WSAEAFNOSUPPORT; - return NULL; - } - sa = (salen <= 128) ? (gchar *)alloca (salen) : (gchar *)g_malloc0 (salen); - - MONO_ENTER_GC_SAFE; - - ret = _wapi_getsockname (sock, (struct sockaddr *)sa, &salen); - - MONO_EXIT_GC_SAFE; - - if (ret == SOCKET_ERROR) { - *werror = WSAGetLastError (); - if (salen > 128) - g_free (sa); - return NULL; - } - - LOGDEBUG (g_message("%s: bound to %s port %d", __func__, inet_ntoa (((struct sockaddr_in *)&sa)->sin_addr), ntohs (((struct sockaddr_in *)&sa)->sin_port))); - - result = create_object_from_sockaddr ((struct sockaddr *)sa, salen, werror, &error); - if (salen > 128) - g_free (sa); - if (!mono_error_ok (&error)) - mono_error_set_pending_exception (&error); - return result; -} - -MonoObject* -ves_icall_System_Net_Sockets_Socket_RemoteEndPoint_internal (SOCKET sock, gint32 af, gint32 *werror) -{ - gchar *sa; - socklen_t salen; - int ret; - MonoObject *result; - MonoError error; - - *werror = 0; - - salen = get_sockaddr_size (convert_family ((MonoAddressFamily)af)); - if (salen == 0) { - *werror = WSAEAFNOSUPPORT; - return NULL; - } - sa = (salen <= 128) ? (gchar *)alloca (salen) : (gchar *)g_malloc0 (salen); - /* Note: linux returns just 2 for AF_UNIX. Always. */ - - MONO_ENTER_GC_SAFE; - - ret = _wapi_getpeername (sock, (struct sockaddr *)sa, &salen); - - MONO_EXIT_GC_SAFE; - - if (ret == SOCKET_ERROR) { - *werror = WSAGetLastError (); - if (salen > 128) - g_free (sa); - return NULL; - } - - LOGDEBUG (g_message("%s: connected to %s port %d", __func__, inet_ntoa (((struct sockaddr_in *)&sa)->sin_addr), ntohs (((struct sockaddr_in *)&sa)->sin_port))); - - result = create_object_from_sockaddr ((struct sockaddr *)sa, salen, werror, &error); - if (salen > 128) - g_free (sa); - if (!mono_error_ok (&error)) - mono_error_set_pending_exception (&error); - return result; -} - -static struct sockaddr* -create_sockaddr_from_object (MonoObject *saddr_obj, socklen_t *sa_size, gint32 *werror, MonoError *error) -{ - MonoDomain *domain = mono_domain_get (); - MonoArray *data; - gint32 family; - int len; - - mono_error_init (error); - - if (!domain->sockaddr_class) - domain->sockaddr_class = mono_class_load_from_name (get_socket_assembly (), "System.Net", "SocketAddress"); - - /* Locate the SocketAddress data buffer in the object */ - if (!domain->sockaddr_data_field) { - domain->sockaddr_data_field = mono_class_get_field_from_name (domain->sockaddr_class, "m_Buffer"); - g_assert (domain->sockaddr_data_field); - } - - /* Locate the SocketAddress data buffer length in the object */ - if (!domain->sockaddr_data_length_field) { - domain->sockaddr_data_length_field = mono_class_get_field_from_name (domain->sockaddr_class, "m_Size"); - g_assert (domain->sockaddr_data_length_field); - } - - data = *(MonoArray **)(((char *)saddr_obj) + domain->sockaddr_data_field->offset); - - /* The data buffer is laid out as follows: - * byte 0 is the address family low byte - * byte 1 is the address family high byte - * INET: - * bytes 2 and 3 are the port info - * the rest is the address info - * UNIX: - * the rest is the file name - */ - len = *(int *)(((char *)saddr_obj) + domain->sockaddr_data_length_field->offset); - g_assert (len >= 2); - - family = convert_family ((MonoAddressFamily)(mono_array_get (data, guint8, 0) + (mono_array_get (data, guint8, 1) << 8))); - if (family == AF_INET) { - struct sockaddr_in *sa; - guint16 port; - guint32 address; - - if (len < 8) { - mono_error_set_exception_instance (error, mono_exception_from_name (mono_get_corlib (), "System", "SystemException")); - return NULL; - } - - 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); - return (struct sockaddr *)sa; - } else if (family == AF_INET6) { - struct sockaddr_in6 *sa; - int i; - guint16 port; - guint32 scopeid; - - if (len < 28) { - mono_error_set_exception_instance (error, mono_exception_from_name (mono_get_corlib (), "System", "SystemException")); - return NULL; - } - - 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_scope_id = scopeid; - - 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); - return (struct sockaddr *)sa; - } -#ifdef HAVE_SYS_UN_H - else if (family == AF_UNIX) { - struct sockaddr_un *sock_un; - int i; - - /* Need a byte for the '\0' terminator/prefix, and the first - * two bytes hold the SocketAddress family - */ - if (len - 2 >= sizeof (sock_un->sun_path)) { - mono_error_set_exception_instance (error, mono_get_exception_index_out_of_range ()); - return NULL; - } - - sock_un = g_new0 (struct sockaddr_un, 1); - - sock_un->sun_family = family; - for (i = 0; i < len - 2; i++) - sock_un->sun_path [i] = mono_array_get (data, guint8, i + 2); - - *sa_size = len; - return (struct sockaddr *)sock_un; - } -#endif - else { - *werror = WSAEAFNOSUPPORT; - return 0; - } -} - -void -ves_icall_System_Net_Sockets_Socket_Bind_internal (SOCKET sock, MonoObject *sockaddr, gint32 *werror) -{ - MonoError error; - struct sockaddr *sa; - socklen_t sa_size; - int ret; - - *werror = 0; - - sa = create_sockaddr_from_object (sockaddr, &sa_size, werror, &error); - if (*werror != 0) - return; - if (!mono_error_ok (&error)) { - mono_error_set_pending_exception (&error); - return; - } - - LOGDEBUG (g_message("%s: binding to %s port %d", __func__, inet_ntoa (((struct sockaddr_in *)sa)->sin_addr), ntohs (((struct sockaddr_in *)sa)->sin_port))); - - ret = _wapi_bind (sock, sa, sa_size); - - if (ret == SOCKET_ERROR) - *werror = WSAGetLastError (); - - g_free (sa); -} - -enum { - SelectModeRead, - SelectModeWrite, - SelectModeError -}; - -MonoBoolean -ves_icall_System_Net_Sockets_Socket_Poll_internal (SOCKET sock, gint mode, - gint timeout, gint32 *werror) -{ - MonoInternalThread *thread = mono_thread_internal_current (); - mono_pollfd *pfds; - int ret; - gboolean interrupted; - time_t start; - - *werror = 0; - - pfds = g_new0 (mono_pollfd, 1); - pfds->fd = GPOINTER_TO_INT (sock); - - switch (mode) { - case SelectModeRead: - pfds->events = MONO_POLLIN; - break; - case SelectModeWrite: - pfds->events = MONO_POLLOUT; - break; - default: - pfds->events = MONO_POLLERR | MONO_POLLHUP | MONO_POLLNVAL; - break; - } - - timeout = (timeout >= 0) ? (timeout / 1000) : -1; - start = time (NULL); - - do { - mono_thread_info_install_interrupt (abort_syscall, (gpointer) (gsize) mono_native_thread_id_get (), &interrupted); - if (interrupted) { - g_free (pfds); - *werror = WSAEINTR; - return FALSE; - } - - MONO_ENTER_GC_SAFE; - - ret = mono_poll (pfds, 1, timeout); - - MONO_EXIT_GC_SAFE; - - mono_thread_info_uninstall_interrupt (&interrupted); - if (interrupted) { - g_free (pfds); - *werror = WSAEINTR; - return FALSE; - } - - if (timeout > 0 && ret < 0) { - int err = errno; - int sec = time (NULL) - start; - - timeout -= sec * 1000; - if (timeout < 0) { - timeout = 0; - } - - errno = err; - } - - if (ret == -1 && errno == EINTR) { - if (mono_thread_test_state (thread, (MonoThreadState)(ThreadState_AbortRequested | ThreadState_StopRequested))) { - g_free (pfds); - return FALSE; - } - - /* Suspend requested? */ - mono_thread_interruption_checkpoint (); - - errno = EINTR; - } - } while (ret == -1 && errno == EINTR); - - if (ret == -1) { -#ifdef HOST_WIN32 - *werror = (errno > 0 && errno < WSABASEERR) ? errno + WSABASEERR : errno; -#else - *werror = errno_to_WSA (errno, __func__); -#endif - g_free (pfds); - return FALSE; - } - - g_free (pfds); - return ret != 0; -} - -void -ves_icall_System_Net_Sockets_Socket_Connect_internal (SOCKET sock, MonoObject *sockaddr, gint32 *werror, gboolean blocking) -{ - MonoError error; - struct sockaddr *sa; - socklen_t sa_size; - int ret; - gboolean interrupted; - - *werror = 0; - - sa = create_sockaddr_from_object (sockaddr, &sa_size, werror, &error); - if (*werror != 0) - return; - if (!mono_error_ok (&error)) { - mono_error_set_pending_exception (&error); - return; - } - - LOGDEBUG (g_message("%s: connecting to %s port %d", __func__, inet_ntoa (((struct sockaddr_in *)sa)->sin_addr), ntohs (((struct sockaddr_in *)sa)->sin_port))); - - mono_thread_info_install_interrupt (abort_syscall, (gpointer) (gsize) mono_native_thread_id_get (), &interrupted); - if (interrupted) { - *werror = WSAEINTR; - return; - } - - MONO_ENTER_GC_SAFE; - -#ifdef HOST_WIN32 - ret = alertable_connect (sock, sa, sa_size, blocking); -#else - ret = _wapi_connect (sock, sa, sa_size); -#endif - - MONO_EXIT_GC_SAFE; - - if (ret == SOCKET_ERROR) - *werror = WSAGetLastError (); - - mono_thread_info_uninstall_interrupt (&interrupted); - if (interrupted) - *werror = WSAEINTR; - - g_free (sa); -} - -#if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT | HAVE_UWP_WINAPI_SUPPORT) -/* 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 - -void -ves_icall_System_Net_Sockets_Socket_Disconnect_internal (SOCKET sock, MonoBoolean reuse, gint32 *werror) -{ - 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 interrupted; - - *werror = 0; - - LOGDEBUG (g_message("%s: disconnecting from socket %p (reuse %d)", __func__, sock, reuse)); - - MONO_ENTER_GC_SAFE; - - /* I _think_ the extension function pointers need to be looked - * up for each socket. FIXME: check the best way to store - * pointers to functions in managed objects that still works - * on 64bit platforms. - */ - ret = WSAIoctl (sock, SIO_GET_EXTENSION_FUNCTION_POINTER, (gchar *)&disco_guid, sizeof (GUID), - (gchar *)&_wapi_disconnectex, sizeof (void *), &output_bytes, NULL, NULL); - - MONO_EXIT_GC_SAFE; - - if (ret != 0) { - /* make sure that WSAIoctl didn't put crap in the - * output pointer - */ - _wapi_disconnectex = NULL; - - MONO_ENTER_GC_SAFE; - - /* - * Use the SIO_GET_EXTENSION_FUNCTION_POINTER to - * determine the address of the disconnect method without - * taking a hard dependency on a single provider - * - * For an explanation of why this is done, you can read - * the article at http://www.codeproject.com/internet/jbsocketserver3.asp - */ - ret = WSAIoctl (sock, SIO_GET_EXTENSION_FUNCTION_POINTER, (gchar *)&trans_guid, sizeof(GUID), - (gchar *)&_wapi_transmitfile, sizeof(void *), &output_bytes, NULL, NULL); - - MONO_EXIT_GC_SAFE; - - if (ret != 0) - _wapi_transmitfile = NULL; - } - - mono_thread_info_install_interrupt (abort_syscall, (gpointer) (gsize) mono_native_thread_id_get (), &interrupted); - if (interrupted) { - *werror = WSAEINTR; - return; - } - - MONO_ENTER_GC_SAFE; - - if (_wapi_disconnectex != NULL) { - if (!_wapi_disconnectex (sock, NULL, reuse ? TF_REUSE_SOCKET : 0, 0)) - *werror = WSAGetLastError (); - } else if (_wapi_transmitfile != NULL) { - if (!_wapi_transmitfile (sock, NULL, 0, 0, NULL, NULL, TF_DISCONNECT | (reuse ? TF_REUSE_SOCKET : 0))) - *werror = WSAGetLastError (); - } else { - *werror = ERROR_NOT_SUPPORTED; - } - - MONO_EXIT_GC_SAFE; - - mono_thread_info_uninstall_interrupt (&interrupted); - if (interrupted) - *werror = WSAEINTR; -} -#endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT | HAVE_UWP_WINAPI_SUPPORT) */ - -gint32 -ves_icall_System_Net_Sockets_Socket_Receive_internal (SOCKET sock, MonoArray *buffer, gint32 offset, gint32 count, gint32 flags, gint32 *werror, gboolean blocking) -{ - int ret; - guchar *buf; - gint32 alen; - int recvflags = 0; - gboolean interrupted; - MonoInternalThread* curthread G_GNUC_UNUSED = mono_thread_internal_current (); - - *werror = 0; - - 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) { - *werror = WSAEOPNOTSUPP; - return 0; - } - - mono_thread_info_install_interrupt (abort_syscall, (gpointer) (gsize) mono_native_thread_id_get (), &interrupted); - if (interrupted) - return 0; - - MONO_ENTER_GC_SAFE; - -#ifdef HOST_WIN32 - { - curthread->interrupt_on_stop = (gpointer)TRUE; - ret = alertable_recv (sock, buf, count, recvflags, blocking); - curthread->interrupt_on_stop = (gpointer)FALSE; - } -#else - ret = _wapi_recv (sock, buf, count, recvflags); -#endif - - MONO_EXIT_GC_SAFE; - - if (ret == SOCKET_ERROR) - *werror = WSAGetLastError (); - - mono_thread_info_uninstall_interrupt (&interrupted); - if (interrupted) - *werror = WSAEINTR; - - if (*werror) - return 0; - - return ret; -} - -gint32 -ves_icall_System_Net_Sockets_Socket_Receive_array_internal (SOCKET sock, MonoArray *buffers, gint32 flags, gint32 *werror, gboolean blocking) -{ - int ret, count; - gboolean interrupted; - DWORD recv; - WSABUF *wsabufs; - DWORD recvflags = 0; - - *werror = 0; - - wsabufs = mono_array_addr (buffers, WSABUF, 0); - count = mono_array_length (buffers); - - recvflags = convert_socketflags (flags); - if (recvflags == -1) { - *werror = WSAEOPNOTSUPP; - return 0; - } - - mono_thread_info_install_interrupt (abort_syscall, (gpointer) (gsize) mono_native_thread_id_get (), &interrupted); - if (interrupted) { - *werror = WSAEINTR; - return 0; - } - - MONO_ENTER_GC_SAFE; - -#ifdef HOST_WIN32 - ret = alertable_WSARecv (sock, wsabufs, count, &recv, &recvflags, NULL, NULL, blocking); -#else - ret = WSARecv (sock, wsabufs, count, &recv, &recvflags, NULL, NULL); -#endif - - MONO_EXIT_GC_SAFE; - - if (ret == SOCKET_ERROR) - *werror = WSAGetLastError (); - - mono_thread_info_uninstall_interrupt (&interrupted); - if (interrupted) - *werror = WSAEINTR; - - if (*werror) - return 0; - - return recv; -} - -gint32 -ves_icall_System_Net_Sockets_Socket_ReceiveFrom_internal (SOCKET sock, MonoArray *buffer, gint32 offset, gint32 count, gint32 flags, MonoObject **sockaddr, gint32 *werror, gboolean blocking) -{ - MonoError error; - int ret; - guchar *buf; - gint32 alen; - int recvflags = 0; - struct sockaddr *sa; - socklen_t sa_size; - gboolean interrupted; - - *werror = 0; - - alen = mono_array_length (buffer); - if (offset > alen - count) - return 0; - - sa = create_sockaddr_from_object (*sockaddr, &sa_size, werror, &error); - if (*werror != 0) - return 0; - if (!mono_error_ok (&error)) { - mono_error_set_pending_exception (&error); - return 0; - } - - buf = mono_array_addr (buffer, guchar, offset); - - recvflags = convert_socketflags (flags); - if (recvflags == -1) { - *werror = WSAEOPNOTSUPP; - return 0; - } - - mono_thread_info_install_interrupt (abort_syscall, (gpointer) (gsize) mono_native_thread_id_get (), &interrupted); - if (interrupted) { - g_free (sa); - *werror = WSAEINTR; - return 0; - } - - MONO_ENTER_GC_SAFE; - -#ifdef HOST_WIN32 - ret = alertable_recvfrom (sock, buf, count, recvflags, sa, &sa_size, blocking); -#else - ret = _wapi_recvfrom (sock, buf, count, recvflags, sa, &sa_size); -#endif - - MONO_EXIT_GC_SAFE; - - if (ret == SOCKET_ERROR) - *werror = WSAGetLastError (); - - mono_thread_info_uninstall_interrupt (&interrupted); - - if (interrupted) - *werror = WSAEINTR; - - if (*werror) { - g_free(sa); - return 0; - } - - /* If we didn't get a socket size, then we're probably a - * connected connection-oriented socket and the stack hasn't - * returned the remote address. All we can do is return null. - */ - if (sa_size) { - *sockaddr = create_object_from_sockaddr (sa, sa_size, werror, &error); - if (!mono_error_ok (&error)) { - mono_error_set_pending_exception (&error); - g_free (sa); - return 0; - } - } else { - *sockaddr = NULL; - } - - g_free (sa); - - return ret; -} - -gint32 -ves_icall_System_Net_Sockets_Socket_Send_internal (SOCKET sock, MonoArray *buffer, gint32 offset, gint32 count, gint32 flags, gint32 *werror, gboolean blocking) -{ - int ret; - guchar *buf; - gint32 alen; - int sendflags = 0; - gboolean interrupted; - - *werror = 0; - - alen = mono_array_length (buffer); - if (offset > alen - count) - return 0; - - LOGDEBUG (g_message("%s: alen: %d", __func__, alen)); - - buf = mono_array_addr (buffer, guchar, offset); - - LOGDEBUG (g_message("%s: Sending %d bytes", __func__, count)); - - sendflags = convert_socketflags (flags); - if (sendflags == -1) { - *werror = WSAEOPNOTSUPP; - return 0; - } - - mono_thread_info_install_interrupt (abort_syscall, (gpointer) (gsize) mono_native_thread_id_get (), &interrupted); - if (interrupted) { - *werror = WSAEINTR; - return 0; - } - - MONO_ENTER_GC_SAFE; - -#ifdef HOST_WIN32 - ret = alertable_send (sock, buf, count, sendflags, blocking); -#else - ret = _wapi_send (sock, buf, count, sendflags); -#endif - - MONO_EXIT_GC_SAFE; - - if (ret == SOCKET_ERROR) - *werror = WSAGetLastError (); - - mono_thread_info_uninstall_interrupt (&interrupted); - if (interrupted) - *werror = WSAEINTR; - - if (*werror) - return 0; - - return ret; -} - -gint32 -ves_icall_System_Net_Sockets_Socket_Send_array_internal (SOCKET sock, MonoArray *buffers, gint32 flags, gint32 *werror, gboolean blocking) -{ - int ret, count; - DWORD sent; - WSABUF *wsabufs; - DWORD sendflags = 0; - gboolean interrupted; - - *werror = 0; - - wsabufs = mono_array_addr (buffers, WSABUF, 0); - count = mono_array_length (buffers); - - sendflags = convert_socketflags (flags); - if (sendflags == -1) { - *werror = WSAEOPNOTSUPP; - return 0; - } - - mono_thread_info_install_interrupt (abort_syscall, (gpointer) (gsize) mono_native_thread_id_get (), &interrupted); - if (interrupted) { - *werror = WSAEINTR; - return 0; - } - - MONO_ENTER_GC_SAFE; - -#ifdef HOST_WIN32 - ret = alertable_WSASend (sock, wsabufs, count, &sent, sendflags, NULL, NULL, blocking); -#else - ret = WSASend (sock, wsabufs, count, &sent, sendflags, NULL, NULL); -#endif - - MONO_EXIT_GC_SAFE; - - if (ret == SOCKET_ERROR) - *werror = WSAGetLastError (); - - mono_thread_info_uninstall_interrupt (&interrupted); - if (interrupted) - *werror = WSAEINTR; - - if (*werror) - return 0; - - return sent; -} - -gint32 -ves_icall_System_Net_Sockets_Socket_SendTo_internal (SOCKET sock, MonoArray *buffer, gint32 offset, gint32 count, gint32 flags, MonoObject *sockaddr, gint32 *werror, gboolean blocking) -{ - MonoError error; - int ret; - guchar *buf; - gint32 alen; - int sendflags = 0; - struct sockaddr *sa; - socklen_t sa_size; - gboolean interrupted; - - *werror = 0; - - alen = mono_array_length (buffer); - if (offset > alen - count) { - return 0; - } - - sa = create_sockaddr_from_object(sockaddr, &sa_size, werror, &error); - if (*werror != 0) - return 0; - if (!mono_error_ok (&error)) { - mono_error_set_pending_exception (&error); - return 0; - } - - LOGDEBUG (g_message ("%s: alen: %d", __func__, alen)); - - buf = mono_array_addr (buffer, guchar, offset); - - LOGDEBUG (g_message("%s: Sending %d bytes", __func__, count)); - - sendflags = convert_socketflags (flags); - if (sendflags == -1) { - g_free (sa); - *werror = WSAEOPNOTSUPP; - return 0; - } - - mono_thread_info_install_interrupt (abort_syscall, (gpointer) (gsize) mono_native_thread_id_get (), &interrupted); - if (interrupted) { - g_free (sa); - *werror = WSAEINTR; - return 0; - } - - MONO_ENTER_GC_SAFE; - -#ifdef HOST_WIN32 - ret = alertable_sendto (sock, buf, count, sendflags, sa, sa_size, blocking); -#else - ret = _wapi_sendto (sock, buf, count, sendflags, sa, sa_size); -#endif - - MONO_EXIT_GC_SAFE; - - if (ret == SOCKET_ERROR) - *werror = WSAGetLastError (); - - mono_thread_info_uninstall_interrupt (&interrupted); - if (interrupted) - *werror = WSAEINTR; - - g_free(sa); - - if (*werror) - return 0; - - return ret; -} - -static SOCKET -Socket_to_SOCKET (MonoObject *sockobj) -{ - MonoSafeHandle *safe_handle; - MonoClassField *field; - - field = mono_class_get_field_from_name (sockobj->vtable->klass, "m_Handle"); - safe_handle = ((MonoSafeHandle *)(*(gpointer *)(((char *)sockobj) + field->offset))); - - if (safe_handle == NULL) - return -1; - - return (SOCKET)safe_handle->handle; -} - -#define POLL_ERRORS (MONO_POLLERR | MONO_POLLHUP | MONO_POLLNVAL) - -void -ves_icall_System_Net_Sockets_Socket_Select_internal (MonoArray **sockets, gint32 timeout, gint32 *werror) -{ - MonoError error; - MonoInternalThread *thread = mono_thread_internal_current (); - MonoObject *obj; - mono_pollfd *pfds; - int nfds, idx; - int ret; - int i, count; - int mode; - MonoClass *sock_arr_class; - MonoArray *socks; - time_t start; - uintptr_t socks_size; - gboolean interrupted; - - *werror = 0; - - /* *sockets -> READ, null, WRITE, null, ERROR, null */ - count = mono_array_length (*sockets); - nfds = count - 3; /* NULL separators */ - pfds = g_new0 (mono_pollfd, nfds); - mode = idx = 0; - for (i = 0; i < count; i++) { - obj = mono_array_get (*sockets, MonoObject *, i); - if (obj == NULL) { - mode++; - continue; - } - - if (idx >= nfds) { - /* The socket array was bogus */ - g_free (pfds); - *werror = WSAEFAULT; - return; - } - - pfds [idx].fd = Socket_to_SOCKET (obj); - pfds [idx].events = (mode == 0) ? MONO_POLLIN : (mode == 1) ? MONO_POLLOUT : POLL_ERRORS; - idx++; - } - - timeout = (timeout >= 0) ? (timeout / 1000) : -1; - start = time (NULL); - do { - mono_thread_info_install_interrupt (abort_syscall, (gpointer) (gsize) mono_native_thread_id_get (), &interrupted); - if (interrupted) { - g_free (pfds); - *werror = WSAEINTR; - return; - } - - MONO_ENTER_GC_SAFE; - - ret = mono_poll (pfds, nfds, timeout); - - MONO_EXIT_GC_SAFE; - - mono_thread_info_uninstall_interrupt (&interrupted); - if (interrupted) { - g_free (pfds); - *werror = WSAEINTR; - return; - } - - if (timeout > 0 && ret < 0) { - int err = errno; - int sec = time (NULL) - start; - - timeout -= sec * 1000; - if (timeout < 0) - timeout = 0; - errno = err; - } - - if (ret == -1 && errno == EINTR) { - if (mono_thread_test_state (thread, (MonoThreadState)(ThreadState_AbortRequested | ThreadState_StopRequested))) { - g_free (pfds); - *sockets = NULL; - return; - } - - /* Suspend requested? */ - mono_thread_interruption_checkpoint (); - - errno = EINTR; - } - } while (ret == -1 && errno == EINTR); - - if (ret == -1) { -#ifdef HOST_WIN32 - *werror = (errno > 0 && errno < WSABASEERR) ? errno + WSABASEERR : errno; -#else - *werror = errno_to_WSA (errno, __func__); -#endif - g_free (pfds); - return; - } - - if (ret == 0) { - g_free (pfds); - *sockets = NULL; - return; - } - - sock_arr_class = ((MonoObject *)*sockets)->vtable->klass; - socks_size = ((uintptr_t)ret) + 3; /* space for the NULL delimiters */ - socks = mono_array_new_full_checked (mono_domain_get (), sock_arr_class, &socks_size, NULL, &error); - if (!mono_error_ok (&error)) { - mono_error_set_pending_exception (&error); - g_free (pfds); - return; - } - - mode = idx = 0; - for (i = 0; i < count && ret > 0; i++) { - mono_pollfd *pfd; - - obj = mono_array_get (*sockets, MonoObject *, i); - if (obj == NULL) { - mode++; - idx++; - continue; - } - - pfd = &pfds [i - mode]; - if (pfd->revents == 0) - continue; - - ret--; - if (mode == 0 && (pfd->revents & (MONO_POLLIN | POLL_ERRORS)) != 0) { - mono_array_setref (socks, idx++, obj); - } else if (mode == 1 && (pfd->revents & (MONO_POLLOUT | POLL_ERRORS)) != 0) { - mono_array_setref (socks, idx++, obj); - } else if ((pfd->revents & POLL_ERRORS) != 0) { - mono_array_setref (socks, idx++, obj); - } - } - - *sockets = socks; - g_free (pfds); -} - -static MonoObject* -int_to_object (MonoDomain *domain, int val, MonoError *error) -{ - return mono_value_box_checked (domain, mono_get_int32_class (), &val, error); -} - -void -ves_icall_System_Net_Sockets_Socket_GetSocketOption_obj_internal (SOCKET sock, gint32 level, gint32 name, MonoObject **obj_val, gint32 *werror) -{ - int system_level = 0; - int system_name = 0; - int ret; - int val = 0; - socklen_t valsize = sizeof (val); - struct linger linger; - socklen_t lingersize = sizeof (linger); - int time_ms = 0; - socklen_t time_ms_size = sizeof (time_ms); -#ifdef SO_PEERCRED -# if defined(__OpenBSD__) - struct sockpeercred cred; -# else - struct ucred cred; -# endif - socklen_t credsize = sizeof (cred); -#endif - MonoError error; - MonoDomain *domain = mono_domain_get (); - MonoObject *obj; - MonoClass *obj_class; - MonoClassField *field; - - *werror = 0; - -#if !defined(SO_EXCLUSIVEADDRUSE) && defined(SO_REUSEADDR) - if (level == SocketOptionLevel_Socket && name == SocketOptionName_ExclusiveAddressUse) { - system_level = SOL_SOCKET; - system_name = SO_REUSEADDR; - ret = 0; - } else -#endif - { - ret = convert_sockopt_level_and_name ((MonoSocketOptionLevel)level, (MonoSocketOptionName)name, &system_level, &system_name); - } - - if (ret == -1) { - *werror = WSAENOPROTOOPT; - return; - } - if (ret == -2) { - *obj_val = int_to_object (domain, 0, &error); - mono_error_set_pending_exception (&error); - return; - } - - MONO_ENTER_GC_SAFE; - - /* No need to deal with MulticastOption names here, because - * you cant getsockopt AddMembership or DropMembership (the - * int getsockopt will error, causing an exception) - */ - switch (name) { - case SocketOptionName_Linger: - case SocketOptionName_DontLinger: - ret = _wapi_getsockopt (sock, system_level, system_name, &linger, &lingersize); - break; - - case SocketOptionName_SendTimeout: - case SocketOptionName_ReceiveTimeout: - ret = _wapi_getsockopt (sock, system_level, system_name, (char *)&time_ms, &time_ms_size); - break; - -#ifdef SO_PEERCRED - case SocketOptionName_PeerCred: - ret = _wapi_getsockopt (sock, system_level, system_name, &cred, &credsize); - break; -#endif - - default: - ret = _wapi_getsockopt (sock, system_level, system_name, &val, &valsize); - } - - MONO_EXIT_GC_SAFE; - - if (ret == SOCKET_ERROR) { - *werror = WSAGetLastError (); - return; - } - - switch (name) { - case SocketOptionName_Linger: - /* build a System.Net.Sockets.LingerOption */ - obj_class = mono_class_load_from_name (get_socket_assembly (), - "System.Net.Sockets", - "LingerOption"); - obj = mono_object_new_checked (domain, obj_class, &error); - if (!mono_error_ok (&error)) { - mono_error_set_pending_exception (&error); - return; - } - - /* Locate and set the fields "bool enabled" and "int - * lingerTime" - */ - field = mono_class_get_field_from_name(obj_class, "enabled"); - *(guint8 *)(((char *)obj)+field->offset) = linger.l_onoff; - - field = mono_class_get_field_from_name(obj_class, "lingerTime"); - *(guint32 *)(((char *)obj)+field->offset)=linger.l_linger; - break; - case SocketOptionName_DontLinger: - /* construct a bool int in val - true if linger is off */ - obj = int_to_object (domain, !linger.l_onoff, &error); - mono_error_set_pending_exception (&error); - break; - case SocketOptionName_SendTimeout: - case SocketOptionName_ReceiveTimeout: - obj = int_to_object (domain, time_ms, &error); - mono_error_set_pending_exception (&error); - break; - -#ifdef SO_PEERCRED - case SocketOptionName_PeerCred: { - /* - * build a Mono.Posix.PeerCred+PeerCredData if - * possible - */ - static MonoImage *mono_posix_image = NULL; - MonoPeerCredData *cred_data; - - if (mono_posix_image == NULL) { - mono_posix_image = mono_image_loaded ("Mono.Posix"); - if (!mono_posix_image) { - MonoAssembly *sa = mono_assembly_open ("Mono.Posix.dll", NULL); - if (!sa) { - *werror = WSAENOPROTOOPT; - return; - } else { - mono_posix_image = mono_assembly_get_image (sa); - } - } - } - - obj_class = mono_class_load_from_name (mono_posix_image, - "Mono.Posix", - "PeerCredData"); - obj = mono_object_new_checked (domain, obj_class, &error); - if (!mono_error_ok (&error)) { - mono_error_set_pending_exception (&error); - return; - } - cred_data = (MonoPeerCredData *)obj; - cred_data->pid = cred.pid; - cred_data->uid = cred.uid; - cred_data->gid = cred.gid; - break; - } -#endif - - default: -#if !defined(SO_EXCLUSIVEADDRUSE) && defined(SO_REUSEADDR) - if (level == SocketOptionLevel_Socket && name == SocketOptionName_ExclusiveAddressUse) - val = val ? 0 : 1; -#endif - obj = int_to_object (domain, val, &error); - mono_error_set_pending_exception (&error); - } - - *obj_val = obj; -} - -void -ves_icall_System_Net_Sockets_Socket_GetSocketOption_arr_internal (SOCKET sock, gint32 level, gint32 name, MonoArray **byte_val, gint32 *werror) -{ - int system_level = 0; - int system_name = 0; - int ret; - guchar *buf; - socklen_t valsize; - - *werror = 0; - - ret = convert_sockopt_level_and_name((MonoSocketOptionLevel)level, (MonoSocketOptionName)name, &system_level, - &system_name); - if (ret == -1) { - *werror = WSAENOPROTOOPT; - return; - } - if (ret == -2) - return; - - valsize = mono_array_length (*byte_val); - buf = mono_array_addr (*byte_val, guchar, 0); - - MONO_ENTER_GC_SAFE; - - ret = _wapi_getsockopt (sock, system_level, system_name, buf, &valsize); - - MONO_EXIT_GC_SAFE; - - if (ret == SOCKET_ERROR) - *werror = WSAGetLastError (); -} - -#if defined(HAVE_STRUCT_IP_MREQN) || defined(HAVE_STRUCT_IP_MREQ) -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, "m_Address"); - - /* No idea why .net uses a 64bit type to hold a 32bit value... - * - * Internal value of IPAddess is in little-endian order - */ - inaddr.s_addr = GUINT_FROM_LE ((guint32)*(guint64 *)(((char *)ipaddr) + field->offset)); - - return inaddr; -} - -static struct in6_addr -ipaddress_to_struct_in6_addr (MonoObject *ipaddr) -{ - struct in6_addr in6addr; - MonoClassField *field; - MonoArray *data; - int i; - - field = mono_class_get_field_from_name (ipaddr->vtable->klass, "m_Numbers"); - g_assert (field); - data = *(MonoArray **)(((char *)ipaddr) + field->offset); - - for (i = 0; i < 8; i++) { - const guint16 s = GUINT16_TO_BE (mono_array_get (data, guint16, i)); - -/* Solaris/MacOS have only the 8 bit version. */ -#ifndef s6_addr16 - in6addr.s6_addr[2 * i + 1] = (s >> 8) & 0xff; - in6addr.s6_addr[2 * i] = s & 0xff; -#else - in6addr.s6_addr16[i] = s; -#endif - } - return in6addr; -} -#endif - -#if defined(__APPLE__) || defined(__FreeBSD__) - -static int -get_local_interface_id (int family) -{ -#if !defined(HAVE_GETIFADDRS) || !defined(HAVE_IF_NAMETOINDEX) - return 0; -#else - struct ifaddrs *ifap = NULL, *ptr; - int idx = 0; - - if (getifaddrs (&ifap)) - return 0; - - for (ptr = ifap; ptr; ptr = ptr->ifa_next) { - if (!ptr->ifa_addr || !ptr->ifa_name) - continue; - if (ptr->ifa_addr->sa_family != family) - continue; - if ((ptr->ifa_flags & IFF_LOOPBACK) != 0) - continue; - if ((ptr->ifa_flags & IFF_MULTICAST) == 0) - continue; - - idx = if_nametoindex (ptr->ifa_name); - break; - } - - freeifaddrs (ifap); - return idx; -#endif -} - -#endif /* defined(__APPLE__) || defined(__FreeBSD__) */ - -void -ves_icall_System_Net_Sockets_Socket_SetSocketOption_internal (SOCKET sock, gint32 level, gint32 name, MonoObject *obj_val, MonoArray *byte_val, gint32 int_val, gint32 *werror) -{ - struct linger linger; - int system_level = 0; - int system_name = 0; - int ret; - int sol_ip; - int sol_ipv6; - - *werror = 0; - - sol_ipv6 = mono_networking_get_ipv6_protocol (); - sol_ip = mono_networking_get_ip_protocol (); - - ret = convert_sockopt_level_and_name ((MonoSocketOptionLevel)level, (MonoSocketOptionName)name, &system_level, - &system_name); - -#if !defined(SO_EXCLUSIVEADDRUSE) && defined(SO_REUSEADDR) - if (level == SocketOptionLevel_Socket && name == SocketOptionName_ExclusiveAddressUse) { - system_name = SO_REUSEADDR; - int_val = int_val ? 0 : 1; - ret = 0; - } -#endif - - if (ret == -1) { - *werror = WSAENOPROTOOPT; - return; - } - if (ret == -2) - return; - - /* Only one of obj_val, byte_val or int_val has data */ - if (obj_val) { - MonoClassField *field; - int valsize; - - switch (name) { - case SocketOptionName_Linger: - /* Dig out "bool enabled" and "int lingerTime" - * fields - */ - field = mono_class_get_field_from_name (obj_val->vtable->klass, "enabled"); - linger.l_onoff = *(guint8 *)(((char *)obj_val) + field->offset); - field = mono_class_get_field_from_name (obj_val->vtable->klass, "lingerTime"); - linger.l_linger = *(guint32 *)(((char *)obj_val) + field->offset); - - valsize = sizeof (linger); - ret = _wapi_setsockopt (sock, system_level, - system_name, &linger, valsize); - break; - case SocketOptionName_AddMembership: - case SocketOptionName_DropMembership: -#if defined(HAVE_STRUCT_IP_MREQN) || defined(HAVE_STRUCT_IP_MREQ) - { - MonoObject *address = NULL; - - if (system_level == sol_ipv6) { - struct ipv6_mreq mreq6; - - /* - * Get group address - */ - field = mono_class_get_field_from_name (obj_val->vtable->klass, "m_Group"); - g_assert (field); - address = *(MonoObject **)(((char *)obj_val) + field->offset); - - if (address) - mreq6.ipv6mr_multiaddr = ipaddress_to_struct_in6_addr (address); - - field = mono_class_get_field_from_name (obj_val->vtable->klass, "m_Interface"); - mreq6.ipv6mr_interface = *(guint64 *)(((char *)obj_val) + field->offset); - -#if defined(__APPLE__) || defined(__FreeBSD__) - /* - * Bug #5504: - * - * Mac OS Lion doesn't allow ipv6mr_interface = 0. - * - * Tests on Windows and Linux show that the multicast group is only - * joined on one NIC when interface = 0, so we simply use the interface - * id from the first non-loopback interface (this is also what - * Dns.GetHostName (string.Empty) would return). - */ - if (!mreq6.ipv6mr_interface) - mreq6.ipv6mr_interface = get_local_interface_id (AF_INET6); -#endif - - ret = _wapi_setsockopt (sock, system_level, - system_name, &mreq6, - sizeof (mreq6)); - } else if (system_level == sol_ip) { -#ifdef HAVE_STRUCT_IP_MREQN - struct ip_mreqn mreq = {{0}}; -#else - struct ip_mreq mreq = {{0}}; -#endif /* HAVE_STRUCT_IP_MREQN */ - - /* - * pain! MulticastOption holds two IPAddress - * members, so I have to dig the value out of - * those :-( - */ - field = mono_class_get_field_from_name (obj_val->vtable->klass, "group"); - address = *(MonoObject **)(((char *)obj_val) + field->offset); - - /* address might not be defined and if so, set the address to ADDR_ANY. - */ - if (address) - mreq.imr_multiaddr = ipaddress_to_struct_in_addr (address); - - field = mono_class_get_field_from_name (obj_val->vtable->klass, "localAddress"); - address = *(MonoObject **)(((char *)obj_val) + field->offset); - -#ifdef HAVE_STRUCT_IP_MREQN - if (address) - mreq.imr_address = ipaddress_to_struct_in_addr (address); - - field = mono_class_get_field_from_name (obj_val->vtable->klass, "ifIndex"); - mreq.imr_ifindex = *(gint32 *)(((char *)obj_val) + field->offset); -#else - if (address) - mreq.imr_interface = ipaddress_to_struct_in_addr (address); -#endif /* HAVE_STRUCT_IP_MREQN */ - - ret = _wapi_setsockopt (sock, system_level, - system_name, &mreq, - sizeof (mreq)); - } - break; - } -#endif /* HAVE_STRUCT_IP_MREQN || HAVE_STRUCT_IP_MREQ */ - default: - /* Cause an exception to be thrown */ - *werror = WSAEINVAL; - return; - } - } else if (byte_val!=NULL) { - int valsize = mono_array_length (byte_val); - guchar *buf = mono_array_addr (byte_val, guchar, 0); - - switch(name) { - case SocketOptionName_DontLinger: - if (valsize == 1) { - linger.l_onoff = (*buf) ? 0 : 1; - linger.l_linger = 0; - ret = _wapi_setsockopt (sock, system_level, system_name, &linger, sizeof (linger)); - } else { - *werror = WSAEINVAL; - } - break; - default: - ret = _wapi_setsockopt (sock, system_level, system_name, buf, valsize); - break; - } - } else { - /* ReceiveTimeout/SendTimeout get here */ - switch (name) { - case SocketOptionName_DontLinger: - linger.l_onoff = !int_val; - linger.l_linger = 0; - ret = _wapi_setsockopt (sock, system_level, system_name, &linger, sizeof (linger)); - break; - case SocketOptionName_MulticastInterface: -#ifndef HOST_WIN32 -#ifdef HAVE_STRUCT_IP_MREQN - int_val = GUINT32_FROM_BE (int_val); - if ((int_val & 0xff000000) == 0) { - /* int_val is interface index */ - struct ip_mreqn mreq = {{0}}; - mreq.imr_ifindex = int_val; - ret = _wapi_setsockopt (sock, system_level, system_name, (char *) &mreq, sizeof (mreq)); - break; - } - int_val = GUINT32_TO_BE (int_val); -#endif /* HAVE_STRUCT_IP_MREQN */ -#endif /* HOST_WIN32 */ - /* int_val is in_addr */ - ret = _wapi_setsockopt (sock, system_level, system_name, (char *) &int_val, sizeof (int_val)); - break; - case SocketOptionName_DontFragment: -#ifdef HAVE_IP_MTU_DISCOVER - /* Fiddle with the value slightly if we're - * turning DF on - */ - if (int_val == 1) - int_val = IP_PMTUDISC_DO; - /* Fall through */ -#endif - - default: - ret = _wapi_setsockopt (sock, system_level, system_name, (char *) &int_val, sizeof (int_val)); - } - } - - if (ret == SOCKET_ERROR) - *werror = WSAGetLastError (); -} - -void -ves_icall_System_Net_Sockets_Socket_Shutdown_internal (SOCKET sock, gint32 how, gint32 *werror) -{ - int ret; - gboolean interrupted; - - *werror = 0; - - mono_thread_info_install_interrupt (abort_syscall, (gpointer) (gsize) mono_native_thread_id_get (), &interrupted); - if (interrupted) { - *werror = WSAEINTR; - return; - } - - MONO_ENTER_GC_SAFE; - - /* Currently, the values for how (recv=0, send=1, both=2) match the BSD API */ - ret = _wapi_shutdown (sock, how); - - MONO_EXIT_GC_SAFE; - - if (ret == SOCKET_ERROR) - *werror = WSAGetLastError (); - - mono_thread_info_uninstall_interrupt (&interrupted); - if (interrupted) { - *werror = WSAEINTR; - } - -} - -gint -ves_icall_System_Net_Sockets_Socket_IOControl_internal (SOCKET sock, gint32 code, MonoArray *input, MonoArray *output, gint32 *werror) -{ - glong output_bytes = 0; - gchar *i_buffer, *o_buffer; - gint i_len, o_len; - gint ret; - - *werror = 0; - - if ((guint32)code == FIONBIO) - /* Invalid command. Must use Socket.Blocking */ - return -1; - - if (input == NULL) { - i_buffer = NULL; - i_len = 0; - } else { - i_buffer = mono_array_addr (input, gchar, 0); - i_len = mono_array_length (input); - } - - if (output == NULL) { - o_buffer = NULL; - o_len = 0; - } else { - o_buffer = mono_array_addr (output, gchar, 0); - o_len = mono_array_length (output); - } - - MONO_ENTER_GC_SAFE; - - ret = WSAIoctl (sock, code, i_buffer, i_len, o_buffer, o_len, &output_bytes, NULL, NULL); - - MONO_EXIT_GC_SAFE; - - if (ret == SOCKET_ERROR) { - *werror = WSAGetLastError (); - return -1; - } - - return (gint)output_bytes; -} - -static gboolean -addrinfo_to_IPHostEntry (MonoAddressInfo *info, MonoString **h_name, MonoArray **h_aliases, MonoArray **h_addr_list, gboolean add_local_ips, MonoError *error) -{ - gint32 count, i; - MonoAddressEntry *ai = NULL; - struct in_addr *local_in = NULL; - int nlocal_in = 0; - struct in6_addr *local_in6 = NULL; - int nlocal_in6 = 0; - int addr_index; - MonoDomain *domain = mono_domain_get (); - - mono_error_init (error); - addr_index = 0; - *h_aliases = mono_array_new_checked (domain, mono_get_string_class (), 0, error); - return_val_if_nok (error, FALSE); - if (add_local_ips) { - local_in = (struct in_addr *) mono_get_local_interfaces (AF_INET, &nlocal_in); - local_in6 = (struct in6_addr *) mono_get_local_interfaces (AF_INET6, &nlocal_in6); - if (nlocal_in || nlocal_in6) { - char addr [INET6_ADDRSTRLEN]; - *h_addr_list = mono_array_new_checked (domain, mono_get_string_class (), nlocal_in + nlocal_in6, error); - if (!is_ok (error)) - goto leave; - - if (nlocal_in) { - MonoString *addr_string; - int i; - - for (i = 0; i < nlocal_in; i++) { - MonoAddress maddr; - mono_address_init (&maddr, AF_INET, &local_in [i]); - if (mono_networking_addr_to_str (&maddr, addr, sizeof (addr))) { - addr_string = mono_string_new (domain, addr); - mono_array_setref (*h_addr_list, addr_index, addr_string); - addr_index++; - } - } - } - - if (nlocal_in6) { - MonoString *addr_string; - int i; - - for (i = 0; i < nlocal_in6; i++) { - MonoAddress maddr; - mono_address_init (&maddr, AF_INET6, &local_in6 [i]); - if (mono_networking_addr_to_str (&maddr, addr, sizeof (addr))) { - addr_string = mono_string_new (domain, addr); - mono_array_setref (*h_addr_list, addr_index, addr_string); - addr_index++; - } - } - } - - leave: - g_free (local_in); - g_free (local_in6); - if (info) - mono_free_address_info (info); - return is_ok (error);; - } - - g_free (local_in); - g_free (local_in6); - } - - for (count = 0, ai = info->entries; ai != NULL; ai = ai->next) { - if (ai->family != AF_INET && ai->family != AF_INET6) - continue; - count++; - } - - *h_addr_list = mono_array_new_checked (domain, mono_get_string_class (), count, error); - if (!is_ok (error)) - goto leave2; - - for (ai = info->entries, i = 0; ai != NULL; ai = ai->next) { - MonoAddress maddr; - MonoString *addr_string; - char buffer [INET6_ADDRSTRLEN]; /* Max. size for IPv6 */ - - if ((ai->family != PF_INET) && (ai->family != PF_INET6)) - continue; - - mono_address_init (&maddr, ai->family, &ai->address); - if (mono_networking_addr_to_str (&maddr, buffer, sizeof (buffer))) - addr_string = mono_string_new (domain, buffer); - else - addr_string = mono_string_new (domain, ""); - - mono_array_setref (*h_addr_list, addr_index, addr_string); - - if (!i) { - i++; - if (ai->canonical_name != NULL) { - *h_name = mono_string_new (domain, ai->canonical_name); - } else { - *h_name = mono_string_new (domain, buffer); - } - } - - addr_index++; - } - -leave2: - if (info) - mono_free_address_info (info); - - return is_ok (error); -} - -MonoBoolean -ves_icall_System_Net_Dns_GetHostByName_internal (MonoString *host, MonoString **h_name, MonoArray **h_aliases, MonoArray **h_addr_list, gint32 hint) -{ - MonoError error; - gboolean add_local_ips = FALSE, add_info_ok = TRUE; - gchar this_hostname [256]; - MonoAddressInfo *info = NULL; - - char *hostname = mono_string_to_utf8_checked (host, &error); - if (mono_error_set_pending_exception (&error)) - return FALSE; - - if (*hostname == '\0') { - add_local_ips = TRUE; - *h_name = host; - } - - if (!add_local_ips && gethostname (this_hostname, sizeof (this_hostname)) != -1) { - if (!strcmp (hostname, this_hostname)) { - add_local_ips = TRUE; - *h_name = host; - } - } - -#ifdef HOST_WIN32 - // Win32 APIs already returns local interface addresses for empty hostname ("") - // so we never want to add them manually. - add_local_ips = FALSE; - if (mono_get_address_info(hostname, 0, MONO_HINT_CANONICAL_NAME | hint, &info)) - add_info_ok = FALSE; -#else - if (*hostname && mono_get_address_info (hostname, 0, MONO_HINT_CANONICAL_NAME | hint, &info)) - add_info_ok = FALSE; -#endif - - g_free(hostname); - - if (add_info_ok) { - MonoBoolean result = addrinfo_to_IPHostEntry (info, h_name, h_aliases, h_addr_list, add_local_ips, &error); - mono_error_set_pending_exception (&error); - return result; - } - return FALSE; -} - -MonoBoolean -ves_icall_System_Net_Dns_GetHostByAddr_internal (MonoString *addr, MonoString **h_name, MonoArray **h_aliases, MonoArray **h_addr_list, gint32 hint) -{ - char *address; - struct sockaddr_in saddr; - struct sockaddr_in6 saddr6; - MonoAddressInfo *info = NULL; - MonoError error; - gint32 family; - gchar hostname [NI_MAXHOST] = { 0 }; - gboolean ret; - - address = mono_string_to_utf8_checked (addr, &error); - if (mono_error_set_pending_exception (&error)) - return FALSE; - - if (inet_pton (AF_INET, address, &saddr.sin_addr ) == 1) { - family = AF_INET; - saddr.sin_family = AF_INET; - } else if (inet_pton (AF_INET6, address, &saddr6.sin6_addr) == 1) { - family = AF_INET6; - saddr6.sin6_family = AF_INET6; - } else { - g_free (address); - return FALSE; - } - - g_free (address); - - MONO_ENTER_GC_SAFE; - - switch (family) { - case AF_INET: { -#if HAVE_SOCKADDR_IN_SIN_LEN - saddr.sin_len = sizeof (saddr); -#endif - ret = getnameinfo ((struct sockaddr*)&saddr, sizeof (saddr), hostname, sizeof (hostname), NULL, 0, 0) == 0; - break; - } - case AF_INET6: { -#if HAVE_SOCKADDR_IN6_SIN_LEN - saddr6.sin6_len = sizeof (saddr6); -#endif - ret = getnameinfo ((struct sockaddr*)&saddr6, sizeof (saddr6), hostname, sizeof (hostname), NULL, 0, 0) == 0; - break; - } - default: - g_assert_not_reached (); - } - - MONO_EXIT_GC_SAFE; - - if (!ret) - return FALSE; - - if (mono_get_address_info (hostname, 0, hint | MONO_HINT_CANONICAL_NAME | MONO_HINT_CONFIGURED_ONLY, &info) != 0) - return FALSE; - - MonoBoolean result = addrinfo_to_IPHostEntry (info, h_name, h_aliases, h_addr_list, FALSE, &error); - mono_error_set_pending_exception (&error); - return result; -} - -MonoBoolean -ves_icall_System_Net_Dns_GetHostName_internal (MonoString **h_name) -{ - gchar hostname [NI_MAXHOST] = { 0 }; - int ret; - - ret = gethostname (hostname, sizeof (hostname)); - if (ret == -1) - return FALSE; - - *h_name = mono_string_new (mono_domain_get (), hostname); - - return TRUE; -} - -#if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT | HAVE_UWP_WINAPI_SUPPORT) -gboolean -ves_icall_System_Net_Sockets_Socket_SendFile_internal (SOCKET sock, MonoString *filename, MonoArray *pre_buffer, MonoArray *post_buffer, gint flags, gint32 *werror, gboolean blocking) -{ - HANDLE file; - gboolean ret; - gboolean interrupted; - TRANSMIT_FILE_BUFFERS buffers; - - if (filename == NULL) - return FALSE; - - /* FIXME: replace file by a proper fd that we can call open and close on, as they are interruptible */ - - file = ves_icall_System_IO_MonoIO_Open (filename, FileMode_Open, FileAccess_Read, FileShare_Read, 0, werror); - - if (file == INVALID_HANDLE_VALUE) { - SetLastError (*werror); - return FALSE; - } - - memset (&buffers, 0, sizeof (buffers)); - if (pre_buffer != NULL) { - buffers.Head = mono_array_addr (pre_buffer, guchar, 0); - buffers.HeadLength = mono_array_length (pre_buffer); - } - if (post_buffer != NULL) { - buffers.Tail = mono_array_addr (post_buffer, guchar, 0); - buffers.TailLength = mono_array_length (post_buffer); - } - - mono_thread_info_install_interrupt (abort_syscall, (gpointer) (gsize) mono_native_thread_id_get (), &interrupted); - if (interrupted) { - CloseHandle (file); - SetLastError (WSAEINTR); - return FALSE; - } - - MONO_ENTER_GC_SAFE; - -#ifdef HOST_WIN32 - ret = alertable_TransmitFile (sock, file, 0, 0, NULL, &buffers, flags, blocking); -#else - ret = TransmitFile (sock, file, 0, 0, NULL, &buffers, flags); -#endif - - MONO_EXIT_GC_SAFE; - - if (!ret) - *werror = WSAGetLastError (); - - mono_thread_info_uninstall_interrupt (&interrupted); - if (interrupted) { - CloseHandle (file); - *werror = WSAEINTR; - return FALSE; - } - - MONO_ENTER_GC_SAFE; - - CloseHandle (file); - - MONO_EXIT_GC_SAFE; - - if (*werror) - return FALSE; - - return ret; -} -#endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT | HAVE_UWP_WINAPI_SUPPORT) */ - -gboolean -ves_icall_System_Net_Sockets_Socket_SupportPortReuse (MonoProtocolType proto) -{ -#if defined (SO_REUSEPORT) || defined (HOST_WIN32) - 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 -} - -void -mono_network_init (void) -{ - mono_networking_init (); -} - -void -mono_network_cleanup (void) -{ - _wapi_cleanup_networking (); - mono_networking_shutdown (); -} - -void -icall_cancel_blocking_socket_operation (MonoThread *thread) -{ - MonoInternalThread *internal; - - internal = thread->internal_thread; - g_assert (internal); - - mono_thread_info_abort_socket_syscall_for_close (MONO_UINT_TO_NATIVE_THREAD_ID (internal->tid)); -} - -#endif /* #ifndef DISABLE_SOCKETS */ diff --git a/mono/metadata/socket-io.h b/mono/metadata/socket-io.h deleted file mode 100644 index 7a8c9361ad6..00000000000 --- a/mono/metadata/socket-io.h +++ /dev/null @@ -1,197 +0,0 @@ -/* - * socket-io.h: Socket IO internal calls - * - * Author: - * Dick Porter (dick@ximian.com) - * - * (C) 2001 Ximian, Inc. - */ - -#ifndef _MONO_METADATA_SOCKET_H_ -#define _MONO_METADATA_SOCKET_H_ - -#include -#include - -#include -#include - -/* This is a copy of System.Net.Sockets.SocketType */ -typedef enum { - SocketType_Stream=1, - SocketType_Dgram=2, - SocketType_Raw=3, - SocketType_Rdm=4, - SocketType_Seqpacket=5, - SocketType_Unknown=-1 -} MonoSocketType; - -/* This is a copy of System.Net.Sockets.AddressFamily */ -typedef enum { - AddressFamily_Unknown=-1, - AddressFamily_Unspecified=0, - AddressFamily_Unix=1, - AddressFamily_InterNetwork=2, - AddressFamily_ImpLink=3, - AddressFamily_Pup=4, - AddressFamily_Chaos=5, - AddressFamily_NS=6, - AddressFamily_Ipx=6, - AddressFamily_Iso=7, - AddressFamily_Osi=7, - AddressFamily_Ecma=8, - AddressFamily_DataKit=9, - AddressFamily_Ccitt=10, - AddressFamily_Sna=11, - AddressFamily_DecNet=12, - AddressFamily_DataLink=13, - AddressFamily_Lat=14, - AddressFamily_HyperChannel=15, - AddressFamily_AppleTalk=16, - AddressFamily_NetBios=17, - AddressFamily_VoiceView=18, - AddressFamily_FireFox=19, - AddressFamily_Banyan=21, - AddressFamily_Atm=22, - AddressFamily_InterNetworkV6=23, - AddressFamily_Cluster=24, - AddressFamily_Ieee12844=25, - AddressFamily_Irda=26, - AddressFamily_NetworkDesigners=28 -} MonoAddressFamily; - -/* This is a copy of System.Net.Sockets.ProtocolType */ -typedef enum { - ProtocolType_IP=0, - ProtocolType_Icmp=1, - ProtocolType_Igmp=2, - ProtocolType_Ggp=3, - ProtocolType_Tcp=6, - ProtocolType_Pup=12, - ProtocolType_Udp=17, - ProtocolType_Idp=22, - ProtocolType_IPv6=41, - ProtocolType_ND=77, - ProtocolType_Raw=255, - ProtocolType_Unspecified=0, - ProtocolType_Ipx=1000, - ProtocolType_Spx=1256, - ProtocolType_SpxII=1257, - ProtocolType_Unknown=-1 -} MonoProtocolType; - -/* This is a copy of System.Net.Sockets.SocketOptionLevel */ -typedef enum { - SocketOptionLevel_Socket=65535, - SocketOptionLevel_IP=0, - SocketOptionLevel_IPv6=41, - SocketOptionLevel_Tcp=6, - SocketOptionLevel_Udp=17 -} MonoSocketOptionLevel; - -/* This is a copy of System.Net.Sockets.SocketOptionName */ -typedef enum { - SocketOptionName_Debug=1, - SocketOptionName_AcceptConnection=2, - SocketOptionName_ReuseAddress=4, - SocketOptionName_KeepAlive=8, - SocketOptionName_DontRoute=16, - SocketOptionName_IPProtectionLevel = 23, - SocketOptionName_IPv6Only = 27, - SocketOptionName_Broadcast=32, - SocketOptionName_UseLoopback=64, - SocketOptionName_Linger=128, - SocketOptionName_OutOfBandInline=256, - SocketOptionName_DontLinger= -129, - SocketOptionName_ExclusiveAddressUse= -5, - SocketOptionName_SendBuffer= 4097, - SocketOptionName_ReceiveBuffer=4098, - SocketOptionName_SendLowWater=4099, - SocketOptionName_ReceiveLowWater=4100, - SocketOptionName_SendTimeout=4101, - SocketOptionName_ReceiveTimeout=4102, - SocketOptionName_Error=4103, - SocketOptionName_Type=4104, - SocketOptionName_MaxConnections=2147483647, - SocketOptionName_IPOptions=1, - SocketOptionName_HeaderIncluded=2, - SocketOptionName_TypeOfService=3, - SocketOptionName_IpTimeToLive=4, - SocketOptionName_MulticastInterface=9, - SocketOptionName_MulticastTimeToLive=10, - SocketOptionName_MulticastLoopback=11, - SocketOptionName_AddMembership=12, - SocketOptionName_DropMembership=13, - SocketOptionName_DontFragment=14, - SocketOptionName_AddSourceMembership=15, - SocketOptionName_DropSourceMembership=16, - SocketOptionName_BlockSource=17, - SocketOptionName_UnblockSource=18, - SocketOptionName_PacketInformation=19, - SocketOptionName_NoDelay=1, - SocketOptionName_BsdUrgent=2, - SocketOptionName_Expedited=2, - SocketOptionName_NoChecksum=1, - SocketOptionName_ChecksumCoverage=20, - SocketOptionName_HopLimit=21, - - /* This is Mono-specific, keep it in sync with - * Mono.Posix/PeerCred.cs - */ - SocketOptionName_PeerCred=10001 -} MonoSocketOptionName; - -/* This is a copy of System.Net.Sockets.SocketFlags */ -typedef enum { - SocketFlags_None = 0x0000, - SocketFlags_OutOfBand = 0x0001, - SocketFlags_MaxIOVectorLength = 0x0010, - SocketFlags_Peek = 0x0002, - SocketFlags_DontRoute = 0x0004, - SocketFlags_Partial = 0x8000 -} MonoSocketFlags; - -typedef struct -{ - MonoObject obj; - gint pid; - gint uid; - gint gid; -} MonoPeerCredData; - -extern gpointer ves_icall_System_Net_Sockets_Socket_Socket_internal(MonoObject *this_obj, gint32 family, gint32 type, gint32 proto, gint32 *error); -extern void ves_icall_System_Net_Sockets_Socket_Close_internal(SOCKET sock, gint32 *error); -extern gint32 ves_icall_System_Net_Sockets_SocketException_WSAGetLastError_internal(void); -extern gint32 ves_icall_System_Net_Sockets_Socket_Available_internal(SOCKET sock, gint32 *error); -extern void ves_icall_System_Net_Sockets_Socket_Blocking_internal(SOCKET sock, gboolean block, gint32 *error); -extern gpointer ves_icall_System_Net_Sockets_Socket_Accept_internal(SOCKET sock, gint32 *error, gboolean blocking); -extern void ves_icall_System_Net_Sockets_Socket_Listen_internal(SOCKET sock, guint32 backlog, gint32 *error); -extern MonoObject *ves_icall_System_Net_Sockets_Socket_LocalEndPoint_internal(SOCKET sock, gint32 af, gint32 *error); -extern MonoObject *ves_icall_System_Net_Sockets_Socket_RemoteEndPoint_internal(SOCKET sock, gint32 af, gint32 *error); -extern void ves_icall_System_Net_Sockets_Socket_Bind_internal(SOCKET sock, MonoObject *sockaddr, gint32 *error); -extern void ves_icall_System_Net_Sockets_Socket_Connect_internal(SOCKET sock, MonoObject *sockaddr, gint32 *error, gboolean blocking); -extern gint32 ves_icall_System_Net_Sockets_Socket_Receive_internal(SOCKET sock, MonoArray *buffer, gint32 offset, gint32 count, gint32 flags, gint32 *error, gboolean blocking); -extern gint32 ves_icall_System_Net_Sockets_Socket_Receive_array_internal(SOCKET sock, MonoArray *buffers, gint32 flags, gint32 *error, gboolean blocking); -extern gint32 ves_icall_System_Net_Sockets_Socket_ReceiveFrom_internal(SOCKET sock, MonoArray *buffer, gint32 offset, gint32 count, gint32 flags, MonoObject **sockaddr, gint32 *error, gboolean blocking); -extern gint32 ves_icall_System_Net_Sockets_Socket_Send_internal(SOCKET sock, MonoArray *buffer, gint32 offset, gint32 count, gint32 flags, gint32 *error, gboolean blocking); -extern gint32 ves_icall_System_Net_Sockets_Socket_Send_array_internal(SOCKET sock, MonoArray *buffers, gint32 flags, gint32 *error, gboolean blocking); -extern gint32 ves_icall_System_Net_Sockets_Socket_SendTo_internal(SOCKET sock, MonoArray *buffer, gint32 offset, gint32 count, gint32 flags, MonoObject *sockaddr, gint32 *error, gboolean blocking); -extern void ves_icall_System_Net_Sockets_Socket_Select_internal(MonoArray **sockets, gint32 timeout, gint32 *error); -extern void ves_icall_System_Net_Sockets_Socket_Shutdown_internal(SOCKET sock, gint32 how, gint32 *error); -extern void ves_icall_System_Net_Sockets_Socket_GetSocketOption_obj_internal(SOCKET sock, gint32 level, gint32 name, MonoObject **obj_val, gint32 *error); -extern void ves_icall_System_Net_Sockets_Socket_GetSocketOption_arr_internal(SOCKET sock, gint32 level, gint32 name, MonoArray **byte_val, gint32 *error); -extern void ves_icall_System_Net_Sockets_Socket_SetSocketOption_internal(SOCKET sock, gint32 level, gint32 name, MonoObject *obj_val, MonoArray *byte_val, gint32 int_val, gint32 *error); -extern int ves_icall_System_Net_Sockets_Socket_IOControl_internal (SOCKET sock, gint32 code, MonoArray *input, MonoArray *output, gint32 *error); -extern MonoBoolean ves_icall_System_Net_Dns_GetHostByName_internal(MonoString *host, MonoString **h_name, MonoArray **h_aliases, MonoArray **h_addr_list, gint32 hint); -extern MonoBoolean ves_icall_System_Net_Dns_GetHostByAddr_internal(MonoString *addr, MonoString **h_name, MonoArray **h_aliases, MonoArray **h_addr_list, gint32 hint); -extern MonoBoolean ves_icall_System_Net_Dns_GetHostName_internal(MonoString **h_name); -extern MonoBoolean ves_icall_System_Net_Sockets_Socket_Poll_internal (SOCKET sock, gint mode, gint timeout, gint32 *error); -extern void ves_icall_System_Net_Sockets_Socket_Disconnect_internal(SOCKET sock, MonoBoolean reuse, gint32 *error); -extern gboolean ves_icall_System_Net_Sockets_Socket_SendFile_internal (SOCKET sock, MonoString *filename, MonoArray *pre_buffer, MonoArray *post_buffer, gint flags, gint32 *error, gboolean blocking); -void icall_cancel_blocking_socket_operation (MonoThread *thread); -extern gboolean ves_icall_System_Net_Sockets_Socket_SupportPortReuse (MonoProtocolType proto); - -extern void mono_network_init(void); -extern void mono_network_cleanup(void); - -#endif /* _MONO_METADATA_SOCKET_H_ */ diff --git a/mono/metadata/threadpool-io.c b/mono/metadata/threadpool-io.c index 00b7e466933..4b59150832a 100644 --- a/mono/metadata/threadpool-io.c +++ b/mono/metadata/threadpool-io.c @@ -29,6 +29,7 @@ #include #include #include +#include typedef struct { gboolean (*init) (gint wakeup_pipe_fd); diff --git a/mono/metadata/threadpool-io.h b/mono/metadata/threadpool-io.h index 0936ee016cd..c7de70f2715 100644 --- a/mono/metadata/threadpool-io.h +++ b/mono/metadata/threadpool-io.h @@ -6,7 +6,6 @@ #include #include -#include typedef struct _MonoIOSelectorJob MonoIOSelectorJob; diff --git a/mono/metadata/threadpool-worker-default.c b/mono/metadata/threadpool-worker-default.c index fa8f850ea66..f78ccf68b2a 100644 --- a/mono/metadata/threadpool-worker-default.c +++ b/mono/metadata/threadpool-worker-default.c @@ -33,6 +33,7 @@ #include #include #include +#include #define CPU_USAGE_LOW 80 #define CPU_USAGE_HIGH 95 diff --git a/mono/metadata/threadpool.c b/mono/metadata/threadpool.c index 84165e226b1..21738485349 100644 --- a/mono/metadata/threadpool.c +++ b/mono/metadata/threadpool.c @@ -45,6 +45,7 @@ #include #include #include +#include typedef struct { MonoDomain *domain; diff --git a/mono/metadata/w32socket-internals.h b/mono/metadata/w32socket-internals.h new file mode 100644 index 00000000000..dd4c61129d0 --- /dev/null +++ b/mono/metadata/w32socket-internals.h @@ -0,0 +1,151 @@ +/* +* w32socket-internals.h +* +* Copyright 2016 Microsoft +* Licensed under the MIT license. See LICENSE file in the project root for full license information. +*/ +#ifndef __MONO_METADATA_W32SOCKET_INTERNALS_H__ +#define __MONO_METADATA_W32SOCKET_INTERNALS_H__ + +#include +#include + +#ifdef HAVE_SYS_TIME_H +#include +#endif +#ifdef HAVE_SYS_SOCKET_H +#include +#endif + +#include + +#ifndef HAVE_SOCKLEN_T +#define socklen_t int +#endif + +#ifndef HOST_WIN32 + +#define TF_DISCONNECT 0x01 +#define TF_REUSE_SOCKET 0x02 + +typedef struct { + guint32 len; + gpointer buf; +} WSABUF; + +typedef struct { + guint32 Internal; + guint32 InternalHigh; + guint32 Offset; + guint32 OffsetHigh; + gpointer hEvent; + gpointer handle1; + gpointer handle2; +} OVERLAPPED; + +typedef struct { + gpointer Head; + guint32 HeadLength; + gpointer Tail; + guint32 TailLength; +} TRANSMIT_FILE_BUFFERS; + +typedef struct { + guint32 Data1; + guint16 Data2; + guint16 Data3; + guint8 Data4[8]; +} GUID; + +typedef BOOL (WINAPI *LPFN_DISCONNECTEX)(SOCKET, OVERLAPPED*, guint32, guint32); +typedef BOOL (WINAPI *LPFN_TRANSMITFILE)(SOCKET, HANDLE, guint32, guint32, OVERLAPPED*, TRANSMIT_FILE_BUFFERS*, guint32); + +#endif + +void +mono_w32socket_initialize (void); + +void +mono_w32socket_cleanup (void); + +SOCKET +mono_w32socket_accept (SOCKET s, struct sockaddr *addr, socklen_t *addrlen, gboolean blocking); + +int +mono_w32socket_connect (SOCKET s, const struct sockaddr *name, int namelen, gboolean blocking); + +int +mono_w32socket_recv (SOCKET s, char *buf, int len, int flags, gboolean blocking); + +int +mono_w32socket_recvfrom (SOCKET s, char *buf, int len, int flags, struct sockaddr *from, socklen_t *fromlen, gboolean blocking); + +int +mono_w32socket_recvbuffers (SOCKET s, WSABUF *lpBuffers, guint32 dwBufferCount, guint32 *lpNumberOfBytesRecvd, guint32 *lpFlags, gpointer lpOverlapped, gpointer lpCompletionRoutine, gboolean blocking); + +int +mono_w32socket_send (SOCKET s, char *buf, int len, int flags, gboolean blocking); + +int +mono_w32socket_sendto (SOCKET s, const char *buf, int len, int flags, const struct sockaddr *to, int tolen, gboolean blocking); + +int +mono_w32socket_sendbuffers (SOCKET s, WSABUF *lpBuffers, guint32 dwBufferCount, guint32 *lpNumberOfBytesRecvd, guint32 lpFlags, gpointer lpOverlapped, gpointer lpCompletionRoutine, gboolean blocking); + +#if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT | HAVE_UWP_WINAPI_SUPPORT) + +BOOL +mono_w32socket_transmit_file (SOCKET hSocket, gpointer hFile, TRANSMIT_FILE_BUFFERS *lpTransmitBuffers, guint32 dwReserved, gboolean blocking); + +#endif + +#ifndef HOST_WIN32 + +SOCKET +mono_w32socket_socket (int domain, int type, int protocol); + +gint +mono_w32socket_bind (SOCKET sock, struct sockaddr *addr, socklen_t addrlen); + +gint +mono_w32socket_getpeername (SOCKET sock, struct sockaddr *name, socklen_t *namelen); + +gint +mono_w32socket_getsockname (SOCKET sock, struct sockaddr *name, socklen_t *namelen); + +gint +mono_w32socket_getsockopt (SOCKET sock, gint level, gint optname, gpointer optval, socklen_t *optlen); + +gint +mono_w32socket_setsockopt (SOCKET sock, gint level, gint optname, const gpointer optval, socklen_t optlen); + +gint +mono_w32socket_listen (SOCKET sock, gint backlog); + +gint +mono_w32socket_shutdown (SOCKET sock, gint how); + +gint +mono_w32socket_ioctl (SOCKET sock, gint32 command, gchar *input, gint inputlen, gchar *output, gint outputlen, glong *written); + +#endif /* HOST_WIN32 */ + +gint +mono_w32socket_disconnect (SOCKET sock, gboolean reuse); + +gint +mono_w32socket_set_blocking (SOCKET socket, gboolean blocking); + +gint +mono_w32socket_get_available (SOCKET socket, guint64 *amount); + +void +mono_w32socket_set_last_error (gint32 error); + +gint32 +mono_w32socket_get_last_error (void); + +gint32 +mono_w32socket_convert_error (gint error); + +#endif // __MONO_METADATA_W32SOCKET_INTERNALS_H__ diff --git a/mono/metadata/w32socket-unix.c b/mono/metadata/w32socket-unix.c new file mode 100644 index 00000000000..4b3d863e746 --- /dev/null +++ b/mono/metadata/w32socket-unix.c @@ -0,0 +1,1331 @@ +/* + * w32socket-unix.c: 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 +#ifdef HAVE_SYS_IOCTL_H +#include +#endif +#include +#include +#ifdef HAVE_NETDB_H +#include +#endif +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#include +#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 "w32socket.h" +#include "w32socket-internals.h" +#include "w32handle.h" +#include "utils/mono-logger-internals.h" +#include "utils/mono-poll.h" + +typedef struct { + int domain; + int type; + int protocol; + int saved_error; + int still_readable; +} MonoW32HandleSocket; + +static guint32 in_cleanup = 0; + +static void +socket_close (gpointer handle, gpointer data) +{ + int ret; + MonoW32HandleSocket *socket_handle = (MonoW32HandleSocket *)data; + MonoThreadInfo *info = mono_thread_info_current (); + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: closing socket handle %p", __func__, handle); + + /* Shutdown the socket for reading, to interrupt any potential + * receives that may be blocking for data. See bug 75705. */ + shutdown (GPOINTER_TO_UINT (handle), SHUT_RD); + + do { + ret = close (GPOINTER_TO_UINT(handle)); + } 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: close error: %s", __func__, g_strerror (errno)); + if (!in_cleanup) + mono_w32socket_set_last_error (mono_w32socket_convert_error (errnum)); + } + + if (!in_cleanup) + socket_handle->saved_error = 0; +} + +static void +socket_details (gpointer data) +{ + /* FIXME: do something */ +} + +static const gchar* +socket_typename (void) +{ + return "Socket"; +} + +static gsize +socket_typesize (void) +{ + return sizeof (MonoW32HandleSocket); +} + +static MonoW32HandleOps ops = { + socket_close, /* close */ + NULL, /* signal */ + NULL, /* own */ + NULL, /* is_owned */ + NULL, /* special_wait */ + NULL, /* prewait */ + socket_details, /* details */ + socket_typename, /* typename */ + socket_typesize, /* typesize */ +}; + +void +mono_w32socket_initialize (void) +{ + mono_w32handle_register_ops (MONO_W32HANDLE_SOCKET, &ops); +} + +static gboolean +cleanup_close (gpointer handle, gpointer data, gpointer user_data) +{ + if (mono_w32handle_get_type (handle) == MONO_W32HANDLE_SOCKET) + mono_w32handle_force_close (handle, data); + + return FALSE; +} + +void +mono_w32socket_cleanup (void) +{ + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: cleaning up", __func__); + + in_cleanup = 1; + mono_w32handle_foreach (cleanup_close, NULL); + in_cleanup = 0; +} + +SOCKET +mono_w32socket_accept (SOCKET sock, struct sockaddr *addr, socklen_t *addrlen, gboolean blocking) +{ + gpointer handle; + gpointer new_handle; + MonoW32HandleSocket *socket_handle; + MonoW32HandleSocket new_socket_handle; + SOCKET new_fd; + MonoThreadInfo *info; + + if (addr != NULL && *addrlen < sizeof(struct sockaddr)) { + mono_w32socket_set_last_error (WSAEFAULT); + return INVALID_SOCKET; + } + + handle = GUINT_TO_POINTER (sock); + if (!mono_w32handle_lookup (handle, MONO_W32HANDLE_SOCKET, (gpointer *)&socket_handle)) { + mono_w32socket_set_last_error (WSAENOTSOCK); + return INVALID_SOCKET; + } + + info = mono_thread_info_current (); + + do { + new_fd = accept (sock, addr, addrlen); + } while (new_fd == -1 && errno == EINTR && !mono_thread_info_is_interrupt_state (info)); + + if (new_fd == -1) { + gint errnum = errno; + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: accept error: %s", __func__, g_strerror(errno)); + mono_w32socket_set_last_error (mono_w32socket_convert_error (errnum)); + return INVALID_SOCKET; + } + + if (new_fd >= mono_w32handle_fd_reserve) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: File descriptor is too big", __func__); + + mono_w32socket_set_last_error (WSASYSCALLFAILURE); + + close (new_fd); + + return INVALID_SOCKET; + } + + new_socket_handle.domain = socket_handle->domain; + new_socket_handle.type = socket_handle->type; + new_socket_handle.protocol = socket_handle->protocol; + new_socket_handle.still_readable = 1; + + new_handle = mono_w32handle_new_fd (MONO_W32HANDLE_SOCKET, new_fd, + &new_socket_handle); + if(new_handle == INVALID_HANDLE_VALUE) { + g_warning ("%s: error creating socket handle", __func__); + mono_w32socket_set_last_error (ERROR_GEN_FAILURE); + return INVALID_SOCKET; + } + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: returning newly accepted socket handle %p with", + __func__, new_handle); + + return new_fd; +} + +int +mono_w32socket_connect (SOCKET sock, const struct sockaddr *addr, int addrlen, gboolean blocking) +{ + gpointer handle; + MonoW32HandleSocket *socket_handle; + + handle = GUINT_TO_POINTER (sock); + if (!mono_w32handle_lookup (handle, MONO_W32HANDLE_SOCKET, (gpointer *)&socket_handle)) { + mono_w32socket_set_last_error (WSAENOTSOCK); + return SOCKET_ERROR; + } + + if (connect (sock, addr, addrlen) == -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. */ + socket_handle->saved_error = errnum; + } + return SOCKET_ERROR; + } + + info = mono_thread_info_current (); + + fds.fd = sock; + fds.events = MONO_POLLOUT; + while (mono_poll (&fds, 1, -1) == -1 && !mono_thread_info_is_interrupt_state (info)) { + 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)); + return SOCKET_ERROR; + } + } + + len = sizeof(so_error); + if (getsockopt (sock, SOL_SOCKET, SO_ERROR, &so_error, &len) == -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)); + return SOCKET_ERROR; + } + + if (so_error != 0) { + gint errnum = mono_w32socket_convert_error (so_error); + + /* Need to save this socket error */ + socket_handle->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); + return SOCKET_ERROR; + } + } + + 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) +{ + gpointer handle; + MonoW32HandleSocket *socket_handle; + int ret; + MonoThreadInfo *info; + + handle = GUINT_TO_POINTER (sock); + if (!mono_w32handle_lookup (handle, MONO_W32HANDLE_SOCKET, (gpointer *)&socket_handle)) { + mono_w32socket_set_last_error (WSAENOTSOCK); + return SOCKET_ERROR; + } + + info = mono_thread_info_current (); + + do { + ret = recvfrom (sock, buf, len, flags, from, fromlen); + } 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 (socket_handle->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)); + return SOCKET_ERROR; + } + 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) +{ + MonoW32HandleSocket *socket_handle; + MonoThreadInfo *info; + gpointer handle; + gint ret; + struct msghdr hdr; + + g_assert (overlapped == NULL); + g_assert (complete == NULL); + + handle = GUINT_TO_POINTER (sock); + if (!mono_w32handle_lookup (handle, MONO_W32HANDLE_SOCKET, (gpointer *)&socket_handle)) { + mono_w32socket_set_last_error (WSAENOTSOCK); + return SOCKET_ERROR; + } + + info = mono_thread_info_current (); + + wsabuf_to_msghdr (buffers, count, &hdr); + + do { + ret = recvmsg (sock, &hdr, *flags); + } while (ret == -1 && errno == EINTR && !mono_thread_info_is_interrupt_state (info)); + + msghdr_iov_free (&hdr); + + if (ret == 0) { + /* see mono_w32socket_recvfrom */ + if (socket_handle->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)); + return SOCKET_ERROR; + } + + *received = ret; + *flags = hdr.msg_flags; + + return 0; +} + +int +mono_w32socket_send (SOCKET sock, char *buf, int len, int flags, gboolean blocking) +{ + gpointer handle; + int ret; + MonoThreadInfo *info; + + handle = GUINT_TO_POINTER (sock); + if (mono_w32handle_get_type (handle) != MONO_W32HANDLE_SOCKET) { + mono_w32socket_set_last_error (WSAENOTSOCK); + return SOCKET_ERROR; + } + + info = mono_thread_info_current (); + + do { + ret = send (sock, buf, len, flags); + } 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) { + ret = fcntl (sock, F_GETFL, 0); + if (ret != -1 && (ret & O_NONBLOCK) == 0) + errnum = ETIMEDOUT; + } +#endif /* O_NONBLOCK */ + mono_w32socket_set_last_error (mono_w32socket_convert_error (errnum)); + return SOCKET_ERROR; + } + return ret; +} + +int +mono_w32socket_sendto (SOCKET sock, const char *buf, int len, int flags, const struct sockaddr *to, int tolen, gboolean blocking) +{ + gpointer handle; + int ret; + MonoThreadInfo *info; + + handle = GUINT_TO_POINTER (sock); + if (mono_w32handle_get_type (handle) != MONO_W32HANDLE_SOCKET) { + mono_w32socket_set_last_error (WSAENOTSOCK); + return SOCKET_ERROR; + } + + info = mono_thread_info_current (); + + do { + ret = sendto (sock, buf, len, flags, to, tolen); + } 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)); + return SOCKET_ERROR; + } + 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; + gpointer handle; + gint ret; + + g_assert (overlapped == NULL); + g_assert (complete == NULL); + + handle = GUINT_TO_POINTER (sock); + if (mono_w32handle_get_type (handle) != MONO_W32HANDLE_SOCKET) { + mono_w32socket_set_last_error (WSAENOTSOCK); + return SOCKET_ERROR; + } + + info = mono_thread_info_current (); + + wsabuf_to_msghdr (buffers, count, &hdr); + + do { + ret = sendmsg (sock, &hdr, flags); + } 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)); + return SOCKET_ERROR; + } + + *sent = ret; + 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; + gpointer handle; + gint file; + gssize ret; +#if defined(HAVE_SENDFILE) && (defined(__linux__) || defined(DARWIN)) + struct stat statbuf; +#else + gchar *buffer; +#endif + + handle = GUINT_TO_POINTER (sock); + if (mono_w32handle_get_type (handle) != MONO_W32HANDLE_SOCKET) { + mono_w32socket_set_last_error (WSAENOTSOCK); + return FALSE; + } + + /* Write the header */ + if (buffers != NULL && buffers->Head != NULL && buffers->HeadLength > 0) { + ret = mono_w32socket_send (sock, buffers->Head, buffers->HeadLength, 0, FALSE); + if (ret == SOCKET_ERROR) + return FALSE; + } + + info = mono_thread_info_current (); + + file = GPOINTER_TO_INT (file_handle); + +#if defined(HAVE_SENDFILE) && (defined(__linux__) || defined(DARWIN)) + ret = fstat (file, &statbuf); + if (ret == -1) { + gint errnum = errno; + mono_w32socket_set_last_error (mono_w32socket_convert_error (errnum)); + return SOCKET_ERROR; + } + + do { +#ifdef __linux__ + ret = sendfile (sock, 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, sock, 0, &statbuf.st_size, NULL, 0); +#endif + } while (ret != -1 && errno == EINTR && !mono_thread_info_is_interrupt_state (info)); +#else + buffer = g_malloc (SF_BUFFER_SIZE); + + do { + do { + ret = read (file, buffer, SF_BUFFER_SIZE); + } while (ret == -1 && errno == EINTR && !mono_thread_info_is_interrupt_state (info)); + + if (ret == -1 || ret == 0) + break; + + do { + ret = send (sock, buffer, ret, 0); /* short sends? enclose this in a loop? */ + } 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)); + return FALSE; + } + + /* Write the tail */ + if (buffers != NULL && buffers->Tail != NULL && buffers->TailLength > 0) { + ret = mono_w32socket_send (sock, buffers->Tail, buffers->TailLength, 0, FALSE); + if (ret == SOCKET_ERROR) + return FALSE; + } + + if ((flags & TF_DISCONNECT) == TF_DISCONNECT) + CloseHandle (handle); + + return TRUE; +} + +SOCKET +mono_w32socket_socket (int domain, int type, int protocol) +{ + MonoW32HandleSocket socket_handle = {0}; + gpointer handle; + SOCKET sock; + + socket_handle.domain = domain; + socket_handle.type = type; + socket_handle.protocol = protocol; + socket_handle.still_readable = 1; + + sock = socket (domain, type, protocol); + if (sock == -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; + sock = socket (AF_INET, SOCK_RAW, 4); + } + + if (sock == -1) { + 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; + } + + if (sock >= mono_w32handle_fd_reserve) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: File descriptor is too big (%d >= %d)", + __func__, sock, mono_w32handle_fd_reserve); + + mono_w32socket_set_last_error (WSASYSCALLFAILURE); + close (sock); + + return INVALID_SOCKET; + } + + /* .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; + + ret = setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, &true_, sizeof (true_)); + if (ret == -1) { + close (sock); + 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)); + return INVALID_SOCKET; + } + } + + + handle = mono_w32handle_new_fd (MONO_W32HANDLE_SOCKET, sock, &socket_handle); + if (handle == INVALID_HANDLE_VALUE) { + g_warning ("%s: error creating socket handle", __func__); + mono_w32socket_set_last_error (WSASYSCALLFAILURE); + close (sock); + return INVALID_SOCKET; + } + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: returning socket handle %p", __func__, handle); + + return sock; +} + +gint +mono_w32socket_bind (SOCKET sock, struct sockaddr *addr, socklen_t addrlen) +{ + gpointer handle; + int ret; + + handle = GUINT_TO_POINTER (sock); + if (mono_w32handle_get_type (handle) != MONO_W32HANDLE_SOCKET) { + mono_w32socket_set_last_error (WSAENOTSOCK); + return SOCKET_ERROR; + } + + ret = bind (sock, addr, addrlen); + 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)); + return SOCKET_ERROR; + } + + return 0; +} + +gint +mono_w32socket_getpeername (SOCKET sock, struct sockaddr *name, socklen_t *namelen) +{ + gpointer handle; + gint ret; + + handle = GUINT_TO_POINTER (sock); + if (mono_w32handle_get_type (handle) != MONO_W32HANDLE_SOCKET) { + mono_w32socket_set_last_error (WSAENOTSOCK); + return SOCKET_ERROR; + } + + ret = getpeername (sock, name, namelen); + 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)); + return SOCKET_ERROR; + } + + return 0; +} + +gint +mono_w32socket_getsockname (SOCKET sock, struct sockaddr *name, socklen_t *namelen) +{ + gpointer handle; + gint ret; + + handle = GUINT_TO_POINTER (sock); + if (mono_w32handle_get_type (handle) != MONO_W32HANDLE_SOCKET) { + mono_w32socket_set_last_error (WSAENOTSOCK); + return SOCKET_ERROR; + } + + ret = getsockname (sock, name, namelen); + 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)); + return SOCKET_ERROR; + } + + return 0; +} + +gint +mono_w32socket_getsockopt (SOCKET sock, gint level, gint optname, gpointer optval, socklen_t *optlen) +{ + gpointer handle; + gint ret; + struct timeval tv; + gpointer tmp_val; + MonoW32HandleSocket *socket_handle; + + handle = GUINT_TO_POINTER (sock); + if (!mono_w32handle_lookup (handle, MONO_W32HANDLE_SOCKET, (gpointer *)&socket_handle)) { + mono_w32socket_set_last_error (WSAENOTSOCK); + return SOCKET_ERROR; + } + + tmp_val = optval; + if (level == SOL_SOCKET && + (optname == SO_RCVTIMEO || optname == SO_SNDTIMEO)) { + tmp_val = &tv; + *optlen = sizeof (tv); + } + + ret = getsockopt (sock, level, optname, tmp_val, optlen); + 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)); + 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)); + socket_handle->saved_error = *((int *)optval); + } else { + *((int *)optval) = socket_handle->saved_error; + } + } + + return 0; +} + +gint +mono_w32socket_setsockopt (SOCKET sock, gint level, gint optname, const gpointer optval, socklen_t optlen) +{ + gpointer handle; + 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; + + handle = GUINT_TO_POINTER (sock); + if (mono_w32handle_get_type (handle) != MONO_W32HANDLE_SOCKET) { + mono_w32socket_set_last_error (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 + + ret = setsockopt (sock, level, optname, tmp_val, optlen); + 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)); + 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); + + if (!getsockopt (sock, level, SO_TYPE, &type, &type_len)) { + if (type == SOCK_DGRAM || type == SOCK_STREAM) + setsockopt (sock, level, SO_REUSEPORT, tmp_val, optlen); + } + } +#endif + + return ret; +} + +gint +mono_w32socket_listen (SOCKET sock, gint backlog) +{ + gpointer handle; + gint ret; + + handle = GUINT_TO_POINTER (sock); + if (mono_w32handle_get_type (handle) != MONO_W32HANDLE_SOCKET) { + mono_w32socket_set_last_error (WSAENOTSOCK); + return SOCKET_ERROR; + } + + ret = listen (sock, backlog); + 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)); + return SOCKET_ERROR; + } + + return 0; +} + +gint +mono_w32socket_shutdown (SOCKET sock, gint how) +{ + MonoW32HandleSocket *socket_handle; + gpointer handle; + gint ret; + + handle = GUINT_TO_POINTER (sock); + if (!mono_w32handle_lookup (handle, MONO_W32HANDLE_SOCKET, (gpointer *)&socket_handle)) { + mono_w32socket_set_last_error (WSAENOTSOCK); + return SOCKET_ERROR; + } + + if (how == SHUT_RD || how == SHUT_RDWR) + socket_handle->still_readable = 0; + + ret = shutdown (sock, how); + 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)); + return SOCKET_ERROR; + } + + return ret; +} + +gint +mono_w32socket_disconnect (SOCKET sock, gboolean reuse) +{ + MonoW32HandleSocket *socket_handle; + gpointer handle; + 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 */ + + handle = GUINT_TO_POINTER (sock); + if (!mono_w32handle_lookup (handle, MONO_W32HANDLE_SOCKET, (gpointer *)&socket_handle)) { + mono_w32socket_set_last_error (WSAENOTSOCK); + return SOCKET_ERROR; + } + + newsock = socket (socket_handle->domain, socket_handle->type, socket_handle->protocol); + 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)); + 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 { + ret = dup2 (newsock, sock); + } 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)); + return SOCKET_ERROR; + } + + close (newsock); + + return 0; +} + +static gboolean +extension_disconect (SOCKET sock, OVERLAPPED *overlapped, guint32 flags, guint32 reserved) +{ + return mono_w32socket_disconnect (sock, flags & TF_REUSE_SOCKET) == 0; +} + +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) +{ + return mono_w32socket_transmit_file (sock, file_handle, buffers, flags, FALSE); +} + +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) +{ + gpointer handle; + gint ret; + gchar *buffer; + + handle = GUINT_TO_POINTER (sock); + if (mono_w32handle_get_type (handle) != MONO_W32HANDLE_SOCKET) { + mono_w32socket_set_last_error (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); + return SOCKET_ERROR; + } + + if (outputlen < sizeof(gpointer)) { + /* Or here... */ + mono_w32socket_set_last_error (WSAEINVAL); + return SOCKET_ERROR; + } + + if (output == NULL) { + /* Or here */ + mono_w32socket_set_last_error (WSAEINVAL); + 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); + return 0; + } + } + + mono_w32socket_set_last_error (WSAEINVAL); + return SOCKET_ERROR; + } + + if (command == 0x98000004 /* SIO_KEEPALIVE_VALS */) { + guint32 onoff; + + if (inputlen < 3 * sizeof (guint32)) { + mono_w32socket_set_last_error (WSAEINVAL); + return SOCKET_ERROR; + } + + onoff = *((guint32*) input); + + ret = setsockopt (sock, SOL_SOCKET, SO_KEEPALIVE, &onoff, sizeof (guint32)); + if (ret < 0) { + mono_w32socket_set_last_error (mono_w32socket_convert_error (errno)); + 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++; + ret = setsockopt (sock, IPPROTO_TCP, TCP_KEEPIDLE, &keepalivetime, sizeof (guint32)); + if (ret == 0) { + rem = keepaliveinterval % 1000; + keepaliveinterval /= 1000; + if (keepaliveinterval == 0 || rem >= 500) + keepaliveinterval++; + ret = setsockopt (sock, IPPROTO_TCP, TCP_KEEPINTVL, &keepaliveinterval, sizeof (guint32)); + } + if (ret != 0) { + mono_w32socket_set_last_error (mono_w32socket_convert_error (errno)); + return SOCKET_ERROR; + } + + return 0; + } +#endif + + return 0; + } + + buffer = inputlen > 0 ? (gchar*) g_memdup (input, inputlen) : NULL; + + ret = ioctl (sock, command, buffer); + 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)); + return SOCKET_ERROR; + } + + if (!buffer) { + *written = 0; + 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; + + return 0; +} + +gint +mono_w32socket_set_blocking (SOCKET socket, gboolean blocking) +{ + gint ret; + gpointer handle; + + handle = GINT_TO_POINTER (socket); + if (mono_w32handle_get_type (handle) != MONO_W32HANDLE_SOCKET) { + mono_w32socket_set_last_error (WSAENOTSOCK); + return SOCKET_ERROR; + } + +#ifdef O_NONBLOCK + /* This works better than ioctl(...FIONBIO...) + * on Linux (it causes connect to return + * EINPROGRESS, but the ioctl doesn't seem to) */ + ret = fcntl (socket, F_GETFL, 0); + if (ret != -1) + ret = fcntl (socket, F_SETFL, blocking ? (ret & (~O_NONBLOCK)) : (ret | (O_NONBLOCK))); +#endif /* O_NONBLOCK */ + + if (ret == -1) { + gint errnum = errno; + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: ioctl error: %s", __func__, g_strerror (errno)); + mono_w32socket_set_last_error (mono_w32socket_convert_error (errnum)); + return SOCKET_ERROR; + } + + return 0; +} + +gint +mono_w32socket_get_available (SOCKET socket, guint64 *amount) +{ + gint ret; + gpointer handle; + + handle = GINT_TO_POINTER (socket); + if (mono_w32handle_get_type (handle) != MONO_W32HANDLE_SOCKET) { + mono_w32socket_set_last_error (WSAENOTSOCK); + return SOCKET_ERROR; + } + +#if defined (PLATFORM_MACOSX) + // 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); + ret = getsockopt (socket, SOL_SOCKET, SO_NREAD, (gulong*) amount, &optlen); +#else + ret = ioctl (socket, FIONREAD, (gulong*) amount); +#endif + + if (ret == -1) { + gint errnum = errno; + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: ioctl error: %s", __func__, g_strerror (errno)); + mono_w32socket_set_last_error (mono_w32socket_convert_error (errnum)); + return SOCKET_ERROR; + } + + return 0; +} + +void +mono_w32socket_set_last_error (gint32 error) +{ + SetLastError (error); +} + +gint32 +mono_w32socket_get_last_error (void) +{ + return GetLastError (); +} + +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; + 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) +{ +#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 +} diff --git a/mono/metadata/w32socket-win32.c b/mono/metadata/w32socket-win32.c new file mode 100644 index 00000000000..9a2d4522167 --- /dev/null +++ b/mono/metadata/w32socket-win32.c @@ -0,0 +1,315 @@ +/* + * w32socket-win32.c: Windows 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 +#ifdef HAVE_UNISTD_H +#include +#endif +#include + +#include + +#include "w32socket.h" +#include "w32socket-internals.h" + +#define LOGDEBUG(...) + +void +mono_w32socket_initialize (void) +{ +} + +void +mono_w32socket_cleanup (void) +{ +} + +static gboolean set_blocking (SOCKET sock, gboolean block) +{ + u_long non_block = block ? 0 : 1; + return ioctlsocket (sock, FIONBIO, &non_block) != SOCKET_ERROR; +} + +static DWORD get_socket_timeout (SOCKET sock, int optname) +{ + DWORD timeout = 0; + int optlen = sizeof (DWORD); + if (getsockopt (sock, SOL_SOCKET, optname, (char *)&timeout, &optlen) == SOCKET_ERROR) { + WSASetLastError (0); + return WSA_INFINITE; + } + if (timeout == 0) + timeout = WSA_INFINITE; // 0 means infinite + return timeout; +} + +/* +* Performs an alertable wait for the specified event (FD_ACCEPT_BIT, +* FD_CONNECT_BIT, FD_READ_BIT, FD_WRITE_BIT) on the specified socket. +* Returns TRUE if the event is fired without errors. Calls WSASetLastError() +* with WSAEINTR and returns FALSE if the thread is alerted. If the event is +* fired but with an error WSASetLastError() is called to set the error and the +* function returns FALSE. +*/ +static gboolean alertable_socket_wait (SOCKET sock, int event_bit) +{ + static char *EVENT_NAMES[] = { "FD_READ", "FD_WRITE", NULL /*FD_OOB*/, "FD_ACCEPT", "FD_CONNECT", "FD_CLOSE" }; + gboolean success = FALSE; + int error = -1; + DWORD timeout = WSA_INFINITE; + if (event_bit == FD_READ_BIT || event_bit == FD_WRITE_BIT) { + timeout = get_socket_timeout (sock, event_bit == FD_READ_BIT ? SO_RCVTIMEO : SO_SNDTIMEO); + } + WSASetLastError (0); + WSAEVENT event = WSACreateEvent (); + if (event != WSA_INVALID_EVENT) { + if (WSAEventSelect (sock, event, (1 << event_bit) | FD_CLOSE) != SOCKET_ERROR) { + LOGDEBUG (g_message ("%06d - Calling WSAWaitForMultipleEvents () on socket %d", GetCurrentThreadId (), sock)); + DWORD ret = WSAWaitForMultipleEvents (1, &event, TRUE, timeout, TRUE); + if (ret == WSA_WAIT_IO_COMPLETION) { + LOGDEBUG (g_message ("%06d - WSAWaitForMultipleEvents () returned WSA_WAIT_IO_COMPLETION for socket %d", GetCurrentThreadId (), sock)); + error = WSAEINTR; + } else if (ret == WSA_WAIT_TIMEOUT) { + error = WSAETIMEDOUT; + } else { + g_assert (ret == WSA_WAIT_EVENT_0); + WSANETWORKEVENTS ne = { 0 }; + if (WSAEnumNetworkEvents (sock, event, &ne) != SOCKET_ERROR) { + if (ne.lNetworkEvents & (1 << event_bit) && ne.iErrorCode[event_bit]) { + LOGDEBUG (g_message ("%06d - %s error %d on socket %d", GetCurrentThreadId (), EVENT_NAMES[event_bit], ne.iErrorCode[event_bit], sock)); + error = ne.iErrorCode[event_bit]; + } else if (ne.lNetworkEvents & FD_CLOSE_BIT && ne.iErrorCode[FD_CLOSE_BIT]) { + LOGDEBUG (g_message ("%06d - FD_CLOSE error %d on socket %d", GetCurrentThreadId (), ne.iErrorCode[FD_CLOSE_BIT], sock)); + error = ne.iErrorCode[FD_CLOSE_BIT]; + } else { + LOGDEBUG (g_message ("%06d - WSAEnumNetworkEvents () finished successfully on socket %d", GetCurrentThreadId (), sock)); + success = TRUE; + error = 0; + } + } + } + WSAEventSelect (sock, NULL, 0); + } + WSACloseEvent (event); + } + if (error != -1) { + WSASetLastError (error); + } + return success; +} + +#define ALERTABLE_SOCKET_CALL(event_bit, blocking, repeat, ret, op, sock, ...) \ + LOGDEBUG (g_message ("%06d - Performing %s " #op " () on socket %d", GetCurrentThreadId (), blocking ? "blocking" : "non-blocking", sock)); \ + if (blocking) { \ + if (set_blocking(sock, FALSE)) { \ + while (-1 == (int) (ret = op (sock, __VA_ARGS__))) { \ + int _error = WSAGetLastError ();\ + if (_error != WSAEWOULDBLOCK && _error != WSA_IO_PENDING) \ + break; \ + if (!alertable_socket_wait (sock, event_bit) || !repeat) \ + break; \ + } \ + int _saved_error = WSAGetLastError (); \ + set_blocking (sock, TRUE); \ + WSASetLastError (_saved_error); \ + } \ + } else { \ + ret = op (sock, __VA_ARGS__); \ + } \ + int _saved_error = WSAGetLastError (); \ + LOGDEBUG (g_message ("%06d - Finished %s " #op " () on socket %d (ret = %d, WSAGetLastError() = %d)", GetCurrentThreadId (), \ + blocking ? "blocking" : "non-blocking", sock, ret, _saved_error)); \ + WSASetLastError (_saved_error); + +SOCKET mono_w32socket_accept (SOCKET s, struct sockaddr *addr, socklen_t *addrlen, gboolean blocking) +{ + MonoInternalThread *curthread = mono_thread_internal_current (); + SOCKET newsock = INVALID_SOCKET; + curthread->interrupt_on_stop = (gpointer)TRUE; + ALERTABLE_SOCKET_CALL (FD_ACCEPT_BIT, blocking, TRUE, newsock, accept, s, addr, addrlen); + curthread->interrupt_on_stop = (gpointer)FALSE; + return newsock; +} + +int mono_w32socket_connect (SOCKET s, const struct sockaddr *name, int namelen, gboolean blocking) +{ + int ret = SOCKET_ERROR; + ALERTABLE_SOCKET_CALL (FD_CONNECT_BIT, blocking, FALSE, ret, connect, s, name, namelen); + ret = WSAGetLastError () != 0 ? SOCKET_ERROR : 0; + return ret; +} + +int mono_w32socket_recv (SOCKET s, char *buf, int len, int flags, gboolean blocking) +{ + MonoInternalThread *curthread = mono_thread_internal_current (); + int ret = SOCKET_ERROR; + curthread->interrupt_on_stop = (gpointer)TRUE; + ALERTABLE_SOCKET_CALL (FD_READ_BIT, blocking, TRUE, ret, recv, s, buf, len, flags); + curthread->interrupt_on_stop = (gpointer)FALSE; + return ret; +} + +int mono_w32socket_recvfrom (SOCKET s, char *buf, int len, int flags, struct sockaddr *from, socklen_t *fromlen, gboolean blocking) +{ + int ret = SOCKET_ERROR; + ALERTABLE_SOCKET_CALL (FD_READ_BIT, blocking, TRUE, ret, recvfrom, s, buf, len, flags, from, fromlen); + return ret; +} + +int mono_w32socket_recvbuffers (SOCKET s, WSABUF *lpBuffers, guint32 dwBufferCount, guint32 *lpNumberOfBytesRecvd, guint32 *lpFlags, gpointer lpOverlapped, gpointer lpCompletionRoutine, gboolean blocking) +{ + int ret = SOCKET_ERROR; + ALERTABLE_SOCKET_CALL (FD_READ_BIT, blocking, TRUE, ret, WSARecv, s, lpBuffers, dwBufferCount, lpNumberOfBytesRecvd, lpFlags, lpOverlapped, lpCompletionRoutine); + return ret; +} + +int mono_w32socket_send (SOCKET s, char *buf, int len, int flags, gboolean blocking) +{ + int ret = SOCKET_ERROR; + ALERTABLE_SOCKET_CALL (FD_WRITE_BIT, blocking, FALSE, ret, send, s, buf, len, flags); + return ret; +} + +int mono_w32socket_sendto (SOCKET s, const char *buf, int len, int flags, const struct sockaddr *to, int tolen, gboolean blocking) +{ + int ret = SOCKET_ERROR; + ALERTABLE_SOCKET_CALL (FD_WRITE_BIT, blocking, FALSE, ret, sendto, s, buf, len, flags, to, tolen); + return ret; +} + +int mono_w32socket_sendbuffers (SOCKET s, WSABUF *lpBuffers, guint32 dwBufferCount, guint32 *lpNumberOfBytesRecvd, guint32 lpFlags, gpointer lpOverlapped, gpointer lpCompletionRoutine, gboolean blocking) +{ + int ret = SOCKET_ERROR; + ALERTABLE_SOCKET_CALL (FD_WRITE_BIT, blocking, FALSE, ret, WSASend, s, lpBuffers, dwBufferCount, lpNumberOfBytesRecvd, lpFlags, lpOverlapped, lpCompletionRoutine); + return ret; +} + +#if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT | HAVE_UWP_WINAPI_SUPPORT) +BOOL mono_w32socket_transmit_file (SOCKET hSocket, gpointer hFile, TRANSMIT_FILE_BUFFERS *lpTransmitBuffers, guint32 dwReserved, gboolean blocking) +{ + LOGDEBUG (g_message ("%06d - Performing %s TransmitFile () on socket %d", GetCurrentThreadId (), blocking ? "blocking" : "non-blocking", hSocket)); + + int error = 0; + if (blocking) { + OVERLAPPED overlapped = { 0 }; + overlapped.hEvent = WSACreateEvent (); + if (overlapped.hEvent == WSA_INVALID_EVENT) + return FALSE; + if (!TransmitFile (hSocket, hFile, 0, 0, &overlapped, lpTransmitBuffers, dwReserved)) { + error = WSAGetLastError (); + if (error == WSA_IO_PENDING) { + error = 0; + // NOTE: .NET's Socket.SendFile() doesn't honor the Socket's SendTimeout so we shouldn't either + DWORD ret = WaitForSingleObjectEx (overlapped.hEvent, INFINITE, TRUE); + if (ret == WAIT_IO_COMPLETION) { + LOGDEBUG (g_message ("%06d - WaitForSingleObjectEx () returned WSA_WAIT_IO_COMPLETION for socket %d", GetCurrentThreadId (), hSocket)); + error = WSAEINTR; + } else if (ret == WAIT_TIMEOUT) { + error = WSAETIMEDOUT; + } else if (ret != WAIT_OBJECT_0) { + error = GetLastError (); + } + } + } + WSACloseEvent (overlapped.hEvent); + } else { + if (!TransmitFile (hSocket, hFile, 0, 0, NULL, lpTransmitBuffers, dwReserved)) { + error = WSAGetLastError (); + } + } + + LOGDEBUG (g_message ("%06d - Finished %s TransmitFile () on socket %d (ret = %d, WSAGetLastError() = %d)", GetCurrentThreadId (), \ + blocking ? "blocking" : "non-blocking", hSocket, error == 0, error)); + WSASetLastError (error); + + return error == 0; +} +#endif /* #if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT | HAVE_UWP_WINAPI_SUPPORT) */ + +gint +mono_w32socket_disconnect (SOCKET sock, gboolean reuse) +{ + LPFN_DISCONNECTEX disconnect; + LPFN_TRANSMITFILE transmit_file; + DWORD output_bytes; + gint ret; + + /* Use the SIO_GET_EXTENSION_FUNCTION_POINTER to determine + * the address of the disconnect method without taking + * a hard dependency on a single provider + * + * For an explanation of why this is done, you can read the + * article at http://www.codeproject.com/internet/jbsocketserver3.asp + * + * 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. */ + + GUID disconnect_guid = WSAID_DISCONNECTEX; + ret = WSAIoctl (sock, SIO_GET_EXTENSION_FUNCTION_POINTER, &disconnect_guid, sizeof (GUID), &disconnect, sizeof (LPFN_DISCONNECTEX), &output_bytes, NULL, NULL); + if (ret == 0) { + if (!disconnect (sock, NULL, reuse ? TF_REUSE_SOCKET : 0, 0)) + return WSAGetLastError (); + + return 0; + } + + GUID transmit_file_guid = WSAID_TRANSMITFILE; + ret = WSAIoctl (sock, SIO_GET_EXTENSION_FUNCTION_POINTER, &transmit_file_guid, sizeof (GUID), &transmit_file, sizeof (LPFN_TRANSMITFILE), &output_bytes, NULL, NULL); + if (ret == 0) { + if (!transmit_file (sock, NULL, 0, 0, NULL, NULL, TF_DISCONNECT | (reuse ? TF_REUSE_SOCKET : 0))) + return WSAGetLastError (); + + return 0; + } + + return ERROR_NOT_SUPPORTED; +} + +gint +mono_w32socket_set_blocking (SOCKET sock, gboolean blocking) +{ + gulong nonblocking_long = !blocking; + return ioctlsocket (sock, FIONBIO, &nonblocking_long); +} + +gint +mono_w32socket_get_available (SOCKET sock, guint64 *amount) +{ + return ioctlsocket (sock, FIONREAD, (int*) amount); +} + +void +mono_w32socket_set_last_error (gint32 error) +{ + WSASetLastError (error); +} + +gint32 +mono_w32socket_get_last_error (void) +{ + return WSAGetLastError (); +} + +gint32 +mono_w32socket_convert_error (gint error) +{ + return (error > 0 && error < WSABASEERR) ? error + WSABASEERR : error; +} + +gboolean +ves_icall_System_Net_Sockets_Socket_SupportPortReuse (MonoProtocolType proto) +{ + return TRUE; +} diff --git a/mono/metadata/w32socket.c b/mono/metadata/w32socket.c new file mode 100644 index 00000000000..9d45ddac921 --- /dev/null +++ b/mono/metadata/w32socket.c @@ -0,0 +1,2716 @@ +/* + * socket-io.c: Socket IO internal calls + * + * Authors: + * Dick Porter (dick@ximian.com) + * Gonzalo Paniagua Javier (gonzalo@ximian.com) + * + * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com) + * Copyright 2004-2009 Novell, Inc (http://www.novell.com) + * + * This file has been re-licensed under the MIT License: + * http://opensource.org/licenses/MIT + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +#include + +#ifndef DISABLE_SOCKETS + +#if defined(__APPLE__) || defined(__FreeBSD__) +#define __APPLE_USE_RFC_3542 +#endif + +#include +#include +#include +#ifdef HOST_WIN32 +#include +#else +#include +#ifdef HAVE_SYS_IOCTL_H +#include +#endif +#include +#include +#ifdef HAVE_NETDB_H +#include +#endif +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +/* FIXME change this code to not mess so much with the internals */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#ifdef HAVE_SYS_TIME_H +#include +#endif +#ifdef HAVE_SYS_IOCTL_H +#include +#endif +#ifdef HAVE_NET_IF_H +#include +#endif + +#ifdef HAVE_NETDB_H +#include +#endif +#ifdef HAVE_SYS_FILIO_H +#include /* defines FIONBIO and FIONREAD */ +#endif +#ifdef HAVE_SYS_SOCKIO_H +#include /* defines SIOCATMARK */ +#endif +#ifdef HAVE_SYS_UN_H +#include +#endif + +#ifdef HAVE_GETIFADDRS +// must be included before +#include +#endif + +#if defined(_MSC_VER) && G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT | HAVE_UWP_WINAPI_SUPPORT) +#include +#endif + +#define LOGDEBUG(...) +/* define LOGDEBUG(...) g_message(__VA_ARGS__) */ + +#ifdef HOST_WIN32 + +static SOCKET +mono_w32socket_socket (int domain, int type, int protocol) +{ + return WSASocket (domain, type, protocol, NULL, 0, WSA_FLAG_OVERLAPPED); +} + +static gint +mono_w32socket_bind (SOCKET sock, struct sockaddr *addr, socklen_t addrlen) +{ + return bind (sock, addr, addrlen); +} + +static gint +mono_w32socket_getpeername (SOCKET sock, struct sockaddr *name, socklen_t *namelen) +{ + return getpeername (sock, name, namelen); +} + +static gint +mono_w32socket_getsockname (SOCKET sock, struct sockaddr *name, socklen_t *namelen) +{ + return getsockname (sock, name, namelen); +} + +static gint +mono_w32socket_getsockopt (SOCKET sock, gint level, gint optname, gpointer optval, socklen_t *optlen) +{ + return getsockopt (sock, level, optname, optval, optlen); +} + +static gint +mono_w32socket_setsockopt (SOCKET sock, gint level, gint optname, const gpointer optval, socklen_t optlen) +{ + return setsockopt (sock, level, optname, optval, optlen); +} + +static gint +mono_w32socket_listen (SOCKET sock, gint backlog) +{ + return listen (sock, backlog); +} + +static gint +mono_w32socket_shutdown (SOCKET sock, gint how) +{ + return shutdown (sock, how); +} + +static gint +mono_w32socket_ioctl (SOCKET sock, gint32 command, gchar *input, gint inputlen, gchar *output, gint outputlen, glong *written) +{ + return WSAIoctl (sock, command, input, inputlen, output, outputlen, written, NULL, NULL); +} + +#endif /* HOST_WIN32 */ + +static void +abort_syscall (gpointer data) +{ + mono_thread_info_abort_socket_syscall_for_close ((MonoNativeThreadId) (gsize) data); +} + +static gint32 +convert_family (MonoAddressFamily mono_family) +{ + switch (mono_family) { + case AddressFamily_Unknown: + case AddressFamily_ImpLink: + case AddressFamily_Pup: + case AddressFamily_Chaos: + case AddressFamily_Iso: + case AddressFamily_Ecma: + case AddressFamily_DataKit: + case AddressFamily_Ccitt: + case AddressFamily_DataLink: + case AddressFamily_Lat: + case AddressFamily_HyperChannel: + case AddressFamily_NetBios: + case AddressFamily_VoiceView: + case AddressFamily_FireFox: + case AddressFamily_Banyan: + case AddressFamily_Atm: + case AddressFamily_Cluster: + case AddressFamily_Ieee12844: + case AddressFamily_NetworkDesigners: + g_warning ("System.Net.Sockets.AddressFamily has unsupported value 0x%x", mono_family); + return -1; + case AddressFamily_Unspecified: + return AF_UNSPEC; + case AddressFamily_Unix: + return AF_UNIX; + case AddressFamily_InterNetwork: + return AF_INET; + case AddressFamily_AppleTalk: + return AF_APPLETALK; + case AddressFamily_InterNetworkV6: + return AF_INET6; + case AddressFamily_DecNet: +#ifdef AF_DECnet + return AF_DECnet; +#else + return -1; +#endif + case AddressFamily_Ipx: +#ifdef AF_IPX + return AF_IPX; +#else + return -1; +#endif + case AddressFamily_Sna: +#ifdef AF_SNA + return AF_SNA; +#else + return -1; +#endif + case AddressFamily_Irda: +#ifdef AF_IRDA + return AF_IRDA; +#else + return -1; +#endif + default: + g_warning ("System.Net.Sockets.AddressFamily has unknown value 0x%x", mono_family); + return -1; + } +} + +static MonoAddressFamily +convert_to_mono_family (guint16 af_family) +{ + switch (af_family) { + case AF_UNSPEC: + return AddressFamily_Unspecified; + case AF_UNIX: + return AddressFamily_Unix; + case AF_INET: + return AddressFamily_InterNetwork; +#ifdef AF_IPX + case AF_IPX: + return AddressFamily_Ipx; +#endif +#ifdef AF_SNA + case AF_SNA: + return AddressFamily_Sna; +#endif +#ifdef AF_DECnet + case AF_DECnet: + return AddressFamily_DecNet; +#endif + case AF_APPLETALK: + return AddressFamily_AppleTalk; + case AF_INET6: + return AddressFamily_InterNetworkV6; +#ifdef AF_IRDA + case AF_IRDA: + return AddressFamily_Irda; +#endif + default: + g_warning ("unknown address family 0x%x", af_family); + return AddressFamily_Unknown; + } +} + +static gint32 +convert_type (MonoSocketType mono_type) +{ + switch (mono_type) { + case SocketType_Stream: + return SOCK_STREAM; + case SocketType_Dgram: + return SOCK_DGRAM; + case SocketType_Raw: + return SOCK_RAW; + case SocketType_Rdm: +#ifdef SOCK_RDM + return SOCK_RDM; +#else + return -1; +#endif + case SocketType_Seqpacket: + return SOCK_SEQPACKET; + case SocketType_Unknown: + g_warning ("System.Net.Sockets.SocketType has unsupported value 0x%x", mono_type); + return -1; + default: + g_warning ("System.Net.Sockets.SocketType has unknown value 0x%x", mono_type); + return -1; + } +} + +static gint32 +convert_proto (MonoProtocolType mono_proto) +{ + switch (mono_proto) { + case ProtocolType_IP: + case ProtocolType_IPv6: + case ProtocolType_Icmp: + case ProtocolType_Igmp: + case ProtocolType_Ggp: + case ProtocolType_Tcp: + case ProtocolType_Pup: + case ProtocolType_Udp: + case ProtocolType_Idp: + /* These protocols are known (on my system at least) */ + return mono_proto; + case ProtocolType_ND: + case ProtocolType_Raw: + case ProtocolType_Ipx: + case ProtocolType_Spx: + case ProtocolType_SpxII: + case ProtocolType_Unknown: + /* These protocols arent */ + g_warning ("System.Net.Sockets.ProtocolType has unsupported value 0x%x", mono_proto); + return -1; + default: + return -1; + } +} + +/* Convert MonoSocketFlags */ +static gint32 +convert_socketflags (gint32 sflags) +{ + gint32 flags = 0; + + if (!sflags) + /* SocketFlags.None */ + return 0; + + if (sflags & ~(SocketFlags_OutOfBand | SocketFlags_MaxIOVectorLength | SocketFlags_Peek | + SocketFlags_DontRoute | SocketFlags_Partial)) + /* Contains invalid flag values */ + return -1; + + if (sflags & SocketFlags_OutOfBand) + flags |= MSG_OOB; + if (sflags & SocketFlags_Peek) + flags |= MSG_PEEK; + if (sflags & SocketFlags_DontRoute) + flags |= MSG_DONTROUTE; + + /* Ignore Partial - see bug 349688. Don't return -1, because + * according to the comment in that bug ms runtime doesn't for + * UDP sockets (this means we will silently ignore it for TCP + * too) + */ +#ifdef MSG_MORE + if (sflags & SocketFlags_Partial) + flags |= MSG_MORE; +#endif +#if 0 + /* Don't do anything for MaxIOVectorLength */ + if (sflags & SocketFlags_MaxIOVectorLength) + return -1; +#endif + return flags; +} + +/* + * Returns: + * 0 on success (mapped mono_level and mono_name to system_level and system_name + * -1 on error + * -2 on non-fatal error (ie, must ignore) + */ +static gint32 +convert_sockopt_level_and_name (MonoSocketOptionLevel mono_level, MonoSocketOptionName mono_name, int *system_level, int *system_name) +{ + switch (mono_level) { + case SocketOptionLevel_Socket: + *system_level = SOL_SOCKET; + + switch (mono_name) { + case SocketOptionName_DontLinger: + /* This is SO_LINGER, because the setsockopt + * internal call maps DontLinger to SO_LINGER + * with l_onoff=0 + */ + *system_name = SO_LINGER; + break; + case SocketOptionName_Debug: + *system_name = SO_DEBUG; + break; +#ifdef SO_ACCEPTCONN + case SocketOptionName_AcceptConnection: + *system_name = SO_ACCEPTCONN; + break; +#endif + case SocketOptionName_ReuseAddress: + *system_name = SO_REUSEADDR; + break; + case SocketOptionName_KeepAlive: + *system_name = SO_KEEPALIVE; + break; + case SocketOptionName_DontRoute: + *system_name = SO_DONTROUTE; + break; + case SocketOptionName_Broadcast: + *system_name = SO_BROADCAST; + break; + case SocketOptionName_Linger: + *system_name = SO_LINGER; + break; + case SocketOptionName_OutOfBandInline: + *system_name = SO_OOBINLINE; + break; + case SocketOptionName_SendBuffer: + *system_name = SO_SNDBUF; + break; + case SocketOptionName_ReceiveBuffer: + *system_name = SO_RCVBUF; + break; + case SocketOptionName_SendLowWater: + *system_name = SO_SNDLOWAT; + break; + case SocketOptionName_ReceiveLowWater: + *system_name = SO_RCVLOWAT; + break; + case SocketOptionName_SendTimeout: + *system_name = SO_SNDTIMEO; + break; + case SocketOptionName_ReceiveTimeout: + *system_name = SO_RCVTIMEO; + break; + case SocketOptionName_Error: + *system_name = SO_ERROR; + break; + case SocketOptionName_Type: + *system_name = SO_TYPE; + break; +#ifdef SO_PEERCRED + case SocketOptionName_PeerCred: + *system_name = SO_PEERCRED; + break; +#endif + case SocketOptionName_ExclusiveAddressUse: +#ifdef SO_EXCLUSIVEADDRUSE + *system_name = SO_EXCLUSIVEADDRUSE; + break; +#endif + case SocketOptionName_UseLoopback: +#ifdef SO_USELOOPBACK + *system_name = SO_USELOOPBACK; + break; +#endif + case SocketOptionName_MaxConnections: +#ifdef SO_MAXCONN + *system_name = SO_MAXCONN; + break; +#elif defined(SOMAXCONN) + *system_name = SOMAXCONN; + break; +#endif + default: + g_warning ("System.Net.Sockets.SocketOptionName 0x%x is not supported at Socket level", mono_name); + return -1; + } + break; + + case SocketOptionLevel_IP: + *system_level = mono_networking_get_ip_protocol (); + + switch (mono_name) { + case SocketOptionName_IPOptions: + *system_name = IP_OPTIONS; + break; +#ifdef IP_HDRINCL + case SocketOptionName_HeaderIncluded: + *system_name = IP_HDRINCL; + break; +#endif +#ifdef IP_TOS + case SocketOptionName_TypeOfService: + *system_name = IP_TOS; + break; +#endif +#ifdef IP_TTL + case SocketOptionName_IpTimeToLive: + *system_name = IP_TTL; + break; +#endif + case SocketOptionName_MulticastInterface: + *system_name = IP_MULTICAST_IF; + break; + case SocketOptionName_MulticastTimeToLive: + *system_name = IP_MULTICAST_TTL; + break; + case SocketOptionName_MulticastLoopback: + *system_name = IP_MULTICAST_LOOP; + break; + case SocketOptionName_AddMembership: + *system_name = IP_ADD_MEMBERSHIP; + break; + case SocketOptionName_DropMembership: + *system_name = IP_DROP_MEMBERSHIP; + break; +#ifdef HAVE_IP_PKTINFO + case SocketOptionName_PacketInformation: + *system_name = IP_PKTINFO; + break; +#endif /* HAVE_IP_PKTINFO */ + + case SocketOptionName_DontFragment: +#ifdef HAVE_IP_DONTFRAGMENT + *system_name = IP_DONTFRAGMENT; + break; +#elif defined HAVE_IP_MTU_DISCOVER + /* Not quite the same */ + *system_name = IP_MTU_DISCOVER; + break; +#else + /* If the flag is not available on this system, we can ignore this error */ + return -2; +#endif /* HAVE_IP_DONTFRAGMENT */ + case SocketOptionName_AddSourceMembership: + case SocketOptionName_DropSourceMembership: + case SocketOptionName_BlockSource: + case SocketOptionName_UnblockSource: + /* Can't figure out how to map these, so fall + * through + */ + default: + g_warning ("System.Net.Sockets.SocketOptionName 0x%x is not supported at IP level", mono_name); + return -1; + } + break; + + case SocketOptionLevel_IPv6: + *system_level = mono_networking_get_ipv6_protocol (); + + switch (mono_name) { + case SocketOptionName_IpTimeToLive: + case SocketOptionName_HopLimit: + *system_name = IPV6_UNICAST_HOPS; + break; + case SocketOptionName_MulticastInterface: + *system_name = IPV6_MULTICAST_IF; + break; + case SocketOptionName_MulticastTimeToLive: + *system_name = IPV6_MULTICAST_HOPS; + break; + case SocketOptionName_MulticastLoopback: + *system_name = IPV6_MULTICAST_LOOP; + break; + case SocketOptionName_AddMembership: + *system_name = IPV6_JOIN_GROUP; + break; + case SocketOptionName_DropMembership: + *system_name = IPV6_LEAVE_GROUP; + break; + case SocketOptionName_IPv6Only: +#ifdef IPV6_V6ONLY + *system_name = IPV6_V6ONLY; +#else + return -1; +#endif + break; + case SocketOptionName_PacketInformation: +#ifdef HAVE_IPV6_PKTINFO + *system_name = IPV6_PKTINFO; +#endif + break; + case SocketOptionName_HeaderIncluded: + case SocketOptionName_IPOptions: + case SocketOptionName_TypeOfService: + case SocketOptionName_DontFragment: + case SocketOptionName_AddSourceMembership: + case SocketOptionName_DropSourceMembership: + case SocketOptionName_BlockSource: + case SocketOptionName_UnblockSource: + /* Can't figure out how to map these, so fall + * through + */ + default: + g_warning ("System.Net.Sockets.SocketOptionName 0x%x is not supported at IPv6 level", mono_name); + return -1; + } + break; /* SocketOptionLevel_IPv6 */ + + case SocketOptionLevel_Tcp: + *system_level = mono_networking_get_tcp_protocol (); + + switch (mono_name) { + case SocketOptionName_NoDelay: + *system_name = TCP_NODELAY; + break; +#if 0 + /* The documentation is talking complete + * bollocks here: rfc-1222 is titled + * 'Advancing the NSFNET Routing Architecture' + * and doesn't mention either of the words + * "expedite" or "urgent". + */ + case SocketOptionName_BsdUrgent: + case SocketOptionName_Expedited: +#endif + default: + g_warning ("System.Net.Sockets.SocketOptionName 0x%x is not supported at TCP level", mono_name); + return -1; + } + break; + + case SocketOptionLevel_Udp: + g_warning ("System.Net.Sockets.SocketOptionLevel has unsupported value 0x%x", mono_level); + + switch(mono_name) { + case SocketOptionName_NoChecksum: + case SocketOptionName_ChecksumCoverage: + default: + g_warning ("System.Net.Sockets.SocketOptionName 0x%x is not supported at UDP level", mono_name); + return -1; + } + return -1; + break; + + default: + g_warning ("System.Net.Sockets.SocketOptionLevel has unknown value 0x%x", mono_level); + return -1; + } + + return 0; +} + +static MonoImage* +get_socket_assembly (void) +{ + MonoDomain *domain = mono_domain_get (); + + if (domain->socket_assembly == NULL) { + MonoImage *socket_assembly; + + socket_assembly = mono_image_loaded ("System"); + if (!socket_assembly) { + MonoAssembly *sa = mono_assembly_open ("System.dll", NULL); + + if (!sa) { + g_assert_not_reached (); + } else { + socket_assembly = mono_assembly_get_image (sa); + } + } + mono_atomic_store_release (&domain->socket_assembly, socket_assembly); + } + + return domain->socket_assembly; +} + +gpointer +ves_icall_System_Net_Sockets_Socket_Socket_internal (MonoObject *this_obj, gint32 family, gint32 type, gint32 proto, gint32 *werror) +{ + SOCKET sock; + gint32 sock_family; + gint32 sock_proto; + gint32 sock_type; + + *werror = 0; + + sock_family = convert_family ((MonoAddressFamily)family); + if (sock_family == -1) { + *werror = WSAEAFNOSUPPORT; + return NULL; + } + + sock_proto = convert_proto ((MonoProtocolType)proto); + if (sock_proto == -1) { + *werror = WSAEPROTONOSUPPORT; + return NULL; + } + + sock_type = convert_type ((MonoSocketType)type); + if (sock_type == -1) { + *werror = WSAESOCKTNOSUPPORT; + return NULL; + } + + sock = mono_w32socket_socket (sock_family, sock_type, sock_proto); + + if (sock == INVALID_SOCKET) { + *werror = mono_w32socket_get_last_error (); + return NULL; + } + + return GUINT_TO_POINTER (sock); +} + +/* FIXME: the SOCKET parameter (here and in other functions in this + * file) is really an IntPtr which needs to be converted to a guint32. + */ +void +ves_icall_System_Net_Sockets_Socket_Close_internal (gsize sock, gint32 *werror) +{ + LOGDEBUG (g_message ("%s: closing 0x%x", __func__, sock)); + + *werror = 0; + + /* Clear any pending work item from this socket if the underlying + * polling system does not notify when the socket is closed */ + mono_threadpool_io_remove_socket (GPOINTER_TO_INT (sock)); + + MONO_ENTER_GC_SAFE; + CloseHandle (GINT_TO_POINTER (sock)); + MONO_EXIT_GC_SAFE; +} + +gint32 +ves_icall_System_Net_Sockets_SocketException_WSAGetLastError_internal (void) +{ + LOGDEBUG (g_message("%s: returning %d", __func__, mono_w32socket_get_last_error ())); + + return mono_w32socket_get_last_error (); +} + +gint32 +ves_icall_System_Net_Sockets_Socket_Available_internal (gsize sock, gint32 *werror) +{ + int ret; + guint64 amount; + + *werror = 0; + + /* FIXME: this might require amount to be unsigned long. */ + ret = mono_w32socket_get_available (sock, &amount); + if (ret == SOCKET_ERROR) { + *werror = mono_w32socket_get_last_error (); + return 0; + } + + return amount; +} + +void +ves_icall_System_Net_Sockets_Socket_Blocking_internal (gsize sock, gboolean block, gint32 *werror) +{ + int ret; + + *werror = 0; + + ret = mono_w32socket_set_blocking (sock, block); + if (ret == SOCKET_ERROR) + *werror = mono_w32socket_get_last_error (); +} + +gpointer +ves_icall_System_Net_Sockets_Socket_Accept_internal (gsize sock, gint32 *werror, gboolean blocking) +{ + gboolean interrupted; + SOCKET newsock; + + *werror = 0; + + mono_thread_info_install_interrupt (abort_syscall, (gpointer) (gsize) mono_native_thread_id_get (), &interrupted); + if (interrupted) { + *werror = WSAEINTR; + return NULL; + } + + MONO_ENTER_GC_SAFE; + newsock = mono_w32socket_accept (sock, NULL, 0, blocking); + MONO_EXIT_GC_SAFE; + + if (newsock == INVALID_SOCKET) + *werror = mono_w32socket_get_last_error (); + + mono_thread_info_uninstall_interrupt (&interrupted); + if (interrupted) + *werror = WSAEINTR; + + if (*werror) + return NULL; + + return GUINT_TO_POINTER (newsock); +} + +void +ves_icall_System_Net_Sockets_Socket_Listen_internal(gsize sock, guint32 backlog, gint32 *werror) +{ + int ret; + + *werror = 0; + + MONO_ENTER_GC_SAFE; + + ret = mono_w32socket_listen (sock, backlog); + + MONO_EXIT_GC_SAFE; + + if (ret == SOCKET_ERROR) + *werror = mono_w32socket_get_last_error (); +} + +// Check whether it's ::ffff::0:0. +static gboolean +is_ipv4_mapped_any (const struct in6_addr *addr) +{ + int i; + + for (i = 0; i < 10; i++) { + if (addr->s6_addr [i]) + return FALSE; + } + if ((addr->s6_addr [10] != 0xff) || (addr->s6_addr [11] != 0xff)) + return FALSE; + for (i = 12; i < 16; i++) { + if (addr->s6_addr [i]) + return FALSE; + } + return TRUE; +} + +static MonoObject* +create_object_from_sockaddr (struct sockaddr *saddr, int sa_size, gint32 *werror, MonoError *error) +{ + MonoDomain *domain = mono_domain_get (); + MonoObject *sockaddr_obj; + MonoArray *data; + MonoAddressFamily family; + + mono_error_init (error); + + /* Build a System.Net.SocketAddress object instance */ + if (!domain->sockaddr_class) + domain->sockaddr_class = mono_class_load_from_name (get_socket_assembly (), "System.Net", "SocketAddress"); + sockaddr_obj = mono_object_new_checked (domain, domain->sockaddr_class, error); + return_val_if_nok (error, NULL); + + /* Locate the SocketAddress data buffer in the object */ + if (!domain->sockaddr_data_field) { + domain->sockaddr_data_field = mono_class_get_field_from_name (domain->sockaddr_class, "m_Buffer"); + g_assert (domain->sockaddr_data_field); + } + + /* Locate the SocketAddress data buffer length in the object */ + if (!domain->sockaddr_data_length_field) { + domain->sockaddr_data_length_field = mono_class_get_field_from_name (domain->sockaddr_class, "m_Size"); + g_assert (domain->sockaddr_data_length_field); + } + + /* May be the +2 here is too conservative, as sa_len returns + * the length of the entire sockaddr_in/in6, including + * sizeof (unsigned short) of the family */ + /* We can't really avoid the +2 as all code below depends on this size - INCLUDING unix domain sockets.*/ + data = mono_array_new_cached (domain, mono_get_byte_class (), sa_size + 2, error); + return_val_if_nok (error, NULL); + + /* The data buffer is laid out as follows: + * bytes 0 and 1 are the address family + * bytes 2 and 3 are the port info + * the rest is the address info + */ + + family = convert_to_mono_family (saddr->sa_family); + if (family == AddressFamily_Unknown) { + *werror = WSAEAFNOSUPPORT; + return NULL; + } + + mono_array_set (data, guint8, 0, family & 0x0FF); + mono_array_set (data, guint8, 1, (family >> 8) & 0x0FF); + + if (saddr->sa_family == AF_INET) { + struct sockaddr_in *sa_in = (struct sockaddr_in *)saddr; + guint16 port = ntohs (sa_in->sin_port); + guint32 address = ntohl (sa_in->sin_addr.s_addr); + int buffer_size = 8; + + if (sa_size < buffer_size) { + mono_error_set_exception_instance (error, mono_exception_from_name (mono_get_corlib (), "System", "SystemException")); + return NULL; + } + + mono_array_set (data, guint8, 2, (port>>8) & 0xff); + mono_array_set (data, guint8, 3, (port) & 0xff); + mono_array_set (data, guint8, 4, (address>>24) & 0xff); + mono_array_set (data, guint8, 5, (address>>16) & 0xff); + mono_array_set (data, guint8, 6, (address>>8) & 0xff); + mono_array_set (data, guint8, 7, (address) & 0xff); + + mono_field_set_value (sockaddr_obj, domain->sockaddr_data_field, data); + mono_field_set_value (sockaddr_obj, domain->sockaddr_data_length_field, &buffer_size); + + return sockaddr_obj; + } else if (saddr->sa_family == AF_INET6) { + struct sockaddr_in6 *sa_in = (struct sockaddr_in6 *)saddr; + int i; + int buffer_size = 28; + + guint16 port = ntohs (sa_in->sin6_port); + + if (sa_size < buffer_size) { + mono_error_set_exception_instance (error, mono_exception_from_name (mono_get_corlib (), "System", "SystemException")); + return NULL; + } + + mono_array_set (data, guint8, 2, (port>>8) & 0xff); + mono_array_set (data, guint8, 3, (port) & 0xff); + + if (is_ipv4_mapped_any (&sa_in->sin6_addr)) { + // Map ::ffff:0:0 to :: (bug #5502) + for (i = 0; i < 16; i++) + mono_array_set (data, guint8, 8 + i, 0); + } else { + for (i = 0; i < 16; i++) { + mono_array_set (data, guint8, 8 + i, + sa_in->sin6_addr.s6_addr [i]); + } + } + + mono_array_set (data, guint8, 24, sa_in->sin6_scope_id & 0xff); + mono_array_set (data, guint8, 25, + (sa_in->sin6_scope_id >> 8) & 0xff); + mono_array_set (data, guint8, 26, + (sa_in->sin6_scope_id >> 16) & 0xff); + mono_array_set (data, guint8, 27, + (sa_in->sin6_scope_id >> 24) & 0xff); + + mono_field_set_value (sockaddr_obj, domain->sockaddr_data_field, data); + mono_field_set_value (sockaddr_obj, domain->sockaddr_data_length_field, &buffer_size); + + return sockaddr_obj; + } +#ifdef HAVE_SYS_UN_H + else if (saddr->sa_family == AF_UNIX) { + int i; + int buffer_size = sa_size + 2; + + for (i = 0; i < sa_size; i++) + mono_array_set (data, guint8, i + 2, saddr->sa_data [i]); + + mono_field_set_value (sockaddr_obj, domain->sockaddr_data_field, data); + mono_field_set_value (sockaddr_obj, domain->sockaddr_data_length_field, &buffer_size); + + return sockaddr_obj; + } +#endif + else { + *werror = WSAEAFNOSUPPORT; + return NULL; + } +} + +static int +get_sockaddr_size (int family) +{ + int size; + + size = 0; + if (family == AF_INET) { + size = sizeof (struct sockaddr_in); + } else if (family == AF_INET6) { + size = sizeof (struct sockaddr_in6); + } +#ifdef HAVE_SYS_UN_H + else if (family == AF_UNIX) { + size = sizeof (struct sockaddr_un); + } +#endif + return size; +} + +MonoObject* +ves_icall_System_Net_Sockets_Socket_LocalEndPoint_internal (gsize sock, gint32 af, gint32 *werror) +{ + gchar *sa; + socklen_t salen; + int ret; + MonoObject *result; + MonoError error; + + *werror = 0; + + salen = get_sockaddr_size (convert_family ((MonoAddressFamily)af)); + if (salen == 0) { + *werror = WSAEAFNOSUPPORT; + return NULL; + } + sa = (salen <= 128) ? (gchar *)alloca (salen) : (gchar *)g_malloc0 (salen); + + MONO_ENTER_GC_SAFE; + + ret = mono_w32socket_getsockname (sock, (struct sockaddr *)sa, &salen); + + MONO_EXIT_GC_SAFE; + + if (ret == SOCKET_ERROR) { + *werror = mono_w32socket_get_last_error (); + if (salen > 128) + g_free (sa); + return NULL; + } + + LOGDEBUG (g_message("%s: bound to %s port %d", __func__, inet_ntoa (((struct sockaddr_in *)&sa)->sin_addr), ntohs (((struct sockaddr_in *)&sa)->sin_port))); + + result = create_object_from_sockaddr ((struct sockaddr *)sa, salen, werror, &error); + if (salen > 128) + g_free (sa); + if (!mono_error_ok (&error)) + mono_error_set_pending_exception (&error); + return result; +} + +MonoObject* +ves_icall_System_Net_Sockets_Socket_RemoteEndPoint_internal (gsize sock, gint32 af, gint32 *werror) +{ + gchar *sa; + socklen_t salen; + int ret; + MonoObject *result; + MonoError error; + + *werror = 0; + + salen = get_sockaddr_size (convert_family ((MonoAddressFamily)af)); + if (salen == 0) { + *werror = WSAEAFNOSUPPORT; + return NULL; + } + sa = (salen <= 128) ? (gchar *)alloca (salen) : (gchar *)g_malloc0 (salen); + /* Note: linux returns just 2 for AF_UNIX. Always. */ + + MONO_ENTER_GC_SAFE; + + ret = mono_w32socket_getpeername (sock, (struct sockaddr *)sa, &salen); + + MONO_EXIT_GC_SAFE; + + if (ret == SOCKET_ERROR) { + *werror = mono_w32socket_get_last_error (); + if (salen > 128) + g_free (sa); + return NULL; + } + + LOGDEBUG (g_message("%s: connected to %s port %d", __func__, inet_ntoa (((struct sockaddr_in *)&sa)->sin_addr), ntohs (((struct sockaddr_in *)&sa)->sin_port))); + + result = create_object_from_sockaddr ((struct sockaddr *)sa, salen, werror, &error); + if (salen > 128) + g_free (sa); + if (!mono_error_ok (&error)) + mono_error_set_pending_exception (&error); + return result; +} + +static struct sockaddr* +create_sockaddr_from_object (MonoObject *saddr_obj, socklen_t *sa_size, gint32 *werror, MonoError *error) +{ + MonoDomain *domain = mono_domain_get (); + MonoArray *data; + gint32 family; + int len; + + mono_error_init (error); + + if (!domain->sockaddr_class) + domain->sockaddr_class = mono_class_load_from_name (get_socket_assembly (), "System.Net", "SocketAddress"); + + /* Locate the SocketAddress data buffer in the object */ + if (!domain->sockaddr_data_field) { + domain->sockaddr_data_field = mono_class_get_field_from_name (domain->sockaddr_class, "m_Buffer"); + g_assert (domain->sockaddr_data_field); + } + + /* Locate the SocketAddress data buffer length in the object */ + if (!domain->sockaddr_data_length_field) { + domain->sockaddr_data_length_field = mono_class_get_field_from_name (domain->sockaddr_class, "m_Size"); + g_assert (domain->sockaddr_data_length_field); + } + + data = *(MonoArray **)(((char *)saddr_obj) + domain->sockaddr_data_field->offset); + + /* The data buffer is laid out as follows: + * byte 0 is the address family low byte + * byte 1 is the address family high byte + * INET: + * bytes 2 and 3 are the port info + * the rest is the address info + * UNIX: + * the rest is the file name + */ + len = *(int *)(((char *)saddr_obj) + domain->sockaddr_data_length_field->offset); + g_assert (len >= 2); + + family = convert_family ((MonoAddressFamily)(mono_array_get (data, guint8, 0) + (mono_array_get (data, guint8, 1) << 8))); + if (family == AF_INET) { + struct sockaddr_in *sa; + guint16 port; + guint32 address; + + if (len < 8) { + mono_error_set_exception_instance (error, mono_exception_from_name (mono_get_corlib (), "System", "SystemException")); + return NULL; + } + + 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); + return (struct sockaddr *)sa; + } else if (family == AF_INET6) { + struct sockaddr_in6 *sa; + int i; + guint16 port; + guint32 scopeid; + + if (len < 28) { + mono_error_set_exception_instance (error, mono_exception_from_name (mono_get_corlib (), "System", "SystemException")); + return NULL; + } + + 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_scope_id = scopeid; + + 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); + return (struct sockaddr *)sa; + } +#ifdef HAVE_SYS_UN_H + else if (family == AF_UNIX) { + struct sockaddr_un *sock_un; + int i; + + /* Need a byte for the '\0' terminator/prefix, and the first + * two bytes hold the SocketAddress family + */ + if (len - 2 >= sizeof (sock_un->sun_path)) { + mono_error_set_exception_instance (error, mono_get_exception_index_out_of_range ()); + return NULL; + } + + sock_un = g_new0 (struct sockaddr_un, 1); + + sock_un->sun_family = family; + for (i = 0; i < len - 2; i++) + sock_un->sun_path [i] = mono_array_get (data, guint8, i + 2); + + *sa_size = len; + return (struct sockaddr *)sock_un; + } +#endif + else { + *werror = WSAEAFNOSUPPORT; + return 0; + } +} + +void +ves_icall_System_Net_Sockets_Socket_Bind_internal (gsize sock, MonoObject *sockaddr, gint32 *werror) +{ + MonoError error; + struct sockaddr *sa; + socklen_t sa_size; + int ret; + + *werror = 0; + + sa = create_sockaddr_from_object (sockaddr, &sa_size, werror, &error); + if (*werror != 0) + return; + if (!mono_error_ok (&error)) { + mono_error_set_pending_exception (&error); + return; + } + + LOGDEBUG (g_message("%s: binding to %s port %d", __func__, inet_ntoa (((struct sockaddr_in *)sa)->sin_addr), ntohs (((struct sockaddr_in *)sa)->sin_port))); + + ret = mono_w32socket_bind (sock, sa, sa_size); + + if (ret == SOCKET_ERROR) + *werror = mono_w32socket_get_last_error (); + + g_free (sa); +} + +enum { + SelectModeRead, + SelectModeWrite, + SelectModeError +}; + +MonoBoolean +ves_icall_System_Net_Sockets_Socket_Poll_internal (gsize sock, gint mode, + gint timeout, gint32 *werror) +{ + MonoInternalThread *thread = mono_thread_internal_current (); + mono_pollfd *pfds; + int ret; + gboolean interrupted; + time_t start; + + *werror = 0; + + pfds = g_new0 (mono_pollfd, 1); + pfds->fd = GPOINTER_TO_INT (sock); + + switch (mode) { + case SelectModeRead: + pfds->events = MONO_POLLIN; + break; + case SelectModeWrite: + pfds->events = MONO_POLLOUT; + break; + default: + pfds->events = MONO_POLLERR | MONO_POLLHUP | MONO_POLLNVAL; + break; + } + + timeout = (timeout >= 0) ? (timeout / 1000) : -1; + start = time (NULL); + + do { + mono_thread_info_install_interrupt (abort_syscall, (gpointer) (gsize) mono_native_thread_id_get (), &interrupted); + if (interrupted) { + g_free (pfds); + *werror = WSAEINTR; + return FALSE; + } + + MONO_ENTER_GC_SAFE; + + ret = mono_poll (pfds, 1, timeout); + + MONO_EXIT_GC_SAFE; + + mono_thread_info_uninstall_interrupt (&interrupted); + if (interrupted) { + g_free (pfds); + *werror = WSAEINTR; + return FALSE; + } + + if (timeout > 0 && ret < 0) { + int err = errno; + int sec = time (NULL) - start; + + timeout -= sec * 1000; + if (timeout < 0) { + timeout = 0; + } + + errno = err; + } + + if (ret == -1 && errno == EINTR) { + if (mono_thread_test_state (thread, (MonoThreadState)(ThreadState_AbortRequested | ThreadState_StopRequested))) { + g_free (pfds); + return FALSE; + } + + /* Suspend requested? */ + mono_thread_interruption_checkpoint (); + + errno = EINTR; + } + } while (ret == -1 && errno == EINTR); + + if (ret == -1) { + *werror = mono_w32socket_convert_error (errno); + g_free (pfds); + return FALSE; + } + + g_free (pfds); + return ret != 0; +} + +void +ves_icall_System_Net_Sockets_Socket_Connect_internal (gsize sock, MonoObject *sockaddr, gint32 *werror, gboolean blocking) +{ + MonoError error; + struct sockaddr *sa; + socklen_t sa_size; + int ret; + gboolean interrupted; + + *werror = 0; + + sa = create_sockaddr_from_object (sockaddr, &sa_size, werror, &error); + if (*werror != 0) + return; + if (!mono_error_ok (&error)) { + mono_error_set_pending_exception (&error); + return; + } + + LOGDEBUG (g_message("%s: connecting to %s port %d", __func__, inet_ntoa (((struct sockaddr_in *)sa)->sin_addr), ntohs (((struct sockaddr_in *)sa)->sin_port))); + + mono_thread_info_install_interrupt (abort_syscall, (gpointer) (gsize) mono_native_thread_id_get (), &interrupted); + if (interrupted) { + *werror = WSAEINTR; + return; + } + + MONO_ENTER_GC_SAFE; + ret = mono_w32socket_connect (sock, sa, sa_size, blocking); + MONO_EXIT_GC_SAFE; + + if (ret == SOCKET_ERROR) + *werror = mono_w32socket_get_last_error (); + + mono_thread_info_uninstall_interrupt (&interrupted); + if (interrupted) + *werror = WSAEINTR; + + g_free (sa); +} + +#if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT | HAVE_UWP_WINAPI_SUPPORT) + +void +ves_icall_System_Net_Sockets_Socket_Disconnect_internal (gsize sock, MonoBoolean reuse, gint32 *werror) +{ + gboolean interrupted; + + LOGDEBUG (g_message("%s: disconnecting from socket %p (reuse %d)", __func__, sock, reuse)); + + mono_thread_info_install_interrupt (abort_syscall, (gpointer) (gsize) mono_native_thread_id_get (), &interrupted); + if (interrupted) { + *werror = WSAEINTR; + return; + } + + MONO_ENTER_GC_SAFE; + *werror = mono_w32socket_disconnect (sock, reuse); + MONO_EXIT_GC_SAFE; + + mono_thread_info_uninstall_interrupt (&interrupted); + if (interrupted) + *werror = WSAEINTR; +} +#endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT | HAVE_UWP_WINAPI_SUPPORT) */ + +gint32 +ves_icall_System_Net_Sockets_Socket_Receive_internal (gsize sock, MonoArray *buffer, gint32 offset, gint32 count, gint32 flags, gint32 *werror, gboolean blocking) +{ + int ret; + gchar *buf; + gint32 alen; + int recvflags = 0; + gboolean interrupted; + MonoInternalThread* curthread G_GNUC_UNUSED = mono_thread_internal_current (); + + *werror = 0; + + alen = mono_array_length (buffer); + if (offset > alen - count) + return 0; + + buf = mono_array_addr (buffer, gchar, offset); + + recvflags = convert_socketflags (flags); + if (recvflags == -1) { + *werror = WSAEOPNOTSUPP; + return 0; + } + + mono_thread_info_install_interrupt (abort_syscall, (gpointer) (gsize) mono_native_thread_id_get (), &interrupted); + if (interrupted) + return 0; + + MONO_ENTER_GC_SAFE; + ret = mono_w32socket_recv (sock, buf, count, recvflags, blocking); + MONO_EXIT_GC_SAFE; + + if (ret == SOCKET_ERROR) + *werror = mono_w32socket_get_last_error (); + + mono_thread_info_uninstall_interrupt (&interrupted); + if (interrupted) + *werror = WSAEINTR; + + if (*werror) + return 0; + + return ret; +} + +gint32 +ves_icall_System_Net_Sockets_Socket_Receive_array_internal (gsize sock, MonoArray *buffers, gint32 flags, gint32 *werror, gboolean blocking) +{ + int ret, count; + gboolean interrupted; + guint32 recv; + WSABUF *wsabufs; + guint32 recvflags = 0; + + *werror = 0; + + wsabufs = mono_array_addr (buffers, WSABUF, 0); + count = mono_array_length (buffers); + + recvflags = convert_socketflags (flags); + if (recvflags == -1) { + *werror = WSAEOPNOTSUPP; + return 0; + } + + mono_thread_info_install_interrupt (abort_syscall, (gpointer) (gsize) mono_native_thread_id_get (), &interrupted); + if (interrupted) { + *werror = WSAEINTR; + return 0; + } + + MONO_ENTER_GC_SAFE; + ret = mono_w32socket_recvbuffers (sock, wsabufs, count, &recv, &recvflags, NULL, NULL, blocking); + MONO_EXIT_GC_SAFE; + + if (ret == SOCKET_ERROR) + *werror = mono_w32socket_get_last_error (); + + mono_thread_info_uninstall_interrupt (&interrupted); + if (interrupted) + *werror = WSAEINTR; + + if (*werror) + return 0; + + return recv; +} + +gint32 +ves_icall_System_Net_Sockets_Socket_ReceiveFrom_internal (gsize sock, MonoArray *buffer, gint32 offset, gint32 count, gint32 flags, MonoObject **sockaddr, gint32 *werror, gboolean blocking) +{ + MonoError error; + int ret; + gchar *buf; + gint32 alen; + int recvflags = 0; + struct sockaddr *sa; + socklen_t sa_size; + gboolean interrupted; + + *werror = 0; + + alen = mono_array_length (buffer); + if (offset > alen - count) + return 0; + + sa = create_sockaddr_from_object (*sockaddr, &sa_size, werror, &error); + if (*werror != 0) + return 0; + if (!mono_error_ok (&error)) { + mono_error_set_pending_exception (&error); + return 0; + } + + buf = mono_array_addr (buffer, gchar, offset); + + recvflags = convert_socketflags (flags); + if (recvflags == -1) { + *werror = WSAEOPNOTSUPP; + return 0; + } + + mono_thread_info_install_interrupt (abort_syscall, (gpointer) (gsize) mono_native_thread_id_get (), &interrupted); + if (interrupted) { + g_free (sa); + *werror = WSAEINTR; + return 0; + } + + MONO_ENTER_GC_SAFE; + ret = mono_w32socket_recvfrom (sock, buf, count, recvflags, sa, &sa_size, blocking); + MONO_EXIT_GC_SAFE; + + if (ret == SOCKET_ERROR) + *werror = mono_w32socket_get_last_error (); + + mono_thread_info_uninstall_interrupt (&interrupted); + + if (interrupted) + *werror = WSAEINTR; + + if (*werror) { + g_free(sa); + return 0; + } + + /* If we didn't get a socket size, then we're probably a + * connected connection-oriented socket and the stack hasn't + * returned the remote address. All we can do is return null. + */ + if (sa_size) { + *sockaddr = create_object_from_sockaddr (sa, sa_size, werror, &error); + if (!mono_error_ok (&error)) { + mono_error_set_pending_exception (&error); + g_free (sa); + return 0; + } + } else { + *sockaddr = NULL; + } + + g_free (sa); + + return ret; +} + +gint32 +ves_icall_System_Net_Sockets_Socket_Send_internal (gsize sock, MonoArray *buffer, gint32 offset, gint32 count, gint32 flags, gint32 *werror, gboolean blocking) +{ + int ret; + gchar *buf; + gint32 alen; + int sendflags = 0; + gboolean interrupted; + + *werror = 0; + + alen = mono_array_length (buffer); + if (offset > alen - count) + return 0; + + LOGDEBUG (g_message("%s: alen: %d", __func__, alen)); + + buf = mono_array_addr (buffer, gchar, offset); + + LOGDEBUG (g_message("%s: Sending %d bytes", __func__, count)); + + sendflags = convert_socketflags (flags); + if (sendflags == -1) { + *werror = WSAEOPNOTSUPP; + return 0; + } + + mono_thread_info_install_interrupt (abort_syscall, (gpointer) (gsize) mono_native_thread_id_get (), &interrupted); + if (interrupted) { + *werror = WSAEINTR; + return 0; + } + + MONO_ENTER_GC_SAFE; + ret = mono_w32socket_send (sock, buf, count, sendflags, blocking); + MONO_EXIT_GC_SAFE; + + if (ret == SOCKET_ERROR) + *werror = mono_w32socket_get_last_error (); + + mono_thread_info_uninstall_interrupt (&interrupted); + if (interrupted) + *werror = WSAEINTR; + + if (*werror) + return 0; + + return ret; +} + +gint32 +ves_icall_System_Net_Sockets_Socket_Send_array_internal (gsize sock, MonoArray *buffers, gint32 flags, gint32 *werror, gboolean blocking) +{ + int ret, count; + guint32 sent; + WSABUF *wsabufs; + guint32 sendflags = 0; + gboolean interrupted; + + *werror = 0; + + wsabufs = mono_array_addr (buffers, WSABUF, 0); + count = mono_array_length (buffers); + + sendflags = convert_socketflags (flags); + if (sendflags == -1) { + *werror = WSAEOPNOTSUPP; + return 0; + } + + mono_thread_info_install_interrupt (abort_syscall, (gpointer) (gsize) mono_native_thread_id_get (), &interrupted); + if (interrupted) { + *werror = WSAEINTR; + return 0; + } + + MONO_ENTER_GC_SAFE; + ret = mono_w32socket_sendbuffers (sock, wsabufs, count, &sent, sendflags, NULL, NULL, blocking); + MONO_EXIT_GC_SAFE; + + if (ret == SOCKET_ERROR) + *werror = mono_w32socket_get_last_error (); + + mono_thread_info_uninstall_interrupt (&interrupted); + if (interrupted) + *werror = WSAEINTR; + + if (*werror) + return 0; + + return sent; +} + +gint32 +ves_icall_System_Net_Sockets_Socket_SendTo_internal (gsize sock, MonoArray *buffer, gint32 offset, gint32 count, gint32 flags, MonoObject *sockaddr, gint32 *werror, gboolean blocking) +{ + MonoError error; + int ret; + gchar *buf; + gint32 alen; + int sendflags = 0; + struct sockaddr *sa; + socklen_t sa_size; + gboolean interrupted; + + *werror = 0; + + alen = mono_array_length (buffer); + if (offset > alen - count) { + return 0; + } + + sa = create_sockaddr_from_object(sockaddr, &sa_size, werror, &error); + if (*werror != 0) + return 0; + if (!mono_error_ok (&error)) { + mono_error_set_pending_exception (&error); + return 0; + } + + LOGDEBUG (g_message ("%s: alen: %d", __func__, alen)); + + buf = mono_array_addr (buffer, gchar, offset); + + LOGDEBUG (g_message("%s: Sending %d bytes", __func__, count)); + + sendflags = convert_socketflags (flags); + if (sendflags == -1) { + g_free (sa); + *werror = WSAEOPNOTSUPP; + return 0; + } + + mono_thread_info_install_interrupt (abort_syscall, (gpointer) (gsize) mono_native_thread_id_get (), &interrupted); + if (interrupted) { + g_free (sa); + *werror = WSAEINTR; + return 0; + } + + MONO_ENTER_GC_SAFE; + ret = mono_w32socket_sendto (sock, buf, count, sendflags, sa, sa_size, blocking); + MONO_EXIT_GC_SAFE; + + if (ret == SOCKET_ERROR) + *werror = mono_w32socket_get_last_error (); + + mono_thread_info_uninstall_interrupt (&interrupted); + if (interrupted) + *werror = WSAEINTR; + + g_free(sa); + + if (*werror) + return 0; + + return ret; +} + +static SOCKET +Socket_to_SOCKET (MonoObject *sockobj) +{ + MonoSafeHandle *safe_handle; + MonoClassField *field; + + field = mono_class_get_field_from_name (sockobj->vtable->klass, "m_Handle"); + safe_handle = ((MonoSafeHandle *)(*(gpointer *)(((char *)sockobj) + field->offset))); + + if (safe_handle == NULL) + return -1; + + return (SOCKET)safe_handle->handle; +} + +#define POLL_ERRORS (MONO_POLLERR | MONO_POLLHUP | MONO_POLLNVAL) + +void +ves_icall_System_Net_Sockets_Socket_Select_internal (MonoArray **sockets, gint32 timeout, gint32 *werror) +{ + MonoError error; + MonoInternalThread *thread = mono_thread_internal_current (); + MonoObject *obj; + mono_pollfd *pfds; + int nfds, idx; + int ret; + int i, count; + int mode; + MonoClass *sock_arr_class; + MonoArray *socks; + time_t start; + uintptr_t socks_size; + gboolean interrupted; + + *werror = 0; + + /* *sockets -> READ, null, WRITE, null, ERROR, null */ + count = mono_array_length (*sockets); + nfds = count - 3; /* NULL separators */ + pfds = g_new0 (mono_pollfd, nfds); + mode = idx = 0; + for (i = 0; i < count; i++) { + obj = mono_array_get (*sockets, MonoObject *, i); + if (obj == NULL) { + mode++; + continue; + } + + if (idx >= nfds) { + /* The socket array was bogus */ + g_free (pfds); + *werror = WSAEFAULT; + return; + } + + pfds [idx].fd = Socket_to_SOCKET (obj); + pfds [idx].events = (mode == 0) ? MONO_POLLIN : (mode == 1) ? MONO_POLLOUT : POLL_ERRORS; + idx++; + } + + timeout = (timeout >= 0) ? (timeout / 1000) : -1; + start = time (NULL); + do { + mono_thread_info_install_interrupt (abort_syscall, (gpointer) (gsize) mono_native_thread_id_get (), &interrupted); + if (interrupted) { + g_free (pfds); + *werror = WSAEINTR; + return; + } + + MONO_ENTER_GC_SAFE; + + ret = mono_poll (pfds, nfds, timeout); + + MONO_EXIT_GC_SAFE; + + mono_thread_info_uninstall_interrupt (&interrupted); + if (interrupted) { + g_free (pfds); + *werror = WSAEINTR; + return; + } + + if (timeout > 0 && ret < 0) { + int err = errno; + int sec = time (NULL) - start; + + timeout -= sec * 1000; + if (timeout < 0) + timeout = 0; + errno = err; + } + + if (ret == -1 && errno == EINTR) { + if (mono_thread_test_state (thread, (MonoThreadState)(ThreadState_AbortRequested | ThreadState_StopRequested))) { + g_free (pfds); + *sockets = NULL; + return; + } + + /* Suspend requested? */ + mono_thread_interruption_checkpoint (); + + errno = EINTR; + } + } while (ret == -1 && errno == EINTR); + + if (ret == -1) { + *werror = mono_w32socket_convert_error (errno); + g_free (pfds); + return; + } + + if (ret == 0) { + g_free (pfds); + *sockets = NULL; + return; + } + + sock_arr_class = ((MonoObject *)*sockets)->vtable->klass; + socks_size = ((uintptr_t)ret) + 3; /* space for the NULL delimiters */ + socks = mono_array_new_full_checked (mono_domain_get (), sock_arr_class, &socks_size, NULL, &error); + if (!mono_error_ok (&error)) { + mono_error_set_pending_exception (&error); + g_free (pfds); + return; + } + + mode = idx = 0; + for (i = 0; i < count && ret > 0; i++) { + mono_pollfd *pfd; + + obj = mono_array_get (*sockets, MonoObject *, i); + if (obj == NULL) { + mode++; + idx++; + continue; + } + + pfd = &pfds [i - mode]; + if (pfd->revents == 0) + continue; + + ret--; + if (mode == 0 && (pfd->revents & (MONO_POLLIN | POLL_ERRORS)) != 0) { + mono_array_setref (socks, idx++, obj); + } else if (mode == 1 && (pfd->revents & (MONO_POLLOUT | POLL_ERRORS)) != 0) { + mono_array_setref (socks, idx++, obj); + } else if ((pfd->revents & POLL_ERRORS) != 0) { + mono_array_setref (socks, idx++, obj); + } + } + + *sockets = socks; + g_free (pfds); +} + +static MonoObject* +int_to_object (MonoDomain *domain, int val, MonoError *error) +{ + return mono_value_box_checked (domain, mono_get_int32_class (), &val, error); +} + +void +ves_icall_System_Net_Sockets_Socket_GetSocketOption_obj_internal (gsize sock, gint32 level, gint32 name, MonoObject **obj_val, gint32 *werror) +{ + int system_level = 0; + int system_name = 0; + int ret; + int val = 0; + socklen_t valsize = sizeof (val); + struct linger linger; + socklen_t lingersize = sizeof (linger); + int time_ms = 0; + socklen_t time_ms_size = sizeof (time_ms); +#ifdef SO_PEERCRED +# if defined(__OpenBSD__) + struct sockpeercred cred; +# else + struct ucred cred; +# endif + socklen_t credsize = sizeof (cred); +#endif + MonoError error; + MonoDomain *domain = mono_domain_get (); + MonoObject *obj; + MonoClass *obj_class; + MonoClassField *field; + + *werror = 0; + +#if !defined(SO_EXCLUSIVEADDRUSE) && defined(SO_REUSEADDR) + if (level == SocketOptionLevel_Socket && name == SocketOptionName_ExclusiveAddressUse) { + system_level = SOL_SOCKET; + system_name = SO_REUSEADDR; + ret = 0; + } else +#endif + { + ret = convert_sockopt_level_and_name ((MonoSocketOptionLevel)level, (MonoSocketOptionName)name, &system_level, &system_name); + } + + if (ret == -1) { + *werror = WSAENOPROTOOPT; + return; + } + if (ret == -2) { + *obj_val = int_to_object (domain, 0, &error); + mono_error_set_pending_exception (&error); + return; + } + + MONO_ENTER_GC_SAFE; + + /* No need to deal with MulticastOption names here, because + * you cant getsockopt AddMembership or DropMembership (the + * int getsockopt will error, causing an exception) + */ + switch (name) { + case SocketOptionName_Linger: + case SocketOptionName_DontLinger: + ret = mono_w32socket_getsockopt (sock, system_level, system_name, &linger, &lingersize); + break; + + case SocketOptionName_SendTimeout: + case SocketOptionName_ReceiveTimeout: + ret = mono_w32socket_getsockopt (sock, system_level, system_name, (char *)&time_ms, &time_ms_size); + break; + +#ifdef SO_PEERCRED + case SocketOptionName_PeerCred: + ret = mono_w32socket_getsockopt (sock, system_level, system_name, &cred, &credsize); + break; +#endif + + default: + ret = mono_w32socket_getsockopt (sock, system_level, system_name, &val, &valsize); + } + + MONO_EXIT_GC_SAFE; + + if (ret == SOCKET_ERROR) { + *werror = mono_w32socket_get_last_error (); + return; + } + + switch (name) { + case SocketOptionName_Linger: + /* build a System.Net.Sockets.LingerOption */ + obj_class = mono_class_load_from_name (get_socket_assembly (), + "System.Net.Sockets", + "LingerOption"); + obj = mono_object_new_checked (domain, obj_class, &error); + if (!mono_error_ok (&error)) { + mono_error_set_pending_exception (&error); + return; + } + + /* Locate and set the fields "bool enabled" and "int + * lingerTime" + */ + field = mono_class_get_field_from_name(obj_class, "enabled"); + *(guint8 *)(((char *)obj)+field->offset) = linger.l_onoff; + + field = mono_class_get_field_from_name(obj_class, "lingerTime"); + *(guint32 *)(((char *)obj)+field->offset)=linger.l_linger; + break; + case SocketOptionName_DontLinger: + /* construct a bool int in val - true if linger is off */ + obj = int_to_object (domain, !linger.l_onoff, &error); + mono_error_set_pending_exception (&error); + break; + case SocketOptionName_SendTimeout: + case SocketOptionName_ReceiveTimeout: + obj = int_to_object (domain, time_ms, &error); + mono_error_set_pending_exception (&error); + break; + +#ifdef SO_PEERCRED + case SocketOptionName_PeerCred: { + /* + * build a Mono.Posix.PeerCred+PeerCredData if + * possible + */ + static MonoImage *mono_posix_image = NULL; + MonoPeerCredData *cred_data; + + if (mono_posix_image == NULL) { + mono_posix_image = mono_image_loaded ("Mono.Posix"); + if (!mono_posix_image) { + MonoAssembly *sa = mono_assembly_open ("Mono.Posix.dll", NULL); + if (!sa) { + *werror = WSAENOPROTOOPT; + return; + } else { + mono_posix_image = mono_assembly_get_image (sa); + } + } + } + + obj_class = mono_class_load_from_name (mono_posix_image, + "Mono.Posix", + "PeerCredData"); + obj = mono_object_new_checked (domain, obj_class, &error); + if (!mono_error_ok (&error)) { + mono_error_set_pending_exception (&error); + return; + } + cred_data = (MonoPeerCredData *)obj; + cred_data->pid = cred.pid; + cred_data->uid = cred.uid; + cred_data->gid = cred.gid; + break; + } +#endif + + default: +#if !defined(SO_EXCLUSIVEADDRUSE) && defined(SO_REUSEADDR) + if (level == SocketOptionLevel_Socket && name == SocketOptionName_ExclusiveAddressUse) + val = val ? 0 : 1; +#endif + obj = int_to_object (domain, val, &error); + mono_error_set_pending_exception (&error); + } + + *obj_val = obj; +} + +void +ves_icall_System_Net_Sockets_Socket_GetSocketOption_arr_internal (gsize sock, gint32 level, gint32 name, MonoArray **byte_val, gint32 *werror) +{ + int system_level = 0; + int system_name = 0; + int ret; + guchar *buf; + socklen_t valsize; + + *werror = 0; + + ret = convert_sockopt_level_and_name((MonoSocketOptionLevel)level, (MonoSocketOptionName)name, &system_level, + &system_name); + if (ret == -1) { + *werror = WSAENOPROTOOPT; + return; + } + if (ret == -2) + return; + + valsize = mono_array_length (*byte_val); + buf = mono_array_addr (*byte_val, guchar, 0); + + MONO_ENTER_GC_SAFE; + + ret = mono_w32socket_getsockopt (sock, system_level, system_name, buf, &valsize); + + MONO_EXIT_GC_SAFE; + + if (ret == SOCKET_ERROR) + *werror = mono_w32socket_get_last_error (); +} + +#if defined(HAVE_STRUCT_IP_MREQN) || defined(HAVE_STRUCT_IP_MREQ) +static struct in_addr +ipaddress_to_struct_in_addr (MonoObject *ipaddr) +{ + struct in_addr inaddr; + MonoClassField *field; + + 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 little-endian order + */ + inaddr.s_addr = GUINT_FROM_LE ((guint32)*(guint64 *)(((char *)ipaddr) + field->offset)); + + return inaddr; +} + +static struct in6_addr +ipaddress_to_struct_in6_addr (MonoObject *ipaddr) +{ + struct in6_addr in6addr; + MonoClassField *field; + MonoArray *data; + int i; + + field = mono_class_get_field_from_name (ipaddr->vtable->klass, "m_Numbers"); + g_assert (field); + data = *(MonoArray **)(((char *)ipaddr) + field->offset); + + for (i = 0; i < 8; i++) { + const guint16 s = GUINT16_TO_BE (mono_array_get (data, guint16, i)); + +/* Solaris/MacOS have only the 8 bit version. */ +#ifndef s6_addr16 + in6addr.s6_addr[2 * i + 1] = (s >> 8) & 0xff; + in6addr.s6_addr[2 * i] = s & 0xff; +#else + in6addr.s6_addr16[i] = s; +#endif + } + return in6addr; +} +#endif + +#if defined(__APPLE__) || defined(__FreeBSD__) + +static int +get_local_interface_id (int family) +{ +#if !defined(HAVE_GETIFADDRS) || !defined(HAVE_IF_NAMETOINDEX) + return 0; +#else + struct ifaddrs *ifap = NULL, *ptr; + int idx = 0; + + if (getifaddrs (&ifap)) + return 0; + + for (ptr = ifap; ptr; ptr = ptr->ifa_next) { + if (!ptr->ifa_addr || !ptr->ifa_name) + continue; + if (ptr->ifa_addr->sa_family != family) + continue; + if ((ptr->ifa_flags & IFF_LOOPBACK) != 0) + continue; + if ((ptr->ifa_flags & IFF_MULTICAST) == 0) + continue; + + idx = if_nametoindex (ptr->ifa_name); + break; + } + + freeifaddrs (ifap); + return idx; +#endif +} + +#endif /* defined(__APPLE__) || defined(__FreeBSD__) */ + +void +ves_icall_System_Net_Sockets_Socket_SetSocketOption_internal (gsize sock, gint32 level, gint32 name, MonoObject *obj_val, MonoArray *byte_val, gint32 int_val, gint32 *werror) +{ + struct linger linger; + int system_level = 0; + int system_name = 0; + int ret; + int sol_ip; + int sol_ipv6; + + *werror = 0; + + sol_ipv6 = mono_networking_get_ipv6_protocol (); + sol_ip = mono_networking_get_ip_protocol (); + + ret = convert_sockopt_level_and_name ((MonoSocketOptionLevel)level, (MonoSocketOptionName)name, &system_level, + &system_name); + +#if !defined(SO_EXCLUSIVEADDRUSE) && defined(SO_REUSEADDR) + if (level == SocketOptionLevel_Socket && name == SocketOptionName_ExclusiveAddressUse) { + system_name = SO_REUSEADDR; + int_val = int_val ? 0 : 1; + ret = 0; + } +#endif + + if (ret == -1) { + *werror = WSAENOPROTOOPT; + return; + } + if (ret == -2) + return; + + /* Only one of obj_val, byte_val or int_val has data */ + if (obj_val) { + MonoClassField *field; + int valsize; + + switch (name) { + case SocketOptionName_Linger: + /* Dig out "bool enabled" and "int lingerTime" + * fields + */ + field = mono_class_get_field_from_name (obj_val->vtable->klass, "enabled"); + linger.l_onoff = *(guint8 *)(((char *)obj_val) + field->offset); + field = mono_class_get_field_from_name (obj_val->vtable->klass, "lingerTime"); + linger.l_linger = *(guint32 *)(((char *)obj_val) + field->offset); + + valsize = sizeof (linger); + ret = mono_w32socket_setsockopt (sock, system_level, system_name, &linger, valsize); + break; + case SocketOptionName_AddMembership: + case SocketOptionName_DropMembership: +#if defined(HAVE_STRUCT_IP_MREQN) || defined(HAVE_STRUCT_IP_MREQ) + { + MonoObject *address = NULL; + + if (system_level == sol_ipv6) { + struct ipv6_mreq mreq6; + + /* + * Get group address + */ + field = mono_class_get_field_from_name (obj_val->vtable->klass, "m_Group"); + g_assert (field); + address = *(MonoObject **)(((char *)obj_val) + field->offset); + + if (address) + mreq6.ipv6mr_multiaddr = ipaddress_to_struct_in6_addr (address); + + field = mono_class_get_field_from_name (obj_val->vtable->klass, "m_Interface"); + mreq6.ipv6mr_interface = *(guint64 *)(((char *)obj_val) + field->offset); + +#if defined(__APPLE__) || defined(__FreeBSD__) + /* + * Bug #5504: + * + * Mac OS Lion doesn't allow ipv6mr_interface = 0. + * + * Tests on Windows and Linux show that the multicast group is only + * joined on one NIC when interface = 0, so we simply use the interface + * id from the first non-loopback interface (this is also what + * Dns.GetHostName (string.Empty) would return). + */ + if (!mreq6.ipv6mr_interface) + mreq6.ipv6mr_interface = get_local_interface_id (AF_INET6); +#endif + + ret = mono_w32socket_setsockopt (sock, system_level, system_name, &mreq6, sizeof (mreq6)); + } else if (system_level == sol_ip) { +#ifdef HAVE_STRUCT_IP_MREQN + struct ip_mreqn mreq = {{0}}; +#else + struct ip_mreq mreq = {{0}}; +#endif /* HAVE_STRUCT_IP_MREQN */ + + /* + * pain! MulticastOption holds two IPAddress + * members, so I have to dig the value out of + * those :-( + */ + field = mono_class_get_field_from_name (obj_val->vtable->klass, "group"); + address = *(MonoObject **)(((char *)obj_val) + field->offset); + + /* address might not be defined and if so, set the address to ADDR_ANY. + */ + if (address) + mreq.imr_multiaddr = ipaddress_to_struct_in_addr (address); + + field = mono_class_get_field_from_name (obj_val->vtable->klass, "localAddress"); + address = *(MonoObject **)(((char *)obj_val) + field->offset); + +#ifdef HAVE_STRUCT_IP_MREQN + if (address) + mreq.imr_address = ipaddress_to_struct_in_addr (address); + + field = mono_class_get_field_from_name (obj_val->vtable->klass, "ifIndex"); + mreq.imr_ifindex = *(gint32 *)(((char *)obj_val) + field->offset); +#else + if (address) + mreq.imr_interface = ipaddress_to_struct_in_addr (address); +#endif /* HAVE_STRUCT_IP_MREQN */ + + ret = mono_w32socket_setsockopt (sock, system_level, system_name, &mreq, sizeof (mreq)); + } + break; + } +#endif /* HAVE_STRUCT_IP_MREQN || HAVE_STRUCT_IP_MREQ */ + default: + /* Cause an exception to be thrown */ + *werror = WSAEINVAL; + return; + } + } else if (byte_val!=NULL) { + int valsize = mono_array_length (byte_val); + guchar *buf = mono_array_addr (byte_val, guchar, 0); + + switch(name) { + case SocketOptionName_DontLinger: + if (valsize == 1) { + linger.l_onoff = (*buf) ? 0 : 1; + linger.l_linger = 0; + ret = mono_w32socket_setsockopt (sock, system_level, system_name, &linger, sizeof (linger)); + } else { + *werror = WSAEINVAL; + } + break; + default: + ret = mono_w32socket_setsockopt (sock, system_level, system_name, buf, valsize); + break; + } + } else { + /* ReceiveTimeout/SendTimeout get here */ + switch (name) { + case SocketOptionName_DontLinger: + linger.l_onoff = !int_val; + linger.l_linger = 0; + ret = mono_w32socket_setsockopt (sock, system_level, system_name, &linger, sizeof (linger)); + break; + case SocketOptionName_MulticastInterface: +#ifndef HOST_WIN32 +#ifdef HAVE_STRUCT_IP_MREQN + int_val = GUINT32_FROM_BE (int_val); + if ((int_val & 0xff000000) == 0) { + /* int_val is interface index */ + struct ip_mreqn mreq = {{0}}; + mreq.imr_ifindex = int_val; + ret = mono_w32socket_setsockopt (sock, system_level, system_name, (char *) &mreq, sizeof (mreq)); + break; + } + int_val = GUINT32_TO_BE (int_val); +#endif /* HAVE_STRUCT_IP_MREQN */ +#endif /* HOST_WIN32 */ + /* int_val is in_addr */ + ret = mono_w32socket_setsockopt (sock, system_level, system_name, (char *) &int_val, sizeof (int_val)); + break; + case SocketOptionName_DontFragment: +#ifdef HAVE_IP_MTU_DISCOVER + /* Fiddle with the value slightly if we're + * turning DF on + */ + if (int_val == 1) + int_val = IP_PMTUDISC_DO; + /* Fall through */ +#endif + + default: + ret = mono_w32socket_setsockopt (sock, system_level, system_name, (char *) &int_val, sizeof (int_val)); + } + } + + if (ret == SOCKET_ERROR) + *werror = mono_w32socket_get_last_error (); +} + +void +ves_icall_System_Net_Sockets_Socket_Shutdown_internal (gsize sock, gint32 how, gint32 *werror) +{ + int ret; + gboolean interrupted; + + *werror = 0; + + mono_thread_info_install_interrupt (abort_syscall, (gpointer) (gsize) mono_native_thread_id_get (), &interrupted); + if (interrupted) { + *werror = WSAEINTR; + return; + } + + MONO_ENTER_GC_SAFE; + + /* Currently, the values for how (recv=0, send=1, both=2) match the BSD API */ + ret = mono_w32socket_shutdown (sock, how); + + MONO_EXIT_GC_SAFE; + + if (ret == SOCKET_ERROR) + *werror = mono_w32socket_get_last_error (); + + mono_thread_info_uninstall_interrupt (&interrupted); + if (interrupted) { + *werror = WSAEINTR; + } + +} + +gint +ves_icall_System_Net_Sockets_Socket_IOControl_internal (gsize sock, gint32 code, MonoArray *input, MonoArray *output, gint32 *werror) +{ + glong output_bytes = 0; + gchar *i_buffer, *o_buffer; + gint i_len, o_len; + gint ret; + + *werror = 0; + + if ((guint32)code == FIONBIO) + /* Invalid command. Must use Socket.Blocking */ + return -1; + + if (input == NULL) { + i_buffer = NULL; + i_len = 0; + } else { + i_buffer = mono_array_addr (input, gchar, 0); + i_len = mono_array_length (input); + } + + if (output == NULL) { + o_buffer = NULL; + o_len = 0; + } else { + o_buffer = mono_array_addr (output, gchar, 0); + o_len = mono_array_length (output); + } + + MONO_ENTER_GC_SAFE; + + ret = mono_w32socket_ioctl (sock, code, i_buffer, i_len, o_buffer, o_len, &output_bytes); + + MONO_EXIT_GC_SAFE; + + if (ret == SOCKET_ERROR) { + *werror = mono_w32socket_get_last_error (); + return -1; + } + + return (gint)output_bytes; +} + +static gboolean +addrinfo_to_IPHostEntry (MonoAddressInfo *info, MonoString **h_name, MonoArray **h_aliases, MonoArray **h_addr_list, gboolean add_local_ips, MonoError *error) +{ + gint32 count, i; + MonoAddressEntry *ai = NULL; + struct in_addr *local_in = NULL; + int nlocal_in = 0; + struct in6_addr *local_in6 = NULL; + int nlocal_in6 = 0; + int addr_index; + MonoDomain *domain = mono_domain_get (); + + mono_error_init (error); + addr_index = 0; + *h_aliases = mono_array_new_checked (domain, mono_get_string_class (), 0, error); + return_val_if_nok (error, FALSE); + if (add_local_ips) { + local_in = (struct in_addr *) mono_get_local_interfaces (AF_INET, &nlocal_in); + local_in6 = (struct in6_addr *) mono_get_local_interfaces (AF_INET6, &nlocal_in6); + if (nlocal_in || nlocal_in6) { + char addr [INET6_ADDRSTRLEN]; + *h_addr_list = mono_array_new_checked (domain, mono_get_string_class (), nlocal_in + nlocal_in6, error); + if (!is_ok (error)) + goto leave; + + if (nlocal_in) { + MonoString *addr_string; + int i; + + for (i = 0; i < nlocal_in; i++) { + MonoAddress maddr; + mono_address_init (&maddr, AF_INET, &local_in [i]); + if (mono_networking_addr_to_str (&maddr, addr, sizeof (addr))) { + addr_string = mono_string_new (domain, addr); + mono_array_setref (*h_addr_list, addr_index, addr_string); + addr_index++; + } + } + } + + if (nlocal_in6) { + MonoString *addr_string; + int i; + + for (i = 0; i < nlocal_in6; i++) { + MonoAddress maddr; + mono_address_init (&maddr, AF_INET6, &local_in6 [i]); + if (mono_networking_addr_to_str (&maddr, addr, sizeof (addr))) { + addr_string = mono_string_new (domain, addr); + mono_array_setref (*h_addr_list, addr_index, addr_string); + addr_index++; + } + } + } + + leave: + g_free (local_in); + g_free (local_in6); + if (info) + mono_free_address_info (info); + return is_ok (error);; + } + + g_free (local_in); + g_free (local_in6); + } + + for (count = 0, ai = info->entries; ai != NULL; ai = ai->next) { + if (ai->family != AF_INET && ai->family != AF_INET6) + continue; + count++; + } + + *h_addr_list = mono_array_new_checked (domain, mono_get_string_class (), count, error); + if (!is_ok (error)) + goto leave2; + + for (ai = info->entries, i = 0; ai != NULL; ai = ai->next) { + MonoAddress maddr; + MonoString *addr_string; + char buffer [INET6_ADDRSTRLEN]; /* Max. size for IPv6 */ + + if ((ai->family != PF_INET) && (ai->family != PF_INET6)) + continue; + + mono_address_init (&maddr, ai->family, &ai->address); + if (mono_networking_addr_to_str (&maddr, buffer, sizeof (buffer))) + addr_string = mono_string_new (domain, buffer); + else + addr_string = mono_string_new (domain, ""); + + mono_array_setref (*h_addr_list, addr_index, addr_string); + + if (!i) { + i++; + if (ai->canonical_name != NULL) { + *h_name = mono_string_new (domain, ai->canonical_name); + } else { + *h_name = mono_string_new (domain, buffer); + } + } + + addr_index++; + } + +leave2: + if (info) + mono_free_address_info (info); + + return is_ok (error); +} + +MonoBoolean +ves_icall_System_Net_Dns_GetHostByName_internal (MonoString *host, MonoString **h_name, MonoArray **h_aliases, MonoArray **h_addr_list, gint32 hint) +{ + MonoError error; + gboolean add_local_ips = FALSE, add_info_ok = TRUE; + gchar this_hostname [256]; + MonoAddressInfo *info = NULL; + + char *hostname = mono_string_to_utf8_checked (host, &error); + if (mono_error_set_pending_exception (&error)) + return FALSE; + + if (*hostname == '\0') { + add_local_ips = TRUE; + *h_name = host; + } + + if (!add_local_ips && gethostname (this_hostname, sizeof (this_hostname)) != -1) { + if (!strcmp (hostname, this_hostname)) { + add_local_ips = TRUE; + *h_name = host; + } + } + +#ifdef HOST_WIN32 + // Win32 APIs already returns local interface addresses for empty hostname ("") + // so we never want to add them manually. + add_local_ips = FALSE; + if (mono_get_address_info(hostname, 0, MONO_HINT_CANONICAL_NAME | hint, &info)) + add_info_ok = FALSE; +#else + if (*hostname && mono_get_address_info (hostname, 0, MONO_HINT_CANONICAL_NAME | hint, &info)) + add_info_ok = FALSE; +#endif + + g_free(hostname); + + if (add_info_ok) { + MonoBoolean result = addrinfo_to_IPHostEntry (info, h_name, h_aliases, h_addr_list, add_local_ips, &error); + mono_error_set_pending_exception (&error); + return result; + } + return FALSE; +} + +MonoBoolean +ves_icall_System_Net_Dns_GetHostByAddr_internal (MonoString *addr, MonoString **h_name, MonoArray **h_aliases, MonoArray **h_addr_list, gint32 hint) +{ + char *address; + struct sockaddr_in saddr; + struct sockaddr_in6 saddr6; + MonoAddressInfo *info = NULL; + MonoError error; + gint32 family; + gchar hostname [NI_MAXHOST] = { 0 }; + gboolean ret; + + address = mono_string_to_utf8_checked (addr, &error); + if (mono_error_set_pending_exception (&error)) + return FALSE; + + if (inet_pton (AF_INET, address, &saddr.sin_addr ) == 1) { + family = AF_INET; + saddr.sin_family = AF_INET; + } else if (inet_pton (AF_INET6, address, &saddr6.sin6_addr) == 1) { + family = AF_INET6; + saddr6.sin6_family = AF_INET6; + } else { + g_free (address); + return FALSE; + } + + g_free (address); + + MONO_ENTER_GC_SAFE; + + switch (family) { + case AF_INET: { +#if HAVE_SOCKADDR_IN_SIN_LEN + saddr.sin_len = sizeof (saddr); +#endif + ret = getnameinfo ((struct sockaddr*)&saddr, sizeof (saddr), hostname, sizeof (hostname), NULL, 0, 0) == 0; + break; + } + case AF_INET6: { +#if HAVE_SOCKADDR_IN6_SIN_LEN + saddr6.sin6_len = sizeof (saddr6); +#endif + ret = getnameinfo ((struct sockaddr*)&saddr6, sizeof (saddr6), hostname, sizeof (hostname), NULL, 0, 0) == 0; + break; + } + default: + g_assert_not_reached (); + } + + MONO_EXIT_GC_SAFE; + + if (!ret) + return FALSE; + + if (mono_get_address_info (hostname, 0, hint | MONO_HINT_CANONICAL_NAME | MONO_HINT_CONFIGURED_ONLY, &info) != 0) + return FALSE; + + MonoBoolean result = addrinfo_to_IPHostEntry (info, h_name, h_aliases, h_addr_list, FALSE, &error); + mono_error_set_pending_exception (&error); + return result; +} + +MonoBoolean +ves_icall_System_Net_Dns_GetHostName_internal (MonoString **h_name) +{ + gchar hostname [NI_MAXHOST] = { 0 }; + int ret; + + ret = gethostname (hostname, sizeof (hostname)); + if (ret == -1) + return FALSE; + + *h_name = mono_string_new (mono_domain_get (), hostname); + + return TRUE; +} + +#if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT | HAVE_UWP_WINAPI_SUPPORT) +gboolean +ves_icall_System_Net_Sockets_Socket_SendFile_internal (gsize sock, MonoString *filename, MonoArray *pre_buffer, MonoArray *post_buffer, gint flags, gint32 *werror, gboolean blocking) +{ + HANDLE file; + gboolean ret; + gboolean interrupted; + TRANSMIT_FILE_BUFFERS buffers; + + if (filename == NULL) + return FALSE; + + /* FIXME: replace file by a proper fd that we can call open and close on, as they are interruptible */ + + file = ves_icall_System_IO_MonoIO_Open (filename, FileMode_Open, FileAccess_Read, FileShare_Read, 0, werror); + + if (file == INVALID_HANDLE_VALUE) { + SetLastError (*werror); + return FALSE; + } + + memset (&buffers, 0, sizeof (buffers)); + if (pre_buffer != NULL) { + buffers.Head = mono_array_addr (pre_buffer, guchar, 0); + buffers.HeadLength = mono_array_length (pre_buffer); + } + if (post_buffer != NULL) { + buffers.Tail = mono_array_addr (post_buffer, guchar, 0); + buffers.TailLength = mono_array_length (post_buffer); + } + + mono_thread_info_install_interrupt (abort_syscall, (gpointer) (gsize) mono_native_thread_id_get (), &interrupted); + if (interrupted) { + CloseHandle (file); + SetLastError (WSAEINTR); + return FALSE; + } + + MONO_ENTER_GC_SAFE; + ret = mono_w32socket_transmit_file (sock, file, &buffers, flags, blocking); + MONO_EXIT_GC_SAFE; + + if (!ret) + *werror = mono_w32socket_get_last_error (); + + mono_thread_info_uninstall_interrupt (&interrupted); + if (interrupted) { + CloseHandle (file); + *werror = WSAEINTR; + return FALSE; + } + + MONO_ENTER_GC_SAFE; + + CloseHandle (file); + + MONO_EXIT_GC_SAFE; + + if (*werror) + return FALSE; + + return ret; +} +#endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT | HAVE_UWP_WINAPI_SUPPORT) */ + +void +mono_network_init (void) +{ + mono_networking_init (); + mono_w32socket_initialize (); +} + +void +mono_network_cleanup (void) +{ + mono_w32socket_cleanup (); + mono_networking_shutdown (); +} + +void +icall_cancel_blocking_socket_operation (MonoThread *thread) +{ + MonoInternalThread *internal; + + internal = thread->internal_thread; + g_assert (internal); + + mono_thread_info_abort_socket_syscall_for_close (MONO_UINT_TO_NATIVE_THREAD_ID (internal->tid)); +} + +#endif /* #ifndef DISABLE_SOCKETS */ diff --git a/mono/metadata/w32socket.h b/mono/metadata/w32socket.h new file mode 100644 index 00000000000..dade775479a --- /dev/null +++ b/mono/metadata/w32socket.h @@ -0,0 +1,281 @@ +/* + * w32socket.h: System.Net.Sockets.Socket support + * + * Author: + * Dick Porter (dick@ximian.com) + * + * (C) 2001 Ximian, Inc. + */ + +#ifndef _MONO_METADATA_W32SOCKET_H_ +#define _MONO_METADATA_W32SOCKET_H_ + +#include +#include + +#include + +#define INVALID_SOCKET ((SOCKET)(guint32)(~0)) +#define SOCKET_ERROR (-1) + +#ifndef HOST_WIN32 +typedef gint SOCKET; +#endif + +/* This is a copy of System.Net.Sockets.SocketType */ +typedef enum { + SocketType_Stream=1, + SocketType_Dgram=2, + SocketType_Raw=3, + SocketType_Rdm=4, + SocketType_Seqpacket=5, + SocketType_Unknown=-1 +} MonoSocketType; + +/* This is a copy of System.Net.Sockets.AddressFamily */ +typedef enum { + AddressFamily_Unknown=-1, + AddressFamily_Unspecified=0, + AddressFamily_Unix=1, + AddressFamily_InterNetwork=2, + AddressFamily_ImpLink=3, + AddressFamily_Pup=4, + AddressFamily_Chaos=5, + AddressFamily_NS=6, + AddressFamily_Ipx=6, + AddressFamily_Iso=7, + AddressFamily_Osi=7, + AddressFamily_Ecma=8, + AddressFamily_DataKit=9, + AddressFamily_Ccitt=10, + AddressFamily_Sna=11, + AddressFamily_DecNet=12, + AddressFamily_DataLink=13, + AddressFamily_Lat=14, + AddressFamily_HyperChannel=15, + AddressFamily_AppleTalk=16, + AddressFamily_NetBios=17, + AddressFamily_VoiceView=18, + AddressFamily_FireFox=19, + AddressFamily_Banyan=21, + AddressFamily_Atm=22, + AddressFamily_InterNetworkV6=23, + AddressFamily_Cluster=24, + AddressFamily_Ieee12844=25, + AddressFamily_Irda=26, + AddressFamily_NetworkDesigners=28 +} MonoAddressFamily; + +/* This is a copy of System.Net.Sockets.ProtocolType */ +typedef enum { + ProtocolType_IP=0, + ProtocolType_Icmp=1, + ProtocolType_Igmp=2, + ProtocolType_Ggp=3, + ProtocolType_Tcp=6, + ProtocolType_Pup=12, + ProtocolType_Udp=17, + ProtocolType_Idp=22, + ProtocolType_IPv6=41, + ProtocolType_ND=77, + ProtocolType_Raw=255, + ProtocolType_Unspecified=0, + ProtocolType_Ipx=1000, + ProtocolType_Spx=1256, + ProtocolType_SpxII=1257, + ProtocolType_Unknown=-1 +} MonoProtocolType; + +/* This is a copy of System.Net.Sockets.SocketOptionLevel */ +typedef enum { + SocketOptionLevel_Socket=65535, + SocketOptionLevel_IP=0, + SocketOptionLevel_IPv6=41, + SocketOptionLevel_Tcp=6, + SocketOptionLevel_Udp=17 +} MonoSocketOptionLevel; + +/* This is a copy of System.Net.Sockets.SocketOptionName */ +typedef enum { + SocketOptionName_Debug=1, + SocketOptionName_AcceptConnection=2, + SocketOptionName_ReuseAddress=4, + SocketOptionName_KeepAlive=8, + SocketOptionName_DontRoute=16, + SocketOptionName_IPProtectionLevel = 23, + SocketOptionName_IPv6Only = 27, + SocketOptionName_Broadcast=32, + SocketOptionName_UseLoopback=64, + SocketOptionName_Linger=128, + SocketOptionName_OutOfBandInline=256, + SocketOptionName_DontLinger= -129, + SocketOptionName_ExclusiveAddressUse= -5, + SocketOptionName_SendBuffer= 4097, + SocketOptionName_ReceiveBuffer=4098, + SocketOptionName_SendLowWater=4099, + SocketOptionName_ReceiveLowWater=4100, + SocketOptionName_SendTimeout=4101, + SocketOptionName_ReceiveTimeout=4102, + SocketOptionName_Error=4103, + SocketOptionName_Type=4104, + SocketOptionName_MaxConnections=2147483647, + SocketOptionName_IPOptions=1, + SocketOptionName_HeaderIncluded=2, + SocketOptionName_TypeOfService=3, + SocketOptionName_IpTimeToLive=4, + SocketOptionName_MulticastInterface=9, + SocketOptionName_MulticastTimeToLive=10, + SocketOptionName_MulticastLoopback=11, + SocketOptionName_AddMembership=12, + SocketOptionName_DropMembership=13, + SocketOptionName_DontFragment=14, + SocketOptionName_AddSourceMembership=15, + SocketOptionName_DropSourceMembership=16, + SocketOptionName_BlockSource=17, + SocketOptionName_UnblockSource=18, + SocketOptionName_PacketInformation=19, + SocketOptionName_NoDelay=1, + SocketOptionName_BsdUrgent=2, + SocketOptionName_Expedited=2, + SocketOptionName_NoChecksum=1, + SocketOptionName_ChecksumCoverage=20, + SocketOptionName_HopLimit=21, + + /* This is Mono-specific, keep it in sync with + * Mono.Posix/PeerCred.cs + */ + SocketOptionName_PeerCred=10001 +} MonoSocketOptionName; + +/* This is a copy of System.Net.Sockets.SocketFlags */ +typedef enum { + SocketFlags_None = 0x0000, + SocketFlags_OutOfBand = 0x0001, + SocketFlags_MaxIOVectorLength = 0x0010, + SocketFlags_Peek = 0x0002, + SocketFlags_DontRoute = 0x0004, + SocketFlags_Partial = 0x8000 +} MonoSocketFlags; + +typedef struct +{ + MonoObject obj; + gint pid; + gint uid; + gint gid; +} MonoPeerCredData; + +gpointer +ves_icall_System_Net_Sockets_Socket_Socket_internal (MonoObject *this_obj, gint32 family, gint32 type, gint32 proto, + gint32 *error); + +void +ves_icall_System_Net_Sockets_Socket_Close_internal (gsize sock, gint32 *error); + +gint32 +ves_icall_System_Net_Sockets_SocketException_WSAGetLastError_internal (void); + +gint32 +ves_icall_System_Net_Sockets_Socket_Available_internal (gsize sock, gint32 *error); + +void +ves_icall_System_Net_Sockets_Socket_Blocking_internal (gsize sock, gboolean block, gint32 *error); + +gpointer +ves_icall_System_Net_Sockets_Socket_Accept_internal (gsize sock, gint32 *error, gboolean blocking); + +void +ves_icall_System_Net_Sockets_Socket_Listen_internal (gsize sock, guint32 backlog, gint32 *error); + +MonoObject* +ves_icall_System_Net_Sockets_Socket_LocalEndPoint_internal (gsize sock, gint32 af, gint32 *error); + +MonoObject* +ves_icall_System_Net_Sockets_Socket_RemoteEndPoint_internal (gsize sock, gint32 af, gint32 *error); + +void +ves_icall_System_Net_Sockets_Socket_Bind_internal (gsize sock, MonoObject *sockaddr, gint32 *error); + +void +ves_icall_System_Net_Sockets_Socket_Connect_internal (gsize sock, MonoObject *sockaddr, gint32 *error, gboolean blocking); + +gint32 +ves_icall_System_Net_Sockets_Socket_Receive_internal (gsize sock, MonoArray *buffer, gint32 offset, gint32 count, + gint32 flags, gint32 *error, gboolean blocking); + +gint32 +ves_icall_System_Net_Sockets_Socket_Receive_array_internal (gsize sock, MonoArray *buffers, gint32 flags, gint32 *error, + gboolean blocking); + +gint32 +ves_icall_System_Net_Sockets_Socket_ReceiveFrom_internal (gsize sock, MonoArray *buffer, gint32 offset, gint32 count, + gint32 flags, MonoObject **sockaddr, gint32 *error, gboolean blocking); + +gint32 +ves_icall_System_Net_Sockets_Socket_Send_internal (gsize sock, MonoArray *buffer, gint32 offset, gint32 count, + gint32 flags, gint32 *error, gboolean blocking); + +gint32 +ves_icall_System_Net_Sockets_Socket_Send_array_internal (gsize sock, MonoArray *buffers, gint32 flags, gint32 *error, + gboolean blocking); + +gint32 +ves_icall_System_Net_Sockets_Socket_SendTo_internal (gsize sock, MonoArray *buffer, gint32 offset, gint32 count, + gint32 flags, MonoObject *sockaddr, gint32 *error, gboolean blocking); + +void +ves_icall_System_Net_Sockets_Socket_Select_internal (MonoArray **sockets, gint32 timeout, gint32 *error); + +void +ves_icall_System_Net_Sockets_Socket_Shutdown_internal (gsize sock, gint32 how, gint32 *error); + +void +ves_icall_System_Net_Sockets_Socket_GetSocketOption_obj_internal (gsize sock, gint32 level, gint32 name, + MonoObject **obj_val, gint32 *error); + +void +ves_icall_System_Net_Sockets_Socket_GetSocketOption_arr_internal (gsize sock, gint32 level, gint32 name, + MonoArray **byte_val, gint32 *error); + +void +ves_icall_System_Net_Sockets_Socket_SetSocketOption_internal (gsize sock, gint32 level, gint32 name, + MonoObject *obj_val, MonoArray *byte_val, gint32 int_val, gint32 *error); + +int +ves_icall_System_Net_Sockets_Socket_IOControl_internal (gsize sock, gint32 code, MonoArray *input, MonoArray *output, + gint32 *error); + +MonoBoolean +ves_icall_System_Net_Dns_GetHostByName_internal (MonoString *host, MonoString **h_name, MonoArray **h_aliases, + MonoArray **h_addr_list, gint32 hint); + +MonoBoolean +ves_icall_System_Net_Dns_GetHostByAddr_internal (MonoString *addr, MonoString **h_name, MonoArray **h_aliases, + MonoArray **h_addr_list, gint32 hint); + +MonoBoolean +ves_icall_System_Net_Dns_GetHostName_internal (MonoString **h_name); + +MonoBoolean +ves_icall_System_Net_Sockets_Socket_Poll_internal (gsize sock, gint mode, gint timeout, gint32 *error); + +void +ves_icall_System_Net_Sockets_Socket_Disconnect_internal (gsize sock, MonoBoolean reuse, gint32 *error); + +gboolean +ves_icall_System_Net_Sockets_Socket_SendFile_internal (gsize sock, MonoString *filename, MonoArray *pre_buffer, + MonoArray *post_buffer, gint flags, gint32 *error, gboolean blocking); + +void +icall_cancel_blocking_socket_operation (MonoThread *thread); + +gboolean +ves_icall_System_Net_Sockets_Socket_SupportPortReuse (MonoProtocolType proto); + +void +mono_network_init(void); + +void +mono_network_cleanup(void); + +#endif /* _MONO_METADATA_W32SOCKET_H_ */ diff --git a/mono/mini/debugger-agent.c b/mono/mini/debugger-agent.c index 0e95ae85f20..ce2656126b6 100644 --- a/mono/mini/debugger-agent.c +++ b/mono/mini/debugger-agent.c @@ -59,11 +59,11 @@ #include #include #include -#include #include #include #include #include +#include #include #include #include diff --git a/mono/mini/driver.c b/mono/mini/driver.c index da2033e5e8c..40de446bc05 100644 --- a/mono/mini/driver.c +++ b/mono/mini/driver.c @@ -33,7 +33,6 @@ #include #include #include -#include #include #include #include diff --git a/msvc/libmonoruntime.vcxproj b/msvc/libmonoruntime.vcxproj index f0e8db4e9a8..6c94d1ff21e 100644 --- a/msvc/libmonoruntime.vcxproj +++ b/msvc/libmonoruntime.vcxproj @@ -28,7 +28,7 @@ - + @@ -100,7 +100,7 @@ - + @@ -154,7 +154,7 @@ - + @@ -199,7 +199,7 @@ - + diff --git a/msvc/libmonoruntime.vcxproj.filters b/msvc/libmonoruntime.vcxproj.filters index 1154e5e8fc7..8de9920a9ea 100644 --- a/msvc/libmonoruntime.vcxproj.filters +++ b/msvc/libmonoruntime.vcxproj.filters @@ -154,7 +154,7 @@ Source Files - + Source Files @@ -247,7 +247,7 @@ Source Files - + Source Files @@ -456,7 +456,7 @@ Header Files - + Header Files @@ -525,7 +525,7 @@ Header Files - + Header Files diff --git a/msvc/pedump.vcxproj b/msvc/pedump.vcxproj index ad0d476fd93..e997a22ae4b 100644 --- a/msvc/pedump.vcxproj +++ b/msvc/pedump.vcxproj @@ -234,7 +234,7 @@ - + diff --git a/msvc/pedump.vcxproj.filters b/msvc/pedump.vcxproj.filters index 72a54e16255..c7f55c9b0d1 100644 --- a/msvc/pedump.vcxproj.filters +++ b/msvc/pedump.vcxproj.filters @@ -79,7 +79,7 @@ Source Files - + Source Files