diff options
Diffstat (limited to 'winsup/cygwin/fhandler_socket.cc')
-rw-r--r-- | winsup/cygwin/fhandler_socket.cc | 2364 |
1 files changed, 0 insertions, 2364 deletions
diff --git a/winsup/cygwin/fhandler_socket.cc b/winsup/cygwin/fhandler_socket.cc deleted file mode 100644 index 59561bc58..000000000 --- a/winsup/cygwin/fhandler_socket.cc +++ /dev/null @@ -1,2364 +0,0 @@ -/* fhandler_socket.cc. See fhandler.h for a description of the fhandler classes. - - Copyright 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, - 2011, 2012, 2013, 2014 Red Hat, Inc. - - This file is part of Cygwin. - - This software is a copyrighted work licensed under the terms of the - Cygwin license. Please consult the file "CYGWIN_LICENSE" for - details. */ - -/* #define DEBUG_NEST_ON 1 */ - -#define __INSIDE_CYGWIN_NET__ -#define USE_SYS_TYPES_FD_SET - -#define _BSDTYPES_DEFINED -#include "winsup.h" -#undef _BSDTYPES_DEFINED -#ifdef __x86_64__ -/* 2014-04-24: Current Mingw headers define sockaddr_in6 using u_long (8 byte) - because a redefinition for LP64 systems is missing. This leads to a wrong - definition and size of sockaddr_in6 when building with winsock headers. - This definition is also required to use the right u_long type in subsequent - function calls. */ -#undef u_long -#define u_long __ms_u_long -#endif -#include <ws2tcpip.h> -#include <mswsock.h> -#include <iphlpapi.h> -#include "cygerrno.h" -#include "security.h" -#include "path.h" -#include "fhandler.h" -#include "dtable.h" -#include "cygheap.h" -#include <asm/byteorder.h> -#include "cygwin/version.h" -#include "perprocess.h" -#include "shared_info.h" -#include "sigproc.h" -#include "wininfo.h" -#include <unistd.h> -#include <sys/param.h> -#include <sys/acl.h> -#include "cygtls.h" -#include <sys/un.h> -#include "ntdll.h" -#include "miscfuncs.h" - -#define ASYNC_MASK (FD_READ|FD_WRITE|FD_OOB|FD_ACCEPT|FD_CONNECT) -#define EVENT_MASK (FD_READ|FD_WRITE|FD_OOB|FD_ACCEPT|FD_CONNECT|FD_CLOSE) - -extern bool fdsock (cygheap_fdmanip& fd, const device *, SOCKET soc); -extern "C" { -int sscanf (const char *, const char *, ...); -} /* End of "C" section */ - -static inline mode_t -adjust_socket_file_mode (mode_t mode) -{ - /* Kludge: Don't allow to remove read bit on socket files for - user/group/other, if the accompanying write bit is set. It would - be nice to have exact permissions on a socket file, but it's - necessary that somebody able to access the socket can always read - the contents of the socket file to avoid spurious "permission - denied" messages. */ - return mode | ((mode & (S_IWUSR | S_IWGRP | S_IWOTH)) << 1); -} - -/* cygwin internal: map sockaddr into internet domain address */ -int -get_inet_addr (const struct sockaddr *in, int inlen, - struct sockaddr_storage *out, int *outlen, - int *type = NULL, int *secret = NULL) -{ - int secret_buf [4]; - int* secret_ptr = (secret ? : secret_buf); - - switch (in->sa_family) - { - case AF_LOCAL: - /* Check for abstract socket. These are generated for AF_LOCAL datagram - sockets in recv_internal, to allow a datagram server to use sendto - after recvfrom. */ - if (inlen >= (int) sizeof (in->sa_family) + 7 - && in->sa_data[0] == '\0' && in->sa_data[1] == 'd' - && in->sa_data[6] == '\0') - { - struct sockaddr_in addr; - addr.sin_family = AF_INET; - sscanf (in->sa_data + 2, "%04hx", &addr.sin_port); - addr.sin_addr.s_addr = htonl (INADDR_LOOPBACK); - *outlen = sizeof addr; - memcpy (out, &addr, *outlen); - return 0; - } - break; - case AF_INET: - memcpy (out, in, inlen); - *outlen = inlen; - /* If the peer address given in connect or sendto is the ANY address, - Winsock fails with WSAEADDRNOTAVAIL, while Linux converts that into - a connection/send attempt to LOOPBACK. We're doing the same here. */ - if (((struct sockaddr_in *) out)->sin_addr.s_addr == htonl (INADDR_ANY)) - ((struct sockaddr_in *) out)->sin_addr.s_addr = htonl (INADDR_LOOPBACK); - return 0; - case AF_INET6: - memcpy (out, in, inlen); - *outlen = inlen; - /* See comment in AF_INET case. */ - if (IN6_IS_ADDR_UNSPECIFIED (&((struct sockaddr_in6 *) out)->sin6_addr)) - ((struct sockaddr_in6 *) out)->sin6_addr = in6addr_loopback; - return 0; - default: - set_errno (EAFNOSUPPORT); - return SOCKET_ERROR; - } - /* AF_LOCAL/AF_UNIX only */ - path_conv pc (in->sa_data, PC_SYM_FOLLOW); - if (pc.error) - { - set_errno (pc.error); - return SOCKET_ERROR; - } - if (!pc.exists ()) - { - set_errno (ENOENT); - return SOCKET_ERROR; - } - /* Do NOT test for the file being a socket file here. The socket file - creation is not an atomic operation, so there is a chance that socket - files which are just in the process of being created are recognized - as non-socket files. To work around this problem we now create the - file with all sharing disabled. If the below NtOpenFile fails - with STATUS_SHARING_VIOLATION we know that the file already exists, - but the creating process isn't finished yet. So we yield and try - again, until we can either open the file successfully, or some error - other than STATUS_SHARING_VIOLATION occurs. - Since we now don't know if the file is actually a socket file, we - perform this check here explicitely. */ - NTSTATUS status; - HANDLE fh; - OBJECT_ATTRIBUTES attr; - IO_STATUS_BLOCK io; - - pc.get_object_attr (attr, sec_none_nih); - do - { - status = NtOpenFile (&fh, GENERIC_READ | SYNCHRONIZE, &attr, &io, - FILE_SHARE_VALID_FLAGS, - FILE_SYNCHRONOUS_IO_NONALERT - | FILE_OPEN_FOR_BACKUP_INTENT - | FILE_NON_DIRECTORY_FILE); - if (status == STATUS_SHARING_VIOLATION) - { - /* While we hope that the sharing violation is only temporary, we - also could easily get stuck here, waiting for a file in use by - some greedy Win32 application. Therefore we should never wait - endlessly without checking for signals and thread cancel event. */ - pthread_testcancel (); - if (cygwait (NULL, cw_nowait, cw_sig_eintr) == WAIT_SIGNALED - && !_my_tls.call_signal_handler ()) - { - set_errno (EINTR); - return SOCKET_ERROR; - } - yield (); - } - else if (!NT_SUCCESS (status)) - { - __seterrno_from_nt_status (status); - return SOCKET_ERROR; - } - } - while (status == STATUS_SHARING_VIOLATION); - /* Now test for the SYSTEM bit. */ - FILE_BASIC_INFORMATION fbi; - status = NtQueryInformationFile (fh, &io, &fbi, sizeof fbi, - FileBasicInformation); - if (!NT_SUCCESS (status)) - { - __seterrno_from_nt_status (status); - return SOCKET_ERROR; - } - if (!(fbi.FileAttributes & FILE_ATTRIBUTE_SYSTEM)) - { - NtClose (fh); - set_errno (EBADF); - return SOCKET_ERROR; - } - /* Eventually check the content and fetch the required information. */ - char buf[128]; - memset (buf, 0, sizeof buf); - status = NtReadFile (fh, NULL, NULL, NULL, &io, buf, 128, NULL, NULL); - NtClose (fh); - if (NT_SUCCESS (status)) - { - struct sockaddr_in sin; - char ctype; - sin.sin_family = AF_INET; - if (strncmp (buf, SOCKET_COOKIE, strlen (SOCKET_COOKIE))) - { - set_errno (EBADF); - return SOCKET_ERROR; - } - sscanf (buf + strlen (SOCKET_COOKIE), "%hu %c %08x-%08x-%08x-%08x", - &sin.sin_port, - &ctype, - secret_ptr, secret_ptr + 1, secret_ptr + 2, secret_ptr + 3); - sin.sin_port = htons (sin.sin_port); - sin.sin_addr.s_addr = htonl (INADDR_LOOPBACK); - memcpy (out, &sin, sizeof sin); - *outlen = sizeof sin; - if (type) - *type = (ctype == 's' ? SOCK_STREAM : - ctype == 'd' ? SOCK_DGRAM - : 0); - return 0; - } - __seterrno_from_nt_status (status); - return SOCKET_ERROR; -} - -/**********************************************************************/ -/* fhandler_socket */ - -fhandler_socket::fhandler_socket () : - fhandler_base (), - wsock_events (NULL), - wsock_mtx (NULL), - wsock_evt (NULL), - prot_info_ptr (NULL), - sun_path (NULL), - peer_sun_path (NULL), - status () -{ - need_fork_fixup (true); -} - -fhandler_socket::~fhandler_socket () -{ - if (prot_info_ptr) - cfree (prot_info_ptr); - if (sun_path) - cfree (sun_path); - if (peer_sun_path) - cfree (peer_sun_path); -} - -char * -fhandler_socket::get_proc_fd_name (char *buf) -{ - __small_sprintf (buf, "socket:[%lu]", get_socket ()); - return buf; -} - -int -fhandler_socket::open (int flags, mode_t mode) -{ - set_errno (ENXIO); - return 0; -} - -void -fhandler_socket::af_local_set_sockpair_cred () -{ - sec_pid = sec_peer_pid = getpid (); - sec_uid = sec_peer_uid = geteuid32 (); - sec_gid = sec_peer_gid = getegid32 (); -} - -void -fhandler_socket::af_local_setblocking (bool &async, bool &nonblocking) -{ - async = async_io (); - nonblocking = is_nonblocking (); - if (async) - { - WSAAsyncSelect (get_socket (), winmsg, 0, 0); - WSAEventSelect (get_socket (), wsock_evt, EVENT_MASK); - } - set_nonblocking (false); - async_io (false); -} - -void -fhandler_socket::af_local_unsetblocking (bool async, bool nonblocking) -{ - if (nonblocking) - set_nonblocking (true); - if (async) - { - WSAAsyncSelect (get_socket (), winmsg, WM_ASYNCIO, ASYNC_MASK); - async_io (true); - } -} - -bool -fhandler_socket::af_local_recv_secret () -{ - int out[4] = { 0, 0, 0, 0 }; - int rest = sizeof out; - char *ptr = (char *) out; - while (rest > 0) - { - int ret = recvfrom (ptr, rest, 0, NULL, NULL); - if (ret <= 0) - break; - rest -= ret; - ptr += ret; - } - if (rest == 0) - { - debug_printf ("Received af_local secret: %08x-%08x-%08x-%08x", - out[0], out[1], out[2], out[3]); - if (out[0] != connect_secret[0] || out[1] != connect_secret[1] - || out[2] != connect_secret[2] || out[3] != connect_secret[3]) - { - debug_printf ("Receiving af_local secret mismatch"); - return false; - } - } - else - debug_printf ("Receiving af_local secret failed"); - return rest == 0; -} - -bool -fhandler_socket::af_local_send_secret () -{ - int rest = sizeof connect_secret; - char *ptr = (char *) connect_secret; - while (rest > 0) - { - int ret = sendto (ptr, rest, 0, NULL, 0); - if (ret <= 0) - break; - rest -= ret; - ptr += ret; - } - debug_printf ("Sending af_local secret %s", rest == 0 ? "succeeded" - : "failed"); - return rest == 0; -} - -bool -fhandler_socket::af_local_recv_cred () -{ - struct ucred out = { (pid_t) 0, (uid_t) -1, (gid_t) -1 }; - int rest = sizeof out; - char *ptr = (char *) &out; - while (rest > 0) - { - int ret = recvfrom (ptr, rest, 0, NULL, NULL); - if (ret <= 0) - break; - rest -= ret; - ptr += ret; - } - if (rest == 0) - { - debug_printf ("Received eid credentials: pid: %d, uid: %d, gid: %d", - out.pid, out.uid, out.gid); - sec_peer_pid = out.pid; - sec_peer_uid = out.uid; - sec_peer_gid = out.gid; - } - else - debug_printf ("Receiving eid credentials failed"); - return rest == 0; -} - -bool -fhandler_socket::af_local_send_cred () -{ - struct ucred in = { sec_pid, sec_uid, sec_gid }; - int rest = sizeof in; - char *ptr = (char *) ∈ - while (rest > 0) - { - int ret = sendto (ptr, rest, 0, NULL, 0); - if (ret <= 0) - break; - rest -= ret; - ptr += ret; - } - if (rest == 0) - debug_printf ("Sending eid credentials succeeded"); - else - debug_printf ("Sending eid credentials failed"); - return rest == 0; -} - -int -fhandler_socket::af_local_connect () -{ - bool orig_async_io, orig_is_nonblocking; - - if (get_addr_family () != AF_LOCAL || get_socket_type () != SOCK_STREAM) - return 0; - - debug_printf ("af_local_connect called, no_getpeereid=%d", no_getpeereid ()); - if (no_getpeereid ()) - return 0; - - af_local_setblocking (orig_async_io, orig_is_nonblocking); - if (!af_local_send_secret () || !af_local_recv_secret () - || !af_local_send_cred () || !af_local_recv_cred ()) - { - debug_printf ("accept from unauthorized server"); - ::shutdown (get_socket (), SD_BOTH); - WSASetLastError (WSAECONNREFUSED); - return -1; - } - af_local_unsetblocking (orig_async_io, orig_is_nonblocking); - return 0; -} - -int -fhandler_socket::af_local_accept () -{ - bool orig_async_io, orig_is_nonblocking; - - debug_printf ("af_local_accept called, no_getpeereid=%d", no_getpeereid ()); - if (no_getpeereid ()) - return 0; - - af_local_setblocking (orig_async_io, orig_is_nonblocking); - if (!af_local_recv_secret () || !af_local_send_secret () - || !af_local_recv_cred () || !af_local_send_cred ()) - { - debug_printf ("connect from unauthorized client"); - ::shutdown (get_socket (), SD_BOTH); - ::closesocket (get_socket ()); - WSASetLastError (WSAECONNABORTED); - return -1; - } - af_local_unsetblocking (orig_async_io, orig_is_nonblocking); - return 0; -} - -int -fhandler_socket::af_local_set_no_getpeereid () -{ - if (get_addr_family () != AF_LOCAL || get_socket_type () != SOCK_STREAM) - { - set_errno (EINVAL); - return -1; - } - if (connect_state () != unconnected) - { - set_errno (EALREADY); - return -1; - } - - debug_printf ("no_getpeereid set"); - no_getpeereid (true); - return 0; -} - -void -fhandler_socket::af_local_set_cred () -{ - sec_pid = getpid (); - sec_uid = geteuid32 (); - sec_gid = getegid32 (); - sec_peer_pid = (pid_t) 0; - sec_peer_uid = (uid_t) -1; - sec_peer_gid = (gid_t) -1; -} - -void -fhandler_socket::af_local_copy (fhandler_socket *sock) -{ - sock->connect_secret[0] = connect_secret[0]; - sock->connect_secret[1] = connect_secret[1]; - sock->connect_secret[2] = connect_secret[2]; - sock->connect_secret[3] = connect_secret[3]; - sock->sec_pid = sec_pid; - sock->sec_uid = sec_uid; - sock->sec_gid = sec_gid; - sock->sec_peer_pid = sec_peer_pid; - sock->sec_peer_uid = sec_peer_uid; - sock->sec_peer_gid = sec_peer_gid; - sock->no_getpeereid (no_getpeereid ()); -} - -void -fhandler_socket::af_local_set_secret (char *buf) -{ - if (!fhandler_dev_random::crypt_gen_random (connect_secret, - sizeof (connect_secret))) - bzero ((char*) connect_secret, sizeof (connect_secret)); - __small_sprintf (buf, "%08x-%08x-%08x-%08x", - connect_secret [0], connect_secret [1], - connect_secret [2], connect_secret [3]); -} - -/* Maximum number of concurrently opened sockets from all Cygwin processes - per session. Note that shared sockets (through dup/fork/exec) are - counted as one socket. */ -#define NUM_SOCKS (32768 / sizeof (wsa_event)) - -#define LOCK_EVENTS WaitForSingleObject (wsock_mtx, INFINITE) -#define UNLOCK_EVENTS ReleaseMutex (wsock_mtx) - -static wsa_event wsa_events[NUM_SOCKS] __attribute__((section (".cygwin_dll_common"), shared)); - -static LONG socket_serial_number __attribute__((section (".cygwin_dll_common"), shared)); - -static HANDLE wsa_slot_mtx; - -static PWCHAR -sock_shared_name (PWCHAR buf, LONG num) -{ - __small_swprintf (buf, L"socket.%d", num); - return buf; -} - -static wsa_event * -search_wsa_event_slot (LONG new_serial_number) -{ - WCHAR name[32], searchname[32]; - UNICODE_STRING uname; - OBJECT_ATTRIBUTES attr; - NTSTATUS status; - - if (!wsa_slot_mtx) - { - RtlInitUnicodeString (&uname, sock_shared_name (name, 0)); - InitializeObjectAttributes (&attr, &uname, OBJ_INHERIT | OBJ_OPENIF, - get_session_parent_dir (), - everyone_sd (CYG_MUTANT_ACCESS)); - status = NtCreateMutant (&wsa_slot_mtx, CYG_MUTANT_ACCESS, &attr, FALSE); - if (!NT_SUCCESS (status)) - api_fatal ("Couldn't create/open shared socket mutex %S, %y", - &uname, status); - } - switch (WaitForSingleObject (wsa_slot_mtx, INFINITE)) - { - case WAIT_OBJECT_0: - case WAIT_ABANDONED: - break; - default: - api_fatal ("WFSO failed for shared socket mutex, %E"); - break; - } - unsigned int slot = new_serial_number % NUM_SOCKS; - while (wsa_events[slot].serial_number) - { - HANDLE searchmtx; - RtlInitUnicodeString (&uname, sock_shared_name (searchname, - wsa_events[slot].serial_number)); - InitializeObjectAttributes (&attr, &uname, 0, get_session_parent_dir (), - NULL); - status = NtOpenMutant (&searchmtx, READ_CONTROL, &attr); - if (!NT_SUCCESS (status)) - break; - /* Mutex still exists, attached socket is active, try next slot. */ - NtClose (searchmtx); - slot = (slot + 1) % NUM_SOCKS; - if (slot == (new_serial_number % NUM_SOCKS)) - { - /* Did the whole array once. Too bad. */ - debug_printf ("No free socket slot"); - ReleaseMutex (wsa_slot_mtx); - return NULL; - } - } - memset (&wsa_events[slot], 0, sizeof (wsa_event)); - wsa_events[slot].serial_number = new_serial_number; - ReleaseMutex (wsa_slot_mtx); - return wsa_events + slot; -} - -bool -fhandler_socket::init_events () -{ - LONG new_serial_number; - WCHAR name[32]; - UNICODE_STRING uname; - OBJECT_ATTRIBUTES attr; - NTSTATUS status; - - do - { - new_serial_number = - InterlockedIncrement (&socket_serial_number); - if (!new_serial_number) /* 0 is reserved for global mutex */ - InterlockedIncrement (&socket_serial_number); - RtlInitUnicodeString (&uname, sock_shared_name (name, new_serial_number)); - InitializeObjectAttributes (&attr, &uname, OBJ_INHERIT | OBJ_OPENIF, - get_session_parent_dir (), - everyone_sd (CYG_MUTANT_ACCESS)); - status = NtCreateMutant (&wsock_mtx, CYG_MUTANT_ACCESS, &attr, FALSE); - if (!NT_SUCCESS (status)) - { - debug_printf ("NtCreateMutant(%S), %y", &uname, status); - set_errno (ENOBUFS); - return false; - } - if (status == STATUS_OBJECT_NAME_EXISTS) - NtClose (wsock_mtx); - } - while (status == STATUS_OBJECT_NAME_EXISTS); - if ((wsock_evt = CreateEvent (&sec_all, TRUE, FALSE, NULL)) - == WSA_INVALID_EVENT) - { - debug_printf ("CreateEvent, %E"); - set_errno (ENOBUFS); - NtClose (wsock_mtx); - return false; - } - if (WSAEventSelect (get_socket (), wsock_evt, EVENT_MASK) == SOCKET_ERROR) - { - debug_printf ("WSAEventSelect, %E"); - set_winsock_errno (); - NtClose (wsock_evt); - NtClose (wsock_mtx); - return false; - } - wsock_events = search_wsa_event_slot (new_serial_number); - /* sock type not yet set here. */ - if (pc.dev == FH_UDP || pc.dev == FH_DGRAM) - wsock_events->events = FD_WRITE; - return true; -} - -int -fhandler_socket::evaluate_events (const long event_mask, long &events, - const bool erase) -{ - int ret = 0; - long events_now = 0; - - WSANETWORKEVENTS evts = { 0 }; - if (!(WSAEnumNetworkEvents (get_socket (), wsock_evt, &evts))) - { - if (evts.lNetworkEvents) - { - LOCK_EVENTS; - wsock_events->events |= evts.lNetworkEvents; - events_now = (wsock_events->events & event_mask); - if (evts.lNetworkEvents & FD_CONNECT) - { - wsock_events->connect_errorcode = evts.iErrorCode[FD_CONNECT_BIT]; - - /* Setting the connect_state and calling the AF_LOCAL handshake - here allows to handle this stuff from a single point. This - is independent of FD_CONNECT being requested. Consider a - server calling connect(2) and then immediately poll(2) with - only polling for POLLIN (example: postfix), or select(2) just - asking for descriptors ready to read. - - Something weird occurs in Winsock: If you fork off and call - recv/send on the duplicated, already connected socket, another - FD_CONNECT event is generated in the child process. This - would trigger a call to af_local_connect which obviously fail. - Avoid this by calling set_connect_state only if connect_state - is connect_pending. */ - if (connect_state () == connect_pending) - { - if (wsock_events->connect_errorcode) - connect_state (connect_failed); - else if (af_local_connect ()) - { - wsock_events->connect_errorcode = WSAGetLastError (); - connect_state (connect_failed); - } - else - connect_state (connected); - } - } - UNLOCK_EVENTS; - if ((evts.lNetworkEvents & FD_OOB) && wsock_events->owner) - kill (wsock_events->owner, SIGURG); - } - } - - LOCK_EVENTS; - if ((events = events_now) != 0 - || (events = (wsock_events->events & event_mask)) != 0) - { - if (events & FD_CONNECT) - { - int wsa_err = wsock_events->connect_errorcode; - if (wsa_err) - { - /* CV 2014-04-23: This is really weird. If you call connect - asynchronously on a socket and then select, an error like - "Connection refused" is set in the event and in the SO_ERROR - socket option. If you call connect, then dup, then select, - the error is set in the event, but not in the SO_ERROR socket - option, despite the dup'ed socket handle referring to the same - socket. We're trying to workaround this problem here by - taking the connect errorcode from the event and write it back - into the SO_ERROR socket option. - - CV 2014-06-16: Call WSASetLastError *after* setsockopt since, - apparently, setsockopt sets the last WSA error code to 0 on - success. */ - setsockopt (get_socket (), SOL_SOCKET, SO_ERROR, - (const char *) &wsa_err, sizeof wsa_err); - WSASetLastError (wsa_err); - ret = SOCKET_ERROR; - } - else - wsock_events->events |= FD_WRITE; - wsock_events->events &= ~FD_CONNECT; - wsock_events->connect_errorcode = 0; - } - /* This test makes the accept function behave as on Linux when - accept is called on a socket for which shutdown for the read side - has been called. The second half of this code is in the shutdown - method. See there for more info. */ - if ((event_mask & FD_ACCEPT) && (events & FD_CLOSE)) - { - WSASetLastError (WSAEINVAL); - ret = SOCKET_ERROR; - } - if (erase) - wsock_events->events &= ~(events & ~(FD_WRITE | FD_CLOSE)); - } - UNLOCK_EVENTS; - - return ret; -} - -int -fhandler_socket::wait_for_events (const long event_mask, const DWORD flags) -{ - if (async_io ()) - return 0; - - int ret; - long events; - - while (!(ret = evaluate_events (event_mask, events, !(flags & MSG_PEEK))) - && !events) - { - if (is_nonblocking () || (flags & MSG_DONTWAIT)) - { - WSASetLastError (WSAEWOULDBLOCK); - return SOCKET_ERROR; - } - - WSAEVENT ev[2] = { wsock_evt }; - set_signal_arrived here (ev[1]); - switch (WSAWaitForMultipleEvents (2, ev, FALSE, 50, FALSE)) - { - case WSA_WAIT_TIMEOUT: - pthread_testcancel (); - /*FALLTHRU*/ - case WSA_WAIT_EVENT_0: - break; - - case WSA_WAIT_EVENT_0 + 1: - if (_my_tls.call_signal_handler ()) - break; - WSASetLastError (WSAEINTR); - return SOCKET_ERROR; - - default: - WSASetLastError (WSAEFAULT); - return SOCKET_ERROR; - } - } - - return ret; -} - -void -fhandler_socket::release_events () -{ - NtClose (wsock_evt); - NtClose (wsock_mtx); -} - -/* Called from net.cc:fdsock() if a freshly created socket is not - inheritable. In that case we use fixup_before_fork_exec. See - the comment in fdsock() for a description of the problem. */ -void -fhandler_socket::init_fixup_before () -{ - prot_info_ptr = (LPWSAPROTOCOL_INFOW) - cmalloc_abort (HEAP_BUF, sizeof (WSAPROTOCOL_INFOW)); - cygheap->fdtab.inc_need_fixup_before (); -} - -int -fhandler_socket::fixup_before_fork_exec (DWORD win_pid) -{ - SOCKET ret = WSADuplicateSocketW (get_socket (), win_pid, prot_info_ptr); - if (ret) - set_winsock_errno (); - else - debug_printf ("WSADuplicateSocket succeeded (%x)", prot_info_ptr->dwProviderReserved); - return (int) ret; -} - -void -fhandler_socket::fixup_after_fork (HANDLE parent) -{ - fork_fixup (parent, wsock_mtx, "wsock_mtx"); - fork_fixup (parent, wsock_evt, "wsock_evt"); - - if (!need_fixup_before ()) - { - fhandler_base::fixup_after_fork (parent); - return; - } - - SOCKET new_sock = WSASocketW (FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO, - FROM_PROTOCOL_INFO, prot_info_ptr, 0, - WSA_FLAG_OVERLAPPED); - if (new_sock == INVALID_SOCKET) - { - set_winsock_errno (); - set_io_handle ((HANDLE) INVALID_SOCKET); - } - else - { - /* Even though the original socket was not inheritable, the duplicated - socket is potentially inheritable again. */ - SetHandleInformation ((HANDLE) new_sock, HANDLE_FLAG_INHERIT, 0); - set_io_handle ((HANDLE) new_sock); - debug_printf ("WSASocket succeeded (%p)", new_sock); - } -} - -void -fhandler_socket::fixup_after_exec () -{ - if (need_fixup_before () && !close_on_exec ()) - fixup_after_fork (NULL); -} - -int -fhandler_socket::dup (fhandler_base *child, int flags) -{ - debug_printf ("here"); - fhandler_socket *fhs = (fhandler_socket *) child; - - if (!DuplicateHandle (GetCurrentProcess (), wsock_mtx, - GetCurrentProcess (), &fhs->wsock_mtx, - 0, TRUE, DUPLICATE_SAME_ACCESS)) - { - __seterrno (); - return -1; - } - if (!DuplicateHandle (GetCurrentProcess (), wsock_evt, - GetCurrentProcess (), &fhs->wsock_evt, - 0, TRUE, DUPLICATE_SAME_ACCESS)) - { - __seterrno (); - NtClose (fhs->wsock_mtx); - return -1; - } - if (get_addr_family () == AF_LOCAL) - { - fhs->set_sun_path (get_sun_path ()); - fhs->set_peer_sun_path (get_peer_sun_path ()); - } - if (!need_fixup_before ()) - { - int ret = fhandler_base::dup (child, flags); - if (ret) - { - NtClose (fhs->wsock_evt); - NtClose (fhs->wsock_mtx); - } - return ret; - } - - cygheap->user.deimpersonate (); - fhs->init_fixup_before (); - fhs->set_io_handle (get_io_handle ()); - int ret = fhs->fixup_before_fork_exec (GetCurrentProcessId ()); - cygheap->user.reimpersonate (); - if (!ret) - { - fhs->fixup_after_fork (GetCurrentProcess ()); - if (fhs->get_io_handle() != (HANDLE) INVALID_SOCKET) - return 0; - } - cygheap->fdtab.dec_need_fixup_before (); - NtClose (fhs->wsock_evt); - NtClose (fhs->wsock_mtx); - return -1; -} - -int __reg2 -fhandler_socket::fstat (struct stat *buf) -{ - int res; - if (get_device () == FH_UNIX) - { - res = fhandler_base::fstat_fs (buf); - if (!res) - { - buf->st_mode = (buf->st_mode & ~S_IFMT) | S_IFSOCK; - buf->st_size = 0; - } - } - else - { - res = fhandler_base::fstat (buf); - if (!res) - { - buf->st_dev = 0; - buf->st_ino = (ino_t) ((uintptr_t) get_handle ()); - buf->st_mode = S_IFSOCK | S_IRWXU | S_IRWXG | S_IRWXO; - buf->st_size = 0; - } - } - return res; -} - -int __reg2 -fhandler_socket::fstatvfs (struct statvfs *sfs) -{ - if (get_device () == FH_UNIX) - { - fhandler_disk_file fh (pc); - fh.get_device () = FH_FS; - return fh.fstatvfs (sfs); - } - set_errno (EBADF); - return -1; -} - -int -fhandler_socket::fchmod (mode_t mode) -{ - if (get_device () == FH_UNIX) - { - fhandler_disk_file fh (pc); - fh.get_device () = FH_FS; - int ret = fh.fchmod (S_IFSOCK | adjust_socket_file_mode (mode)); - return ret; - } - set_errno (EBADF); - return -1; -} - -int -fhandler_socket::fchown (uid_t uid, gid_t gid) -{ - if (get_device () == FH_UNIX) - { - fhandler_disk_file fh (pc); - return fh.fchown (uid, gid); - } - set_errno (EBADF); - return -1; -} - -int -fhandler_socket::facl (int cmd, int nentries, aclent_t *aclbufp) -{ - if (get_device () == FH_UNIX) - { - fhandler_disk_file fh (pc); - return fh.facl (cmd, nentries, aclbufp); - } - set_errno (EBADF); - return -1; -} - -int -fhandler_socket::link (const char *newpath) -{ - if (get_device () == FH_UNIX) - { - fhandler_disk_file fh (pc); - return fh.link (newpath); - } - return fhandler_base::link (newpath); -} - -int -fhandler_socket::bind (const struct sockaddr *name, int namelen) -{ - int res = -1; - - if (name->sa_family == AF_LOCAL) - { -#define un_addr ((struct sockaddr_un *) name) - struct sockaddr_in sin; - int len = namelen - offsetof (struct sockaddr_un, sun_path); - - /* Check that name is within bounds. Don't check if the string is - NUL-terminated, because there are projects out there which set - namelen to a value which doesn't cover the trailing NUL. */ - if (len <= 1 || (len = strnlen (un_addr->sun_path, len)) > UNIX_PATH_MAX) - { - set_errno (len <= 1 ? (len == 1 ? ENOENT : EINVAL) : ENAMETOOLONG); - goto out; - } - /* Copy over the sun_path string into a buffer big enough to add a - trailing NUL. */ - char sun_path[len + 1]; - strncpy (sun_path, un_addr->sun_path, len); - sun_path[len] = '\0'; - - /* This isn't entirely foolproof, but we check first if the file exists - so we can return with EADDRINUSE before having bound the socket. - This allows an application to call bind again on the same socket using - another filename. If we bind first, the application will not be able - to call bind successfully ever again. */ - path_conv pc (sun_path, PC_SYM_FOLLOW); - if (pc.error) - { - set_errno (pc.error); - goto out; - } - if (pc.exists ()) - { - set_errno (EADDRINUSE); - goto out; - } - - sin.sin_family = AF_INET; - sin.sin_port = 0; - sin.sin_addr.s_addr = htonl (INADDR_LOOPBACK); - if (::bind (get_socket (), (sockaddr *) &sin, len = sizeof sin)) - { - syscall_printf ("AF_LOCAL: bind failed"); - set_winsock_errno (); - goto out; - } - if (::getsockname (get_socket (), (sockaddr *) &sin, &len)) - { - syscall_printf ("AF_LOCAL: getsockname failed"); - set_winsock_errno (); - goto out; - } - - sin.sin_port = ntohs (sin.sin_port); - debug_printf ("AF_LOCAL: socket bound to port %u", sin.sin_port); - - mode_t mode = adjust_socket_file_mode ((S_IRWXU | S_IRWXG | S_IRWXO) - & ~cygheap->umask); - DWORD fattr = FILE_ATTRIBUTE_SYSTEM; - if (!(mode & (S_IWUSR | S_IWGRP | S_IWOTH)) && !pc.has_acls ()) - fattr |= FILE_ATTRIBUTE_READONLY; - SECURITY_ATTRIBUTES sa = sec_none_nih; - NTSTATUS status; - HANDLE fh; - OBJECT_ATTRIBUTES attr; - IO_STATUS_BLOCK io; - ULONG access = DELETE | FILE_GENERIC_WRITE; - - /* If the filesystem supports ACLs, we will overwrite the DACL after the - call to NtCreateFile. This requires a handle with READ_CONTROL and - WRITE_DAC access, otherwise get_file_sd and set_file_sd both have to - open the file again. - FIXME: On remote NTFS shares open sometimes fails because even the - creator of the file doesn't have the right to change the DACL. - I don't know what setting that is or how to recognize such a share, - so for now we don't request WRITE_DAC on remote drives. */ - if (pc.has_acls () && !pc.isremote ()) - access |= READ_CONTROL | WRITE_DAC; - - status = NtCreateFile (&fh, access, pc.get_object_attr (attr, sa), &io, - NULL, fattr, 0, FILE_CREATE, - FILE_NON_DIRECTORY_FILE - | FILE_SYNCHRONOUS_IO_NONALERT - | FILE_OPEN_FOR_BACKUP_INTENT, - NULL, 0); - if (!NT_SUCCESS (status)) - { - if (io.Information == FILE_EXISTS) - set_errno (EADDRINUSE); - else - __seterrno_from_nt_status (status); - } - else - { - if (pc.has_acls ()) - set_file_attribute (fh, pc, ILLEGAL_UID, ILLEGAL_GID, - S_JUSTCREATED | mode); - char buf[sizeof (SOCKET_COOKIE) + 80]; - __small_sprintf (buf, "%s%u %c ", SOCKET_COOKIE, sin.sin_port, - get_socket_type () == SOCK_STREAM ? 's' - : get_socket_type () == SOCK_DGRAM ? 'd' : '-'); - af_local_set_secret (strchr (buf, '\0')); - DWORD blen = strlen (buf) + 1; - status = NtWriteFile (fh, NULL, NULL, NULL, &io, buf, blen, NULL, 0); - if (!NT_SUCCESS (status)) - { - __seterrno_from_nt_status (status); - FILE_DISPOSITION_INFORMATION fdi = { TRUE }; - status = NtSetInformationFile (fh, &io, &fdi, sizeof fdi, - FileDispositionInformation); - if (!NT_SUCCESS (status)) - debug_printf ("Setting delete dispostion failed, status = %y", - status); - } - else - { - set_sun_path (sun_path); - res = 0; - } - NtClose (fh); - } -#undef un_addr - } - else - { - if (!saw_reuseaddr ()) - { - /* If the application didn't explicitely request SO_REUSEADDR, - enforce POSIX standard socket binding behaviour by setting the - SO_EXCLUSIVEADDRUSE socket option. See cygwin_setsockopt() - for a more detailed description. */ - int on = 1; - int ret = ::setsockopt (get_socket (), SOL_SOCKET, - ~(SO_REUSEADDR), - (const char *) &on, sizeof on); - debug_printf ("%d = setsockopt(SO_EXCLUSIVEADDRUSE), %E", ret); - } - if (::bind (get_socket (), name, namelen)) - set_winsock_errno (); - else - res = 0; - } - -out: - return res; -} - -int -fhandler_socket::connect (const struct sockaddr *name, int namelen) -{ - struct sockaddr_storage sst; - int type; - - if (get_inet_addr (name, namelen, &sst, &namelen, &type, connect_secret) - == SOCKET_ERROR) - return SOCKET_ERROR; - - if (get_addr_family () == AF_LOCAL) - { - if (get_socket_type () != type) - { - WSASetLastError (WSAEPROTOTYPE); - set_winsock_errno (); - return SOCKET_ERROR; - } - - set_peer_sun_path (name->sa_data); - - /* Don't move af_local_set_cred into af_local_connect which may be called - via select, possibly running under another identity. Call early here, - because af_local_connect is called in wait_for_events. */ - if (get_socket_type () == SOCK_STREAM) - af_local_set_cred (); - } - - /* Initialize connect state to "connect_pending". State is ultimately set - to "connected" or "connect_failed" in wait_for_events when the FD_CONNECT - event occurs. Note that the underlying OS sockets are always non-blocking - and a successfully initiated non-blocking Winsock connect always returns - WSAEWOULDBLOCK. Thus it's safe to rely on event handling. - - Check for either unconnected or connect_failed since in both cases it's - allowed to retry connecting the socket. It's also ok (albeit ugly) to - call connect to check if a previous non-blocking connect finished. - - Set connect_state before calling connect, otherwise a race condition with - an already running select or poll might occur. */ - if (connect_state () == unconnected || connect_state () == connect_failed) - connect_state (connect_pending); - - int res = ::connect (get_socket (), (struct sockaddr *) &sst, namelen); - if (!is_nonblocking () - && res == SOCKET_ERROR - && WSAGetLastError () == WSAEWOULDBLOCK) - res = wait_for_events (FD_CONNECT | FD_CLOSE, 0); - - if (res) - { - DWORD err = WSAGetLastError (); - - /* Some applications use the ugly technique to check if a non-blocking - connect succeeded by calling connect again, until it returns EISCONN. - This circumvents the event handling and connect_state is never set. - Thus we check for this situation here. */ - if (err == WSAEISCONN) - connect_state (connected); - /* Winsock returns WSAEWOULDBLOCK if the non-blocking socket cannot be - conected immediately. Convert to POSIX/Linux compliant EINPROGRESS. */ - else if (is_nonblocking () && err == WSAEWOULDBLOCK) - WSASetLastError (WSAEINPROGRESS); - /* Winsock returns WSAEINVAL if the socket is already a listener. - Convert to POSIX/Linux compliant EISCONN. */ - else if (err == WSAEINVAL && connect_state () == listener) - WSASetLastError (WSAEISCONN); - /* Any other error except WSAEALREADY during connect_pending means the - connect failed. */ - else if (connect_state () == connect_pending && err != WSAEALREADY) - connect_state (connect_failed); - set_winsock_errno (); - } - - return res; -} - -int -fhandler_socket::listen (int backlog) -{ - int res = ::listen (get_socket (), backlog); - if (res && WSAGetLastError () == WSAEINVAL) - { - /* It's perfectly valid to call listen on an unbound INET socket. - In this case the socket is automatically bound to an unused - port number, listening on all interfaces. On WinSock, listen - fails with WSAEINVAL when it's called on an unbound socket. - So we have to bind manually here to have POSIX semantics. */ - if (get_addr_family () == AF_INET) - { - struct sockaddr_in sin; - sin.sin_family = AF_INET; - sin.sin_port = 0; - sin.sin_addr.s_addr = INADDR_ANY; - if (!::bind (get_socket (), (struct sockaddr *) &sin, sizeof sin)) - res = ::listen (get_socket (), backlog); - } - else if (get_addr_family () == AF_INET6) - { - struct sockaddr_in6 sin6; - memset (&sin6, 0, sizeof sin6); - sin6.sin6_family = AF_INET6; - if (!::bind (get_socket (), (struct sockaddr *) &sin6, sizeof sin6)) - res = ::listen (get_socket (), backlog); - } - } - if (!res) - { - if (get_addr_family () == AF_LOCAL && get_socket_type () == SOCK_STREAM) - af_local_set_cred (); - connect_state (listener); /* gets set to connected on accepted socket. */ - } - else - set_winsock_errno (); - return res; -} - -int -fhandler_socket::accept4 (struct sockaddr *peer, int *len, int flags) -{ - /* Allows NULL peer and len parameters. */ - struct sockaddr_storage lpeer; - int llen = sizeof (struct sockaddr_storage); - - int res = (int) INVALID_SOCKET; - - /* Windows event handling does not check for the validity of the desired - flags so we have to do it here. */ - if (connect_state () != listener) - { - WSASetLastError (WSAEINVAL); - set_winsock_errno (); - goto out; - } - - while (!(res = wait_for_events (FD_ACCEPT | FD_CLOSE, 0)) - && (res = ::accept (get_socket (), (struct sockaddr *) &lpeer, &llen)) - == SOCKET_ERROR - && WSAGetLastError () == WSAEWOULDBLOCK) - ; - if (res == (int) INVALID_SOCKET) - set_winsock_errno (); - else - { - cygheap_fdnew res_fd; - if (res_fd >= 0 && fdsock (res_fd, &dev (), res)) - { - fhandler_socket *sock = (fhandler_socket *) res_fd; - sock->set_addr_family (get_addr_family ()); - sock->set_socket_type (get_socket_type ()); - sock->async_io (false); /* fdsock switches async mode off. */ - if (get_addr_family () == AF_LOCAL) - { - sock->set_sun_path (get_sun_path ()); - sock->set_peer_sun_path (get_peer_sun_path ()); - if (get_socket_type () == SOCK_STREAM) - { - /* Don't forget to copy credentials from accepting - socket to accepted socket and start transaction - on accepted socket! */ - af_local_copy (sock); - res = sock->af_local_accept (); - if (res == -1) - { - res_fd.release (); - set_winsock_errno (); - goto out; - } - } - } - sock->set_nonblocking (flags & SOCK_NONBLOCK); - if (flags & SOCK_CLOEXEC) - sock->set_close_on_exec (true); - /* No locking necessary at this point. */ - sock->wsock_events->events = wsock_events->events | FD_WRITE; - sock->wsock_events->owner = wsock_events->owner; - sock->connect_state (connected); - res = res_fd; - if (peer) - { - if (get_addr_family () == AF_LOCAL) - { - /* FIXME: Right now we have no way to determine the - bound socket name of the peer's socket. For now - we just fake an unbound socket on the other side. */ - static struct sockaddr_un un = { AF_LOCAL, "" }; - memcpy (peer, &un, MIN (*len, (int) sizeof (un.sun_family))); - *len = (int) sizeof (un.sun_family); - } - else - { - memcpy (peer, &lpeer, MIN (*len, llen)); - *len = llen; - } - } - } - else - { - closesocket (res); - res = -1; - } - } - -out: - debug_printf ("res %d", res); - return res; -} - -int -fhandler_socket::getsockname (struct sockaddr *name, int *namelen) -{ - int res = -1; - - if (get_addr_family () == AF_LOCAL) - { - struct sockaddr_un sun; - sun.sun_family = AF_LOCAL; - sun.sun_path[0] = '\0'; - if (get_sun_path ()) - strncat (sun.sun_path, get_sun_path (), UNIX_PATH_MAX - 1); - memcpy (name, &sun, MIN (*namelen, (int) SUN_LEN (&sun) + 1)); - *namelen = (int) SUN_LEN (&sun) + (get_sun_path () ? 1 : 0); - res = 0; - } - else - { - /* Always use a local big enough buffer and truncate later as necessary - per POSIX. WinSock unfortunaltey only returns WSAEFAULT if the buffer - is too small. */ - struct sockaddr_storage sock; - int len = sizeof sock; - res = ::getsockname (get_socket (), (struct sockaddr *) &sock, &len); - if (!res) - { - memcpy (name, &sock, MIN (*namelen, len)); - *namelen = len; - } - else - { - if (WSAGetLastError () == WSAEINVAL) - { - /* WinSock returns WSAEINVAL if the socket is locally - unbound. Per SUSv3 this is not an error condition. - We're faking a valid return value here by creating the - same content in the sockaddr structure as on Linux. */ - memset (&sock, 0, sizeof sock); - sock.ss_family = get_addr_family (); - switch (get_addr_family ()) - { - case AF_INET: - res = 0; - len = (int) sizeof (struct sockaddr_in); - break; - case AF_INET6: - res = 0; - len = (int) sizeof (struct sockaddr_in6); - break; - default: - WSASetLastError (WSAEOPNOTSUPP); - break; - } - if (!res) - { - memcpy (name, &sock, MIN (*namelen, len)); - *namelen = len; - } - } - if (res) - set_winsock_errno (); - } - } - - return res; -} - -int -fhandler_socket::getpeername (struct sockaddr *name, int *namelen) -{ - /* Always use a local big enough buffer and truncate later as necessary - per POSIX. WinSock unfortunately only returns WSAEFAULT if the buffer - is too small. */ - struct sockaddr_storage sock; - int len = sizeof sock; - int res = ::getpeername (get_socket (), (struct sockaddr *) &sock, &len); - if (res) - set_winsock_errno (); - else if (get_addr_family () == AF_LOCAL) - { - struct sockaddr_un sun; - memset (&sun, 0, sizeof sun); - sun.sun_family = AF_LOCAL; - sun.sun_path[0] = '\0'; - if (get_peer_sun_path ()) - strncat (sun.sun_path, get_peer_sun_path (), UNIX_PATH_MAX - 1); - memcpy (name, &sun, MIN (*namelen, (int) SUN_LEN (&sun) + 1)); - *namelen = (int) SUN_LEN (&sun) + (get_peer_sun_path () ? 1 : 0); - } - else - { - memcpy (name, &sock, MIN (*namelen, len)); - *namelen = len; - } - - return res; -} - -/* There's no DLL which exports the symbol WSARecvMsg. One has to call - WSAIoctl as below to fetch the function pointer. Why on earth did the - MS developers decide not to export a normal symbol for these extension - functions? */ -inline int -get_ext_funcptr (SOCKET sock, void *funcptr) -{ - DWORD bret; - const GUID guid = WSAID_WSARECVMSG; - return WSAIoctl (sock, SIO_GET_EXTENSION_FUNCTION_POINTER, - (void *) &guid, sizeof (GUID), funcptr, sizeof (void *), - &bret, NULL, NULL); -} - -inline ssize_t -fhandler_socket::recv_internal (LPWSAMSG wsamsg, bool use_recvmsg) -{ - ssize_t res = 0; - DWORD ret = 0, wret; - int evt_mask = FD_READ | ((wsamsg->dwFlags & MSG_OOB) ? FD_OOB : 0); - LPWSABUF &wsabuf = wsamsg->lpBuffers; - ULONG &wsacnt = wsamsg->dwBufferCount; - static NO_COPY LPFN_WSARECVMSG WSARecvMsg; - int orig_namelen = wsamsg->namelen; - - /* CV 2014-10-26: Do not check for the connect_state at this point. In - certain scenarios there's no way to check the connect state reliably. - Example (hexchat): Parent process creates socket, forks, child process - calls connect, parent process calls read. Even if the event handling - allows to check for FD_CONNECT in the parent, there is always yet another - scenario we can easily break. */ - - DWORD wait_flags = wsamsg->dwFlags; - bool waitall = !!(wait_flags & MSG_WAITALL); - wsamsg->dwFlags &= (MSG_OOB | MSG_PEEK | MSG_DONTROUTE); - if (use_recvmsg) - { - if (!WSARecvMsg - && get_ext_funcptr (get_socket (), &WSARecvMsg) == SOCKET_ERROR) - { - if (wsamsg->Control.len > 0) - { - set_winsock_errno (); - return SOCKET_ERROR; - } - use_recvmsg = false; - } - else /* Only MSG_PEEK is supported by WSARecvMsg. */ - wsamsg->dwFlags &= MSG_PEEK; - } - if (waitall) - { - if (get_socket_type () != SOCK_STREAM) - { - WSASetLastError (WSAEOPNOTSUPP); - set_winsock_errno (); - return SOCKET_ERROR; - } - if (is_nonblocking () || (wsamsg->dwFlags & (MSG_OOB | MSG_PEEK))) - waitall = false; - } - - /* Note: Don't call WSARecvFrom(MSG_PEEK) without actually having data - waiting in the buffers, otherwise the event handling gets messed up - for some reason. */ - while (!(res = wait_for_events (evt_mask | FD_CLOSE, wait_flags)) - || saw_shutdown_read ()) - { - if (use_recvmsg) - res = WSARecvMsg (get_socket (), wsamsg, &wret, NULL, NULL); - /* This is working around a really weird problem in WinSock. - - Assume you create a socket, fork the process (thus duplicating - the socket), connect the socket in the child, then call recv - on the original socket handle in the parent process. - In this scenario, calls to WinSock's recvfrom and WSARecvFrom - in the parent will fail with WSAEINVAL, regardless whether both - address parameters, name and namelen, are NULL or point to valid - storage. However, calls to recv and WSARecv succeed as expected. - Per MSDN, WSAEINVAL in the context of recv means "The socket has not - been bound". It is as if the recvfrom functions test if the socket - is bound locally, but in the parent process, WinSock doesn't know - about that and fails, while the same test is omitted in the recv - functions. - - This also covers another weird case: WinSock returns WSAEFAULT if - namelen is a valid pointer while name is NULL. Both parameters are - ignored for TCP sockets, so this only occurs when using UDP socket. */ - else if (!wsamsg->name || get_socket_type () == SOCK_STREAM) - res = WSARecv (get_socket (), wsabuf, wsacnt, &wret, &wsamsg->dwFlags, - NULL, NULL); - else - res = WSARecvFrom (get_socket (), wsabuf, wsacnt, &wret, - &wsamsg->dwFlags, wsamsg->name, &wsamsg->namelen, - NULL, NULL); - if (!res) - { - ret += wret; - if (!waitall) - break; - while (wret && wsacnt) - { - if (wsabuf->len > wret) - { - wsabuf->len -= wret; - wsabuf->buf += wret; - wret = 0; - } - else - { - wret -= wsabuf->len; - ++wsabuf; - --wsacnt; - } - } - if (!wret) - break; - } - else if (WSAGetLastError () != WSAEWOULDBLOCK) - break; - } - - if (res) - { - /* According to SUSv3, errno isn't set in that case and no error - condition is returned. */ - if (WSAGetLastError () == WSAEMSGSIZE) - ret += wret; - else if (!ret) - { - /* ESHUTDOWN isn't defined for recv in SUSv3. Simply EOF is returned - in this case. */ - if (WSAGetLastError () == WSAESHUTDOWN) - ret = 0; - else - { - set_winsock_errno (); - return SOCKET_ERROR; - } - } - } - - if (get_addr_family () == AF_LOCAL && wsamsg->name != NULL - && orig_namelen >= (int) sizeof (sa_family_t)) - { - /* WSARecvFrom copied the sockaddr_in block to wsamsg->name. We have to - overwrite it with a sockaddr_un block. For datagram sockets we - generate a sockaddr_un with a filename analogue to abstract socket - names under Linux. See `man 7 unix' under Linux for a description. */ - sockaddr_un *un = (sockaddr_un *) wsamsg->name; - un->sun_family = AF_LOCAL; - int len = orig_namelen - offsetof (struct sockaddr_un, sun_path); - if (len > 0) - { - if (get_socket_type () == SOCK_DGRAM) - { - if (len >= 7) - { - __small_sprintf (un->sun_path + 1, "d%04x", - ((struct sockaddr_in *) wsamsg->name)->sin_port); - wsamsg->namelen = offsetof (struct sockaddr_un, sun_path) + 7; - } - else - wsamsg->namelen = offsetof (struct sockaddr_un, sun_path) + 1; - un->sun_path[0] = '\0'; - } - else if (!get_peer_sun_path ()) - wsamsg->namelen = sizeof (sa_family_t); - else - { - memset (un->sun_path, 0, len); - strncpy (un->sun_path, get_peer_sun_path (), len); - if (un->sun_path[len - 1] == '\0') - len = strlen (un->sun_path) + 1; - if (len > UNIX_PATH_MAX) - len = UNIX_PATH_MAX; - wsamsg->namelen = offsetof (struct sockaddr_un, sun_path) + len; - } - } - } - - return ret; -} - -void __reg3 -fhandler_socket::read (void *in_ptr, size_t& len) -{ - char *ptr = (char *) in_ptr; - -#ifdef __x86_64__ - /* size_t is 64 bit, but the len member in WSABUF is 32 bit. - Split buffer if necessary. */ - DWORD bufcnt = len / UINT32_MAX + ((!len || (len % UINT32_MAX)) ? 1 : 0); - WSABUF wsabuf[bufcnt]; - WSAMSG wsamsg = { NULL, 0, wsabuf, bufcnt, { 0, NULL }, 0 }; - /* Don't use len as loop condition, it could be 0. */ - for (WSABUF *wsaptr = wsabuf; bufcnt--; ++wsaptr) - { - wsaptr->len = MIN (len, UINT32_MAX); - wsaptr->buf = ptr; - len -= wsaptr->len; - ptr += wsaptr->len; - } -#else - WSABUF wsabuf = { len, ptr }; - WSAMSG wsamsg = { NULL, 0, &wsabuf, 1, { 0, NULL }, 0 }; -#endif - - len = recv_internal (&wsamsg, false); -} - -ssize_t -fhandler_socket::readv (const struct iovec *const iov, const int iovcnt, - ssize_t tot) -{ - WSABUF wsabuf[iovcnt]; - WSABUF *wsaptr = wsabuf + iovcnt; - const struct iovec *iovptr = iov + iovcnt; - while (--wsaptr >= wsabuf) - { - wsaptr->len = (--iovptr)->iov_len; - wsaptr->buf = (char *) iovptr->iov_base; - } - WSAMSG wsamsg = { NULL, 0, wsabuf, (DWORD) iovcnt, { 0, NULL}, 0 }; - return recv_internal (&wsamsg, false); -} - -ssize_t -fhandler_socket::recvfrom (void *in_ptr, size_t len, int flags, - struct sockaddr *from, int *fromlen) -{ - char *ptr = (char *) in_ptr; - -#ifdef __x86_64__ - /* size_t is 64 bit, but the len member in WSABUF is 32 bit. - Split buffer if necessary. */ - DWORD bufcnt = len / UINT32_MAX + ((!len || (len % UINT32_MAX)) ? 1 : 0); - WSABUF wsabuf[bufcnt]; - WSAMSG wsamsg = { from, from && fromlen ? *fromlen : 0, - wsabuf, bufcnt, - { 0, NULL }, - (DWORD) flags }; - /* Don't use len as loop condition, it could be 0. */ - for (WSABUF *wsaptr = wsabuf; bufcnt--; ++wsaptr) - { - wsaptr->len = MIN (len, UINT32_MAX); - wsaptr->buf = ptr; - len -= wsaptr->len; - ptr += wsaptr->len; - } -#else - WSABUF wsabuf = { len, ptr }; - WSAMSG wsamsg = { from, from && fromlen ? *fromlen : 0, - &wsabuf, 1, - { 0, NULL}, - (DWORD) flags }; -#endif - ssize_t ret = recv_internal (&wsamsg, false); - if (fromlen) - *fromlen = wsamsg.namelen; - return ret; -} - -ssize_t -fhandler_socket::recvmsg (struct msghdr *msg, int flags) -{ - /* TODO: Descriptor passing on AF_LOCAL sockets. */ - - /* Disappointing but true: Even if WSARecvMsg is supported, it's only - supported for datagram and raw sockets. */ - bool use_recvmsg = true; - if (get_socket_type () == SOCK_STREAM || get_addr_family () == AF_LOCAL) - { - use_recvmsg = false; - msg->msg_controllen = 0; - } - - WSABUF wsabuf[msg->msg_iovlen]; - WSABUF *wsaptr = wsabuf + msg->msg_iovlen; - const struct iovec *iovptr = msg->msg_iov + msg->msg_iovlen; - while (--wsaptr >= wsabuf) - { - wsaptr->len = (--iovptr)->iov_len; - wsaptr->buf = (char *) iovptr->iov_base; - } - WSAMSG wsamsg = { (struct sockaddr *) msg->msg_name, msg->msg_namelen, - wsabuf, (DWORD) msg->msg_iovlen, - { (DWORD) msg->msg_controllen, (char *) msg->msg_control }, - (DWORD) flags }; - ssize_t ret = recv_internal (&wsamsg, use_recvmsg); - if (ret >= 0) - { - msg->msg_namelen = wsamsg.namelen; - msg->msg_controllen = wsamsg.Control.len; - if (!CYGWIN_VERSION_CHECK_FOR_USING_ANCIENT_MSGHDR) - msg->msg_flags = wsamsg.dwFlags; - } - return ret; -} - -inline ssize_t -fhandler_socket::send_internal (struct _WSAMSG *wsamsg, int flags) -{ - ssize_t res = 0; - DWORD ret = 0, err = 0, sum = 0; - WSABUF out_buf[wsamsg->dwBufferCount]; - bool use_sendmsg = false; - DWORD wait_flags = flags & MSG_DONTWAIT; - bool nosignal = !!(flags & MSG_NOSIGNAL); - - flags &= (MSG_OOB | MSG_DONTROUTE); - if (wsamsg->Control.len > 0) - use_sendmsg = true; - /* Workaround for MSDN KB 823764: Split a message into chunks <= SO_SNDBUF. - in_idx is the index of the current lpBuffers from the input wsamsg buffer. - in_off is used to keep track of the next byte to write from a wsamsg - buffer which only gets partially written. */ - for (DWORD in_idx = 0, in_off = 0; - in_idx < wsamsg->dwBufferCount; - in_off >= wsamsg->lpBuffers[in_idx].len && (++in_idx, in_off = 0)) - { - /* Split a message into the least number of pieces to minimize the - number of WsaSendTo calls. Don't split datagram messages (bad idea). - out_idx is the index of the next buffer in the out_buf WSABUF, - also the number of buffers given to WSASendTo. - out_len is the number of bytes in the buffers given to WSASendTo. - Don't split datagram messages (very bad idea). */ - DWORD out_idx = 0; - DWORD out_len = 0; - if (get_socket_type () == SOCK_STREAM) - { - do - { - out_buf[out_idx].buf = wsamsg->lpBuffers[in_idx].buf + in_off; - out_buf[out_idx].len = wsamsg->lpBuffers[in_idx].len - in_off; - out_len += out_buf[out_idx].len; - out_idx++; - } - while (out_len < (unsigned) wmem () - && (in_off = 0, ++in_idx < wsamsg->dwBufferCount)); - /* Tweak len of the last out_buf buffer so the entire number of bytes - is (less than or) equal to wmem (). Fix out_len as well since it's - used in a subsequent test expression. */ - if (out_len > (unsigned) wmem ()) - { - out_buf[out_idx - 1].len -= out_len - (unsigned) wmem (); - out_len = (unsigned) wmem (); - } - /* Add the bytes written from the current last buffer to in_off, - so in_off points to the next byte to be written from that buffer, - or beyond which lets the outper loop skip to the next buffer. */ - in_off += out_buf[out_idx - 1].len; - } - - do - { - if (use_sendmsg) - res = WSASendMsg (get_socket (), wsamsg, flags, &ret, NULL, NULL); - else if (get_socket_type () == SOCK_STREAM) - res = WSASendTo (get_socket (), out_buf, out_idx, &ret, flags, - wsamsg->name, wsamsg->namelen, NULL, NULL); - else - res = WSASendTo (get_socket (), wsamsg->lpBuffers, - wsamsg->dwBufferCount, &ret, flags, - wsamsg->name, wsamsg->namelen, NULL, NULL); - if (res && (err = WSAGetLastError ()) == WSAEWOULDBLOCK) - { - LOCK_EVENTS; - wsock_events->events &= ~FD_WRITE; - UNLOCK_EVENTS; - } - } - while (res && err == WSAEWOULDBLOCK - && !(res = wait_for_events (FD_WRITE | FD_CLOSE, wait_flags))); - - if (!res) - { - sum += ret; - /* For streams, return to application if the number of bytes written - is less than the number of bytes we intended to write in a single - call to WSASendTo. Otherwise we would have to add code to - backtrack in the input buffers, which is questionable. There was - probably a good reason we couldn't write more. */ - if (get_socket_type () != SOCK_STREAM || ret < out_len) - break; - } - else if (is_nonblocking () || err != WSAEWOULDBLOCK) - break; - } - - if (sum) - res = sum; - else if (res == SOCKET_ERROR) - { - set_winsock_errno (); - - /* Special handling for EPIPE and SIGPIPE. - - EPIPE is generated if the local end has been shut down on a connection - oriented socket. In this case the process will also receive a SIGPIPE - unless MSG_NOSIGNAL is set. */ - if ((get_errno () == ECONNABORTED || get_errno () == ESHUTDOWN) - && get_socket_type () == SOCK_STREAM) - { - set_errno (EPIPE); - if (!nosignal) - raise (SIGPIPE); - } - } - - return res; -} - -ssize_t -fhandler_socket::write (const void *in_ptr, size_t len) -{ - char *ptr = (char *) in_ptr; - -#ifdef __x86_64__ - /* size_t is 64 bit, but the len member in WSABUF is 32 bit. - Split buffer if necessary. */ - DWORD bufcnt = len / UINT32_MAX + ((!len || (len % UINT32_MAX)) ? 1 : 0); - WSABUF wsabuf[bufcnt]; - WSAMSG wsamsg = { NULL, 0, wsabuf, bufcnt, { 0, NULL }, 0 }; - /* Don't use len as loop condition, it could be 0. */ - for (WSABUF *wsaptr = wsabuf; bufcnt--; ++wsaptr) - { - wsaptr->len = MIN (len, UINT32_MAX); - wsaptr->buf = ptr; - len -= wsaptr->len; - ptr += wsaptr->len; - } -#else - WSABUF wsabuf = { len, ptr }; - WSAMSG wsamsg = { NULL, 0, &wsabuf, 1, { 0, NULL }, 0 }; -#endif - return send_internal (&wsamsg, 0); -} - -ssize_t -fhandler_socket::writev (const struct iovec *const iov, const int iovcnt, - ssize_t tot) -{ - WSABUF wsabuf[iovcnt]; - WSABUF *wsaptr = wsabuf; - const struct iovec *iovptr = iov; - for (int i = 0; i < iovcnt; ++i) - { - wsaptr->len = iovptr->iov_len; - (wsaptr++)->buf = (char *) (iovptr++)->iov_base; - } - WSAMSG wsamsg = { NULL, 0, wsabuf, (DWORD) iovcnt, { 0, NULL}, 0 }; - return send_internal (&wsamsg, 0); -} - -ssize_t -fhandler_socket::sendto (const void *in_ptr, size_t len, int flags, - const struct sockaddr *to, int tolen) -{ - char *ptr = (char *) in_ptr; - struct sockaddr_storage sst; - - if (to && get_inet_addr (to, tolen, &sst, &tolen) == SOCKET_ERROR) - return SOCKET_ERROR; - -#ifdef __x86_64__ - /* size_t is 64 bit, but the len member in WSABUF is 32 bit. - Split buffer if necessary. */ - DWORD bufcnt = len / UINT32_MAX + ((!len || (len % UINT32_MAX)) ? 1 : 0); - WSABUF wsabuf[bufcnt]; - WSAMSG wsamsg = { to ? (struct sockaddr *) &sst : NULL, tolen, - wsabuf, bufcnt, - { 0, NULL }, - 0 }; - /* Don't use len as loop condition, it could be 0. */ - for (WSABUF *wsaptr = wsabuf; bufcnt--; ++wsaptr) - { - wsaptr->len = MIN (len, UINT32_MAX); - wsaptr->buf = ptr; - len -= wsaptr->len; - ptr += wsaptr->len; - } -#else - WSABUF wsabuf = { len, ptr }; - WSAMSG wsamsg = { to ? (struct sockaddr *) &sst : NULL, tolen, - &wsabuf, 1, - { 0, NULL}, - 0 }; -#endif - return send_internal (&wsamsg, flags); -} - -ssize_t -fhandler_socket::sendmsg (const struct msghdr *msg, int flags) -{ - /* TODO: Descriptor passing on AF_LOCAL sockets. */ - - struct sockaddr_storage sst; - int len = 0; - - if (msg->msg_name - && get_inet_addr ((struct sockaddr *) msg->msg_name, msg->msg_namelen, - &sst, &len) == SOCKET_ERROR) - return SOCKET_ERROR; - - WSABUF wsabuf[msg->msg_iovlen]; - WSABUF *wsaptr = wsabuf; - const struct iovec *iovptr = msg->msg_iov; - for (int i = 0; i < msg->msg_iovlen; ++i) - { - wsaptr->len = iovptr->iov_len; - (wsaptr++)->buf = (char *) (iovptr++)->iov_base; - } - /* Disappointing but true: Even if WSASendMsg is supported, it's only - supported for datagram and raw sockets. */ - DWORD controllen = (DWORD) (!wincap.has_sendmsg () - || get_socket_type () == SOCK_STREAM - || get_addr_family () == AF_LOCAL - ? 0 : msg->msg_controllen); - WSAMSG wsamsg = { msg->msg_name ? (struct sockaddr *) &sst : NULL, len, - wsabuf, (DWORD) msg->msg_iovlen, - { controllen, (char *) msg->msg_control }, - 0 }; - return send_internal (&wsamsg, flags); -} - -int -fhandler_socket::shutdown (int how) -{ - int res = ::shutdown (get_socket (), how); - - /* Linux allows to call shutdown for any socket, even if it's not connected. - This also disables to call accept on this socket, if shutdown has been - called with the SHUT_RD or SHUT_RDWR parameter. In contrast, WinSock - only allows to call shutdown on a connected socket. The accept function - is in no way affected. So, what we do here is to fake success, and to - change the event settings so that an FD_CLOSE event is triggered for the - calling Cygwin function. The evaluate_events method handles the call - from accept specially to generate a Linux-compatible behaviour. */ - if (res && WSAGetLastError () != WSAENOTCONN) - set_winsock_errno (); - else - { - res = 0; - switch (how) - { - case SHUT_RD: - saw_shutdown_read (true); - wsock_events->events |= FD_CLOSE; - SetEvent (wsock_evt); - break; - case SHUT_WR: - saw_shutdown_write (true); - break; - case SHUT_RDWR: - saw_shutdown_read (true); - saw_shutdown_write (true); - wsock_events->events |= FD_CLOSE; - SetEvent (wsock_evt); - break; - } - } - return res; -} - -int -fhandler_socket::close () -{ - int res = 0; - - release_events (); - while ((res = closesocket (get_socket ())) != 0) - { - if (WSAGetLastError () != WSAEWOULDBLOCK) - { - set_winsock_errno (); - res = -1; - break; - } - if (cygwait (10) == WAIT_SIGNALED) - { - set_errno (EINTR); - res = -1; - break; - } - WSASetLastError (0); - } - - debug_printf ("%d = fhandler_socket::close()", res); - return res; -} - -/* Definitions of old ifreq stuff used prior to Cygwin 1.7.0. */ -#define OLD_SIOCGIFFLAGS _IOW('s', 101, struct __old_ifreq) -#define OLD_SIOCGIFADDR _IOW('s', 102, struct __old_ifreq) -#define OLD_SIOCGIFBRDADDR _IOW('s', 103, struct __old_ifreq) -#define OLD_SIOCGIFNETMASK _IOW('s', 104, struct __old_ifreq) -#define OLD_SIOCGIFHWADDR _IOW('s', 105, struct __old_ifreq) -#define OLD_SIOCGIFMETRIC _IOW('s', 106, struct __old_ifreq) -#define OLD_SIOCGIFMTU _IOW('s', 107, struct __old_ifreq) -#define OLD_SIOCGIFINDEX _IOW('s', 108, struct __old_ifreq) - -#define CONV_OLD_TO_NEW_SIO(old) (((old)&0xff00ffff)|(((long)sizeof(struct ifreq)&IOCPARM_MASK)<<16)) - -struct __old_ifreq { -#define __OLD_IFNAMSIZ 16 - union { - char ifrn_name[__OLD_IFNAMSIZ]; /* if name, e.g. "en0" */ - } ifr_ifrn; - - union { - struct sockaddr ifru_addr; - struct sockaddr ifru_broadaddr; - struct sockaddr ifru_netmask; - struct sockaddr ifru_hwaddr; - short ifru_flags; - int ifru_metric; - int ifru_mtu; - int ifru_ifindex; - } ifr_ifru; -}; - -int -fhandler_socket::ioctl (unsigned int cmd, void *p) -{ - extern int get_ifconf (struct ifconf *ifc, int what); /* net.cc */ - int res; - struct ifconf ifc, *ifcp; - struct ifreq *ifrp; - - switch (cmd) - { - case SIOCGIFCONF: - ifcp = (struct ifconf *) p; - if (!ifcp) - { - set_errno (EINVAL); - return -1; - } - if (CYGWIN_VERSION_CHECK_FOR_OLD_IFREQ) - { - ifc.ifc_len = ifcp->ifc_len / sizeof (struct __old_ifreq) - * sizeof (struct ifreq); - ifc.ifc_buf = (caddr_t) alloca (ifc.ifc_len); - } - else - { - ifc.ifc_len = ifcp->ifc_len; - ifc.ifc_buf = ifcp->ifc_buf; - } - res = get_ifconf (&ifc, cmd); - if (res) - debug_printf ("error in get_ifconf"); - if (CYGWIN_VERSION_CHECK_FOR_OLD_IFREQ) - { - struct __old_ifreq *ifr = (struct __old_ifreq *) ifcp->ifc_buf; - for (ifrp = ifc.ifc_req; - (caddr_t) ifrp < ifc.ifc_buf + ifc.ifc_len; - ++ifrp, ++ifr) - { - memcpy (&ifr->ifr_ifrn, &ifrp->ifr_ifrn, sizeof ifr->ifr_ifrn); - ifr->ifr_name[__OLD_IFNAMSIZ - 1] = '\0'; - memcpy (&ifr->ifr_ifru, &ifrp->ifr_ifru, sizeof ifr->ifr_ifru); - } - ifcp->ifc_len = ifc.ifc_len / sizeof (struct ifreq) - * sizeof (struct __old_ifreq); - } - else - ifcp->ifc_len = ifc.ifc_len; - break; - case OLD_SIOCGIFFLAGS: - case OLD_SIOCGIFADDR: - case OLD_SIOCGIFBRDADDR: - case OLD_SIOCGIFNETMASK: - case OLD_SIOCGIFHWADDR: - case OLD_SIOCGIFMETRIC: - case OLD_SIOCGIFMTU: - case OLD_SIOCGIFINDEX: - cmd = CONV_OLD_TO_NEW_SIO (cmd); - /*FALLTHRU*/ - case SIOCGIFFLAGS: - case SIOCGIFBRDADDR: - case SIOCGIFNETMASK: - case SIOCGIFADDR: - case SIOCGIFHWADDR: - case SIOCGIFMETRIC: - case SIOCGIFMTU: - case SIOCGIFINDEX: - case SIOCGIFFRNDLYNAM: - case SIOCGIFDSTADDR: - { - if (!p) - { - debug_printf ("ifr == NULL"); - set_errno (EINVAL); - return -1; - } - - if (cmd > SIOCGIFINDEX && CYGWIN_VERSION_CHECK_FOR_OLD_IFREQ) - { - debug_printf ("cmd not supported on this platform"); - set_errno (EINVAL); - return -1; - } - ifc.ifc_len = 64 * sizeof (struct ifreq); - ifc.ifc_buf = (caddr_t) alloca (ifc.ifc_len); - if (cmd == SIOCGIFFRNDLYNAM) - { - struct ifreq_frndlyname *iff = (struct ifreq_frndlyname *) - alloca (64 * sizeof (struct ifreq_frndlyname)); - for (int i = 0; i < 64; ++i) - ifc.ifc_req[i].ifr_frndlyname = &iff[i]; - } - - res = get_ifconf (&ifc, cmd); - if (res) - { - debug_printf ("error in get_ifconf"); - break; - } - - if (CYGWIN_VERSION_CHECK_FOR_OLD_IFREQ) - { - struct __old_ifreq *ifr = (struct __old_ifreq *) p; - debug_printf (" name: %s", ifr->ifr_name); - for (ifrp = ifc.ifc_req; - (caddr_t) ifrp < ifc.ifc_buf + ifc.ifc_len; - ++ifrp) - { - debug_printf ("testname: %s", ifrp->ifr_name); - if (! strcmp (ifrp->ifr_name, ifr->ifr_name)) - { - memcpy (&ifr->ifr_ifru, &ifrp->ifr_ifru, - sizeof ifr->ifr_ifru); - break; - } - } - } - else - { - struct ifreq *ifr = (struct ifreq *) p; - debug_printf (" name: %s", ifr->ifr_name); - for (ifrp = ifc.ifc_req; - (caddr_t) ifrp < ifc.ifc_buf + ifc.ifc_len; - ++ifrp) - { - debug_printf ("testname: %s", ifrp->ifr_name); - if (! strcmp (ifrp->ifr_name, ifr->ifr_name)) - { - if (cmd == SIOCGIFFRNDLYNAM) - /* The application has to care for the space. */ - memcpy (ifr->ifr_frndlyname, ifrp->ifr_frndlyname, - sizeof (struct ifreq_frndlyname)); - else - memcpy (&ifr->ifr_ifru, &ifrp->ifr_ifru, - sizeof ifr->ifr_ifru); - break; - } - } - } - if ((caddr_t) ifrp >= ifc.ifc_buf + ifc.ifc_len) - { - set_errno (EINVAL); - return -1; - } - break; - } - /* From this point on we handle only ioctl commands which are understood by - Winsock. However, we have a problem, which is, the different size of - u_long in Windows and 64 bit Cygwin. This affects the definitions of - FIOASYNC, etc, because they are defined in terms of sizeof(u_long). - So we have to use case labels which are independent of the sizeof - u_long. Since we're redefining u_long at the start of this file to - matching Winsock's idea of u_long, we can use the real definitions in - calls to Windows. In theory we also have to make sure to convert the - different ideas of u_long between the application and Winsock, but - fortunately, the parameters defined as u_long pointers are on Linux - and BSD systems defined as int pointer, so the applications will - use a type of the expected size. Hopefully. */ - case FIOASYNC: -#ifdef __x86_64__ - case _IOW('f', 125, unsigned long): -#endif - res = WSAAsyncSelect (get_socket (), winmsg, WM_ASYNCIO, - *(int *) p ? ASYNC_MASK : 0); - syscall_printf ("Async I/O on socket %s", - *(int *) p ? "started" : "cancelled"); - async_io (*(int *) p != 0); - /* If async_io is switched off, revert the event handling. */ - if (*(int *) p == 0) - WSAEventSelect (get_socket (), wsock_evt, EVENT_MASK); - break; - case FIONREAD: -#ifdef __x86_64__ - case _IOR('f', 127, unsigned long): -#endif - res = ioctlsocket (get_socket (), FIONREAD, (u_long *) p); - if (res == SOCKET_ERROR) - set_winsock_errno (); - break; - default: - /* Sockets are always non-blocking internally. So we just note the - state here. */ -#ifdef __x86_64__ - /* Convert the different idea of u_long in the definition of cmd. */ - if (((cmd >> 16) & IOCPARM_MASK) == sizeof (unsigned long)) - cmd = (cmd & ~(IOCPARM_MASK << 16)) | (sizeof (u_long) << 16); -#endif - if (cmd == FIONBIO) - { - syscall_printf ("socket is now %sblocking", - *(int *) p ? "non" : ""); - set_nonblocking (*(int *) p); - res = 0; - } - else - res = ioctlsocket (get_socket (), cmd, (u_long *) p); - break; - } - syscall_printf ("%d = ioctl_socket(%x, %p)", res, cmd, p); - return res; -} - -int -fhandler_socket::fcntl (int cmd, intptr_t arg) -{ - int res = 0; - int request, current; - - switch (cmd) - { - case F_SETOWN: - { - pid_t pid = (pid_t) arg; - LOCK_EVENTS; - wsock_events->owner = pid; - UNLOCK_EVENTS; - debug_printf ("owner set to %d", pid); - } - break; - case F_GETOWN: - res = wsock_events->owner; - break; - case F_SETFL: - { - /* Carefully test for the O_NONBLOCK or deprecated OLD_O_NDELAY flag. - Set only the flag that has been passed in. If both are set, just - record O_NONBLOCK. */ - int new_flags = arg & O_NONBLOCK_MASK; - if ((new_flags & OLD_O_NDELAY) && (new_flags & O_NONBLOCK)) - new_flags = O_NONBLOCK; - current = get_flags () & O_NONBLOCK_MASK; - request = new_flags ? 1 : 0; - if (!!current != !!new_flags && (res = ioctl (FIONBIO, &request))) - break; - set_flags ((get_flags () & ~O_NONBLOCK_MASK) | new_flags); - break; - } - default: - res = fhandler_base::fcntl (cmd, arg); - break; - } - return res; -} - -void -fhandler_socket::set_close_on_exec (bool val) -{ - set_no_inheritance (wsock_mtx, val); - set_no_inheritance (wsock_evt, val); - if (need_fixup_before ()) - { - close_on_exec (val); - debug_printf ("set close_on_exec for %s to %d", get_name (), val); - } - else - fhandler_base::set_close_on_exec (val); -} - -void -fhandler_socket::set_sun_path (const char *path) -{ - sun_path = path ? cstrdup (path) : NULL; -} - -void -fhandler_socket::set_peer_sun_path (const char *path) -{ - peer_sun_path = path ? cstrdup (path) : NULL; -} - -int -fhandler_socket::getpeereid (pid_t *pid, uid_t *euid, gid_t *egid) -{ - if (get_addr_family () != AF_LOCAL || get_socket_type () != SOCK_STREAM) - { - set_errno (EINVAL); - return -1; - } - if (no_getpeereid ()) - { - set_errno (ENOTSUP); - return -1; - } - if (connect_state () != connected) - { - set_errno (ENOTCONN); - return -1; - } - - __try - { - if (pid) - *pid = sec_peer_pid; - if (euid) - *euid = sec_peer_uid; - if (egid) - *egid = sec_peer_gid; - return 0; - } - __except (EFAULT) {} - __endtry - return -1; -} |