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>
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
Reference in New Issue
Block a user