aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCorinna Vinschen <corinna@vinschen.de>2018-03-18 20:46:43 +0100
committerCorinna Vinschen <corinna@vinschen.de>2018-03-18 20:46:43 +0100
commit4fe086c84f05a4fc4740faec3f613a2ea5e4b2c7 (patch)
treea82fb19a1db21a0f0a11b2b4fc7e90292a09f86b
parenta366a8fc42e7b8b2be13879990fee352bdd66f78 (diff)
Cygwin: AF_UNIX: Redesign various aspects
* Change set_socket_type/get_socket_type to virtual methods * Move various variables into af_unix_shmem_t * Change sun_name_t to match new usage pattern * Move shut_state definition and add a name for the 0 value * Allow marking packet as administrative packet. This allows filtering out info packets exchange between peers and tweak data accordingly. * Rename send_my_name to send_sock_info and send credentials if not called from bind (so the socket was already connected) * Handle SO_PASSCRED in setsockopt/getsockopt * Add input size checking to setsockopt/getsockopt * Use NT functions where appropriate Signed-off-by: Corinna Vinschen <corinna@vinschen.de>
-rw-r--r--winsup/cygwin/fhandler.h99
-rw-r--r--winsup/cygwin/fhandler_socket_unix.cc533
2 files changed, 442 insertions, 190 deletions
diff --git a/winsup/cygwin/fhandler.h b/winsup/cygwin/fhandler.h
index a4bf1d315..be5537956 100644
--- a/winsup/cygwin/fhandler.h
+++ b/winsup/cygwin/fhandler.h
@@ -579,8 +579,8 @@ class fhandler_socket: public fhandler_base
void set_addr_family (int af) {addr_family = af;}
int get_addr_family () {return addr_family;}
- void set_socket_type (int st) { type = st;}
- int get_socket_type () {return type;}
+ virtual void set_socket_type (int st) { type = st;}
+ virtual int get_socket_type () {return type;}
/* select.cc */
virtual select_record *select_read (select_stuff *) = 0;
@@ -851,6 +851,14 @@ public:
}
};
+/* Internal representation of shutdown states */
+enum shut_state {
+ _SHUT_NONE = 0,
+ _SHUT_RECV = 1,
+ _SHUT_SEND = 2,
+ _SHUT_MASK = 3
+};
+
class sun_name_t
{
public:
@@ -861,19 +869,10 @@ class sun_name_t
/* Allows 108 bytes sun_path plus trailing NUL */
char _nul[sizeof (struct sockaddr_un) + 1];
};
- sun_name_t ();
- sun_name_t (const struct sockaddr *name, __socklen_t namelen);
-
- void *operator new (size_t) __attribute__ ((nothrow))
- { return cmalloc_abort (HEAP_FHANDLER, sizeof (sun_name_t)); }
- void operator delete (void *p) {cfree (p);}
-};
-
-/* Internal representation of shutdown states */
-enum shut_state {
- _SHUT_READ = 1,
- _SHUT_WRITE = 2,
- _SHUT_RW = 3
+ sun_name_t () { set (NULL, 0); }
+ sun_name_t (const struct sockaddr *name, __socklen_t namelen)
+ { set ((const struct sockaddr_un *) name, namelen); }
+ void set (const struct sockaddr_un *name, __socklen_t namelen);
};
/* For each AF_UNIX socket, we need to maintain socket-wide data,
@@ -893,7 +892,13 @@ class af_unix_shmem_t
LONG _binding_state; /* bind_state */
LONG _shutdown; /* shut_state */
LONG _so_error; /* SO_ERROR */
+ LONG _so_passcred; /* SO_PASSCRED */
LONG _reuseaddr; /* dummy */
+ int _type; /* socket type */
+ sun_name_t _sun_path;
+ sun_name_t _peer_sun_path;
+ struct ucred _sock_cred; /* filled at listen time */
+ struct ucred _peer_cred; /* filled at connect time */
public:
void bind_lock () { _bind_lock.lock (); }
@@ -917,13 +922,31 @@ class af_unix_shmem_t
{ return (int) InterlockedExchange (&_shutdown, shut); }
int shutdown () const { return (int) _shutdown; }
- int so_error (int err)
- { return (int) InterlockedExchange (&_so_error, err); }
+ int so_error (int err) { return (int) InterlockedExchange (&_so_error, err); }
int so_error () const { return _so_error; }
+ bool so_passcred (bool pc)
+ { return (bool) InterlockedExchange (&_so_passcred, pc); }
+ bool so_passcred () const { return _so_passcred; }
+
int reuseaddr (int val)
{ return (int) InterlockedExchange (&_reuseaddr, val); }
int reuseaddr () const { return _reuseaddr; }
+
+ void set_socket_type (int val) { _type = val; }
+ int get_socket_type () const { return _type; }
+
+ void sun_path (struct sockaddr_un *un, __socklen_t unlen)
+ { _sun_path.set (un, unlen); }
+ void peer_sun_path (struct sockaddr_un *un, __socklen_t unlen)
+ { _peer_sun_path.set (un, unlen); }
+ sun_name_t *sun_path () {return &_sun_path;}
+ sun_name_t *peer_sun_path () {return &_peer_sun_path;}
+
+ void sock_cred (struct ucred *uc) { _sock_cred = *uc; }
+ struct ucred *sock_cred () { return &_sock_cred; }
+ void peer_cred (struct ucred *uc) { _peer_cred = *uc; }
+ struct ucred *peer_cred () { return &_peer_cred; }
};
class fhandler_socket_unix : public fhandler_socket
@@ -938,9 +961,6 @@ class fhandler_socket_unix : public fhandler_socket
HANDLE connect_wait_thr;
HANDLE cwt_termination_evt;
PVOID cwt_param;
- sun_name_t *sun_path;
- sun_name_t *peer_sun_path;
- struct ucred peer_cred;
void bind_lock () { shmem->bind_lock (); }
void bind_unlock () { shmem->bind_unlock (); }
@@ -960,8 +980,12 @@ class fhandler_socket_unix : public fhandler_socket
int saw_shutdown () const { return shmem->shutdown (); }
int so_error (int err) { return shmem->so_error (err); }
int so_error () const { return shmem->so_error (); }
+ bool so_passcred (bool pc) { return shmem->so_passcred (pc); }
+ bool so_passcred () const { return shmem->so_passcred (); }
int reuseaddr (int err) { return shmem->reuseaddr (err); }
int reuseaddr () const { return shmem->reuseaddr (); }
+ void set_socket_type (int val) { shmem->set_socket_type (val); }
+ int get_socket_type () const { return shmem->get_socket_type (); }
int create_shmem ();
int reopen_shmem ();
@@ -977,26 +1001,37 @@ class fhandler_socket_unix : public fhandler_socket
HANDLE autobind (sun_name_t *sun);
wchar_t get_type_char ();
void set_pipe_non_blocking (bool nonblocking);
- int send_my_name ();
- int recv_peer_name ();
+ int send_sock_info (bool from_bind);
+ int grab_admin_pkg ();
+ int recv_peer_info ();
static NTSTATUS npfs_handle (HANDLE &nph);
HANDLE create_pipe (bool single_instance);
HANDLE create_pipe_instance ();
- NTSTATUS open_pipe (PUNICODE_STRING pipe_name, bool send_name);
+ NTSTATUS open_pipe (PUNICODE_STRING pipe_name, bool xchg_sock_info);
int wait_pipe (PUNICODE_STRING pipe_name);
int connect_pipe (PUNICODE_STRING pipe_name);
int listen_pipe ();
+ ULONG peek_pipe (PFILE_PIPE_PEEK_BUFFER pbuf, ULONG psize, HANDLE evt);
int disconnect_pipe (HANDLE ph);
- sun_name_t *get_sun_path () {return sun_path;}
- sun_name_t *get_peer_sun_path () {return peer_sun_path;}
- void set_sun_path (struct sockaddr_un *un, __socklen_t unlen);
- void set_sun_path (sun_name_t *snt)
- { snt ? set_sun_path (&snt->un, snt->un_len) : set_sun_path (NULL, 0); }
- void set_peer_sun_path (struct sockaddr_un *un, __socklen_t unlen);
- void set_peer_sun_path (sun_name_t *snt)
- { snt ? set_peer_sun_path (&snt->un, snt->un_len)
- : set_peer_sun_path (NULL, 0); }
+ /* The NULL pointer check is required for FS methods like fstat. When
+ called via stat or lstat, there's no shared memory, just a path in pc. */
+ sun_name_t *sun_path () {return shmem ? shmem->sun_path () : NULL;}
+ sun_name_t *peer_sun_path () {return shmem->peer_sun_path ();}
+ void sun_path (struct sockaddr_un *un, __socklen_t unlen)
+ { shmem->sun_path (un, unlen); }
+ void sun_path (sun_name_t *snt)
+ { snt ? sun_path (&snt->un, snt->un_len) : sun_path (NULL, 0); }
+ void peer_sun_path (struct sockaddr_un *un, __socklen_t unlen)
+ { shmem->peer_sun_path (un, unlen); }
+ void peer_sun_path (sun_name_t *snt)
+ { snt ? peer_sun_path (&snt->un, snt->un_len)
+ : peer_sun_path (NULL, 0); }
+ void init_cred ();
void set_cred ();
+ void sock_cred (struct ucred *uc) { shmem->sock_cred (uc); }
+ struct ucred *sock_cred () { return shmem->sock_cred (); }
+ void peer_cred (struct ucred *uc) { shmem->peer_cred (uc); }
+ struct ucred *peer_cred () { return shmem->peer_cred (); }
void fixup_after_fork (HANDLE parent);
void fixup_after_exec ();
void set_close_on_exec (bool val);
diff --git a/winsup/cygwin/fhandler_socket_unix.cc b/winsup/cygwin/fhandler_socket_unix.cc
index 89cf5b1f7..295e07bb4 100644
--- a/winsup/cygwin/fhandler_socket_unix.cc
+++ b/winsup/cygwin/fhandler_socket_unix.cc
@@ -58,8 +58,8 @@
- <uniq_id> is an 8 byte hex unique number
Note: We use MAX_PATH below for convenience where sufficient. It's
- big enough to hold sun_paths as well as pipe names so we don't have
- to use tmp_pathbuf as often.
+ big enough to hold sun_paths as well as pipe names as well as packet
+ headers etc., so we don't have to use tmp_pathbuf as often.
Every packet sent to a peer is a combination of the socket name of the
local socket, the ancillary data, and the actual user data. The data
@@ -78,18 +78,17 @@ class af_unix_pkt_hdr_t
{
public:
uint16_t pckt_len; /* size of packet including header */
- uint8_t shut_info; /* shutdown info. SHUT_RD means
- SHUT_RD on the local side, so the
- peer must not send further packets,
- vice versa for SHUT_WR. SHUT_RDWR
- is followed by closing the pipe
- handle. */
+ bool admin_pkg : 1; /* admin packets are marked as such */
+ shut_state shut_info : 2; /* _SHUT_RECV /_SHUT_SEND. */
uint8_t name_len; /* size of name, a sockaddr_un */
uint16_t cmsg_len; /* size of ancillary data block */
uint16_t data_len; /* size of user data */
- void init (uint8_t s, uint8_t n, uint16_t c, uint16_t d)
+ af_unix_pkt_hdr_t (bool a, shut_state s, uint8_t n, uint16_t c, uint16_t d)
+ { init (a, s, n, c, d); }
+ void init (bool a, shut_state s, uint8_t n, uint16_t c, uint16_t d)
{
+ admin_pkg = a;
shut_info = s;
name_len = n;
cmsg_len = c;
@@ -116,7 +115,7 @@ class af_unix_pkt_hdr_t
#define AF_UNIX_PKT_CMSG(phdr) \
({ \
af_unix_pkt_hdr_t *_p = phdr; \
- (void *)(((PBYTE)(_p)) + AF_UNIX_PKT_OFFSETOF_CMSG (_p)); \
+ (struct cmsghdr *)(((PBYTE)(_p)) + AF_UNIX_PKT_OFFSETOF_CMSG (_p)); \
})
#define AF_UNIX_PKT_DATA(phdr) \
({ \
@@ -157,21 +156,16 @@ GUID __cygwin_socket_guid = {
/* Default timeout value of connect: 20 secs, as on Linux. */
#define AF_UNIX_CONNECT_TIMEOUT (-20 * NS100PERSEC)
-sun_name_t::sun_name_t ()
-{
- un_len = sizeof (sa_family_t);
- un.sun_family = AF_UNIX;
- _nul[sizeof (struct sockaddr_un)] = '\0';
-}
-
-sun_name_t::sun_name_t (const struct sockaddr *name, socklen_t namelen)
+void
+sun_name_t::set (const struct sockaddr_un *name, socklen_t namelen)
{
if (namelen < 0)
namelen = 0;
un_len = namelen < (__socklen_t) sizeof un ? namelen : sizeof un;
- if (name)
+ un.sun_family = AF_UNIX;
+ if (name && un_len)
memcpy (&un, name, un_len);
- _nul[sizeof (struct sockaddr_un)] = '\0';
+ _nul[un_len] = '\0';
}
static HANDLE
@@ -618,24 +612,49 @@ fhandler_socket_unix::set_pipe_non_blocking (bool nonblocking)
}
}
+/* Apart from being called from bind(), from_bind indicates that the caller
+ already locked state_lock, so send_sock_info doesn't lock, only unlocks
+ state_lock. */
int
-fhandler_socket_unix::send_my_name ()
+fhandler_socket_unix::send_sock_info (bool from_bind)
{
sun_name_t *sun;
- size_t name_len = 0;
+ size_t plen;
+ size_t clen = 0;
af_unix_pkt_hdr_t *packet;
NTSTATUS status;
IO_STATUS_BLOCK io;
- bind_lock ();
- sun = get_sun_path ();
- name_len = sun ? sun->un_len : 0;
- packet = (af_unix_pkt_hdr_t *) alloca (sizeof *packet + name_len);
+ if (!from_bind)
+ {
+ state_lock ();
+ /* When called from connect, initialize credentials. accept4 already
+ did it (copied from listening socket). */
+ if (sock_cred ()->pid == 0)
+ set_cred ();
+ }
+ sun = sun_path ();
+ plen = sizeof *packet + sun->un_len;
+ /* When called from connect/accept4, send SCM_CREDENTIALS, too. */
+ if (!from_bind)
+ {
+ clen = CMSG_SPACE (sizeof (struct ucred));
+ plen += clen;
+ }
+ packet = (af_unix_pkt_hdr_t *) alloca (plen);
+ packet->init (true, _SHUT_NONE, sun->un_len, clen, 0);
if (sun)
- memcpy (AF_UNIX_PKT_NAME (packet), &sun->un, name_len);
- bind_unlock ();
+ memcpy (AF_UNIX_PKT_NAME (packet), &sun->un, sun->un_len);
+ if (!from_bind)
+ {
+ struct cmsghdr *cmsg = AF_UNIX_PKT_CMSG (packet);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_CREDENTIALS;
+ cmsg->cmsg_len = CMSG_LEN (sizeof (struct ucred));
+ memcpy (CMSG_DATA(cmsg), sock_cred (), sizeof (struct ucred));
+ }
- packet->init (0, name_len, 0, 0);
+ state_unlock ();
/* The theory: Fire and forget. */
io_lock ();
@@ -652,10 +671,79 @@ fhandler_socket_unix::send_my_name ()
return 0;
}
-/* Returns an error code. Locking is not required, user space doesn't know
- about this socket yet. */
+/* Checks if the next packet in the pipe is an administrative packet.
+ If so, it reads it from the pipe, handles it. Returns an error code. */
int
-fhandler_socket_unix::recv_peer_name ()
+fhandler_socket_unix::grab_admin_pkg ()
+{
+ HANDLE evt;
+ NTSTATUS status;
+ IO_STATUS_BLOCK io;
+ /* MAX_PATH is more than sufficient for admin packets. */
+ PFILE_PIPE_PEEK_BUFFER pbuf = (PFILE_PIPE_PEEK_BUFFER) alloca (MAX_PATH);
+ if (!(evt = create_event ()))
+ return 0;
+ io_lock ();
+ ULONG ret_len = peek_pipe (pbuf, MAX_PATH, evt);
+ if (pbuf->NumberOfMessages == 0 || ret_len < sizeof (af_unix_pkt_hdr_t))
+ {
+ io_unlock ();
+ NtClose (evt);
+ return 0;
+ }
+ af_unix_pkt_hdr_t *packet = (af_unix_pkt_hdr_t *) pbuf->Data;
+ if (!packet->admin_pkg)
+ io_unlock ();
+ else
+ {
+ packet = (af_unix_pkt_hdr_t *) pbuf;
+ status = NtReadFile (get_handle (), evt, NULL, NULL, &io, packet,
+ MAX_PATH, NULL, NULL);
+ if (status == STATUS_PENDING)
+ {
+ /* Very short-lived */
+ status = NtWaitForSingleObject (evt, FALSE, NULL);
+ if (NT_SUCCESS (status))
+ status = io.Status;
+ }
+ io_unlock ();
+ if (NT_SUCCESS (status))
+ {
+ state_lock ();
+ if (packet->shut_info)
+ {
+ /* Peer's shutdown sends the SHUT flags as used by the peer.
+ They have to be reversed for our side. */
+ int shut_info = saw_shutdown ();
+ if (packet->shut_info & _SHUT_RECV)
+ shut_info |= _SHUT_SEND;
+ if (packet->shut_info & _SHUT_SEND)
+ shut_info |= _SHUT_RECV;
+ saw_shutdown (shut_info);
+ /* FIXME: anything else here? */
+ }
+ if (packet->name_len > 0)
+ peer_sun_path (AF_UNIX_PKT_NAME (packet), packet->name_len);
+ if (packet->cmsg_len > 0)
+ {
+ struct cmsghdr *cmsg = (struct cmsghdr *)
+ alloca (packet->cmsg_len);
+ memcpy (cmsg, AF_UNIX_PKT_CMSG (packet), packet->cmsg_len);
+ if (cmsg->cmsg_level == SOL_SOCKET
+ && cmsg->cmsg_type == SCM_CREDENTIALS)
+ peer_cred ((struct ucred *) CMSG_DATA(cmsg));
+ }
+ state_unlock ();
+ }
+ }
+ NtClose (evt);
+ return 0;
+}
+
+/* Returns an error code. Locking is not required when called from accept4,
+ user space doesn't know about this socket yet. */
+int
+fhandler_socket_unix::recv_peer_info ()
{
HANDLE evt;
NTSTATUS status;
@@ -667,7 +755,7 @@ fhandler_socket_unix::recv_peer_name ()
if (!(evt = create_event ()))
return ENOBUFS;
- len = sizeof *packet + sizeof *un;
+ len = sizeof *packet + sizeof *un + CMSG_SPACE (sizeof (struct ucred));
packet = (af_unix_pkt_hdr_t *) alloca (len);
set_pipe_non_blocking (false);
status = NtReadFile (get_handle (), evt, NULL, NULL, &io, packet, len,
@@ -695,11 +783,23 @@ fhandler_socket_unix::recv_peer_name ()
break;
}
}
+ set_pipe_non_blocking (is_nonblocking ());
+ NtClose (evt);
if (!NT_SUCCESS (status) && ret == 0)
ret = geterrno_from_nt_status (status);
- if (ret == 0 && packet->name_len > 0)
- set_peer_sun_path (AF_UNIX_PKT_NAME (packet), packet->name_len);
- set_pipe_non_blocking (is_nonblocking ());
+ if (ret == 0)
+ {
+ if (packet->name_len > 0)
+ peer_sun_path (AF_UNIX_PKT_NAME (packet), packet->name_len);
+ if (packet->cmsg_len > 0)
+ {
+ struct cmsghdr *cmsg = (struct cmsghdr *) alloca (packet->cmsg_len);
+ memcpy (cmsg, AF_UNIX_PKT_CMSG (packet), packet->cmsg_len);
+ if (cmsg->cmsg_level == SOL_SOCKET
+ && cmsg->cmsg_type == SCM_CREDENTIALS)
+ peer_cred ((struct ucred *) CMSG_DATA(cmsg));
+ }
+ }
return ret;
}
@@ -818,7 +918,7 @@ fhandler_socket_unix::create_pipe_instance ()
}
NTSTATUS
-fhandler_socket_unix::open_pipe (PUNICODE_STRING pipe_name, bool send_name)
+fhandler_socket_unix::open_pipe (PUNICODE_STRING pipe_name, bool xchg_sock_info)
{
NTSTATUS status;
HANDLE npfsh;
@@ -838,8 +938,8 @@ fhandler_socket_unix::open_pipe (PUNICODE_STRING pipe_name, bool send_name)
if (NT_SUCCESS (status))
{
set_io_handle (ph);
- if (send_name)
- send_my_name ();
+ if (xchg_sock_info)
+ send_sock_info (false);
}
return status;
}
@@ -902,13 +1002,13 @@ fhandler_socket_unix::wait_pipe (PUNICODE_STRING pipe_name)
else
{
SetEvent (cwt_termination_evt);
- WaitForSingleObject (connect_wait_thr, INFINITE);
+ NtWaitForSingleObject (connect_wait_thr, FALSE, NULL);
GetExitCodeThread (connect_wait_thr, &err);
waitret = WAIT_SIGNALED;
}
thr = InterlockedExchangePointer (&connect_wait_thr, NULL);
if (thr)
- CloseHandle (thr);
+ NtClose (thr);
param = InterlockedExchangePointer (&cwt_param, NULL);
if (param)
cfree (param);
@@ -988,6 +1088,27 @@ fhandler_socket_unix::listen_pipe ()
return (status == STATUS_PIPE_CONNECTED) ? 0 : -1;
}
+ULONG
+fhandler_socket_unix::peek_pipe (PFILE_PIPE_PEEK_BUFFER pbuf, ULONG psize,
+ HANDLE evt)
+{
+ NTSTATUS status;
+ IO_STATUS_BLOCK io;
+
+ status = NtFsControlFile (get_handle (), evt, NULL, NULL, &io,
+ FSCTL_PIPE_PEEK, NULL, 0, pbuf, psize);
+ if (status == STATUS_PENDING)
+ {
+ /* Very short-lived */
+ status = NtWaitForSingleObject (evt ?: get_handle (), FALSE, NULL);
+ if (NT_SUCCESS (status))
+ status = io.Status;
+ }
+ return NT_SUCCESS (status) ? (io.Information
+ - offsetof (FILE_PIPE_PEEK_BUFFER, Data))
+ : 0;
+}
+
int
fhandler_socket_unix::disconnect_pipe (HANDLE ph)
{
@@ -998,7 +1119,7 @@ fhandler_socket_unix::disconnect_pipe (HANDLE ph)
NULL, 0, NULL, 0);
/* Short-lived. Don't use cygwait. We don't want to be interrupted. */
if (status == STATUS_PENDING
- && WaitForSingleObject (ph, INFINITE) == WAIT_OBJECT_0)
+ && NtWaitForSingleObject (ph, FALSE, NULL) == WAIT_OBJECT_0)
status = io.Status;
if (!NT_SUCCESS (status))
{
@@ -1009,32 +1130,22 @@ fhandler_socket_unix::disconnect_pipe (HANDLE ph)
}
void
-fhandler_socket_unix::set_sun_path (struct sockaddr_un *un, socklen_t unlen)
-{
- if (peer_sun_path)
- delete peer_sun_path;
- if (!un)
- sun_path = NULL;
- sun_path = new sun_name_t ((const struct sockaddr *) un, unlen);
-}
-
-void
-fhandler_socket_unix::set_peer_sun_path (struct sockaddr_un *un,
- socklen_t unlen)
+fhandler_socket_unix::init_cred ()
{
- if (peer_sun_path)
- delete peer_sun_path;
- if (!un)
- peer_sun_path = NULL;
- peer_sun_path = new sun_name_t ((const struct sockaddr *) un, unlen);
+ struct ucred *scred = shmem->sock_cred ();
+ struct ucred *pcred = shmem->peer_cred ();
+ scred->pid = pcred->pid = (pid_t) 0;
+ scred->uid = pcred->uid = (uid_t) -1;
+ scred->gid = pcred->gid = (gid_t) -1;
}
void
fhandler_socket_unix::set_cred ()
{
- peer_cred.pid = (pid_t) 0;
- peer_cred.uid = (uid_t) -1;
- peer_cred.gid = (gid_t) -1;
+ struct ucred *scred = shmem->sock_cred ();
+ scred->pid = myself->pid;
+ scred->uid = myself->uid;
+ scred->gid = myself->gid;
}
/* ========================== public methods ========================= */
@@ -1074,15 +1185,10 @@ fhandler_socket_unix::set_close_on_exec (bool val)
fhandler_socket_unix::fhandler_socket_unix ()
{
- set_cred ();
}
fhandler_socket_unix::~fhandler_socket_unix ()
{
- if (sun_path)
- delete sun_path;
- if (peer_sun_path)
- delete peer_sun_path;
}
int
@@ -1094,6 +1200,12 @@ fhandler_socket_unix::dup (fhandler_base *child, int flags)
return -1;
}
fhandler_socket_unix *fhs = (fhandler_socket_unix *) child;
+ if (reopen_shmem () < 0)
+ {
+ __seterrno ();
+ fhs->close ();
+ return -1;
+ }
if (backing_file_handle && backing_file_handle != INVALID_HANDLE_VALUE
&& !DuplicateHandle (GetCurrentProcess (), backing_file_handle,
GetCurrentProcess (), &fhs->backing_file_handle,
@@ -1111,14 +1223,8 @@ fhandler_socket_unix::dup (fhandler_base *child, int flags)
fhs->close ();
return -1;
}
- if (reopen_shmem () < 0)
- {
- __seterrno ();
- fhs->close ();
- return -1;
- }
- fhs->set_sun_path (get_sun_path ());
- fhs->set_peer_sun_path (get_peer_sun_path ());
+ fhs->sun_path (sun_path ());
+ fhs->peer_sun_path (peer_sun_path ());
fhs->connect_wait_thr = NULL;
fhs->cwt_termination_evt = NULL;
fhs->cwt_param = NULL;
@@ -1250,12 +1356,13 @@ fhandler_socket_unix::socket (int af, int type, int protocol, int flags)
return -1;
rmem (262144);
wmem (262144);
- set_addr_family (af);
+ set_addr_family (AF_UNIX);
set_socket_type (type);
if (flags & SOCK_NONBLOCK)
set_nonblocking (true);
if (flags & SOCK_CLOEXEC)
set_close_on_exec (true);
+ init_cred ();
set_io_handle (NULL);
set_unique_id ();
set_ino (get_unique_id ());
@@ -1281,46 +1388,36 @@ fhandler_socket_unix::socketpair (int af, int type, int protocol, int flags,
return -1;
}
+ if (create_shmem () < 0)
+ return -1;
+ if (fh->create_shmem () < 0)
+ goto fh_shmem_failed;
/* socket() on both sockets */
rmem (262144);
fh->rmem (262144);
wmem (262144);
fh->wmem (262144);
- set_addr_family (af);
- fh->set_addr_family (af);
+ set_addr_family (AF_UNIX);
+ fh->set_addr_family (AF_UNIX);
set_socket_type (type);
fh->set_socket_type (type);
+ set_cred ();
+ fh->set_cred ();
set_unique_id ();
set_ino (get_unique_id ());
/* bind/listen 1st socket */
gen_pipe_name ();
pipe = create_pipe (true);
if (!pipe)
- return -1;
+ goto create_pipe_failed;
set_io_handle (pipe);
- set_sun_path (&sun);
- fh->set_peer_sun_path (&sun);
- if (create_shmem () < 0)
- {
- NtClose (pipe);
- return -1;
- }
- binding_state (bound);
+ sun_path (&sun);
+ fh->peer_sun_path (&sun);
connect_state (listener);
- /* connect 2nd socket */
- if (type != SOCK_DGRAM
- && fh->open_pipe (pc.get_nt_native_path (), false) < 0)
- {
- NtClose (shmem_handle);
- NtClose (pipe);
- return -1;
- }
- if (fh->create_shmem () < 0)
- {
- NtClose (fh->get_handle ());
- NtClose (shmem_handle);
- NtClose (pipe);
- }
+ /* connect 2nd socket, even for DGRAM. There's no difference as far
+ as socketpairs are concerned. */
+ if (fh->open_pipe (pc.get_nt_native_path (), false) < 0)
+ goto fh_open_pipe_failed;
fh->connect_state (connected);
if (flags & SOCK_NONBLOCK)
{
@@ -1333,6 +1430,16 @@ fhandler_socket_unix::socketpair (int af, int type, int protocol, int flags,
fh->set_close_on_exec (true);
}
return 0;
+
+fh_open_pipe_failed:
+ NtClose (pipe);
+create_pipe_failed:
+ NtUnmapViewOfSection (GetCurrentProcess (), fh->shmem);
+ NtClose (fh->shmem_handle);
+fh_shmem_failed:
+ NtUnmapViewOfSection (GetCurrentProcess (), shmem);
+ NtClose (shmem_handle);
+ return -1;
}
/* Bind creates the backing file, generates the pipe name and sets
@@ -1385,10 +1492,14 @@ fhandler_socket_unix::bind (const struct sockaddr *name, int namelen)
binding_state (unbound);
return -1;
}
- set_sun_path (&sun);
- /* If we're already connected, send name to peer. */
+ state_lock ();
+ sun_path (&sun);
+ /* If we're already connected, send socket info to peer. In this case
+ send_sock_info calls state_unlock */
if (connect_state () == connected)
- send_my_name ();
+ send_sock_info (true);
+ else
+ state_unlock ();
binding_state (bound);
return 0;
}
@@ -1426,6 +1537,9 @@ fhandler_socket_unix::listen (int backlog)
return -1;
}
set_io_handle (pipe);
+ state_lock ();
+ set_cred ();
+ state_unlock ();
connect_state (listener);
conn_unlock ();
return 0;
@@ -1473,7 +1587,7 @@ fhandler_socket_unix::accept4 (struct sockaddr *peer, int *len, int flags)
if (sock->create_shmem () < 0)
goto create_shmem_failed;
- sock->set_addr_family (get_addr_family ());
+ sock->set_addr_family (AF_UNIX);
sock->set_socket_type (get_socket_type ());
if (flags & SOCK_NONBLOCK)
sock->set_nonblocking (true);
@@ -1486,15 +1600,20 @@ fhandler_socket_unix::accept4 (struct sockaddr *peer, int *len, int flags)
sock->binding_state (binding_state ());
sock->set_io_handle (accepted);
- sock->set_sun_path (get_sun_path ());
- error = sock->recv_peer_name ();
+ sock->sun_path (sun_path ());
+ sock->sock_cred (sock_cred ());
+ /* Send this socket info to connecting socket. */
+ sock->send_sock_info (false);
+ /* Fetch the packet sent by send_sock_info called by
+ connecting peer. */
+ error = sock->recv_peer_info ();
if (error == 0)
{
__try
{
if (peer)
{
- sun_name_t *sun = sock->get_peer_sun_path ();
+ sun_name_t *sun = sock->peer_sun_path ();
if (sun)
{
memcpy (peer, &sun->un,
@@ -1591,14 +1710,14 @@ fhandler_socket_unix::connect (const struct sockaddr *name, int namelen)
connect_state (unconnected);
return -1;
}
- set_peer_sun_path (&sun);
+ peer_sun_path (&sun);
if (get_socket_type () != SOCK_DGRAM)
{
if (connect_pipe (&pipe_name) < 0)
{
if (get_errno () != EINPROGRESS)
{
- set_peer_sun_path (NULL);
+ peer_sun_path (NULL);
connect_state (connect_failed);
}
return -1;
@@ -1611,36 +1730,60 @@ fhandler_socket_unix::connect (const struct sockaddr *name, int namelen)
int
fhandler_socket_unix::getsockname (struct sockaddr *name, int *namelen)
{
- sun_name_t sun;
+ sun_name_t *sun = sun_path ();
- if (get_sun_path ())
- memcpy (&sun, &get_sun_path ()->un, get_sun_path ()->un_len);
- else
- sun.un_len = 0;
- memcpy (name, &sun, MIN (*namelen, sun.un_len));
- *namelen = sun.un_len;
+ memcpy (name, sun, MIN (*namelen, sun->un_len));
+ *namelen = sun->un_len;
return 0;
}
int
fhandler_socket_unix::getpeername (struct sockaddr *name, int *namelen)
{
- sun_name_t sun;
-
- if (get_peer_sun_path ())
- memcpy (&sun, &get_peer_sun_path ()->un, get_peer_sun_path ()->un_len);
- else
- sun.un_len = 0;
- memcpy (name, &sun, MIN (*namelen, sun.un_len));
- *namelen = sun.un_len;
+ sun_name_t *sun = peer_sun_path ();
+ memcpy (name, sun, MIN (*namelen, sun->un_len));
+ *namelen = sun->un_len;
return 0;
}
int
fhandler_socket_unix::shutdown (int how)
{
- set_errno (EAFNOSUPPORT);
- return -1;
+ NTSTATUS status = STATUS_SUCCESS;
+ IO_STATUS_BLOCK io;
+
+ if (how < SHUT_RD || how > SHUT_RDWR)
+ {
+ set_errno (EINVAL);
+ return -1;
+ }
+ /* Convert SHUT_RD/SHUT_WR/SHUT_RDWR to _SHUT_RECV/_SHUT_SEND bits. */
+ ++how;
+ state_lock ();
+ int old_shutdown_mask = saw_shutdown ();
+ int new_shutdown_mask = old_shutdown_mask | how;
+ if (new_shutdown_mask != old_shutdown_mask)
+ saw_shutdown (new_shutdown_mask);
+ state_unlock ();
+ if (new_shutdown_mask != old_shutdown_mask)
+ {
+ /* Send shutdown info to peer. Note that it's not necessarily fatal
+ if the info isn't sent here. The info will be reproduced by any
+ followup package sent to the peer. */
+ af_unix_pkt_hdr_t packet (true, (shut_state) new_shutdown_mask, 0, 0, 0);
+ io_lock ();
+ set_pipe_non_blocking (true);
+ status = NtWriteFile (get_handle (), NULL, NULL, NULL, &io, &packet,
+ packet.pckt_len, NULL, NULL);
+ set_pipe_non_blocking (is_nonblocking ());
+ io_unlock ();
+ }
+ if (!NT_SUCCESS (status))
+ {
+ debug_printf ("Couldn't send shutdown info: NtWriteFile: %y", status);
+ return -1;
+ }
+ return 0;
}
int
@@ -1652,24 +1795,25 @@ fhandler_socket_unix::close ()
{
if (evt)
SetEvent (evt);
- WaitForSingleObject (thr, INFINITE);
- CloseHandle (thr);
+ NtWaitForSingleObject (thr, FALSE, NULL);
+ NtClose (thr);
}
if (evt)
NtClose (evt);
PVOID param = InterlockedExchangePointer (&cwt_param, NULL);
if (param)
cfree (param);
+ HANDLE hdl = InterlockedExchangePointer (&get_handle (), NULL);
+ if (hdl)
+ NtClose (hdl);
+ if (backing_file_handle && backing_file_handle != INVALID_HANDLE_VALUE)
+ NtClose (backing_file_handle);
HANDLE shm = InterlockedExchangePointer (&shmem_handle, NULL);
if (shm)
NtClose (shm);
param = InterlockedExchangePointer ((PVOID *) &shmem, NULL);
if (param)
NtUnmapViewOfSection (GetCurrentProcess (), param);
- if (get_handle ())
- NtClose (get_handle ());
- if (backing_file_handle && backing_file_handle != INVALID_HANDLE_VALUE)
- NtClose (backing_file_handle);
return 0;
}
@@ -1683,7 +1827,6 @@ fhandler_socket_unix::getpeereid (pid_t *pid, uid_t *euid, gid_t *egid)
set_errno (EINVAL);
return -1;
}
- conn_lock ();
if (connect_state () != connected)
set_errno (ENOTCONN);
else
@@ -1691,19 +1834,19 @@ fhandler_socket_unix::getpeereid (pid_t *pid, uid_t *euid, gid_t *egid)
__try
{
state_lock ();
+ struct ucred *pcred = peer_cred ();
if (pid)
- *pid = peer_cred.pid;
+ *pid = pcred->pid;
if (euid)
- *euid = peer_cred.uid;
+ *euid = pcred->uid;
if (egid)
- *egid = peer_cred.gid;
+ *egid = pcred->gid;
state_unlock ();
ret = 0;
}
__except (EFAULT) {}
__endtry
}
- conn_unlock ();
return ret;
}
@@ -1844,17 +1987,60 @@ fhandler_socket_unix::setsockopt (int level, int optname, const void *optval,
switch (optname)
{
case SO_PASSCRED:
+ if (optlen < (socklen_t) sizeof (int))
+ {
+ set_errno (EINVAL);
+ return -1;
+ }
+
+ bool val;
+ val = !!*(int *) optval;
+ /* Using bind_lock here to make sure the autobind below is
+ covered. This is the only place to set so_passcred anyway. */
+ bind_lock ();
+ if (val && binding_state () == unbound)
+ {
+ sun_name_t sun;
+
+ binding_state (bind_pending);
+ backing_file_handle = autobind (&sun);
+ if (!backing_file_handle)
+ {
+ binding_state (unbound);
+ bind_unlock ();
+ return -1;
+ }
+ sun_path (&sun);
+ binding_state (bound);
+ }
+ so_passcred (val);
+ bind_unlock ();
break;
case SO_REUSEADDR:
- reuseaddr (*(int *) optval);
+ if (optlen < (socklen_t) sizeof (int))
+ {
+ set_errno (EINVAL);
+ return -1;
+ }
+ reuseaddr (!!*(int *) optval);
break;
case SO_RCVBUF:
+ if (optlen < (socklen_t) sizeof (int))
+ {
+ set_errno (EINVAL);
+ return -1;
+ }
rmem (*(int *) optval);
break;
case SO_SNDBUF:
+ if (optlen < (socklen_t) sizeof (int))
+ {
+ set_errno (EINVAL);
+ return -1;
+ }
wmem (*(int *) optval);
break;
@@ -1900,6 +2086,12 @@ fhandler_socket_unix::getsockopt (int level, int optname, const void *optval,
{
case SO_ERROR:
{
+ if (*optlen < (socklen_t) sizeof (int))
+ {
+ set_errno (EINVAL);
+ return -1;
+ }
+
int *e = (int *) optval;
LONG err;
@@ -1909,7 +2101,17 @@ fhandler_socket_unix::getsockopt (int level, int optname, const void *optval,
}
case SO_PASSCRED:
- break;
+ {
+ if (*optlen < (socklen_t) sizeof (int))
+ {
+ set_errno (EINVAL);
+ return -1;
+ }
+
+ int *e = (int *) optval;
+ *e = so_passcred ();
+ break;
+ }
case SO_PEERCRED:
{
@@ -1977,6 +2179,11 @@ fhandler_socket_unix::getsockopt (int level, int optname, const void *optval,
case SO_TYPE:
{
+ if (*optlen < (socklen_t) sizeof (int))
+ {
+ set_errno (EINVAL);
+ return -1;
+ }
unsigned int *type = (unsigned int *) optval;
*type = get_socket_type ();
*optlen = (socklen_t) sizeof *type;
@@ -1987,6 +2194,11 @@ fhandler_socket_unix::getsockopt (int level, int optname, const void *optval,
case SO_LINGER:
{
+ if (*optlen < (socklen_t) sizeof (struct linger))
+ {
+ set_errno (EINVAL);
+ return -1;
+ }
struct linger *linger = (struct linger *) optval;
memset (linger, 0, sizeof *linger);
*optlen = (socklen_t) sizeof *linger;
@@ -1995,6 +2207,11 @@ fhandler_socket_unix::getsockopt (int level, int optname, const void *optval,
default:
{
+ if (*optlen < (socklen_t) sizeof (int))
+ {
+ set_errno (EINVAL);
+ return -1;
+ }
unsigned int *val = (unsigned int *) optval;
*val = 0;
*optlen = (socklen_t) sizeof *val;
@@ -2083,9 +2300,9 @@ fhandler_socket_unix::fstat (struct stat *buf)
{
int ret = 0;
- if (!get_sun_path ()
- || get_sun_path ()->un_len <= (socklen_t) sizeof (sa_family_t)
- || get_sun_path ()->un.sun_path[0] == '\0')
+ if (sun_path ()
+ && (sun_path ()->un_len <= (socklen_t) sizeof (sa_family_t)
+ || sun_path ()->un.sun_path[0] == '\0'))
return fhandler_socket::fstat (buf);
ret = fhandler_base::fstat_fs (buf);
if (!ret)
@@ -2099,9 +2316,9 @@ fhandler_socket_unix::fstat (struct stat *buf)
int __reg2
fhandler_socket_unix::fstatvfs (struct statvfs *sfs)
{
- if (!get_sun_path ()
- || get_sun_path ()->un_len <= (socklen_t) sizeof (sa_family_t)
- || get_sun_path ()->un.sun_path[0] == '\0')
+ if (sun_path ()
+ && (sun_path ()->un_len <= (socklen_t) sizeof (sa_family_t)
+ || sun_path ()->un.sun_path[0] == '\0'))
return fhandler_socket::fstatvfs (sfs);
fhandler_disk_file fh (pc);
fh.get_device () = FH_FS;
@@ -2111,9 +2328,9 @@ fhandler_socket_unix::fstatvfs (struct statvfs *sfs)
int
fhandler_socket_unix::fchmod (mode_t newmode)
{
- if (!get_sun_path ()
- || get_sun_path ()->un_len <= (socklen_t) sizeof (sa_family_t)
- || get_sun_path ()->un.sun_path[0] == '\0')
+ if (sun_path ()
+ && (sun_path ()->un_len <= (socklen_t) sizeof (sa_family_t)
+ || sun_path ()->un.sun_path[0] == '\0'))
return fhandler_socket::fchmod (newmode);
fhandler_disk_file fh (pc);
fh.get_device () = FH_FS;
@@ -2130,9 +2347,9 @@ fhandler_socket_unix::fchmod (mode_t newmode)
int
fhandler_socket_unix::fchown (uid_t uid, gid_t gid)
{
- if (!get_sun_path ()
- || get_sun_path ()->un_len <= (socklen_t) sizeof (sa_family_t)
- || get_sun_path ()->un.sun_path[0] == '\0')
+ if (sun_path ()
+ && (sun_path ()->un_len <= (socklen_t) sizeof (sa_family_t)
+ || sun_path ()->un.sun_path[0] == '\0'))
return fhandler_socket::fchown (uid, gid);
fhandler_disk_file fh (pc);
return fh.fchown (uid, gid);
@@ -2141,9 +2358,9 @@ fhandler_socket_unix::fchown (uid_t uid, gid_t gid)
int
fhandler_socket_unix::facl (int cmd, int nentries, aclent_t *aclbufp)
{
- if (!get_sun_path ()
- || get_sun_path ()->un_len <= (socklen_t) sizeof (sa_family_t)
- || get_sun_path ()->un.sun_path[0] == '\0')
+ if (sun_path ()
+ && (sun_path ()->un_len <= (socklen_t) sizeof (sa_family_t)
+ || sun_path ()->un.sun_path[0] == '\0'))
return fhandler_socket::facl (cmd, nentries, aclbufp);
fhandler_disk_file fh (pc);
return fh.facl (cmd, nentries, aclbufp);
@@ -2152,9 +2369,9 @@ fhandler_socket_unix::facl (int cmd, int nentries, aclent_t *aclbufp)
int
fhandler_socket_unix::link (const char *newpath)
{
- if (!get_sun_path ()
- || get_sun_path ()->un_len <= (socklen_t) sizeof (sa_family_t)
- || get_sun_path ()->un.sun_path[0] == '\0')
+ if (sun_path ()
+ && (sun_path ()->un_len <= (socklen_t) sizeof (sa_family_t)
+ || sun_path ()->un.sun_path[0] == '\0'))
return fhandler_socket::link (newpath);
fhandler_disk_file fh (pc);
return fh.link (newpath);