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

commit ea1e5318d5479ca841beab601a4c3dd7cce627ad
Author: Corinna Vinschen <cori...@vinschen.de>
Date:   Tue Feb 20 18:01:40 2018 +0100

    Cygwin: set/getsockopt: Move implementation into fhandler_socket class
    
    This requires to export find_winsock_errno from net.cc.
    
    Signed-off-by: Corinna Vinschen <cori...@vinschen.de>

Diff:
---
 winsup/cygwin/cygerrno.h         |   1 +
 winsup/cygwin/fhandler.h         |   4 +
 winsup/cygwin/fhandler_socket.cc | 330 +++++++++++++++++++++++++++++++++++++
 winsup/cygwin/net.cc             | 347 ++-------------------------------------
 4 files changed, 348 insertions(+), 334 deletions(-)

diff --git a/winsup/cygwin/cygerrno.h b/winsup/cygwin/cygerrno.h
index afcae4c..009ae63 100644
--- a/winsup/cygwin/cygerrno.h
+++ b/winsup/cygwin/cygerrno.h
@@ -41,6 +41,7 @@ __set_errno (const char *fn, int ln, int val)
 }
 #define set_errno(val) __set_errno (__PRETTY_FUNCTION__, __LINE__, (val))
 
+int find_winsock_errno (DWORD why);
 void __reg2 __set_winsock_errno (const char *fn, int ln);
 #define set_winsock_errno() __set_winsock_errno (__FUNCTION__, __LINE__)
 
diff --git a/winsup/cygwin/fhandler.h b/winsup/cygwin/fhandler.h
index ab6fb6e..ce9d924 100644
--- a/winsup/cygwin/fhandler.h
+++ b/winsup/cygwin/fhandler.h
@@ -590,6 +590,10 @@ class fhandler_socket: public fhandler_base
   int getpeereid (pid_t *pid, uid_t *euid, gid_t *egid);
   int socketpair (int af, int type, int protocol, int flags,
                  fhandler_socket *fh_out);
+  int setsockopt (int level, int optname, const void *optval,
+                 __socklen_t optlen);
+  int getsockopt (int level, int optname, const void *optval,
+                 __socklen_t *optlen);
 
   int open (int flags, mode_t mode = 0);
   void __reg3 read (void *ptr, size_t& len);
diff --git a/winsup/cygwin/fhandler_socket.cc b/winsup/cygwin/fhandler_socket.cc
index a7c702e..26d4716 100644
--- a/winsup/cygwin/fhandler_socket.cc
+++ b/winsup/cygwin/fhandler_socket.cc
@@ -2685,3 +2685,333 @@ fhandler_socket::getpeereid (pid_t *pid, uid_t *euid, 
gid_t *egid)
   __endtry
   return -1;
 }
