Merge pull request #3715 from kumpera/fix-44707
[mono-project.git] / mono / io-layer / sockets.c
blob05012d26fbbb505967a484272cb4ed1c99f6bad7
1 /*
2 * sockets.c: Socket handles
4 * Author:
5 * Dick Porter (dick@ximian.com)
7 * (C) 2002 Ximian, Inc.
8 */
10 #include <config.h>
12 #ifndef DISABLE_SOCKETS
14 #include <glib.h>
15 #include <pthread.h>
16 #include <errno.h>
17 #include <string.h>
18 #include <sys/types.h>
19 #include <sys/socket.h>
20 #ifdef HAVE_SYS_UIO_H
21 # include <sys/uio.h>
22 #endif
23 #ifdef HAVE_SYS_IOCTL_H
24 # include <sys/ioctl.h>
25 #endif
26 #ifdef HAVE_SYS_FILIO_H
27 #include <sys/filio.h> /* defines FIONBIO and FIONREAD */
28 #endif
29 #ifdef HAVE_SYS_SOCKIO_H
30 #include <sys/sockio.h> /* defines SIOCATMARK */
31 #endif
32 #include <unistd.h>
33 #include <fcntl.h>
35 #ifndef HAVE_MSG_NOSIGNAL
36 #include <signal.h>
37 #endif
39 #include <mono/io-layer/wapi.h>
40 #include <mono/io-layer/wapi-private.h>
41 #include <mono/io-layer/socket-private.h>
42 #include <mono/io-layer/socket-wrappers.h>
43 #include <mono/io-layer/io-trace.h>
44 #include <mono/utils/mono-poll.h>
45 #include <mono/utils/mono-once.h>
46 #include <mono/utils/mono-logger-internals.h>
47 #include <mono/utils/w32handle.h>
49 #include <netinet/in.h>
50 #include <netinet/tcp.h>
51 #include <arpa/inet.h>
52 #ifdef HAVE_SYS_SENDFILE_H
53 #include <sys/sendfile.h>
54 #endif
55 #ifdef HAVE_NETDB_H
56 #include <netdb.h>
57 #endif
59 static guint32 in_cleanup = 0;
61 static void socket_close (gpointer handle, gpointer data);
62 static void socket_details (gpointer data);
63 static const gchar* socket_typename (void);
64 static gsize socket_typesize (void);
66 static MonoW32HandleOps _wapi_socket_ops = {
67 socket_close, /* close */
68 NULL, /* signal */
69 NULL, /* own */
70 NULL, /* is_owned */
71 NULL, /* special_wait */
72 NULL, /* prewait */
73 socket_details, /* details */
74 socket_typename, /* typename */
75 socket_typesize, /* typesize */
78 void
79 _wapi_socket_init (void)
81 mono_w32handle_register_ops (MONO_W32HANDLE_SOCKET, &_wapi_socket_ops);
84 static void socket_close (gpointer handle, gpointer data)
86 int ret;
87 struct _WapiHandle_socket *socket_handle = (struct _WapiHandle_socket *)data;
88 MonoThreadInfo *info = mono_thread_info_current ();
90 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: closing socket handle %p", __func__, handle);
92 /* Shutdown the socket for reading, to interrupt any potential
93 * receives that may be blocking for data. See bug 75705.
95 shutdown (GPOINTER_TO_UINT (handle), SHUT_RD);
97 do {
98 ret = close (GPOINTER_TO_UINT(handle));
99 } while (ret == -1 && errno == EINTR &&
100 !mono_thread_info_is_interrupt_state (info));
102 if (ret == -1) {
103 gint errnum = errno;
104 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: close error: %s", __func__, strerror (errno));
105 errnum = errno_to_WSA (errnum, __func__);
106 if (!in_cleanup)
107 WSASetLastError (errnum);
110 if (!in_cleanup)
111 socket_handle->saved_error = 0;
114 static void socket_details (gpointer data)
116 /* FIXME: do something */
119 static const gchar* socket_typename (void)
121 return "Socket";
124 static gsize socket_typesize (void)
126 return sizeof (struct _WapiHandle_socket);
129 static gboolean
130 cleanup_close (gpointer handle, gpointer data, gpointer user_data)
132 if (mono_w32handle_get_type (handle) == MONO_W32HANDLE_SOCKET)
133 mono_w32handle_ops_close (handle, data);
135 return FALSE;
138 void _wapi_cleanup_networking(void)
140 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: cleaning up", __func__);
142 in_cleanup = 1;
143 mono_w32handle_foreach (cleanup_close, NULL);
144 in_cleanup = 0;
147 void WSASetLastError(int error)
149 SetLastError (error);
152 int WSAGetLastError(void)
154 return(GetLastError ());
157 int closesocket(guint32 fd)
159 gpointer handle = GUINT_TO_POINTER (fd);
161 if (mono_w32handle_get_type (handle) != MONO_W32HANDLE_SOCKET) {
162 WSASetLastError (WSAENOTSOCK);
163 return(0);
166 mono_w32handle_unref (handle);
167 return(0);
170 guint32 _wapi_accept(guint32 fd, struct sockaddr *addr, socklen_t *addrlen)
172 gpointer handle = GUINT_TO_POINTER (fd);
173 gpointer new_handle;
174 struct _WapiHandle_socket *socket_handle;
175 struct _WapiHandle_socket new_socket_handle = {0};
176 gboolean ok;
177 int new_fd;
178 MonoThreadInfo *info = mono_thread_info_current ();
180 if (addr != NULL && *addrlen < sizeof(struct sockaddr)) {
181 WSASetLastError (WSAEFAULT);
182 return(INVALID_SOCKET);
185 if (mono_w32handle_get_type (handle) != MONO_W32HANDLE_SOCKET) {
186 WSASetLastError (WSAENOTSOCK);
187 return(INVALID_SOCKET);
190 ok = mono_w32handle_lookup (handle, MONO_W32HANDLE_SOCKET,
191 (gpointer *)&socket_handle);
192 if (ok == FALSE) {
193 g_warning ("%s: error looking up socket handle %p",
194 __func__, handle);
195 WSASetLastError (WSAENOTSOCK);
196 return(INVALID_SOCKET);
199 do {
200 new_fd = accept (fd, addr, addrlen);
201 } while (new_fd == -1 && errno == EINTR &&
202 !mono_thread_info_is_interrupt_state (info));
204 if (new_fd == -1) {
205 gint errnum = errno;
206 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: accept error: %s", __func__, strerror(errno));
208 errnum = errno_to_WSA (errnum, __func__);
209 WSASetLastError (errnum);
211 return(INVALID_SOCKET);
214 if (new_fd >= mono_w32handle_fd_reserve) {
215 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: File descriptor is too big", __func__);
217 WSASetLastError (WSASYSCALLFAILURE);
219 close (new_fd);
221 return(INVALID_SOCKET);
224 new_socket_handle.domain = socket_handle->domain;
225 new_socket_handle.type = socket_handle->type;
226 new_socket_handle.protocol = socket_handle->protocol;
227 new_socket_handle.still_readable = 1;
229 new_handle = mono_w32handle_new_fd (MONO_W32HANDLE_SOCKET, new_fd,
230 &new_socket_handle);
231 if(new_handle == INVALID_HANDLE_VALUE) {
232 g_warning ("%s: error creating socket handle", __func__);
233 WSASetLastError (ERROR_GEN_FAILURE);
234 return(INVALID_SOCKET);
237 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: returning newly accepted socket handle %p with",
238 __func__, new_handle);
240 return(new_fd);
243 int _wapi_bind(guint32 fd, struct sockaddr *my_addr, socklen_t addrlen)
245 gpointer handle = GUINT_TO_POINTER (fd);
246 int ret;
248 if (mono_w32handle_get_type (handle) != MONO_W32HANDLE_SOCKET) {
249 WSASetLastError (WSAENOTSOCK);
250 return(SOCKET_ERROR);
253 ret = bind (fd, my_addr, addrlen);
254 if (ret == -1) {
255 gint errnum = errno;
256 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: bind error: %s", __func__, strerror(errno));
257 errnum = errno_to_WSA (errnum, __func__);
258 WSASetLastError (errnum);
260 return(SOCKET_ERROR);
262 return(ret);
265 int _wapi_connect(guint32 fd, const struct sockaddr *serv_addr,
266 socklen_t addrlen)
268 gpointer handle = GUINT_TO_POINTER (fd);
269 struct _WapiHandle_socket *socket_handle;
270 gboolean ok;
271 gint errnum;
272 MonoThreadInfo *info = mono_thread_info_current ();
274 if (mono_w32handle_get_type (handle) != MONO_W32HANDLE_SOCKET) {
275 WSASetLastError (WSAENOTSOCK);
276 return(SOCKET_ERROR);
279 if (connect (fd, serv_addr, addrlen) == -1) {
280 mono_pollfd fds;
281 int so_error;
282 socklen_t len;
284 errnum = errno;
286 if (errno != EINTR) {
287 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: connect error: %s", __func__,
288 strerror (errnum));
290 errnum = errno_to_WSA (errnum, __func__);
291 if (errnum == WSAEINPROGRESS)
292 errnum = WSAEWOULDBLOCK; /* see bug #73053 */
294 WSASetLastError (errnum);
297 * On solaris x86 getsockopt (SO_ERROR) is not set after
298 * connect () fails so we need to save this error.
300 * But don't do this for EWOULDBLOCK (bug 317315)
302 if (errnum != WSAEWOULDBLOCK) {
303 ok = mono_w32handle_lookup (handle,
304 MONO_W32HANDLE_SOCKET,
305 (gpointer *)&socket_handle);
306 if (ok == FALSE) {
307 /* ECONNRESET means the socket was closed by another thread */
308 /* Async close on mac raises ECONNABORTED. */
309 if (errnum != WSAECONNRESET && errnum != WSAENETDOWN)
310 g_warning ("%s: error looking up socket handle %p (error %d)", __func__, handle, errnum);
311 } else {
312 socket_handle->saved_error = errnum;
315 return(SOCKET_ERROR);
318 fds.fd = fd;
319 fds.events = MONO_POLLOUT;
320 while (mono_poll (&fds, 1, -1) == -1 &&
321 !mono_thread_info_is_interrupt_state (info)) {
322 if (errno != EINTR) {
323 errnum = errno_to_WSA (errno, __func__);
325 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: connect poll error: %s",
326 __func__, strerror (errno));
328 WSASetLastError (errnum);
329 return(SOCKET_ERROR);
333 len = sizeof(so_error);
334 if (getsockopt (fd, SOL_SOCKET, SO_ERROR, &so_error,
335 &len) == -1) {
336 errnum = errno_to_WSA (errno, __func__);
338 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: connect getsockopt error: %s",
339 __func__, strerror (errno));
341 WSASetLastError (errnum);
342 return(SOCKET_ERROR);
345 if (so_error != 0) {
346 errnum = errno_to_WSA (so_error, __func__);
348 /* Need to save this socket error */
349 ok = mono_w32handle_lookup (handle, MONO_W32HANDLE_SOCKET,
350 (gpointer *)&socket_handle);
351 if (ok == FALSE) {
352 g_warning ("%s: error looking up socket handle %p", __func__, handle);
353 } else {
354 socket_handle->saved_error = errnum;
357 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: connect getsockopt returned error: %s",
358 __func__, strerror (so_error));
360 WSASetLastError (errnum);
361 return(SOCKET_ERROR);
365 return(0);
368 int _wapi_getpeername(guint32 fd, struct sockaddr *name, socklen_t *namelen)
370 gpointer handle = GUINT_TO_POINTER (fd);
371 int ret;
373 if (mono_w32handle_get_type (handle) != MONO_W32HANDLE_SOCKET) {
374 WSASetLastError (WSAENOTSOCK);
375 return(SOCKET_ERROR);
378 ret = getpeername (fd, name, namelen);
379 if (ret == -1) {
380 gint errnum = errno;
381 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: getpeername error: %s", __func__,
382 strerror (errno));
384 errnum = errno_to_WSA (errnum, __func__);
385 WSASetLastError (errnum);
387 return(SOCKET_ERROR);
390 return(ret);
393 int _wapi_getsockname(guint32 fd, struct sockaddr *name, socklen_t *namelen)
395 gpointer handle = GUINT_TO_POINTER (fd);
396 int ret;
398 if (mono_w32handle_get_type (handle) != MONO_W32HANDLE_SOCKET) {
399 WSASetLastError (WSAENOTSOCK);
400 return(SOCKET_ERROR);
403 ret = getsockname (fd, name, namelen);
404 if (ret == -1) {
405 gint errnum = errno;
406 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: getsockname error: %s", __func__,
407 strerror (errno));
409 errnum = errno_to_WSA (errnum, __func__);
410 WSASetLastError (errnum);
412 return(SOCKET_ERROR);
415 return(ret);
418 int _wapi_getsockopt(guint32 fd, int level, int optname, void *optval,
419 socklen_t *optlen)
421 gpointer handle = GUINT_TO_POINTER (fd);
422 int ret;
423 struct timeval tv;
424 void *tmp_val;
425 struct _WapiHandle_socket *socket_handle;
426 gboolean ok;
428 if (mono_w32handle_get_type (handle) != MONO_W32HANDLE_SOCKET) {
429 WSASetLastError (WSAENOTSOCK);
430 return(SOCKET_ERROR);
433 tmp_val = optval;
434 if (level == SOL_SOCKET &&
435 (optname == SO_RCVTIMEO || optname == SO_SNDTIMEO)) {
436 tmp_val = &tv;
437 *optlen = sizeof (tv);
440 ret = getsockopt (fd, level, optname, tmp_val, optlen);
441 if (ret == -1) {
442 gint errnum = errno;
443 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: getsockopt error: %s", __func__,
444 strerror (errno));
446 errnum = errno_to_WSA (errnum, __func__);
447 WSASetLastError (errnum);
449 return(SOCKET_ERROR);
452 if (level == SOL_SOCKET &&
453 (optname == SO_RCVTIMEO || optname == SO_SNDTIMEO)) {
454 *((int *) optval) = tv.tv_sec * 1000 + (tv.tv_usec / 1000); // milli from micro
455 *optlen = sizeof (int);
458 if (optname == SO_ERROR) {
459 ok = mono_w32handle_lookup (handle, MONO_W32HANDLE_SOCKET,
460 (gpointer *)&socket_handle);
461 if (ok == FALSE) {
462 g_warning ("%s: error looking up socket handle %p",
463 __func__, handle);
465 /* can't extract the last error */
466 *((int *) optval) = errno_to_WSA (*((int *)optval),
467 __func__);
468 } else {
469 if (*((int *)optval) != 0) {
470 *((int *) optval) = errno_to_WSA (*((int *)optval),
471 __func__);
472 socket_handle->saved_error = *((int *)optval);
473 } else {
474 *((int *)optval) = socket_handle->saved_error;
479 return(ret);
482 int _wapi_listen(guint32 fd, int backlog)
484 gpointer handle = GUINT_TO_POINTER (fd);
485 int ret;
487 if (mono_w32handle_get_type (handle) != MONO_W32HANDLE_SOCKET) {
488 WSASetLastError (WSAENOTSOCK);
489 return(SOCKET_ERROR);
492 ret = listen (fd, backlog);
493 if (ret == -1) {
494 gint errnum = errno;
495 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: listen error: %s", __func__, strerror (errno));
497 errnum = errno_to_WSA (errnum, __func__);
498 WSASetLastError (errnum);
500 return(SOCKET_ERROR);
503 return(0);
506 int _wapi_recv(guint32 fd, void *buf, size_t len, int recv_flags)
508 return(_wapi_recvfrom (fd, buf, len, recv_flags, NULL, 0));
511 int _wapi_recvfrom(guint32 fd, void *buf, size_t len, int recv_flags,
512 struct sockaddr *from, socklen_t *fromlen)
514 gpointer handle = GUINT_TO_POINTER (fd);
515 struct _WapiHandle_socket *socket_handle;
516 gboolean ok;
517 int ret;
518 MonoThreadInfo *info = mono_thread_info_current ();
520 if (mono_w32handle_get_type (handle) != MONO_W32HANDLE_SOCKET) {
521 WSASetLastError (WSAENOTSOCK);
522 return(SOCKET_ERROR);
525 do {
526 ret = recvfrom (fd, buf, len, recv_flags, from, fromlen);
527 } while (ret == -1 && errno == EINTR &&
528 !mono_thread_info_is_interrupt_state (info));
530 if (ret == 0 && len > 0) {
531 /* According to the Linux man page, recvfrom only
532 * returns 0 when the socket has been shut down
533 * cleanly. Turn this into an EINTR to simulate win32
534 * behaviour of returning EINTR when a socket is
535 * closed while the recvfrom is blocking (we use a
536 * shutdown() in socket_close() to trigger this.) See
537 * bug 75705.
539 /* Distinguish between the socket being shut down at
540 * the local or remote ends, and reads that request 0
541 * bytes to be read
544 /* If this returns FALSE, it means the socket has been
545 * closed locally. If it returns TRUE, but
546 * still_readable != 1 then shutdown
547 * (SHUT_RD|SHUT_RDWR) has been called locally.
549 ok = mono_w32handle_lookup (handle, MONO_W32HANDLE_SOCKET,
550 (gpointer *)&socket_handle);
551 if (ok == FALSE || socket_handle->still_readable != 1) {
552 ret = -1;
553 errno = EINTR;
557 if (ret == -1) {
558 gint errnum = errno;
559 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: recv error: %s", __func__, strerror(errno));
561 errnum = errno_to_WSA (errnum, __func__);
562 WSASetLastError (errnum);
564 return(SOCKET_ERROR);
566 return(ret);
569 static int
570 _wapi_recvmsg(guint32 fd, struct msghdr *msg, int recv_flags)
572 gpointer handle = GUINT_TO_POINTER (fd);
573 struct _WapiHandle_socket *socket_handle;
574 gboolean ok;
575 int ret;
576 MonoThreadInfo *info = mono_thread_info_current ();
578 if (mono_w32handle_get_type (handle) != MONO_W32HANDLE_SOCKET) {
579 WSASetLastError (WSAENOTSOCK);
580 return(SOCKET_ERROR);
583 do {
584 ret = recvmsg (fd, msg, recv_flags);
585 } while (ret == -1 && errno == EINTR &&
586 !mono_thread_info_is_interrupt_state (info));
588 if (ret == 0) {
589 /* see _wapi_recvfrom */
590 ok = mono_w32handle_lookup (handle, MONO_W32HANDLE_SOCKET,
591 (gpointer *)&socket_handle);
592 if (ok == FALSE || socket_handle->still_readable != 1) {
593 ret = -1;
594 errno = EINTR;
598 if (ret == -1) {
599 gint errnum = errno;
600 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: recvmsg error: %s", __func__, strerror(errno));
602 errnum = errno_to_WSA (errnum, __func__);
603 WSASetLastError (errnum);
605 return(SOCKET_ERROR);
607 return(ret);
610 int _wapi_send(guint32 fd, const void *msg, size_t len, int send_flags)
612 gpointer handle = GUINT_TO_POINTER (fd);
613 int ret;
614 MonoThreadInfo *info = mono_thread_info_current ();
616 if (mono_w32handle_get_type (handle) != MONO_W32HANDLE_SOCKET) {
617 WSASetLastError (WSAENOTSOCK);
618 return(SOCKET_ERROR);
621 do {
622 ret = send (fd, msg, len, send_flags);
623 } while (ret == -1 && errno == EINTR &&
624 !mono_thread_info_is_interrupt_state (info));
626 if (ret == -1) {
627 gint errnum = errno;
628 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: send error: %s", __func__, strerror (errno));
630 #ifdef O_NONBLOCK
631 /* At least linux returns EAGAIN/EWOULDBLOCK when the timeout has been set on
632 * a blocking socket. See bug #599488 */
633 if (errnum == EAGAIN) {
634 ret = fcntl (fd, F_GETFL, 0);
635 if (ret != -1 && (ret & O_NONBLOCK) == 0)
636 errnum = ETIMEDOUT;
638 #endif /* O_NONBLOCK */
639 errnum = errno_to_WSA (errnum, __func__);
640 WSASetLastError (errnum);
642 return(SOCKET_ERROR);
644 return(ret);
647 int _wapi_sendto(guint32 fd, const void *msg, size_t len, int send_flags,
648 const struct sockaddr *to, socklen_t tolen)
650 gpointer handle = GUINT_TO_POINTER (fd);
651 int ret;
652 MonoThreadInfo *info = mono_thread_info_current ();
654 if (mono_w32handle_get_type (handle) != MONO_W32HANDLE_SOCKET) {
655 WSASetLastError (WSAENOTSOCK);
656 return(SOCKET_ERROR);
659 do {
660 ret = sendto (fd, msg, len, send_flags, to, tolen);
661 } while (ret == -1 && errno == EINTR &&
662 !mono_thread_info_is_interrupt_state (info));
664 if (ret == -1) {
665 gint errnum = errno;
666 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: send error: %s", __func__, strerror (errno));
668 errnum = errno_to_WSA (errnum, __func__);
669 WSASetLastError (errnum);
671 return(SOCKET_ERROR);
673 return(ret);
676 static int
677 _wapi_sendmsg(guint32 fd, const struct msghdr *msg, int send_flags)
679 gpointer handle = GUINT_TO_POINTER (fd);
680 int ret;
681 MonoThreadInfo *info = mono_thread_info_current ();
683 if (mono_w32handle_get_type (handle) != MONO_W32HANDLE_SOCKET) {
684 WSASetLastError (WSAENOTSOCK);
685 return(SOCKET_ERROR);
688 do {
689 ret = sendmsg (fd, msg, send_flags);
690 } while (ret == -1 && errno == EINTR &&
691 !mono_thread_info_is_interrupt_state (info));
693 if (ret == -1) {
694 gint errnum = errno;
695 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: sendmsg error: %s", __func__, strerror (errno));
697 errnum = errno_to_WSA (errnum, __func__);
698 WSASetLastError (errnum);
700 return(SOCKET_ERROR);
702 return(ret);
705 int _wapi_setsockopt(guint32 fd, int level, int optname,
706 const void *optval, socklen_t optlen)
708 gpointer handle = GUINT_TO_POINTER (fd);
709 int ret;
710 const void *tmp_val;
711 #if defined (__linux__)
712 /* This has its address taken so it cannot be moved to the if block which uses it */
713 int bufsize = 0;
714 #endif
715 struct timeval tv;
717 if (mono_w32handle_get_type (handle) != MONO_W32HANDLE_SOCKET) {
718 WSASetLastError (WSAENOTSOCK);
719 return(SOCKET_ERROR);
722 tmp_val = optval;
723 if (level == SOL_SOCKET &&
724 (optname == SO_RCVTIMEO || optname == SO_SNDTIMEO)) {
725 int ms = *((int *) optval);
726 tv.tv_sec = ms / 1000;
727 tv.tv_usec = (ms % 1000) * 1000; // micro from milli
728 tmp_val = &tv;
729 optlen = sizeof (tv);
731 #if defined (__linux__)
732 else if (level == SOL_SOCKET &&
733 (optname == SO_SNDBUF || optname == SO_RCVBUF)) {
734 /* According to socket(7) the Linux kernel doubles the
735 * buffer sizes "to allow space for bookkeeping
736 * overhead."
738 bufsize = *((int *) optval);
740 bufsize /= 2;
741 tmp_val = &bufsize;
743 #endif
745 ret = setsockopt (fd, level, optname, tmp_val, optlen);
746 if (ret == -1) {
747 gint errnum = errno;
748 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: setsockopt error: %s", __func__,
749 strerror (errno));
751 errnum = errno_to_WSA (errnum, __func__);
752 WSASetLastError (errnum);
754 return(SOCKET_ERROR);
757 #if defined (SO_REUSEPORT)
758 /* BSD's and MacOS X multicast sockets also need SO_REUSEPORT when SO_REUSEADDR is requested. */
759 if (level == SOL_SOCKET && optname == SO_REUSEADDR) {
760 int type;
761 socklen_t type_len = sizeof (type);
763 if (!getsockopt (fd, level, SO_TYPE, &type, &type_len)) {
764 if (type == SOCK_DGRAM || type == SOCK_STREAM)
765 setsockopt (fd, level, SO_REUSEPORT, tmp_val, optlen);
768 #endif
770 return(ret);
773 int _wapi_shutdown(guint32 fd, int how)
775 struct _WapiHandle_socket *socket_handle;
776 gboolean ok;
777 gpointer handle = GUINT_TO_POINTER (fd);
778 int ret;
780 if (mono_w32handle_get_type (handle) != MONO_W32HANDLE_SOCKET) {
781 WSASetLastError (WSAENOTSOCK);
782 return(SOCKET_ERROR);
785 if (how == SHUT_RD ||
786 how == SHUT_RDWR) {
787 ok = mono_w32handle_lookup (handle, MONO_W32HANDLE_SOCKET,
788 (gpointer *)&socket_handle);
789 if (ok == FALSE) {
790 g_warning ("%s: error looking up socket handle %p",
791 __func__, handle);
792 WSASetLastError (WSAENOTSOCK);
793 return(SOCKET_ERROR);
796 socket_handle->still_readable = 0;
799 ret = shutdown (fd, how);
800 if (ret == -1) {
801 gint errnum = errno;
802 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: shutdown error: %s", __func__,
803 strerror (errno));
805 errnum = errno_to_WSA (errnum, __func__);
806 WSASetLastError (errnum);
808 return(SOCKET_ERROR);
811 return(ret);
814 guint32 _wapi_socket(int domain, int type, int protocol, void *unused,
815 guint32 unused2, guint32 unused3)
817 struct _WapiHandle_socket socket_handle = {0};
818 gpointer handle;
819 int fd;
821 socket_handle.domain = domain;
822 socket_handle.type = type;
823 socket_handle.protocol = protocol;
824 socket_handle.still_readable = 1;
826 fd = socket (domain, type, protocol);
827 if (fd == -1 && domain == AF_INET && type == SOCK_RAW &&
828 protocol == 0) {
829 /* Retry with protocol == 4 (see bug #54565) */
830 // https://bugzilla.novell.com/show_bug.cgi?id=MONO54565
831 socket_handle.protocol = 4;
832 fd = socket (AF_INET, SOCK_RAW, 4);
835 if (fd == -1) {
836 gint errnum = errno;
837 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: socket error: %s", __func__, strerror (errno));
838 errnum = errno_to_WSA (errnum, __func__);
839 WSASetLastError (errnum);
841 return(INVALID_SOCKET);
844 if (fd >= mono_w32handle_fd_reserve) {
845 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: File descriptor is too big (%d >= %d)",
846 __func__, fd, mono_w32handle_fd_reserve);
848 WSASetLastError (WSASYSCALLFAILURE);
849 close (fd);
851 return(INVALID_SOCKET);
854 /* .net seems to set this by default for SOCK_STREAM, not for
855 * SOCK_DGRAM (see bug #36322)
856 * https://bugzilla.novell.com/show_bug.cgi?id=MONO36322
858 * It seems winsock has a rather different idea of what
859 * SO_REUSEADDR means. If it's set, then a new socket can be
860 * bound over an existing listening socket. There's a new
861 * windows-specific option called SO_EXCLUSIVEADDRUSE but
862 * using that means the socket MUST be closed properly, or a
863 * denial of service can occur. Luckily for us, winsock
864 * behaves as though any other system would when SO_REUSEADDR
865 * is true, so we don't need to do anything else here. See
866 * bug 53992.
867 * https://bugzilla.novell.com/show_bug.cgi?id=MONO53992
870 int ret, true_ = 1;
872 ret = setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, &true_,
873 sizeof (true_));
874 if (ret == -1) {
875 int errnum = errno;
877 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Error setting SO_REUSEADDR", __func__);
879 errnum = errno_to_WSA (errnum, __func__);
880 WSASetLastError (errnum);
882 close (fd);
884 return(INVALID_SOCKET);
889 handle = mono_w32handle_new_fd (MONO_W32HANDLE_SOCKET, fd, &socket_handle);
890 if (handle == INVALID_HANDLE_VALUE) {
891 g_warning ("%s: error creating socket handle", __func__);
892 WSASetLastError (WSASYSCALLFAILURE);
893 close (fd);
894 return(INVALID_SOCKET);
897 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: returning socket handle %p", __func__, handle);
899 return(fd);
902 static gboolean socket_disconnect (guint32 fd)
904 struct _WapiHandle_socket *socket_handle;
905 gboolean ok;
906 gpointer handle = GUINT_TO_POINTER (fd);
907 int newsock, ret;
909 ok = mono_w32handle_lookup (handle, MONO_W32HANDLE_SOCKET,
910 (gpointer *)&socket_handle);
911 if (ok == FALSE) {
912 g_warning ("%s: error looking up socket handle %p", __func__,
913 handle);
914 WSASetLastError (WSAENOTSOCK);
915 return(FALSE);
918 newsock = socket (socket_handle->domain, socket_handle->type,
919 socket_handle->protocol);
920 if (newsock == -1) {
921 gint errnum = errno;
923 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: socket error: %s", __func__, strerror (errno));
925 errnum = errno_to_WSA (errnum, __func__);
926 WSASetLastError (errnum);
928 return(FALSE);
931 /* According to Stevens "Advanced Programming in the UNIX
932 * Environment: UNIX File I/O" dup2() is atomic so there
933 * should not be a race condition between the old fd being
934 * closed and the new socket fd being copied over
936 do {
937 ret = dup2 (newsock, fd);
938 } while (ret == -1 && errno == EAGAIN);
940 if (ret == -1) {
941 gint errnum = errno;
943 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: dup2 error: %s", __func__, strerror (errno));
945 errnum = errno_to_WSA (errnum, __func__);
946 WSASetLastError (errnum);
948 return(FALSE);
951 close (newsock);
953 return(TRUE);
956 static gboolean wapi_disconnectex (guint32 fd, WapiOverlapped *overlapped,
957 guint32 flags, guint32 reserved)
959 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: called on socket %d!", __func__, fd);
961 if (reserved != 0) {
962 WSASetLastError (WSAEINVAL);
963 return(FALSE);
966 /* We could check the socket type here and fail unless its
967 * SOCK_STREAM, SOCK_SEQPACKET or SOCK_RDM (according to msdn)
968 * if we really wanted to
971 return(socket_disconnect (fd));
974 #define SF_BUFFER_SIZE 16384
975 static gint
976 wapi_sendfile (guint32 socket, gpointer fd, guint32 bytes_to_write, guint32 bytes_per_send, guint32 flags)
978 MonoThreadInfo *info = mono_thread_info_current ();
979 #if defined(HAVE_SENDFILE) && (defined(__linux__) || defined(DARWIN))
980 gint file = GPOINTER_TO_INT (fd);
981 gint n;
982 gint errnum;
983 gssize res;
984 struct stat statbuf;
986 n = fstat (file, &statbuf);
987 if (n == -1) {
988 errnum = errno;
989 errnum = errno_to_WSA (errnum, __func__);
990 WSASetLastError (errnum);
991 return SOCKET_ERROR;
993 do {
994 #ifdef __linux__
995 res = sendfile (socket, file, NULL, statbuf.st_size);
996 #elif defined(DARWIN)
997 /* TODO: header/tail could be sent in the 5th argument */
998 /* TODO: Might not send the entire file for non-blocking sockets */
999 res = sendfile (file, socket, 0, &statbuf.st_size, NULL, 0);
1000 #endif
1001 } while (res != -1 && errno == EINTR && !mono_thread_info_is_interrupt_state (info));
1002 if (res == -1) {
1003 errnum = errno;
1004 errnum = errno_to_WSA (errnum, __func__);
1005 WSASetLastError (errnum);
1006 return SOCKET_ERROR;
1008 #else
1009 /* Default implementation */
1010 gint file = GPOINTER_TO_INT (fd);
1011 gchar *buffer;
1012 gint n;
1014 buffer = g_malloc (SF_BUFFER_SIZE);
1015 do {
1016 do {
1017 n = read (file, buffer, SF_BUFFER_SIZE);
1018 } while (n == -1 && errno == EINTR && !mono_thread_info_is_interrupt_state (info));
1019 if (n == -1)
1020 break;
1021 if (n == 0) {
1022 g_free (buffer);
1023 return 0; /* We're done reading */
1025 do {
1026 n = send (socket, buffer, n, 0); /* short sends? enclose this in a loop? */
1027 } while (n == -1 && errno == EINTR && !mono_thread_info_is_interrupt_state (info));
1028 } while (n != -1 && errno == EINTR && !mono_thread_info_is_interrupt_state (info));
1030 if (n == -1) {
1031 gint errnum = errno;
1032 errnum = errno_to_WSA (errnum, __func__);
1033 WSASetLastError (errnum);
1034 g_free (buffer);
1035 return SOCKET_ERROR;
1037 g_free (buffer);
1038 #endif
1039 return 0;
1042 gboolean
1043 TransmitFile (guint32 socket, gpointer file, guint32 bytes_to_write, guint32 bytes_per_send, WapiOverlapped *ol,
1044 WapiTransmitFileBuffers *buffers, guint32 flags)
1046 gpointer sock = GUINT_TO_POINTER (socket);
1047 gint ret;
1049 if (mono_w32handle_get_type (sock) != MONO_W32HANDLE_SOCKET) {
1050 WSASetLastError (WSAENOTSOCK);
1051 return FALSE;
1054 /* Write the header */
1055 if (buffers != NULL && buffers->Head != NULL && buffers->HeadLength > 0) {
1056 ret = _wapi_send (socket, buffers->Head, buffers->HeadLength, 0);
1057 if (ret == SOCKET_ERROR)
1058 return FALSE;
1061 ret = wapi_sendfile (socket, file, bytes_to_write, bytes_per_send, flags);
1062 if (ret == SOCKET_ERROR)
1063 return FALSE;
1065 /* Write the tail */
1066 if (buffers != NULL && buffers->Tail != NULL && buffers->TailLength > 0) {
1067 ret = _wapi_send (socket, buffers->Tail, buffers->TailLength, 0);
1068 if (ret == SOCKET_ERROR)
1069 return FALSE;
1072 if ((flags & TF_DISCONNECT) == TF_DISCONNECT)
1073 closesocket (socket);
1075 return TRUE;
1078 static struct
1080 WapiGuid guid;
1081 gpointer func;
1082 } extension_functions[] = {
1083 {WSAID_DISCONNECTEX, wapi_disconnectex},
1084 {WSAID_TRANSMITFILE, TransmitFile},
1085 {{0}, NULL},
1089 WSAIoctl (guint32 fd, gint32 command,
1090 gchar *input, gint i_len,
1091 gchar *output, gint o_len, glong *written,
1092 void *unused1, void *unused2)
1094 gpointer handle = GUINT_TO_POINTER (fd);
1095 int ret;
1096 gchar *buffer = NULL;
1098 if (mono_w32handle_get_type (handle) != MONO_W32HANDLE_SOCKET) {
1099 WSASetLastError (WSAENOTSOCK);
1100 return SOCKET_ERROR;
1103 if (command == SIO_GET_EXTENSION_FUNCTION_POINTER) {
1104 int i = 0;
1105 WapiGuid *guid = (WapiGuid *)input;
1107 if (i_len < sizeof(WapiGuid)) {
1108 /* As far as I can tell, windows doesn't
1109 * actually set an error here...
1111 WSASetLastError (WSAEINVAL);
1112 return(SOCKET_ERROR);
1115 if (o_len < sizeof(gpointer)) {
1116 /* Or here... */
1117 WSASetLastError (WSAEINVAL);
1118 return(SOCKET_ERROR);
1121 if (output == NULL) {
1122 /* Or here */
1123 WSASetLastError (WSAEINVAL);
1124 return(SOCKET_ERROR);
1127 while(extension_functions[i].func != NULL) {
1128 if (!memcmp (guid, &extension_functions[i].guid,
1129 sizeof(WapiGuid))) {
1130 memcpy (output, &extension_functions[i].func,
1131 sizeof(gpointer));
1132 *written = sizeof(gpointer);
1133 return(0);
1136 i++;
1139 WSASetLastError (WSAEINVAL);
1140 return(SOCKET_ERROR);
1143 if (command == SIO_KEEPALIVE_VALS) {
1144 uint32_t onoff;
1145 uint32_t keepalivetime;
1146 uint32_t keepaliveinterval;
1148 if (i_len < (3 * sizeof (uint32_t))) {
1149 WSASetLastError (WSAEINVAL);
1150 return SOCKET_ERROR;
1152 memcpy (&onoff, input, sizeof (uint32_t));
1153 memcpy (&keepalivetime, input + sizeof (uint32_t), sizeof (uint32_t));
1154 memcpy (&keepaliveinterval, input + 2 * sizeof (uint32_t), sizeof (uint32_t));
1155 ret = setsockopt (fd, SOL_SOCKET, SO_KEEPALIVE, &onoff, sizeof (uint32_t));
1156 if (ret < 0) {
1157 gint errnum = errno;
1158 errnum = errno_to_WSA (errnum, __func__);
1159 WSASetLastError (errnum);
1160 return SOCKET_ERROR;
1162 if (onoff != 0) {
1163 #if defined(TCP_KEEPIDLE) && defined(TCP_KEEPINTVL)
1164 /* Values are in ms, but we need s */
1165 uint32_t rem;
1167 /* keepalivetime and keepaliveinterval are > 0 (checked in managed code) */
1168 rem = keepalivetime % 1000;
1169 keepalivetime /= 1000;
1170 if (keepalivetime == 0 || rem >= 500)
1171 keepalivetime++;
1172 ret = setsockopt (fd, IPPROTO_TCP, TCP_KEEPIDLE, &keepalivetime, sizeof (uint32_t));
1173 if (ret == 0) {
1174 rem = keepaliveinterval % 1000;
1175 keepaliveinterval /= 1000;
1176 if (keepaliveinterval == 0 || rem >= 500)
1177 keepaliveinterval++;
1178 ret = setsockopt (fd, IPPROTO_TCP, TCP_KEEPINTVL, &keepaliveinterval, sizeof (uint32_t));
1180 if (ret != 0) {
1181 gint errnum = errno;
1182 errnum = errno_to_WSA (errnum, __func__);
1183 WSASetLastError (errnum);
1184 return SOCKET_ERROR;
1186 return 0;
1187 #endif
1189 return 0;
1192 if (i_len > 0) {
1193 buffer = (char *)g_memdup (input, i_len);
1196 ret = ioctl (fd, command, buffer);
1197 if (ret == -1) {
1198 gint errnum = errno;
1199 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: WSAIoctl error: %s", __func__,
1200 strerror (errno));
1202 errnum = errno_to_WSA (errnum, __func__);
1203 WSASetLastError (errnum);
1204 g_free (buffer);
1206 return(SOCKET_ERROR);
1209 if (buffer == NULL) {
1210 *written = 0;
1211 } else {
1212 /* We just copy the buffer to the output. Some ioctls
1213 * don't even output any data, but, well...
1215 * NB windows returns WSAEFAULT if o_len is too small
1217 i_len = (i_len > o_len) ? o_len : i_len;
1219 if (i_len > 0 && output != NULL) {
1220 memcpy (output, buffer, i_len);
1223 g_free (buffer);
1224 *written = i_len;
1227 return(0);
1230 #ifndef PLATFORM_PORT_PROVIDES_IOCTLSOCKET
1231 int ioctlsocket(guint32 fd, unsigned long command, gpointer arg)
1233 gpointer handle = GUINT_TO_POINTER (fd);
1234 int ret;
1236 if (mono_w32handle_get_type (handle) != MONO_W32HANDLE_SOCKET) {
1237 WSASetLastError (WSAENOTSOCK);
1238 return(SOCKET_ERROR);
1241 switch(command){
1242 case FIONBIO:
1243 #ifdef O_NONBLOCK
1244 /* This works better than ioctl(...FIONBIO...)
1245 * on Linux (it causes connect to return
1246 * EINPROGRESS, but the ioctl doesn't seem to)
1248 ret = fcntl(fd, F_GETFL, 0);
1249 if (ret != -1) {
1250 if (*(gboolean *)arg) {
1251 ret |= O_NONBLOCK;
1252 } else {
1253 ret &= ~O_NONBLOCK;
1255 ret = fcntl(fd, F_SETFL, ret);
1257 break;
1258 #endif /* O_NONBLOCK */
1259 /* Unused in Mono */
1260 case SIOCATMARK:
1261 ret = ioctl (fd, command, arg);
1262 break;
1264 case FIONREAD:
1266 #if defined (PLATFORM_MACOSX)
1268 // ioctl (fd, FIONREAD, XXX) returns the size of
1269 // the UDP header as well on
1270 // Darwin.
1272 // Use getsockopt SO_NREAD instead to get the
1273 // right values for TCP and UDP.
1275 // ai_canonname can be null in some cases on darwin, where the runtime assumes it will
1276 // be the value of the ip buffer.
1278 socklen_t optlen = sizeof (int);
1279 ret = getsockopt (fd, SOL_SOCKET, SO_NREAD, arg, &optlen);
1280 #else
1281 ret = ioctl (fd, command, arg);
1282 #endif
1283 break;
1285 default:
1286 WSASetLastError (WSAEINVAL);
1287 return(SOCKET_ERROR);
1290 if (ret == -1) {
1291 gint errnum = errno;
1292 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: ioctl error: %s", __func__, strerror (errno));
1294 errnum = errno_to_WSA (errnum, __func__);
1295 WSASetLastError (errnum);
1297 return(SOCKET_ERROR);
1300 return(0);
1303 int _wapi_select(int nfds G_GNUC_UNUSED, fd_set *readfds, fd_set *writefds,
1304 fd_set *exceptfds, struct timeval *timeout)
1306 int ret, maxfd;
1307 MonoThreadInfo *info = mono_thread_info_current ();
1309 for (maxfd = FD_SETSIZE-1; maxfd >= 0; maxfd--) {
1310 if ((readfds && FD_ISSET (maxfd, readfds)) ||
1311 (writefds && FD_ISSET (maxfd, writefds)) ||
1312 (exceptfds && FD_ISSET (maxfd, exceptfds))) {
1313 break;
1317 if (maxfd == -1) {
1318 WSASetLastError (WSAEINVAL);
1319 return(SOCKET_ERROR);
1322 do {
1323 ret = select(maxfd + 1, readfds, writefds, exceptfds,
1324 timeout);
1325 } while (ret == -1 && errno == EINTR &&
1326 !mono_thread_info_is_interrupt_state (info));
1328 if (ret == -1) {
1329 gint errnum = errno;
1330 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: select error: %s", __func__, strerror (errno));
1331 errnum = errno_to_WSA (errnum, __func__);
1332 WSASetLastError (errnum);
1334 return(SOCKET_ERROR);
1337 return(ret);
1340 void _wapi_FD_CLR(guint32 fd, fd_set *set)
1342 gpointer handle = GUINT_TO_POINTER (fd);
1344 if (fd >= FD_SETSIZE) {
1345 WSASetLastError (WSAEINVAL);
1346 return;
1349 if (mono_w32handle_get_type (handle) != MONO_W32HANDLE_SOCKET) {
1350 WSASetLastError (WSAENOTSOCK);
1351 return;
1354 FD_CLR (fd, set);
1357 int _wapi_FD_ISSET(guint32 fd, fd_set *set)
1359 gpointer handle = GUINT_TO_POINTER (fd);
1361 if (fd >= FD_SETSIZE) {
1362 WSASetLastError (WSAEINVAL);
1363 return(0);
1366 if (mono_w32handle_get_type (handle) != MONO_W32HANDLE_SOCKET) {
1367 WSASetLastError (WSAENOTSOCK);
1368 return(0);
1371 return(FD_ISSET (fd, set));
1374 void _wapi_FD_SET(guint32 fd, fd_set *set)
1376 gpointer handle = GUINT_TO_POINTER (fd);
1378 if (fd >= FD_SETSIZE) {
1379 WSASetLastError (WSAEINVAL);
1380 return;
1383 if (mono_w32handle_get_type (handle) != MONO_W32HANDLE_SOCKET) {
1384 WSASetLastError (WSAENOTSOCK);
1385 return;
1388 FD_SET (fd, set);
1390 #endif
1392 static void
1393 wsabuf_to_msghdr (WapiWSABuf *buffers, guint32 count, struct msghdr *hdr)
1395 guint32 i;
1397 memset (hdr, 0, sizeof (struct msghdr));
1398 hdr->msg_iovlen = count;
1399 hdr->msg_iov = g_new0 (struct iovec, count);
1400 for (i = 0; i < count; i++) {
1401 hdr->msg_iov [i].iov_base = buffers [i].buf;
1402 hdr->msg_iov [i].iov_len = buffers [i].len;
1406 static void
1407 msghdr_iov_free (struct msghdr *hdr)
1409 g_free (hdr->msg_iov);
1412 int WSARecv (guint32 fd, WapiWSABuf *buffers, guint32 count, guint32 *received,
1413 guint32 *flags, WapiOverlapped *overlapped,
1414 WapiOverlappedCB *complete)
1416 int ret;
1417 struct msghdr hdr;
1419 g_assert (overlapped == NULL);
1420 g_assert (complete == NULL);
1422 wsabuf_to_msghdr (buffers, count, &hdr);
1423 ret = _wapi_recvmsg (fd, &hdr, *flags);
1424 msghdr_iov_free (&hdr);
1426 if(ret == SOCKET_ERROR) {
1427 return(ret);
1430 *received = ret;
1431 *flags = hdr.msg_flags;
1433 return(0);
1436 int WSASend (guint32 fd, WapiWSABuf *buffers, guint32 count, guint32 *sent,
1437 guint32 flags, WapiOverlapped *overlapped,
1438 WapiOverlappedCB *complete)
1440 int ret;
1441 struct msghdr hdr;
1443 g_assert (overlapped == NULL);
1444 g_assert (complete == NULL);
1446 wsabuf_to_msghdr (buffers, count, &hdr);
1447 ret = _wapi_sendmsg (fd, &hdr, flags);
1448 msghdr_iov_free (&hdr);
1450 if(ret == SOCKET_ERROR)
1451 return ret;
1453 *sent = ret;
1454 return 0;
1457 #endif /* ifndef DISABLE_SOCKETS */