This is the mail archive of the cygwin-cvs@cygwin.com mailing list for the Cygwin project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[newlib-cygwin] Cygwin: AF_UNIX: Redesign various aspects


https://sourceware.org/git/gitweb.cgi?p=newlib-cygwin.git;h=4fe086c84f05a4fc4740faec3f613a2ea5e4b2c7

commit 4fe086c84f05a4fc4740faec3f613a2ea5e4b2c7
Author: Corinna Vinschen <corinna@vinschen.de>
Date:   Sun Mar 18 20:46:43 2018 +0100

    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>

Diff:
---
 winsup/cygwin/fhandler.h              |  99 +++++--
 winsup/cygwin/fhandler_socket_unix.cc | 533 ++++++++++++++++++++++++----------
 2 files changed, 442 insertions(+), 190 deletions(-)

diff --git a/winsup/cygwin/fhandler.h b/winsup/cygwin/fhandler.h
index a4bf1d3..be55379 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 89cf5b1..295e07b 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);


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]