Cygwin: split out fhandler_socket into inet and local classes

First cut, still incomplete

* fhandler_socket is now base class for other socket classes
* fhandler_socket_inet handles AF_INET and AF_INET6 sockets
* fhandler_socket_local handles AF_LOCAL/AF_UNIX sockets
* finally get rid of fdsock by using set_socket_handle in accept4
* align file-related calls (fstat,  fstatvfs, fchown, fchmod, facl)
  to Linux.

Signed-off-by: Corinna Vinschen <corinna@vinschen.de>
This commit is contained in:
Corinna Vinschen 2018-02-21 21:40:01 +01:00
parent dff3bc9a86
commit 859d215b7e
10 changed files with 3293 additions and 2218 deletions

View File

@ -296,6 +296,8 @@ DLL_OFILES:= \
fhandler_registry.o \
fhandler_serial.o \
fhandler_socket.o \
fhandler_socket_inet.o \
fhandler_socket_local.o \
fhandler_tape.o \
fhandler_termios.o \
fhandler_tty.o \

View File

@ -304,8 +304,8 @@ dtable::init_std_file_from_handle (int fd, HANDLE handle)
dev.parse (name);
else if (strcmp (name, ":sock:") == 0
/* NtQueryObject returns an error when called on an LSP socket
handle. fdsock tries to fetch the underlying base socket,
but this might fail. */
handle. fhandler_socket::set_socket_handle tries to fetch
the underlying base socket, but this might fail. */
|| (strcmp (name, unknown_file) == 0
&& !::getsockopt ((SOCKET) handle, SOL_SOCKET, SO_RCVBUF,
(char *) &rcv, &len)))
@ -517,10 +517,12 @@ fh_alloc (path_conv& pc)
case FH_TCP:
case FH_UDP:
case FH_ICMP:
fh = cnew (fhandler_socket_inet);
break;
case FH_UNIX:
case FH_STREAM:
case FH_DGRAM:
fh = cnew (fhandler_socket);
fh = cnew (fhandler_socket_local);
break;
case FH_FS:
fh = cnew (fhandler_disk_file);

View File

