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

commit 9c84bfd47922aad4881f80243320422b621c95dc
Author: Takashi Yano <[email protected]>
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)

Reply via email to