+
+static int
+convert_ws1_ip_optname (int optname)
+{
+  static int ws2_optname[] =
+  {
+    0,
+    IP_OPTIONS,
+    IP_MULTICAST_IF,
+    IP_MULTICAST_TTL,
+    IP_MULTICAST_LOOP,
+    IP_ADD_MEMBERSHIP,
+    IP_DROP_MEMBERSHIP,
+    IP_TTL,
+    IP_TOS,
+    IP_DONTFRAGMENT
+  };
+  return (optname < 1 || optname > _WS1_IP_DONTFRAGMENT)
+        ? optname
+        : ws2_optname[optname];
+}
+
+int
+fhandler_socket::setsockopt (int level, int optname, const void *optval,
+                            socklen_t optlen)
+{
+  bool ignore = false;
+  int ret = -1;
+
+  /* Preprocessing setsockopt.  Set ignore to true if setsockopt call should
+     get skipped entirely. */
+  switch (level)
+    {
+    case SOL_SOCKET:
+      switch (optname)
+       {
+       case SO_PEERCRED:
+         /* Switch off the AF_LOCAL handshake and thus SO_PEERCRED handling
+            for AF_LOCAL/SOCK_STREAM sockets.  This allows to handle special
+            situations in which connect is called before a listening socket
+            accepts connections.
+            FIXME: In the long run we should find a more generic solution
+            which doesn't require a blocking handshake in accept/connect
+            to exchange SO_PEERCRED credentials. */
+         if (optval || optlen)
+           set_errno (EINVAL);
+         else
+           ret = af_local_set_no_getpeereid ();
+         return ret;
+
+       case SO_REUSEADDR:
+         /* Per POSIX we must not be able to reuse a complete duplicate of a
+            local TCP address (same IP, same port), even if SO_REUSEADDR has
+            been set.  This behaviour is maintained in WinSock for backward
+            compatibility, while the WinSock standard behaviour of stream
+            socket binding is equivalent to the POSIX behaviour as if
+            SO_REUSEADDR has been set.  The SO_EXCLUSIVEADDRUSE option has
+            been added to allow an application to request POSIX standard
+            behaviour in the non-SO_REUSEADDR case.
+
+            To emulate POSIX socket binding behaviour, note that SO_REUSEADDR
+            has been set but don't call setsockopt.  Instead
+            fhandler_socket::bind sets SO_EXCLUSIVEADDRUSE if the application
+            did not set SO_REUSEADDR. */
+         if (optlen < (socklen_t) sizeof (int))
+           {
+             set_errno (EINVAL);
+             return ret;
+           }
+         if (get_socket_type () == SOCK_STREAM)
+           ignore = true;
+         break;
+
+       case SO_RCVTIMEO:
+       case SO_SNDTIMEO:
+         if (optlen < (socklen_t) sizeof (struct timeval))
+           {
+             set_errno (EINVAL);
+             return ret;
+           }
+         if (timeval_to_ms ((struct timeval *) optval,
+                            (optname == SO_RCVTIMEO) ? rcvtimeo ()
+                                                     : sndtimeo ()))
+           ret = 0;
+         else
+           set_errno (EDOM);
+         return ret;
+
+       default:
+         break;
+       }
+      break;
+
+    case IPPROTO_IP:
+      /* Old applications still use the old WinSock1 IPPROTO_IP values. */
+      if (CYGWIN_VERSION_CHECK_FOR_USING_WINSOCK1_VALUES)
+       optname = convert_ws1_ip_optname (optname);
+      switch (optname)
+       {
+       case IP_TOS:
+         /* Winsock doesn't support setting the IP_TOS field with setsockopt
+            and TOS was never implemented for TCP anyway.  setsockopt returns
+            WinSock error 10022, WSAEINVAL when trying to set the IP_TOS
+            field.  We just return 0 instead. */
+         ignore = true;
+         break;
+
+       default:
+         break;
+       }
+      break;
+
+    case IPPROTO_IPV6:
+      {
+      switch (optname)
+       {
+       case IPV6_TCLASS:
+         /* Unsupported */
+         ignore = true;
+         break;
+
+       default:
+         break;
+       }
+      }
+    default:
+      break;
+    }
+
+  /* Call Winsock setsockopt (or not) */
+  if (ignore)
+    ret = 0;
+  else
+    {
+      ret = ::setsockopt (get_socket (), level, optname, (const char *) optval,
+                         optlen);
+      if (ret == SOCKET_ERROR)
+       {
+         set_winsock_errno ();
+         return ret;
+       }
+    }
+
+  if (optlen == (socklen_t) sizeof (int))
+    debug_printf ("setsockopt optval=%x", *(int *) optval);
+
+  /* Postprocessing setsockopt, setting fhandler_socket members, etc. */
+  switch (level)
+    {
+    case SOL_SOCKET:
+      switch (optname)
+       {
+       case SO_REUSEADDR:
+         saw_reuseaddr (*(int *) optval);
+         break;
+
+       case SO_RCVBUF:
+         rmem (*(int *) optval);
+         break;
+
+       case SO_SNDBUF:
+         wmem (*(int *) optval);
+         break;
+
+       default:
+         break;
+       }
+      break;
+
+    default:
+      break;
+    }
+
+  return ret;
+}
+
+int
+fhandler_socket::getsockopt (int level, int optname, const void *optval,
+                            socklen_t *optlen)
+{
+  bool ignore = false;
+  bool onebyte = false;
+  int ret = -1;
+
+  /* Preprocessing getsockopt.  Set ignore to true if getsockopt call should
+     get skipped entirely. */
+  switch (level)
+    {
+    case SOL_SOCKET:
+      switch (optname)
+       {
+       case SO_PEERCRED:
+         {
+           struct ucred *cred = (struct ucred *) optval;
+
+           if (*optlen < (socklen_t) sizeof *cred)
+             {
+               set_errno (EINVAL);
+               return ret;
+             }
+           ret = getpeereid (&cred->pid, &cred->uid, &cred->gid);
+           if (!ret)
+             *optlen = (socklen_t) sizeof *cred;
+           return ret;
+         }
+         break;
+
+       case SO_REUSEADDR:
+         {
+           unsigned int *reuseaddr = (unsigned int *) optval;
+
+           if (*optlen < (socklen_t) sizeof *reuseaddr)
+             {
+               set_errno (EINVAL);
+               return ret;
+             }
+           *reuseaddr = saw_reuseaddr();
+           *optlen = (socklen_t) sizeof *reuseaddr;
+           ignore = true;
+         }
+         break;
+
+       case SO_RCVTIMEO:
+       case SO_SNDTIMEO:
+         {
+           struct timeval *time_out = (struct timeval *) optval;
+
+           if (*optlen < (socklen_t) sizeof *time_out)
+             {
+               set_errno (EINVAL);
+               return ret;
+             }
+           DWORD ms = (optname == SO_RCVTIMEO) ? rcvtimeo () : sndtimeo ();
+           if (ms == 0 || ms == INFINITE)
+             {
+               time_out->tv_sec = 0;
+               time_out->tv_usec = 0;
+             }
+           else
+             {
+               time_out->tv_sec = ms / MSPERSEC;
+               time_out->tv_usec = ((ms % MSPERSEC) * USPERSEC) / MSPERSEC;
+             }
+           *optlen = (socklen_t) sizeof *time_out;
+           ret = 0;
+           return ret;
+         }
+
+       default:
+         break;
+       }
+      break;
+
+    case IPPROTO_IP:
+      /* Old applications still use the old WinSock1 IPPROTO_IP values. */
+      if (CYGWIN_VERSION_CHECK_FOR_USING_WINSOCK1_VALUES)
+       optname = convert_ws1_ip_optname (optname);
+      break;
+
+    default:
+      break;
+    }
+
+  /* Call Winsock getsockopt (or not) */
+  if (ignore)
+    ret = 0;
+  else
+    {
+      ret = ::getsockopt (get_socket (), level, optname, (char *) optval,
+                         (int *) optlen);
+      if (ret == SOCKET_ERROR)
+       {
+         set_winsock_errno ();
+         return ret;
+       }
+    }
+
+  /* Postprocessing getsockopt, setting fhandler_socket members, etc.  Set
+     onebyte true for options returning BOOLEAN instead of a boolean DWORD. */
+  switch (level)
+    {
+    case SOL_SOCKET:
+      switch (optname)
+       {
+       case SO_ERROR:
+         {
+           int *e = (int *) optval;
+           debug_printf ("WinSock SO_ERROR = %d", *e);
+           *e = find_winsock_errno (*e);
+         }
+         break;
+
+       case SO_KEEPALIVE:
+       case SO_DONTROUTE:
+         onebyte = true;
+         break;
+
+       default:
+         break;
+       }
+      break;
+    case IPPROTO_TCP:  
+      switch (optname)
+       {
+       case TCP_NODELAY:
+         onebyte = true;
+         break;
+
+       default:
+         break;
+       }
+    default:
+      break;
+    }
+
+  if (onebyte)
+    {
+      /* Regression in Vista and later: instead of a 4 byte BOOL value, a
+        1 byte BOOLEAN value is returned, in contrast to older systems and
+        the documentation.  Since an int type is expected by the calling
+        application, we convert the result here.  For some reason only three
+        BSD-compatible socket options seem to be affected. */
+      BOOLEAN *in = (BOOLEAN *) optval;
+      int *out = (int *) optval;
+      *out = *in;
+      *optlen = 4;
+    }
+
+  return ret;
+}
diff --git a/winsup/cygwin/net.cc b/winsup/cygwin/net.cc
index 7d73790..dc81fb7 100644
--- a/winsup/cygwin/net.cc
+++ b/winsup/cygwin/net.cc
@@ -198,7 +198,7 @@ static const errmap_t wsock_errmap[] = {
   {0, NULL, 0}
 };
 