@ -479,9 +479,16 @@ struct wsa_event
class fhandler_socket: public fhandler_base
{
private:
/* permission fake following Linux rules */
uid_t uid;
uid_t gid;
mode_t mode;
protected:
int addr_family;
int type;
int connect_secret[4];
virtual int af_local_connect () = 0;
int get_socket_flags ();
wsa_event *wsock_events;
HANDLE wsock_mtx;
@ -491,32 +498,11 @@ class fhandler_socket: public fhandler_base
int evaluate_events (const long event_mask, long &events, const bool erase);
const HANDLE wsock_event () const { return wsock_evt; }
const LONG serial_number () const { return wsock_events->serial_number; }
private:
protected:
int wait_for_events (const long event_mask, const DWORD flags);
void release_events ();
pid_t sec_pid;
uid_t sec_uid;
gid_t sec_gid;
pid_t sec_peer_pid;
uid_t sec_peer_uid;
gid_t sec_peer_gid;
void af_local_set_secret (char *);
void af_local_setblocking (bool &, bool &);
void af_local_unsetblocking (bool, bool);
void af_local_set_cred ();
void af_local_copy (fhandler_socket *);
bool af_local_recv_secret ();
bool af_local_send_secret ();
bool af_local_recv_cred ();
bool af_local_send_cred ();
int af_local_accept ();
public:
int af_local_connect ();
int af_local_set_no_getpeereid ();
void af_local_set_sockpair_cred ();
private:
protected:
int _rmem;
int _wmem;
public:
@ -525,22 +511,20 @@ class fhandler_socket: public fhandler_base
void rmem (int nrmem) { _rmem = nrmem; }
void wmem (int nwmem) { _wmem = nwmem; }
private:
protected:
DWORD _rcvtimeo; /* msecs */
DWORD _sndtimeo; /* msecs */
public:
DWORD &rcvtimeo () { return _rcvtimeo; }
DWORD &sndtimeo () { return _sndtimeo; }
private:
protected:
struct _WSAPROTOCOL_INFOW *prot_info_ptr;
public:
void init_fixup_before ();
bool need_fixup_before () const {return prot_info_ptr != NULL;}
private:
char *sun_path;
char *peer_sun_path;
protected:
struct status_flags
{
unsigned async_io : 1; /* async I/O */
@ -580,35 +564,34 @@ 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);
int accept4 (struct sockaddr *peer, int *len, int flags);
int getsockname (struct sockaddr *name, int *namelen);
int getpeername (struct sockaddr *name, int *namelen);
int getpeereid (pid_t *pid, uid_t *euid, gid_t *egid);
int socketpair (int af, int type, int protocol, int flags,
fhandler_socket *fh_out);
int setsockopt (int level, int optname, const void *optval,
__socklen_t optlen);
int getsockopt (int level, int optname, const void *optval,
__socklen_t *optlen);
virtual int socket (int af, int type, int protocol, int flags) = 0;
virtual int socketpair (int af, int type, int protocol, int flags,
fhandler_socket *fh_out) = 0;
virtual int bind (const struct sockaddr *name, int namelen) = 0;
virtual int listen (int backlog) = 0;
virtual int accept4 (struct sockaddr *peer, int *len, int flags) = 0;
virtual int connect (const struct sockaddr *name, int namelen) = 0;
virtual int getsockname (struct sockaddr *name, int *namelen) = 0;
virtual int getpeername (struct sockaddr *name, int *namelen) = 0;
virtual int getpeereid (pid_t *pid, uid_t *euid, gid_t *egid);
virtual int setsockopt (int level, int optname, const void *optval,
__socklen_t optlen) = 0;
virtual int getsockopt (int level, int optname, const void *optval,
__socklen_t *optlen) = 0;
int open (int flags, mode_t mode = 0);
void __reg3 read (void *ptr, size_t& len);
ssize_t __stdcall readv (const struct iovec *, int iovcnt, ssize_t tot = -1);
inline ssize_t __reg3 recv_internal (struct _WSAMSG *wsamsg, bool use_recvmsg);
ssize_t recvfrom (void *ptr, size_t len, int flags,
struct sockaddr *from, int *fromlen);
ssize_t recvmsg (struct msghdr *msg, int flags);
virtual ssize_t recvfrom (void *ptr, size_t len, int flags,
struct sockaddr *from, int *fromlen) = 0;
virtual ssize_t recvmsg (struct msghdr *msg, int flags) = 0;
virtual void __reg3 read (void *ptr, size_t& len) = 0;
virtual ssize_t __stdcall readv (const struct iovec *, int iovcnt,
ssize_t tot = -1) = 0;
ssize_t __stdcall write (const void *ptr, size_t len);
ssize_t __stdcall writev (const struct iovec *, int iovcnt, ssize_t tot = -1);
inline ssize_t send_internal (struct _WSAMSG *wsamsg, int flags);
ssize_t sendto (const void *ptr, size_t len, int flags,
const struct sockaddr *to, int tolen);
ssize_t sendmsg (const struct msghdr *msg, int flags);
virtual ssize_t sendto (const void *ptr, size_t len, int flags,
const struct sockaddr *to, int tolen) = 0;
virtual ssize_t sendmsg (const struct msghdr *msg, int flags) = 0;
virtual ssize_t __stdcall write (const void *ptr, size_t len) = 0;
virtual ssize_t __stdcall writev (const struct iovec *, int iovcnt, ssize_t tot = -1) = 0;
int ioctl (unsigned int cmd, void *);
int fcntl (int cmd, intptr_t);
@ -635,31 +618,159 @@ class fhandler_socket: public fhandler_base
int get_addr_family () {return addr_family;}
void set_socket_type (int st) { type = st;}
int get_socket_type () {return type;}
int __reg2 fstat (struct stat *buf);
int __reg2 fstatvfs (struct statvfs *buf);
int __reg1 fchmod (mode_t newmode);
int __reg2 fchown (uid_t newuid, gid_t newgid);
int __reg3 facl (int, int, struct acl *);
int __reg2 link (const char *);
};
class fhandler_socket_inet: public fhandler_socket
{
protected:
int af_local_connect () { return 0; }
private:
inline ssize_t recv_internal (struct _WSAMSG *wsamsg, bool use_recvmsg);
inline ssize_t send_internal (struct _WSAMSG *wsamsg, int flags);
public:
fhandler_socket_inet ();
~fhandler_socket_inet ();
int socket (int af, int type, int protocol, int flags);
int socketpair (int af, int type, int protocol, int flags,
fhandler_socket *fh_out);
int bind (const struct sockaddr *name, int namelen);
int listen (int backlog);
int accept4 (struct sockaddr *peer, int *len, int flags);
int connect (const struct sockaddr *name, int namelen);
int getsockname (struct sockaddr *name, int *namelen);
int getpeername (struct sockaddr *name, int *namelen);
int setsockopt (int level, int optname, const void *optval,
__socklen_t optlen);
int getsockopt (int level, int optname, const void *optval,
__socklen_t *optlen);
ssize_t recvfrom (void *ptr, size_t len, int flags,
struct sockaddr *from, int *fromlen);
ssize_t recvmsg (struct msghdr *msg, int flags);
void __reg3 read (void *ptr, size_t& len);
ssize_t __stdcall readv (const struct iovec *, int iovcnt, ssize_t tot = -1);
ssize_t sendto (const void *ptr, size_t len, int flags,
const struct sockaddr *to, int tolen);
ssize_t sendmsg (const struct msghdr *msg, int flags);
ssize_t __stdcall write (const void *ptr, size_t len);
ssize_t __stdcall writev (const struct iovec *, int iovcnt, ssize_t tot = -1);
/* from here on: CLONING */
fhandler_socket_inet (void *) {}
void copyto (fhandler_base *x)
{
x->pc.free_strings ();
*reinterpret_cast<fhandler_socket_inet *> (x) = *this;
x->reset (this);
}
fhandler_socket_inet *clone (cygheap_types malloc_type = HEAP_FHANDLER)
{
void *ptr = (void *) ccalloc (malloc_type, 1, sizeof (fhandler_socket_inet));
fhandler_socket_inet *fh = new (ptr) fhandler_socket_inet (ptr);
copyto (fh);
return fh;
}
};
class fhandler_socket_local: public fhandler_socket
{
protected:
char *sun_path;
char *peer_sun_path;
void set_sun_path (const char *path);
char *get_sun_path () {return sun_path;}
void set_peer_sun_path (const char *path);
char *get_peer_sun_path () {return peer_sun_path;}
protected:
int connect_secret[4];
pid_t sec_pid;
uid_t sec_uid;
gid_t sec_gid;
pid_t sec_peer_pid;
uid_t sec_peer_uid;
gid_t sec_peer_gid;
void af_local_set_secret (char *);
void af_local_setblocking (bool &, bool &);
void af_local_unsetblocking (bool, bool);
void af_local_set_cred ();
void af_local_copy (fhandler_socket_local *);
bool af_local_recv_secret ();
bool af_local_send_secret ();
bool af_local_recv_cred ();
bool af_local_send_cred ();
int af_local_accept ();
int af_local_connect ();
int af_local_set_no_getpeereid ();
void af_local_set_sockpair_cred ();
private:
inline ssize_t recv_internal (struct _WSAMSG *wsamsg, bool use_recvmsg);
inline ssize_t send_internal (struct _WSAMSG *wsamsg, int flags);
public:
fhandler_socket_local ();
~fhandler_socket_local ();
int dup (fhandler_base *child, int);
int socket (int af, int type, int protocol, int flags);
int socketpair (int af, int type, int protocol, int flags,
fhandler_socket *fh_out);
int bind (const struct sockaddr *name, int namelen);
int listen (int backlog);
int accept4 (struct sockaddr *peer, int *len, int flags);
int connect (const struct sockaddr *name, int namelen);
int getsockname (struct sockaddr *name, int *namelen);
int getpeername (struct sockaddr *name, int *namelen);
int getpeereid (pid_t *pid, uid_t *euid, gid_t *egid);
int setsockopt (int level, int optname, const void *optval,
__socklen_t optlen);
int getsockopt (int level, int optname, const void *optval,
__socklen_t *optlen);
ssize_t recvfrom (void *ptr, size_t len, int flags,
struct sockaddr *from, int *fromlen);
ssize_t recvmsg (struct msghdr *msg, int flags);
void __reg3 read (void *ptr, size_t& len);
ssize_t __stdcall readv (const struct iovec *, int iovcnt, ssize_t tot = -1);
ssize_t sendto (const void *ptr, size_t len, int flags,
const struct sockaddr *to, int tolen);
ssize_t sendmsg (const struct msghdr *msg, int flags);
ssize_t __stdcall write (const void *ptr, size_t len);
ssize_t __stdcall writev (const struct iovec *, int iovcnt, ssize_t tot = -1);
int __reg2 fstat (struct stat *buf);
int __reg2 fstatvfs (struct statvfs *buf);
int __reg1 fchmod (mode_t mode);
int __reg2 fchown (uid_t uid, gid_t gid);
int __reg1 fchmod (mode_t newmode);
int __reg2 fchown (uid_t newuid, gid_t newgid);
int __reg3 facl (int, int, struct acl *);
int __reg2 link (const char *);
fhandler_socket (void *) {}
/* from here on: CLONING */
fhandler_socket_local (void *) {}
void copyto (fhandler_base *x)
{
x->pc.free_strings ();
*reinterpret_cast<fhandler_socket *> (x) = *this;
*reinterpret_cast<fhandler_socket_local *> (x) = *this;
x->reset (this);
}
fhandler_socket *clone (cygheap_types malloc_type = HEAP_FHANDLER)
fhandler_socket_local *clone (cygheap_types malloc_type = HEAP_FHANDLER)
{
void *ptr = (void *) ccalloc (malloc_type, 1, sizeof (fhandler_socket));
fhandler_socket *fh = new (ptr) fhandler_socket (ptr);
void *ptr = (void *) ccalloc (malloc_type, 1, sizeof (fhandler_socket_local));
fhandler_socket_local *fh = new (ptr) fhandler_socket_local (ptr);
copyto (fh);
return fh;
}
@ -2223,6 +2334,8 @@ typedef union
char __registry[sizeof (fhandler_registry)];
char __serial[sizeof (fhandler_serial)];
char __socket[sizeof (fhandler_socket)];
char __socket_inet[sizeof (fhandler_socket_inet)];
char __socket_local[sizeof (fhandler_socket_local)];
char __termios[sizeof (fhandler_termios)];
char __pty_common[sizeof (fhandler_pty_common)];
char __pty_slave[sizeof (fhandler_pty_slave)];

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -500,146 +500,6 @@ cygwin_getprotobynumber (int number)
return dup_ent (getprotobynumber (number));
}
#ifndef SIO_BASE_HANDLE
#define SIO_BASE_HANDLE _WSAIOR(IOC_WS2,34)
#endif
bool
fdsock (cygheap_fdmanip& fd, const device *dev, SOCKET soc)
{
fd = build_fh_dev (*dev);
if (!fd.isopen ())
return 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).
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. */
DWORD flags;
bool fixup = false;
if (!GetHandleInformation ((HANDLE) soc, &flags)
|| !(flags & HANDLE_FLAG_INHERIT))
{
int ret;
SOCKET base_soc;
DWORD bret;
fixup = true;
debug_printf ("LSP handle: %p", soc);
ret = WSAIoctl (soc, SIO_BASE_HANDLE, NULL, 0, (void *) &base_soc,
sizeof (base_soc), &bret, NULL, NULL);
if (ret)
debug_printf ("WSAIoctl: %u", WSAGetLastError ());
else if (base_soc != soc)
{
if (GetHandleInformation ((HANDLE) base_soc, &flags)
&& (flags & HANDLE_FLAG_INHERIT))
{
if (!DuplicateHandle (GetCurrentProcess (), (HANDLE) base_soc,
GetCurrentProcess (), (PHANDLE) &base_soc,
0, TRUE, DUPLICATE_SAME_ACCESS))
debug_printf ("DuplicateHandle failed, %E");
else
{
closesocket (soc);
soc = base_soc;
fixup = false;
}
}
}
}
fd->set_io_handle ((HANDLE) soc);
if (!((fhandler_socket *) fd)->init_events ())
return false;
if (fixup)
((fhandler_socket *) fd)->init_fixup_before ();
fd->set_flags (O_RDWR | O_BINARY);
debug_printf ("fd %d, name '%s', soc %p", (int) fd, dev->name (), soc);
/* Raise default buffer sizes (instead of WinSock default 8K).
64K appear to have the best size/performance ratio for a default
value. Tested with ssh/scp on Vista over Gigabit LAN.
NOTE. If the SO_RCVBUF size exceeds 65535(*), and if the socket is
connected to a remote machine, then calling WSADuplicateSocket on
fork/exec fails with WinSock error 10022, WSAEINVAL. Fortunately
we don't use WSADuplicateSocket anymore, rather we just utilize
handle inheritance. An explanation for this weird behaviour would
be nice, though.
NOTE 2. Testing on x86_64 (Vista, 2008 R2, W8) indicates that
this is no problem on 64 bit. So we set the default buffer size to
the default values in current 3.x Linux versions.
NOTE 3. Setting the window size to 65535 results in extremely bad
performance for apps that send data in multiples of Kb, as they
eventually end up sending 1 byte on the network and naggle + delay
ack kicks in. For example, iperf on a 10Gb network gives only 10
Mbits/sec with a 65535 send buffer. We want this to be a multiple
of 1k, but since 64k breaks WSADuplicateSocket we use 63Kb.
NOTE 4. Tests with iperf uncover a problem in setting the SO_RCVBUF
and SO_SNDBUF sizes. Windows is using autotuning since Windows Vista.
Manually setting SO_RCVBUF/SO_SNDBUF disables autotuning and leads to
inferior send/recv performance in scenarios with larger RTTs, as is
basically standard when accessing the internet. For a discussion,
see https://cygwin.com/ml/cygwin-patches/2017-q1/msg00010.html.
(*) Maximum normal TCP window size. Coincidence? */
#ifdef __x86_64__
((fhandler_socket *) fd)->rmem () = 212992;
((fhandler_socket *) fd)->wmem () = 212992;
#else
((fhandler_socket *) fd)->rmem () = 64512;
((fhandler_socket *) fd)->wmem () = 64512;
#endif
#if 0 /* See NOTE 4 above. */
int size;
if (::setsockopt (soc, SOL_SOCKET, SO_RCVBUF,
(char *) &((fhandler_socket *) fd)->rmem (), sizeof (int)))
{
debug_printf ("setsockopt(SO_RCVBUF) failed, %u", WSAGetLastError ());
if (::getsockopt (soc, SOL_SOCKET, SO_RCVBUF,
(char *) &((fhandler_socket *) fd)->rmem (),
(size = sizeof (int), &size)))
system_printf ("getsockopt(SO_RCVBUF) failed, %u", WSAGetLastError ());
}
if (::setsockopt (soc, SOL_SOCKET, SO_SNDBUF,
(char *) &((fhandler_socket *) fd)->wmem (), sizeof (int)))
{
debug_printf ("setsockopt(SO_SNDBUF) failed, %u", WSAGetLastError ());
if (::getsockopt (soc, SOL_SOCKET, SO_SNDBUF,
(char *) &((fhandler_socket *) fd)->wmem (),
(size = sizeof (int), &size)))
system_printf ("getsockopt(SO_SNDBUF) failed, %u", WSAGetLastError ());
}
#endif
/* A unique ID is necessary to recognize fhandler entries which are
duplicated by dup(2) or fork(2). This is used in BSD flock calls
to identify the descriptor. */
((fhandler_socket *) fd)->set_unique_id ();
return true;
}
/* exported as socket: POSIX.1-2001, POSIX.1-2008, 4.4BSD */
extern "C" int
cygwin_socket (int af, int type, int protocol)

