[newlib-cygwin] Fix the handling of out-of-band (OOB) data in a socket.

Corinna Vinschen corinna@sourceware.org
Fri Jun 22 10:15:00 GMT 2018


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

commit 9c84bfd47922aad4881f80243320422b621c95dc
Author: Takashi Yano <takashi.yano@nifty.ne.jp>
Date:   Thu Jun 21 23:11:49 2018 +0900

    Fix the handling of out-of-band (OOB) data in a socket.
    
    * fhandler.h (class fhandler_socket_inet): Add variable bool oobinline.
    * fhandler_socket_inet.cc (fhandler_socket_inet::fhandler_socket_inet):
      Initialize variable oobinline.
    (fhandler_socket_inet::recv_internal): Make the handling of OOB data
      as consistent with POSIX as possible. Add simulation of inline mode
      for OOB data as a workaround for broken winsock behavior.
    (fhandler_socket_inet::setsockopt): Ditto.
    (fhandler_socket_inet::getsockopt): Ditto.
    (fhandler_socket_wsock::ioctl): Fix return value of SIOCATMARK command.
      The return value of SIOCATMARK of winsock is almost opposite to
      expectation.
    * fhandler_socket_local.cc (fhandler_socket_local::recv_internal):
      Remove the handling of OOB data from AF_LOCAL domain socket. Operation
      related to OOB data will result in an error like Linux does.
    (fhandler_socket_local::sendto): Ditto.
    (fhandler_socket_local::sendmsg): Ditto.
    
    This fixes the issue reported in following post.
    https://cygwin.com/ml/cygwin/2018-06/msg00143.html

Diff:
---
 winsup/cygwin/fhandler.h               |  2 +
 winsup/cygwin/fhandler_socket_inet.cc  | 94 ++++++++++++++++++++++++++++++++--
 winsup/cygwin/fhandler_socket_local.cc | 33 ++++++++++--
 3 files changed, 120 insertions(+), 9 deletions(-)