-static int
+int
 find_winsock_errno (DWORD why)
 {
   for (int i = 0; wsock_errmap[i].s != NULL; ++i)
@@ -762,194 +762,24 @@ cygwin_recvfrom (int fd, void *buf, size_t len, int 
flags,
   return res;
 }
 
-static int
-convert_ws1_ip_optname (int optname)
-{
-  static int ws2_optname[] =
-  {
-    0,
-    IP_OPTIONS,
-    IP_MULTICAST_IF,
-    IP_MULTICAST_TTL,
-    IP_MULTICAST_LOOP,
-    IP_ADD_MEMBERSHIP,
-    IP_DROP_MEMBERSHIP,
-    IP_TTL,
-    IP_TOS,
-    IP_DONTFRAGMENT
-  };
-  return (optname < 1 || optname > _WS1_IP_DONTFRAGMENT)
-        ? optname
-        : ws2_optname[optname];
-}
-
 /* exported as setsockopt: POSIX.1-2001,  POSIX.1-2008,  SVr4,  4.4BSD */
 extern "C" int
 cygwin_setsockopt (int fd, int level, int optname, const void *optval,
                   socklen_t optlen)
 {
-  bool ignore = false;
-  int res = -1;
+  int ret = -1;
 
   __try
     {
       fhandler_socket *fh = get (fd);
-      if (!fh)
-       __leave;
-
-      /* Preprocessing setsockopt.  Set ignore to true if setsockopt call
-        should get skipped entirely. */
-      switch (level)
-       {
-       case SOL_SOCKET:
-         switch (optname)
-           {
-           case SO_PEERCRED:
-             /* Switch off the AF_LOCAL handshake and thus SO_PEERCRED
-                handling for AF_LOCAL/SOCK_STREAM sockets.  This allows to
-                handle special situations in which connect is called before
-                a listening socket accepts connections.
-                FIXME: In the long run we should find a more generic solution
-                which doesn't require a blocking handshake in accept/connect
-                to exchange SO_PEERCRED credentials. */
-             if (optval || optlen)
-               set_errno (EINVAL);
-             else
-               res = fh->af_local_set_no_getpeereid ();
-             __leave;
-
-           case SO_REUSEADDR:
-             /* Per POSIX we must not be able to reuse a complete duplicate
-                of a local TCP address (same IP, same port), even if
-                SO_REUSEADDR has been set.  This behaviour is maintained in
-                WinSock for backward compatibility, while the WinSock
-                standard behaviour of stream socket binding is equivalent to
-                the POSIX behaviour as if SO_REUSEADDR has been set.
-                The SO_EXCLUSIVEADDRUSE option has been added to allow an
-                application to request POSIX standard behaviour in the
-                non-SO_REUSEADDR case.
-
-                To emulate POSIX socket binding behaviour, note that
-                SO_REUSEADDR has been set but don't call setsockopt.
-                Instead fhandler_socket::bind sets SO_EXCLUSIVEADDRUSE if
-                the application did not set SO_REUSEADDR. */
-             if (optlen < (socklen_t) sizeof (int))
-               {
-                 set_errno (EINVAL);
-                 __leave;
-               }
-             if (fh->get_socket_type () == SOCK_STREAM)
-               ignore = true;
-             break;
-
-           case SO_RCVTIMEO:
-           case SO_SNDTIMEO:
-             if (optlen < (socklen_t) sizeof (struct timeval))
-               {
-                 set_errno (EINVAL);
-                 __leave;
-               }
-             if (timeval_to_ms ((struct timeval *) optval,
-                                (optname == SO_RCVTIMEO)
-                                ? fh->rcvtimeo () : fh->sndtimeo ()))
-               res = 0;
-             else
-               set_errno (EDOM);
-             __leave;
-
-           default:
-             break;
-           }
-         break;
-
-       case IPPROTO_IP:
-         /* Old applications still use the old WinSock1 IPPROTO_IP values. */
-         if (CYGWIN_VERSION_CHECK_FOR_USING_WINSOCK1_VALUES)
-           optname = convert_ws1_ip_optname (optname);
-         switch (optname)
-           {
-           case IP_TOS:
-             /* Winsock doesn't support setting the IP_TOS field with
-                setsockopt and TOS was never implemented for TCP anyway.
-                setsockopt returns WinSock error 10022, WSAEINVAL when
-                trying to set the IP_TOS field.  We just return 0 instead. */
-             ignore = true;
-             break;
-
-           default:
-             break;
-           }
-         break;
-
-       case IPPROTO_IPV6:
-         {
-         switch (optname)
-           {
-           case IPV6_TCLASS:
-             /* Unsupported */
-             ignore = true;
-             break;
-
-           default:
-             break;
-           }
-         }
-       default:
-         break;
-       }
-
-      /* Call setsockopt (or not) */
-      if (ignore)
-       res = 0;
-      else
-       {
-         res = setsockopt (fh->get_socket (), level, optname,
-                           (const char *) optval, optlen);
-         if (res == SOCKET_ERROR)
-           {
-             set_winsock_errno ();
-             __leave;
-           }
-       }
-
-      if (optlen == (socklen_t) sizeof (int))
-       debug_printf ("setsockopt optval=%x", *(int *) optval);
-
-      /* Postprocessing setsockopt, setting fhandler_socket members, etc. */
-      switch (level)
-       {
-       case SOL_SOCKET:
-         switch (optname)
-           {
-           case SO_REUSEADDR:
-             fh->saw_reuseaddr (*(int *) optval);
-             break;
-
-           case SO_RCVBUF:
-             fh->rmem (*(int *) optval);
-             break;
-
-           case SO_SNDBUF:
-             fh->wmem (*(int *) optval);
-             break;
-
-           default:
-             break;
-           }
-         break;
-
-       default:
-         break;
-       }
-    }
-  __except (EFAULT)
-    {
-      res = -1;
+      if (fh)
+       ret = fh->setsockopt (level, optname, optval, optlen);
     }
+  __except (EFAULT) {}
   __endtry
   syscall_printf ("%R = setsockopt(%d, %d, %y, %p, %d)",
-                 res, fd, level, optname, optval, optlen);
-  return res;
+                  ret, fd, level, optname, optval, optlen);
+  return ret;
 }
 
 /* exported as getsockopt: POSIX.1-2001,  POSIX.1-2008,  SVr4,  4.4BSD */
