From 913c6ca2c1467893bb0c3b7598cccdb02ca9f598 Mon Sep 17 00:00:00 2001 From: Corinna Vinschen Date: Wed, 14 Feb 2018 22:21:58 +0100 Subject: [PATCH] Cygwin: socket: move socket creation inside fhandler_socket class Add fhandler_socket::socket method Add fhandler_socket::set_socket_handle method, basically duplicating what fdsock is doing. This is the first step in getting rid of fdsock. Signed-off-by: Corinna Vinschen --- winsup/cygwin/fhandler.h | 5 ++ winsup/cygwin/fhandler_socket.cc | 117 +++++++++++++++++++++++++++++++ winsup/cygwin/net.cc | 90 ++++++++++++------------ 3 files changed, 166 insertions(+), 46 deletions(-) diff --git a/winsup/cygwin/fhandler.h b/winsup/cygwin/fhandler.h index 9d2b26342..e1659db5a 100644 --- a/winsup/cygwin/fhandler.h +++ b/winsup/cygwin/fhandler.h @@ -556,6 +556,10 @@ class fhandler_socket: public fhandler_base {} } status; +#ifdef __INSIDE_CYGWIN_NET__ + int set_socket_handle (SOCKET sock); +#endif + public: fhandler_socket (); ~fhandler_socket (); @@ -576,6 +580,7 @@ class fhandler_socket: public fhandler_base IMPLEMENT_STATUS_FLAG (conn_state, connect_state) IMPLEMENT_STATUS_FLAG (bool, no_getpeereid) + int socket (int af, int type, int protocol, int flags); int bind (const struct sockaddr *name, int namelen); int connect (const struct sockaddr *name, int namelen); int listen (int backlog); diff --git a/winsup/cygwin/fhandler_socket.cc b/winsup/cygwin/fhandler_socket.cc index 2d043bded..a30b49511 100644 --- a/winsup/cygwin/fhandler_socket.cc +++ b/winsup/cygwin/fhandler_socket.cc @@ -262,6 +262,120 @@ fhandler_socket::open (int flags, mode_t mode) return 0; } +int +fhandler_socket::set_socket_handle (SOCKET sock) +{ + DWORD flags; + bool lsp_fixup = false; + + /* Usually sockets are inheritable IFS objects. Unfortunately some virus + scanners or other network-oriented software replace normal sockets + with their own kind, which is running through a filter driver called + "layered service provider" (LSP) which, fortunately, are deprecated. + + LSP sockets are not kernel objects. They are typically not marked as + inheritable, nor are they IFS handles. They are in fact not inheritable + to child processes, and it does not help to mark them inheritable via + SetHandleInformation. Subsequent socket calls in the child process fail + with error 10038, WSAENOTSOCK. + + There's a neat way to workaround these annoying LSP sockets. WSAIoctl + allows to fetch the underlying base socket, which is a normal, inheritable + IFS handle. So we fetch the base socket, duplicate it, and close the + original socket. Now we have a standard IFS socket which (hopefully) + works as expected. + + If that doesn't work for some reason, mark the sockets for duplication + via WSADuplicateSocket/WSASocket. This requires to start the child + process in SUSPENDED state so we only do this if really necessary. */ + if (!GetHandleInformation ((HANDLE) sock, &flags) + || !(flags & HANDLE_FLAG_INHERIT)) + { + int ret; + SOCKET base_sock; + DWORD bret; + + lsp_fixup = true; + debug_printf ("LSP handle: %p", sock); + ret = WSAIoctl (sock, SIO_BASE_HANDLE, NULL, 0, (void *) &base_sock, + sizeof (base_sock), &bret, NULL, NULL); + if (ret) + debug_printf ("WSAIoctl: %u", WSAGetLastError ()); + else if (base_sock != sock) + { + if (GetHandleInformation ((HANDLE) base_sock, &flags) + && (flags & HANDLE_FLAG_INHERIT)) + { + if (!DuplicateHandle (GetCurrentProcess (), (HANDLE) base_sock, + GetCurrentProcess (), (PHANDLE) &base_sock, + 0, TRUE, DUPLICATE_SAME_ACCESS)) + debug_printf ("DuplicateHandle failed, %E"); + else + { + closesocket (sock); + sock = base_sock; + lsp_fixup = false; + } + } + } + } + set_io_handle ((HANDLE) sock); + if (!init_events ()) + { + closesocket (sock); + return -1; + } + if (lsp_fixup) + init_fixup_before (); + set_flags (O_RDWR | O_BINARY); + set_unique_id (); + if (get_socket_type () == SOCK_DGRAM) + { + /* Workaround the problem that a missing listener on a UDP socket + in a call to sendto will result in select/WSAEnumNetworkEvents + reporting that the socket has pending data and a subsequent call + to recvfrom will return -1 with error set to WSAECONNRESET. + + This problem is a regression introduced in Windows 2000. + Instead of fixing the problem, a new socket IOCTL code has + been added, see http://support.microsoft.com/kb/263823 */ + BOOL cr = FALSE; + DWORD blen; + if (WSAIoctl (sock, SIO_UDP_CONNRESET, &cr, sizeof cr, NULL, 0, + &blen, NULL, NULL) == SOCKET_ERROR) + debug_printf ("Reset SIO_UDP_CONNRESET: WinSock error %u", + WSAGetLastError ()); + } +#ifdef __x86_64__ + rmem () = 212992; + wmem () = 212992; +#else + rmem () = 64512; + wmem () = 64512; +#endif + return 0; +} + +int +fhandler_socket::socket (int af, int type, int protocol, int flags) +{ + SOCKET sock; + + sock = ::socket (af == AF_LOCAL ? AF_INET : af, type, protocol); + if (sock == INVALID_SOCKET) + { + set_winsock_errno (); + return -1; + } + set_addr_family (af); + set_socket_type (type); + if (flags & SOCK_NONBLOCK) + set_nonblocking (true); + if (flags & SOCK_CLOEXEC) + set_close_on_exec (true); + return set_socket_handle (sock); +} + void fhandler_socket::af_local_set_sockpair_cred () { @@ -635,6 +749,9 @@ fhandler_socket::init_events () } /* sock type not yet set here. */ + /* FIXME: as soon as we switch to socket method, we're good to use + get_socket_type (). */ + if (pc.dev == FH_UDP || pc.dev == FH_DGRAM) wsock_events->events = FD_WRITE; return true; diff --git a/winsup/cygwin/net.cc b/winsup/cygwin/net.cc index c7bd73611..8497d5944 100644 --- a/winsup/cygwin/net.cc +++ b/winsup/cygwin/net.cc @@ -655,67 +655,65 @@ extern "C" int cygwin_socket (int af, int type, int protocol) { int res = -1; - SOCKET soc = 0; + const device *dev; + fhandler_socket *fh; int flags = type & _SOCK_FLAG_MASK; type &= ~_SOCK_FLAG_MASK; debug_printf ("socket (%d, %d (flags %y), %d)", af, type, flags, protocol); - if ((flags & ~(SOCK_NONBLOCK | SOCK_CLOEXEC)) != 0) + switch (af) { - set_errno (EINVAL); + case AF_LOCAL: + if (type != SOCK_STREAM && type != SOCK_DGRAM) + { + set_errno (EINVAL); + goto done; + } + if (protocol != 0) + { + set_errno (EPROTONOSUPPORT); + goto done; + } + dev = type == SOCK_STREAM ? stream_dev : dgram_dev; + break; + case AF_INET: + case AF_INET6: + if (type != SOCK_STREAM && type != SOCK_DGRAM && type != SOCK_RAW) + { + set_errno (EINVAL); + goto done; + } + dev = type == SOCK_STREAM ? tcp_dev : udp_dev; + break; + default: + set_errno (EAFNOSUPPORT); goto done; } - soc = socket (af == AF_LOCAL ? AF_INET : af, type, - af == AF_LOCAL ? 0 : protocol); - - if (soc == INVALID_SOCKET) + if ((flags & ~(SOCK_NONBLOCK | SOCK_CLOEXEC)) != 0) { - set_winsock_errno (); + set_errno (EINVAL); goto done; } - const device *dev; - - if (af == AF_LOCAL) - dev = type == SOCK_STREAM ? stream_dev : dgram_dev; - else - dev = type == SOCK_STREAM ? tcp_dev : udp_dev; + { + cygheap_fdnew fd; - { - cygheap_fdnew fd; - if (fd < 0 || !fdsock (fd, dev, soc)) - closesocket (soc); - else - { - ((fhandler_socket *) fd)->set_addr_family (af); - ((fhandler_socket *) fd)->set_socket_type (type); - if (flags & SOCK_NONBLOCK) - ((fhandler_socket *) fd)->set_nonblocking (true); - if (flags & SOCK_CLOEXEC) - ((fhandler_socket *) fd)->set_close_on_exec (true); - if (type == SOCK_DGRAM) - { - /* Workaround the problem that a missing listener on a UDP socket - in a call to sendto will result in select/WSAEnumNetworkEvents - reporting that the socket has pending data and a subsequent call - to recvfrom will return -1 with error set to WSAECONNRESET. - - This problem is a regression introduced in Windows 2000. - Instead of fixing the problem, a new socket IOCTL code has - been added, see http://support.microsoft.com/kb/263823 */ - BOOL cr = FALSE; - DWORD blen; - if (WSAIoctl (soc, SIO_UDP_CONNRESET, &cr, sizeof cr, NULL, 0, - &blen, NULL, NULL) == SOCKET_ERROR) - debug_printf ("Reset SIO_UDP_CONNRESET: WinSock error %u", - WSAGetLastError ()); - } - res = fd; - } - } + if (fd < 0) + goto done; + fh = (fhandler_socket *) build_fh_dev (*dev); + if (fh && fh->socket (af, type, protocol, flags) == 0) + { + fd = fh; + if (fd <= 2) + set_std_handle (fd); + res = fd; + } + else + fd.release (); + } done: syscall_printf ("%R = socket(%d, %d (flags %y), %d)", -- 2.43.5