*/
#include <config.h>
+
+#ifndef DISABLE_SOCKETS
+
#include <glib.h>
#include <pthread.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
-#include <sys/ioctl.h>
+#ifdef HAVE_SYS_UIO_H
+# include <sys/uio.h>
+#endif
+#ifdef HAVE_SYS_IOCTL_H
+# include <sys/ioctl.h>
+#endif
#ifdef HAVE_SYS_FILIO_H
#include <sys/filio.h> /* defines FIONBIO and FIONREAD */
#endif
#include <mono/io-layer/socket-private.h>
#include <mono/io-layer/handles-private.h>
#include <mono/io-layer/socket-wrappers.h>
+#include <mono/utils/mono-poll.h>
+
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <netdb.h>
+#include <arpa/inet.h>
+#ifdef HAVE_SYS_SENDFILE_H
+#include <sys/sendfile.h>
+#endif
#undef DEBUG
static guint32 startup_count=0;
-static pthread_key_t error_key;
-static mono_once_t error_key_once=MONO_ONCE_INIT;
static void socket_close (gpointer handle, gpointer data);
/* No capabilities to register */
}
-static void socket_close (gpointer handle, gpointer data G_GNUC_UNUSED)
+static void socket_close (gpointer handle, gpointer data)
{
int ret;
+ struct _WapiHandle_socket *socket_handle = (struct _WapiHandle_socket *)data;
#ifdef DEBUG
g_message ("%s: closing socket handle %p", __func__, handle);
return;
}
+ /* 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 &&
errnum = errno_to_WSA (errnum, __func__);
WSASetLastError (errnum);
}
+
+ socket_handle->saved_error = 0;
}
int WSAStartup(guint32 requested, WapiWSAData *data)
return(0);
}
-static void error_init(void)
-{
- int ret;
-
- ret = pthread_key_create (&error_key, NULL);
- g_assert (ret == 0);
-}
-
void WSASetLastError(int error)
{
- int ret;
-
- mono_once (&error_key_once, error_init);
- ret = pthread_setspecific (error_key, GINT_TO_POINTER(error));
- g_assert (ret == 0);
+ SetLastError (error);
}
int WSAGetLastError(void)
{
- int err;
- void *errptr;
-
- mono_once (&error_key_once, error_init);
- errptr = pthread_getspecific (error_key);
- err = GPOINTER_TO_INT(errptr);
-
- return(err);
+ return(GetLastError ());
}
int closesocket(guint32 fd)
{
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;
if (startup_count == 0) {
WSASetLastError (WSANOTINITIALISED);
return(INVALID_SOCKET);
}
+
+ if (addr != NULL && *addrlen < sizeof(struct sockaddr)) {
+ WSASetLastError (WSAEFAULT);
+ return(INVALID_SOCKET);
+ }
if (_wapi_handle_type (handle) != WAPI_HANDLE_SOCKET) {
WSASetLastError (WSAENOTSOCK);
return(INVALID_SOCKET);
}
+ ok = _wapi_lookup_handle (handle, WAPI_HANDLE_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 &&
return(INVALID_SOCKET);
}
- new_handle = _wapi_handle_new_fd (WAPI_HANDLE_SOCKET, new_fd, NULL);
+ 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 = _wapi_handle_new_fd (WAPI_HANDLE_SOCKET, new_fd,
+ &new_socket_handle);
if(new_handle == _WAPI_HANDLE_INVALID) {
g_warning ("%s: error creating socket handle", __func__);
WSASetLastError (ERROR_GEN_FAILURE);
socklen_t addrlen)
{
gpointer handle = GUINT_TO_POINTER (fd);
- int ret;
+ struct _WapiHandle_socket *socket_handle;
+ gboolean ok;
gint errnum;
if (startup_count == 0) {
return(SOCKET_ERROR);
}
- do {
- ret = connect (fd, serv_addr, addrlen);
- } while (ret==-1 && errno==EINTR && !_wapi_thread_cur_apc_pending());
-
- if (ret == -1 && errno == EACCES) {
- /* Try setting SO_BROADCAST and connecting again, but
- * keep the original errno
- */
- int true=1;
+ if (connect (fd, serv_addr, addrlen) == -1) {
+ mono_pollfd fds;
+ int so_error;
+ socklen_t len;
errnum = errno;
+
+ if (errno != EINTR) {
+#ifdef DEBUG
+ g_message ("%s: connect error: %s", __func__,
+ strerror (errnum));
+#endif
+
+ errnum = errno_to_WSA (errnum, __func__);
+ if (errnum == WSAEINPROGRESS)
+ errnum = WSAEWOULDBLOCK; /* see bug #73053 */
- ret = setsockopt (fd, SOL_SOCKET, SO_BROADCAST, &true,
- sizeof(true));
- if (ret == 0) {
- do {
- ret = connect (fd, serv_addr, addrlen);
- } while (ret==-1 && errno==EINTR &&
- !_wapi_thread_cur_apc_pending());
+ 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 = _wapi_lookup_handle (handle,
+ WAPI_HANDLE_SOCKET,
+ (gpointer *)&socket_handle);
+ if (ok == FALSE) {
+ /* ECONNRESET means the socket was closed by another thread */
+ if (errnum != WSAECONNRESET)
+ g_warning ("%s: error looking up socket handle %p (error %d)", __func__, handle, errnum);
+ } else {
+ socket_handle->saved_error = errnum;
+ }
+ }
+ return(SOCKET_ERROR);
}
- } else if (ret == -1) {
- errnum = errno;
- }
-
- if (ret == -1) {
+
+ fds.fd = fd;
+ fds.events = POLLOUT;
+ while (mono_poll (&fds, 1, -1) == -1 &&
+ !_wapi_thread_cur_apc_pending ()) {
+ if (errno != EINTR) {
+ errnum = errno_to_WSA (errno, __func__);
+
#ifdef DEBUG
- g_message ("%s: connect error: %s", __func__,
- strerror (errnum));
+ g_message ("%s: connect poll error: %s",
+ __func__, strerror (errno));
#endif
- errnum = errno_to_WSA (errnum, __func__);
- if (errnum == WSAEINPROGRESS)
- errnum = WSAEWOULDBLOCK; /* see bug #73053 */
- WSASetLastError (errnum);
+ 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__);
+
+#ifdef DEBUG
+ g_message ("%s: connect getsockopt error: %s",
+ __func__, strerror (errno));
+#endif
+
+ WSASetLastError (errnum);
+ return(SOCKET_ERROR);
+ }
- return(SOCKET_ERROR);
+ if (so_error != 0) {
+ errnum = errno_to_WSA (so_error, __func__);
+
+ /* Need to save this socket error */
+ ok = _wapi_lookup_handle (handle, WAPI_HANDLE_SOCKET,
+ (gpointer *)&socket_handle);
+ if (ok == FALSE) {
+ g_warning ("%s: error looking up socket handle %p", __func__, handle);
+ } else {
+ socket_handle->saved_error = errnum;
+ }
+
+#ifdef DEBUG
+ g_message ("%s: connect getsockopt returned error: %s",
+ __func__, strerror (so_error));
+#endif
+
+ WSASetLastError (errnum);
+ return(SOCKET_ERROR);
+ }
}
- return(ret);
+
+ return(0);
}
int _wapi_getpeername(guint32 fd, struct sockaddr *name, socklen_t *namelen)
int ret;
struct timeval tv;
void *tmp_val;
-
+ struct _WapiHandle_socket *socket_handle;
+ gboolean ok;
+
if (startup_count == 0) {
WSASetLastError (WSANOTINITIALISED);
return(SOCKET_ERROR);
}
tmp_val = optval;
- if (optname == SO_RCVTIMEO || optname == SO_SNDTIMEO) {
+ if (level == SOL_SOCKET &&
+ (optname == SO_RCVTIMEO || optname == SO_SNDTIMEO)) {
tmp_val = &tv;
*optlen = sizeof (tv);
}
return(SOCKET_ERROR);
}
- if (optname == SO_RCVTIMEO || optname == SO_SNDTIMEO) {
- *((int *) optval) = tv.tv_sec * 1000 + tv.tv_usec;
+ 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) {
+ ok = _wapi_lookup_handle (handle, WAPI_HANDLE_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;
+ }
}
}
struct sockaddr *from, socklen_t *fromlen)
{
gpointer handle = GUINT_TO_POINTER (fd);
+ struct _WapiHandle_socket *socket_handle;
+ gboolean ok;
int ret;
if (startup_count == 0) {
} while (ret == -1 && errno == EINTR &&
!_wapi_thread_cur_apc_pending ());
+ 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 = _wapi_lookup_handle (handle, WAPI_HANDLE_SOCKET,
+ (gpointer *)&socket_handle);
+ if (ok == FALSE || socket_handle->still_readable != 1) {
+ ret = -1;
+ errno = EINTR;
+ }
+ }
+
if (ret == -1) {
gint errnum = errno;
#ifdef DEBUG
return(ret);
}
+static int
+_wapi_recvmsg(guint32 fd, struct msghdr *msg, int recv_flags)
+{
+ gpointer handle = GUINT_TO_POINTER (fd);
+ struct _WapiHandle_socket *socket_handle;
+ gboolean ok;
+ int ret;
+
+ if (startup_count == 0) {
+ WSASetLastError (WSANOTINITIALISED);
+ return(SOCKET_ERROR);
+ }
+
+ if (_wapi_handle_type (handle) != WAPI_HANDLE_SOCKET) {
+ WSASetLastError (WSAENOTSOCK);
+ return(SOCKET_ERROR);
+ }
+
+ do {
+ ret = recvmsg (fd, msg, recv_flags);
+ } while (ret == -1 && errno == EINTR &&
+ !_wapi_thread_cur_apc_pending ());
+
+ if (ret == 0) {
+ /* see _wapi_recvfrom */
+ ok = _wapi_lookup_handle (handle, WAPI_HANDLE_SOCKET,
+ (gpointer *)&socket_handle);
+ if (ok == FALSE || socket_handle->still_readable != 1) {
+ ret = -1;
+ errno = EINTR;
+ }
+ }
+
+ if (ret == -1) {
+ gint errnum = errno;
+#ifdef DEBUG
+ g_message ("%s: recvmsg error: %s", __func__, strerror(errno));
+#endif
+
+ 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);
g_message ("%s: send error: %s", __func__, strerror (errno));
#endif
+#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(ret);
}
+static int
+_wapi_sendmsg(guint32 fd, const struct msghdr *msg, int send_flags)
+{
+ gpointer handle = GUINT_TO_POINTER (fd);
+ int ret;
+
+ if (startup_count == 0) {
+ WSASetLastError (WSANOTINITIALISED);
+ return(SOCKET_ERROR);
+ }
+
+ if (_wapi_handle_type (handle) != WAPI_HANDLE_SOCKET) {
+ WSASetLastError (WSAENOTSOCK);
+ return(SOCKET_ERROR);
+ }
+
+ do {
+ ret = sendmsg (fd, msg, send_flags);
+ } while (ret == -1 && errno == EINTR &&
+ !_wapi_thread_cur_apc_pending ());
+
+ if (ret == -1) {
+ gint errnum = errno;
+#ifdef DEBUG
+ g_message ("%s: sendmsg error: %s", __func__, strerror (errno));
+#endif
+
+ 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)
{
}
tmp_val = optval;
- if (optname == SO_RCVTIMEO || optname == SO_SNDTIMEO) {
+ if (level == SOL_SOCKET &&
+ (optname == SO_RCVTIMEO || optname == SO_SNDTIMEO)) {
int ms = *((int *) optval);
tv.tv_sec = ms / 1000;
- tv.tv_usec = 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."
+ */
+ int bufsize = *((int *) optval);
+
+ bufsize /= 2;
+ tmp_val = &bufsize;
+#endif
}
ret = setsockopt (fd, level, optname, tmp_val, optlen);
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)
+ 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;
WSASetLastError (WSAENOTSOCK);
return(SOCKET_ERROR);
}
+
+ if (how == SHUT_RD ||
+ how == SHUT_RDWR) {
+ ok = _wapi_lookup_handle (handle, WAPI_HANDLE_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) {
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) */
+ socket_handle.protocol = 4;
fd = socket (AF_INET, SOCK_RAW, 4);
}
if (fd >= _wapi_fd_reserve) {
#ifdef DEBUG
g_message ("%s: File descriptor is too big (%d >= %d)",
- __func__, fd, _wapi_fd_offset_table_size);
+ __func__, fd, _wapi_fd_reserve);
#endif
WSASetLastError (WSASYSCALLFAILURE);
return(INVALID_SOCKET);
}
+
+ /* .net seems to set this by default for SOCK_STREAM, not for
+ * SOCK_DGRAM (see bug #36322)
+ *
+ * It seems winsock has a rather different idea of what
+ * SO_REUSEADDR means. If it's set, then a new socket can be
+ * bound over an existing listening socket. There's a new
+ * windows-specific option called SO_EXCLUSIVEADDRUSE but
+ * using that means the socket MUST be closed properly, or a
+ * denial of service can occur. Luckily for us, winsock
+ * behaves as though any other system would when SO_REUSEADDR
+ * is true, so we don't need to do anything else here. See
+ * bug 53992.
+ */
+ {
+ int ret, true = 1;
+
+ ret = setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, &true,
+ sizeof (true));
+ if (ret == -1) {
+ int errnum = errno;
+
+#ifdef DEBUG
+ g_message ("%s: Error setting SO_REUSEADDR", __func__);
+#endif
+
+ errnum = errno_to_WSA (errnum, __func__);
+ WSASetLastError (errnum);
+
+ close (fd);
+
+ return(INVALID_SOCKET);
+ }
+ }
mono_once (&socket_ops_once, socket_ops_init);
- handle = _wapi_handle_new_fd (WAPI_HANDLE_SOCKET, fd, NULL);
+ handle = _wapi_handle_new_fd (WAPI_HANDLE_SOCKET, fd, &socket_handle);
if (handle == _WAPI_HANDLE_INVALID) {
g_warning ("%s: error creating socket handle", __func__);
+ WSASetLastError (WSASYSCALLFAILURE);
+ close (fd);
return(INVALID_SOCKET);
}
return(he);
}
+static gboolean socket_disconnect (guint32 fd)
+{
+ struct _WapiHandle_socket *socket_handle;
+ gboolean ok;
+ gpointer handle = GUINT_TO_POINTER (fd);
+ int newsock, ret;
+
+ ok = _wapi_lookup_handle (handle, WAPI_HANDLE_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;
+
+#ifdef DEBUG
+ g_message ("%s: socket error: %s", __func__, strerror (errno));
+#endif
+
+ 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;
+
+#ifdef DEBUG
+ g_message ("%s: dup2 error: %s", __func__, strerror (errno));
+#endif
+
+ 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)
+{
+#ifdef DEBUG
+ g_message ("%s: called on socket %d!", __func__, fd);
+#endif
+
+ 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)
+{
+#if defined(HAVE_SENDFILE) && (defined(__linux__) || defined(DARWIN))
+ gint file = GPOINTER_TO_INT (fd);
+ gint n;
+ gint errnum;
+ gssize res;
+ struct stat statbuf;
+
+ n = fstat (file, &statbuf);
+ if (n == -1) {
+ errnum = errno;
+ errnum = errno_to_WSA (errnum, __func__);
+ WSASetLastError (errnum);
+ return SOCKET_ERROR;
+ }
+ do {
+#ifdef __linux__
+ res = sendfile (socket, file, NULL, statbuf.st_size);
+#elif defined(DARWIN)
+ /* TODO: header/tail could be sent in the 5th argument */
+ /* TODO: Might not send the entire file for non-blocking sockets */
+ res = sendfile (file, socket, 0, &statbuf.st_size, NULL, 0);
+#endif
+ } while (res != -1 && (errno == EINTR || errno == EAGAIN) && !_wapi_thread_cur_apc_pending ());
+ if (res == -1) {
+ errnum = errno;
+ errnum = errno_to_WSA (errnum, __func__);
+ WSASetLastError (errnum);
+ return SOCKET_ERROR;
+ }
+#else
+ /* Default implementation */
+ gint file = GPOINTER_TO_INT (fd);
+ gchar *buffer;
+ gint n;
+
+ buffer = g_malloc (SF_BUFFER_SIZE);
+ do {
+ do {
+ n = read (file, buffer, SF_BUFFER_SIZE);
+ } while (n == -1 && errno == EINTR && !_wapi_thread_cur_apc_pending ());
+ if (n == -1)
+ break;
+ if (n == 0) {
+ g_free (buffer);
+ return 0; /* We're done reading */
+ }
+ do {
+ n = send (socket, buffer, n, 0); /* short sends? enclose this in a loop? */
+ } while (n == -1 && errno == EINTR && !_wapi_thread_cur_apc_pending ());
+ } while (n != -1);
+
+ if (n == -1) {
+ gint errnum = errno;
+ errnum = errno_to_WSA (errnum, __func__);
+ WSASetLastError (errnum);
+ g_free (buffer);
+ return SOCKET_ERROR;
+ }
+ g_free (buffer);
+#endif
+ return 0;
+}
+
+gboolean
+TransmitFile (guint32 socket, gpointer file, guint32 bytes_to_write, guint32 bytes_per_send, WapiOverlapped *ol,
+ WapiTransmitFileBuffers *buffers, guint32 flags)
+{
+ gpointer sock = GUINT_TO_POINTER (socket);
+ gint ret;
+
+ if (startup_count == 0) {
+ WSASetLastError (WSANOTINITIALISED);
+ return FALSE;
+ }
+
+ if (_wapi_handle_type (sock) != WAPI_HANDLE_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,
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 (i_len > 0) {
buffer = g_memdup (input, i_len);
}
return(0);
}
+#ifndef PLATFORM_PORT_PROVIDES_IOCTLSOCKET
int ioctlsocket(guint32 fd, gint32 command, gpointer arg)
{
gpointer handle = GUINT_TO_POINTER (fd);
}
break;
#endif /* O_NONBLOCK */
- case FIONREAD:
+ /* Unused in Mono */
case SIOCATMARK:
ret = ioctl (fd, command, arg);
break;
+
+ case FIONREAD:
+ {
+#if defined (PLATFORM_MACOSX)
+
+ // ioctl (fd, FIONREAD, XXX) returns the size of
+ // the UDP header as well on
+ // Darwin.
+ //
+ // Use getsockopt SO_NREAD instead to get the
+ // right values for TCP and UDP.
+ //
+ // ai_canonname can be null in some cases on darwin, where the runtime assumes it will
+ // be the value of the ip buffer.
+
+ socklen_t optlen = sizeof (int);
+ ret = getsockopt (fd, SOL_SOCKET, SO_NREAD, arg, &optlen);
+#else
+ ret = ioctl (fd, command, arg);
+#endif
+ break;
+ }
default:
WSASetLastError (WSAEINVAL);
return(SOCKET_ERROR);
int _wapi_select(int nfds G_GNUC_UNUSED, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout)
{
- int ret;
+ int ret, maxfd;
if (startup_count == 0) {
WSASetLastError (WSANOTINITIALISED);
return(SOCKET_ERROR);
}
+ 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(getdtablesize (), readfds, writefds, exceptfds,
+ ret = select(maxfd + 1, readfds, writefds, exceptfds,
timeout);
} while (ret == -1 && errno == EINTR &&
!_wapi_thread_cur_apc_pending ());
{
gpointer handle = GUINT_TO_POINTER (fd);
+ if (fd >= FD_SETSIZE) {
+ WSASetLastError (WSAEINVAL);
+ return;
+ }
+
if (_wapi_handle_type (handle) != WAPI_HANDLE_SOCKET) {
WSASetLastError (WSAENOTSOCK);
return;
{
gpointer handle = GUINT_TO_POINTER (fd);
+ if (fd >= FD_SETSIZE) {
+ WSASetLastError (WSAEINVAL);
+ return(0);
+ }
+
if (_wapi_handle_type (handle) != WAPI_HANDLE_SOCKET) {
WSASetLastError (WSAENOTSOCK);
return(0);
{
gpointer handle = GUINT_TO_POINTER (fd);
+ if (fd >= FD_SETSIZE) {
+ WSASetLastError (WSAEINVAL);
+ return;
+ }
+
if (_wapi_handle_type (handle) != WAPI_HANDLE_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 */