View File

@ -17,6 +17,7 @@ details. */
/* UID/GID */
void uinfo_init ();
bool check_token_membership (PSID);
#define ILLEGAL_UID ((uid_t)-1)
#define ILLEGAL_GID ((gid_t)-1)

View File

@ -185,17 +185,12 @@ static enum {
static int syslogd_sock = -1;
extern "C" int cygwin_socket (int, int, int);
extern "C" int cygwin_connect (int, const struct sockaddr *, int);
extern int get_inet_addr (const struct sockaddr *, int,
struct sockaddr_storage *, int *,
int * = NULL, int * = NULL);
static void
connect_syslogd ()
{
int fd;
struct sockaddr_un sun;
struct sockaddr_storage sst;
int len, type;
if (syslogd_inited != not_inited && syslogd_sock >= 0)
close (syslogd_sock);
@ -203,20 +198,38 @@ connect_syslogd ()
syslogd_sock = -1;
sun.sun_family = AF_LOCAL;
strncpy (sun.sun_path, _PATH_LOG, sizeof sun.sun_path);
if (get_inet_addr ((struct sockaddr *) &sun, sizeof sun, &sst, &len, &type))
return;
if ((fd = cygwin_socket (AF_LOCAL, type, 0)) < 0)
if ((fd = cygwin_socket (AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0)) < 0)
return;
if (cygwin_connect (fd, (struct sockaddr *) &sun, sizeof sun) == 0)
syslogd_inited = inited_stream;
else
{
/* connect on a dgram socket always succeeds. We still don't know
if syslogd is actually listening. */
if (type == SOCK_DGRAM)
close (fd);
if ((fd = cygwin_socket (AF_LOCAL, SOCK_DGRAM | SOCK_CLOEXEC, 0)) < 0)
return;
if (cygwin_connect (fd, (struct sockaddr *) &sun, sizeof sun) == 0)
{
/*
* FIXME
*
* As soon as AF_LOCAL sockets are using pipes, this code has to
* got away.
*/
/* connect on a dgram socket always succeeds. We still don't know
if syslogd is actually listening. */
cygheap_fdget cfd (fd);
fhandler_socket_local *const fh = (fhandler_socket_local *)
cfd->is_socket ();
tmp_pathbuf tp;
PMIB_UDPTABLE tab = (PMIB_UDPTABLE) tp.w_get ();
DWORD size = 65536;
bool found = false;
struct sockaddr_storage sst;
int len;
len = sizeof sst;
::getsockname (fh->get_socket (), (struct sockaddr *) &sst, &len);
struct sockaddr_in *sa = (struct sockaddr_in *) &sst;
if (GetUdpTable (tab, &size, FALSE) == NO_ERROR)
@ -235,11 +248,12 @@ connect_syslogd ()
return;
}
}
syslogd_inited = inited_dgram;
}
syslogd_inited = type == SOCK_DGRAM ? inited_dgram : inited_stream;
else
close (fd);
}
syslogd_sock = fd;
fcntl64 (syslogd_sock, F_SETFD, FD_CLOEXEC);
debug_printf ("found /dev/log, fd = %d, type = %s",
fd, syslogd_inited == inited_stream ? "STREAM" : "DGRAM");
return;

View File

@ -117,7 +117,7 @@ cygheap_user::init ()
This needs careful checking should we use check_token_membership in other
circumstances. */
static bool
bool
check_token_membership (PSID sid)
{
NTSTATUS status;
@ -142,7 +142,7 @@ check_token_membership (PSID sid)
return false;
}
void
static void
internal_getlogin (cygheap_user &user)
{
struct passwd *pwd;