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:
parent
044ab77dcc
commit
ea1e5318d5
|
@ -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__)
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
|
Loading…
Reference in New Issue