@@ -957,170 +787,19 @@ extern "C" int
 cygwin_getsockopt (int fd, int level, int optname, void *optval,
                   socklen_t *optlen)
 {
-  bool ignore = false;
-  bool onebyte = false;
-  int res = -1;
+  int ret = -1;
 
   __try
     {
       fhandler_socket *fh = get (fd);
-      if (!fh)
-       __leave;
-
-      /* Preprocessing getsockopt.  Set ignore to true if getsockopt call
-        should get skipped entirely. */
-      switch (level)
-       {
-       case SOL_SOCKET:
-         switch (optname)
-           {
-           case SO_PEERCRED:
-             {
-               struct ucred *cred = (struct ucred *) optval;
-
-               if (*optlen < (socklen_t) sizeof *cred)
-                 {
-                   set_errno (EINVAL);
-                   __leave;
-                 }
-               res = fh->getpeereid (&cred->pid, &cred->uid, &cred->gid);
-               if (!res)
-                 *optlen = (socklen_t) sizeof *cred;
-               __leave;
-             }
-             break;
-
-           case SO_REUSEADDR:
-             {
-               unsigned int *reuseaddr = (unsigned int *) optval;
-
-               if (*optlen < (socklen_t) sizeof *reuseaddr)
-                 {
-                   set_errno (EINVAL);
-                   __leave;
-                 }
-               *reuseaddr = fh->saw_reuseaddr();
-               *optlen = (socklen_t) sizeof *reuseaddr;
-               ignore = true;
-             }
-             break;
-
-           case SO_RCVTIMEO:
-           case SO_SNDTIMEO:
-             {
-               struct timeval *time_out = (struct timeval *) optval;
-
-               if (*optlen < (socklen_t) sizeof *time_out)
-                 {
-                   set_errno (EINVAL);
-                   __leave;
-                 }
-               DWORD ms = (optname == SO_RCVTIMEO) ? fh->rcvtimeo ()
-                                                   : fh->sndtimeo ();
-               if (ms == 0 || ms == INFINITE)
-                 {
-                   time_out->tv_sec = 0;
-                   time_out->tv_usec = 0;
-                 }
-               else
-                 {
-                   time_out->tv_sec = ms / MSPERSEC;
-                   time_out->tv_usec = ((ms % MSPERSEC) * USPERSEC) / MSPERSEC;
-                 }
-               *optlen = (socklen_t) sizeof *time_out;
-               res = 0;
-               __leave;
-             }
-
-           default:
-             break;
-           }
-         break;
-
-       case IPPROTO_IP:
-         /* Old applications still use the old WinSock1 IPPROTO_IP values. */
-         if (CYGWIN_VERSION_CHECK_FOR_USING_WINSOCK1_VALUES)
-           optname = convert_ws1_ip_optname (optname);
-         break;
-
-       default:
-         break;
-       }
-
-      /* Call getsockopt (or not) */
-      if (ignore)
-       res = 0;
-      else
-       {
-         res = getsockopt (fh->get_socket (), level, optname, (char *) optval,
-                           (int *) optlen);
-         if (res == SOCKET_ERROR)
-           {
-             set_winsock_errno ();
-             __leave;
-           }
-       }
-
-      /* Postprocessing getsockopt, setting fhandler_socket members, etc.
-         Set onebyte to true for options returning a BOOLEAN instead of a
-        boolean DWORD. */
-      switch (level)
-       {
-       case SOL_SOCKET:
-         switch (optname)
-           {
-           case SO_ERROR:
-             {
-               int *e = (int *) optval;
-               debug_printf ("WinSock SO_ERROR = %d", *e);
-               *e = find_winsock_errno (*e);
-             }
-             break;
-
-           case SO_KEEPALIVE:
-           case SO_DONTROUTE:
-             onebyte = true;
-             break;
-
-           default:
-             break;
-           }
-         break;
-       case IPPROTO_TCP:       
-         switch (optname)
-           {
-           case TCP_NODELAY:
-             onebyte = true;
-             break;
-
-           default:
-             break;
-           }
-       default:
-         break;
-       }
-
-      if (onebyte)
-       {
-         /* Regression in Vista and later: instead of a 4 byte BOOL value,
-            a 1 byte BOOLEAN value is returned, in contrast to older systems
-            and the documentation.  Since an int type is expected by the
-            calling application, we convert the result here.  For some reason
-            only three BSD-compatible socket options seem to be affected. */
-         BOOLEAN *in = (BOOLEAN *) optval;
-         int *out = (int *) optval;
-         *out = *in;
-         *optlen = 4;
-       }
-    }
-  __except (EFAULT)
-    {
-      res = -1;
+      if (fh)
+       ret = fh->getsockopt (level, optname, optval, optlen);
     }
+  __except (EFAULT) {}
   __endtry
   syscall_printf ("%R = getsockopt(%d, %d, %y, %p, %p)",
-                 res, fd, level, optname, optval, optlen);
-  return res;
+                  ret, fd, level, optname, optval, optlen);
+  return ret;
 }
 
 /* POSIX.1-2001 */

Reply via email to