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 <corinna@vinschen.de>
This commit is contained in:
Corinna Vinschen 2018-02-14 22:21:58 +01:00
parent 7ae73be141
commit 913c6ca2c1
3 changed files with 167 additions and 47 deletions

View File

@ -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);

View File

@ -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;

View File

@ -655,68 +655,66 @@ 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);
switch (af)
{
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;
}
if ((flags & ~(SOCK_NONBLOCK | SOCK_CLOEXEC)) != 0)
{
set_errno (EINVAL);
goto done;
}
soc = socket (af == AF_LOCAL ? AF_INET : af, type,
af == AF_LOCAL ? 0 : protocol);
if (soc == INVALID_SOCKET)
{
set_winsock_errno ();
goto done;
cygheap_fdnew 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 ();
}
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;
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;
}
}
done:
syscall_printf ("%R = socket(%d, %d (flags %y), %d)",
res, af, type, flags, protocol);