Welcome to mirror list, hosted at ThFree Co, Russian Federation.

cygwin.com/git/newlib-cygwin.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/winsup
diff options
context:
space:
mode:
authorCorinna Vinschen <corinna@vinschen.de>2018-02-20 20:01:40 +0300
committerCorinna Vinschen <corinna@vinschen.de>2018-02-20 20:01:40 +0300
commitea1e5318d5479ca841beab601a4c3dd7cce627ad (patch)
treec866d310a1762c11906391867d7efd5e5d9312aa /winsup
parent044ab77dcc59ec7eea0e3880d47c22f5f62cc502 (diff)
Cygwin: set/getsockopt: Move implementation into fhandler_socket class
This requires to export find_winsock_errno from net.cc. Signed-off-by: Corinna Vinschen <corinna@vinschen.de>
Diffstat (limited to 'winsup')
-rw-r--r--winsup/cygwin/cygerrno.h1
-rw-r--r--winsup/cygwin/fhandler.h4
-rw-r--r--winsup/cygwin/fhandler_socket.cc330
-rw-r--r--winsup/cygwin/net.cc347
4 files changed, 348 insertions, 334 deletions
diff --git a/winsup/cygwin/cygerrno.h b/winsup/cygwin/cygerrno.h
index afcae4cb0..009ae635a 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 ab6fb6e91..ce9d9246e 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 a7c702e7e..26d4716b4 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 7d73790a6..dc81fb7de 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 */