diff --git a/winsup/cygwin/fhandler.h b/winsup/cygwin/fhandler.h
index 2ec460a..39a674c 100644
--- a/winsup/cygwin/fhandler.h
+++ b/winsup/cygwin/fhandler.h
@@ -684,6 +684,8 @@ class fhandler_socket_wsock: public fhandler_socket
 
 class fhandler_socket_inet: public fhandler_socket_wsock
 {
+ private:
+  bool oobinline; /* True if option SO_OOBINLINE is set */
  protected:
   int af_local_connect () { return 0; }
 
diff --git a/winsup/cygwin/fhandler_socket_inet.cc b/winsup/cygwin/fhandler_socket_inet.cc
index db301f3..555c3a9 100644
--- a/winsup/cygwin/fhandler_socket_inet.cc
+++ b/winsup/cygwin/fhandler_socket_inet.cc
@@ -685,7 +685,8 @@ fhandler_socket_wsock::set_socket_handle (SOCKET sock, int af, int type,
 }
 
 fhandler_socket_inet::fhandler_socket_inet () :
-  fhandler_socket_wsock ()
+  fhandler_socket_wsock (),
+  oobinline (false)
 {
 }
 
@@ -1044,10 +1045,11 @@ fhandler_socket_inet::recv_internal (LPWSAMSG wsamsg, bool use_recvmsg)
 {
   ssize_t res = 0;
   DWORD ret = 0, wret;
-  int evt_mask = FD_READ | ((wsamsg->dwFlags & MSG_OOB) ? FD_OOB : 0);
+  int evt_mask = (wsamsg->dwFlags & MSG_OOB) ? FD_OOB : FD_READ;
   LPWSABUF &wsabuf = wsamsg->lpBuffers;
   ULONG &wsacnt = wsamsg->dwBufferCount;
   static NO_COPY LPFN_WSARECVMSG WSARecvMsg;
+  bool read_oob = false;
 
   /* CV 2014-10-26: Do not check for the connect_state at this point.  In
      certain scenarios there's no way to check the connect state reliably.
@@ -1086,12 +1088,64 @@ fhandler_socket_inet::recv_internal (LPWSAMSG wsamsg, bool use_recvmsg)
 	waitall = false;
     }
 
+  /* recv() returns EINVAL if MSG_OOB flag is set in inline mode. */
+  if (oobinline && (wsamsg->dwFlags & MSG_OOB))
+    {
+      set_errno (EINVAL);
+      return SOCKET_ERROR;
+    }
+
+  /* Check whether OOB data is ready or not */
+  if (get_socket_type () == SOCK_STREAM)
+    if ((wsamsg->dwFlags & MSG_OOB) || oobinline)
+      {
+	u_long atmark = 0;
+#ifdef __x86_64__
+	/* SIOCATMARK = _IOR('s',7,u_long) */
+	int err = ::ioctlsocket (get_socket (), _IOR('s',7,u_long), &atmark);
+#else
+	int err = ::ioctlsocket (get_socket (), SIOCATMARK, &atmark);
+#endif
+	if (err)
+	  {
+	    set_winsock_errno ();
+	    return SOCKET_ERROR;
+	  }
+	/* If there is no OOB data, recv() with MSG_OOB returns EINVAL.
+	   Note: The return value of SIOCATMARK in non-inline mode of
+	   winsock is FALSE if OOB data exists, TRUE otherwise. */
+	if (atmark && (wsamsg->dwFlags & MSG_OOB))
+	  {
+	    /* No OOB data */
+	    set_errno (EINVAL);
+	    return SOCKET_ERROR;
+	  }
+	/* Inline mode for out-of-band (OOB) data of winsock is
+	   completely broken. That is, SIOCATMARK always returns
+	   TRUE in inline mode. Due to this problem, application
+	   cannot determine OOB data at all. Therefore the behavior
+	   of a socket with SO_OOBINLINE set is simulated using
+	   a socket with SO_OOBINLINE not set. In this fake inline
+	   mode, the order of the OOB and non-OOB data is not
+	   preserved. OOB data is read before non-OOB data sent
+	   prior to the OOB data.  However, this most likely is
+	   not a problem in most cases. */
+	/* If there is OOB data, read OOB data using MSG_OOB in
+	   fake inline mode. */
+	if (!atmark && oobinline)
+	  {
+	    read_oob = true;
+	    evt_mask = FD_OOB;
+	  }
+      }
+
   /* Note: Don't call WSARecvFrom(MSG_PEEK) without actually having data
      waiting in the buffers, otherwise the event handling gets messed up
      for some reason. */
   while (!(res = wait_for_events (evt_mask | FD_CLOSE, wait_flags))
 	 || saw_shutdown_read ())
     {
+      DWORD dwFlags = wsamsg->dwFlags | (read_oob ? MSG_OOB : 0);
       if (use_recvmsg)
 	res = WSARecvMsg (get_socket (), wsamsg, &wret, NULL, NULL);
       /* This is working around a really weird problem in WinSock.
@@ -1113,11 +1167,11 @@ fhandler_socket_inet::recv_internal (LPWSAMSG wsamsg, bool use_recvmsg)
 	 namelen is a valid pointer while name is NULL.  Both parameters are
 	 ignored for TCP sockets, so this only occurs when using UDP socket. */
       else if (!wsamsg->name || get_socket_type () == SOCK_STREAM)
-	res = WSARecv (get_socket (), wsabuf, wsacnt, &wret, &wsamsg->dwFlags,
+	res = WSARecv (get_socket (), wsabuf, wsacnt, &wret, &dwFlags,
 		       NULL, NULL);
       else
 	res = WSARecvFrom (get_socket (), wsabuf, wsacnt, &wret,
-			   &wsamsg->dwFlags, wsamsg->name, &wsamsg->namelen,
+			   &dwFlags, wsamsg->name, &wsamsg->namelen,
 			   NULL, NULL);
       if (!res)
 	{
@@ -1561,6 +1615,23 @@ fhandler_socket_inet::setsockopt (int level, int optname, const void *optval,
 	    set_errno (EDOM);
 	  return ret;
 
+	case SO_OOBINLINE:
+	  /* Inline mode for out-of-band (OOB) data of winsock is
+	     completely broken. That is, SIOCATMARK always returns
+	     TRUE in inline mode. Due to this problem, application
+	     cannot determine OOB data at all. Therefore the behavior
+	     of a socket with SO_OOBINLINE set is simulated using
+	     a socket with SO_OOBINLINE not set. In this fake inline
+	     mode, the order of the OOB and non-OOB data is not
+	     preserved. OOB data is read before non-OOB data sent
+	     prior to the OOB data.  However, this most likely is
+	     not a problem in most cases. */
+	  /* Here, instead of actually setting inline mode, simply
+	     set the variable oobinline. */
+	  oobinline = *(int *) optval ? true : false;
+	  ignore = true;
+	  break;
+
 	default:
 	  break;
 	}
@@ -1713,6 +1784,10 @@ fhandler_socket_inet::getsockopt (int level, int optname, const void *optval,
 	    return 0;
 	  }
 
+	case SO_OOBINLINE:
+	  *(int *) optval = oobinline ? 1 : 0;
+	  return 0;
+
 	default:
 	  break;
 	}
@@ -1850,6 +1925,17 @@ fhandler_socket_wsock::ioctl (unsigned int cmd, void *p)
 	}
       else
 	res = ::ioctlsocket (get_socket (), cmd, (u_long *) p);
+      /* In winsock, the return value of SIOCATMARK is FALSE if
+	 OOB data exists, TRUE otherwise. This is almost opposite
+	 to expectation. */
+#ifdef __x86_64__
+      /* SIOCATMARK = _IOR('s',7,u_long) */
+      if (cmd == _IOR('s',7,u_long) && !res)
+	*(u_long *)p = !*(u_long *)p;
+#else
+      if (cmd == SIOCATMARK && !res)
+	*(u_long *)p = !*(u_long *)p;
+#endif
       break;
     default:
       res = fhandler_socket::ioctl (cmd, p);
diff --git a/winsup/cygwin/fhandler_socket_local.cc b/winsup/cygwin/fhandler_socket_local.cc
index 90d8d5a..2e01f30 100644
--- a/winsup/cygwin/fhandler_socket_local.cc
+++ b/winsup/cygwin/fhandler_socket_local.cc
@@ -1065,7 +1065,7 @@ fhandler_socket_local::recv_internal (LPWSAMSG wsamsg, bool use_recvmsg)
 {
   ssize_t res = 0;
   DWORD ret = 0, wret;
-  int evt_mask = FD_READ | ((wsamsg->dwFlags & MSG_OOB) ? FD_OOB : 0);
+  int evt_mask = FD_READ;
   LPWSABUF &wsabuf = wsamsg->lpBuffers;
   ULONG &wsacnt = wsamsg->dwBufferCount;
   static NO_COPY LPFN_WSARECVMSG WSARecvMsg;
@@ -1080,7 +1080,15 @@ fhandler_socket_local::recv_internal (LPWSAMSG wsamsg, bool use_recvmsg)
 
   DWORD wait_flags = wsamsg->dwFlags;
   bool waitall = !!(wait_flags & MSG_WAITALL);
-  wsamsg->dwFlags &= (MSG_OOB | MSG_PEEK | MSG_DONTROUTE);
+
+  /* Out-of-band data not supported by AF_LOCAL */
+  if (wsamsg->dwFlags & MSG_OOB)
+    {
+      set_errno (EOPNOTSUPP);
+      return SOCKET_ERROR;
+    }
+
+  wsamsg->dwFlags &= (MSG_PEEK | MSG_DONTROUTE);
   if (use_recvmsg)
     {
       if (!WSARecvMsg
@@ -1104,7 +1112,7 @@ fhandler_socket_local::recv_internal (LPWSAMSG wsamsg, bool use_recvmsg)
 	  set_winsock_errno ();
 	  return SOCKET_ERROR;
 	}
-      if (is_nonblocking () || (wsamsg->dwFlags & (MSG_OOB | MSG_PEEK)))
+      if (is_nonblocking () || (wsamsg->dwFlags & MSG_PEEK))
 	waitall = false;
     }
 
@@ -1114,6 +1122,7 @@ fhandler_socket_local::recv_internal (LPWSAMSG wsamsg, bool use_recvmsg)
   while (!(res = wait_for_events (evt_mask | FD_CLOSE, wait_flags))
 	 || saw_shutdown_read ())
     {
+      DWORD dwFlags = wsamsg->dwFlags;
       if (use_recvmsg)
 	res = WSARecvMsg (get_socket (), wsamsg, &wret, NULL, NULL);
       /* This is working around a really weird problem in WinSock.
@@ -1135,11 +1144,11 @@ fhandler_socket_local::recv_internal (LPWSAMSG wsamsg, bool use_recvmsg)
 	 namelen is a valid pointer while name is NULL.  Both parameters are
 	 ignored for TCP sockets, so this only occurs when using UDP socket. */
       else if (!wsamsg->name || get_socket_type () == SOCK_STREAM)
-	res = WSARecv (get_socket (), wsabuf, wsacnt, &wret, &wsamsg->dwFlags,
+	res = WSARecv (get_socket (), wsabuf, wsacnt, &wret, &dwFlags,
 		       NULL, NULL);
       else
 	res = WSARecvFrom (get_socket (), wsabuf, wsacnt, &wret,
-			   &wsamsg->dwFlags, wsamsg->name, &wsamsg->namelen,
+			   &dwFlags, wsamsg->name, &wsamsg->namelen,
 			   NULL, NULL);
       if (!res)
 	{
@@ -1236,6 +1245,13 @@ fhandler_socket_local::sendto (const void *in_ptr, size_t len, int flags,
   char *ptr = (char *) in_ptr;
   struct sockaddr_storage sst;
 
+  /* Out-of-band data not supported by AF_LOCAL */
+  if (flags & MSG_OOB)
+    {
+      set_errno (EOPNOTSUPP);
+      return SOCKET_ERROR;
+    }
+
   if (to && get_inet_addr_local (to, tolen, &sst, &tolen) == SOCKET_ERROR)
     return SOCKET_ERROR;
 
@@ -1274,6 +1290,13 @@ fhandler_socket_local::sendmsg (const struct msghdr *msg, int flags)
   struct sockaddr_storage sst;
   int len = 0;
 
+  /* Out-of-band data not supported by AF_LOCAL */
+  if (flags & MSG_OOB)
+    {
+      set_errno (EOPNOTSUPP);
+      return SOCKET_ERROR;
+    }
+
   if (msg->msg_name
       && get_inet_addr_local ((struct sockaddr *) msg->msg_name,
 			      msg->msg_namelen, &sst, &len) == SOCKET_ERROR)



More information about the Cygwin-cvs mailing list