ws2_32: Rewrite setsockopt to be more readable.
This commit is contained in:
parent
a10c40df23
commit
829e6f14aa
|
@ -4,6 +4,7 @@
|
|||
*
|
||||
* Copyright (C) 1993,1994,1996,1997 John Brezak, Erik Bos, Alex Korobka.
|
||||
* Copyright (C) 2005 Marcus Meissner
|
||||
* Copyright (C) 2006 Kai Blin
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
|
@ -2870,117 +2871,100 @@ int WINAPI WS_setsockopt(SOCKET s, int level, int optname,
|
|||
TRACE("socket: %04x, level %d, name %d, ptr %p, len %d\n",
|
||||
s, level, optname, optval, optlen);
|
||||
|
||||
/* SO_OPENTYPE does not require a valid socket handle. */
|
||||
if (level == WS_SOL_SOCKET && optname == WS_SO_OPENTYPE)
|
||||
switch(level)
|
||||
{
|
||||
if (optlen < sizeof(int) || !optval)
|
||||
case WS_SOL_SOCKET:
|
||||
switch(optname)
|
||||
{
|
||||
/* Some options need some conversion before they can be sent to
|
||||
* setsockopt. The conversions are done here, then they will fall though
|
||||
* to the general case. Special options that are not passed to
|
||||
* setsockopt follow below that.*/
|
||||
|
||||
case WS_SO_DONTLINGER:
|
||||
linger.l_onoff = *((const int*)optval) ? 0: 1;
|
||||
linger.l_linger = 0;
|
||||
level = SOL_SOCKET;
|
||||
optname = SO_LINGER;
|
||||
optval = (char*)&linger;
|
||||
optlen = sizeof(struct linger);
|
||||
break;
|
||||
|
||||
case WS_SO_LINGER:
|
||||
linger.l_onoff = ((LINGER*)optval)->l_onoff;
|
||||
linger.l_linger = ((LINGER*)optval)->l_linger;
|
||||
/* FIXME: what is documented behavior if SO_LINGER optval
|
||||
is null?? */
|
||||
level = SOL_SOCKET;
|
||||
optname = SO_LINGER;
|
||||
optval = (char*)&linger;
|
||||
optlen = sizeof(struct linger);
|
||||
break;
|
||||
|
||||
case WS_SO_RCVBUF:
|
||||
if (*(const int*)optval < 2048)
|
||||
{
|
||||
WARN("SO_RCVBF for %d bytes is too small: ignored\n", *(const int*)optval );
|
||||
return 0;
|
||||
}
|
||||
/* Fall through */
|
||||
|
||||
/* The options listed here don't need any special handling. Thanks to
|
||||
* the conversion happening above, options from there will fall through
|
||||
* to this, too.*/
|
||||
case WS_SO_ACCEPTCONN:
|
||||
case WS_SO_BROADCAST:
|
||||
case WS_SO_ERROR:
|
||||
case WS_SO_KEEPALIVE:
|
||||
case WS_SO_OOBINLINE:
|
||||
case WS_SO_SNDBUF:
|
||||
case WS_SO_TYPE:
|
||||
convert_sockopt(&level, &optname);
|
||||
break;
|
||||
|
||||
/* SO_DEBUG is a privileged operation, ignore it. */
|
||||
case WS_SO_DEBUG:
|
||||
TRACE("Ignoring SO_DEBUG\n");
|
||||
return 0;
|
||||
|
||||
/* For some reason the game GrandPrixLegends does set SO_DONTROUTE on its
|
||||
* socket. According to MSDN, this option is silently ignored.*/
|
||||
case WS_SO_DONTROUTE:
|
||||
TRACE("Ignoring SO_DONTROUTE\n");
|
||||
return 0;
|
||||
|
||||
/* Stops two sockets from being bound to the same port. Always happens
|
||||
* on unix systems, so just drop it. */
|
||||
case WS_SO_EXCLUSIVEADDRUSE:
|
||||
TRACE("Ignoring SO_EXCLUSIVEADDRUSE, is always set.\n");
|
||||
return 0;
|
||||
|
||||
/* SO_OPENTYPE does not require a valid socket handle. */
|
||||
case WS_SO_OPENTYPE:
|
||||
if (!optlen || optlen < sizeof(int) || !optval)
|
||||
{
|
||||
SetLastError(WSAEFAULT);
|
||||
return SOCKET_ERROR;
|
||||
}
|
||||
get_per_thread_data()->opentype = *(const int *)optval;
|
||||
TRACE("setting global SO_OPENTYPE to 0x%x\n", *(const int *)optval );
|
||||
TRACE("setting global SO_OPENTYPE = 0x%x\n", *((int*)optval) );
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* For some reason the game GrandPrixLegends does set SO_DONTROUTE on its
|
||||
* socket. According to MSDN, this option is silently ignored.*/
|
||||
if ( level==WS_SOL_SOCKET && optname==WS_SO_DONTROUTE )
|
||||
{
|
||||
TRACE("Ignoring SO_DONTROUTE.\n");
|
||||
/* SO_REUSEADDR allows two applications to bind to the same port at at
|
||||
* same time. There is no direct way to do that in unix. While Wineserver
|
||||
* might do this, it does not seem useful for now, so just ignore it.*/
|
||||
case WS_SO_REUSEADDR:
|
||||
FIXME("Ignoring SO_REUSEADDR, does not translate\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* SO_EXCLUSIVEADDRUSE stops applications from binding to a port that is
|
||||
* already used. This never works on Unix systems, so just ignore this
|
||||
* setting*/
|
||||
if ( level == WS_SOL_SOCKET && optname == WS_SO_EXCLUSIVEADDRUSE )
|
||||
{
|
||||
TRACE("Ignoring SO_EXCLUSIVEADDRUSE\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef HAVE_IPX
|
||||
if(level == NSPROTO_IPX)
|
||||
{
|
||||
switch(optname)
|
||||
{
|
||||
case IPX_PTYPE:
|
||||
fd = get_sock_fd( s, 0, NULL );
|
||||
TRACE("trying to set IPX_PTYPE: %d (fd: %d)\n", *(const int*)optval, fd);
|
||||
|
||||
/* We try to set the ipx type on ipx socket level. */
|
||||
#ifdef SOL_IPX
|
||||
if(setsockopt(fd, SOL_IPX, IPX_TYPE, optval, optlen) == -1)
|
||||
{
|
||||
ERR("IPX: could not set ipx option type; expect weird behaviour\n");
|
||||
return SOCKET_ERROR;
|
||||
}
|
||||
#else
|
||||
{
|
||||
struct ipx val;
|
||||
/* Should we retrieve val using a getsockopt call and then
|
||||
* set the modified one? */
|
||||
val.ipx_pt = *optval;
|
||||
setsockopt(fd, 0, SO_DEFAULT_HEADERS, &val, sizeof(struct ipx));
|
||||
}
|
||||
#ifdef SO_RCVTIMEO
|
||||
case WS_SO_RCVTIMEO:
|
||||
#endif
|
||||
release_sock_fd( s, fd );
|
||||
return 0;
|
||||
case IPX_FILTERPTYPE:
|
||||
/* Sets the receive filter packet type, at the moment we don't support it */
|
||||
FIXME("IPX_FILTERPTYPE: %x\n", *optval);
|
||||
|
||||
/* Returning 0 is better for now than returning a SOCKET_ERROR */
|
||||
return 0;
|
||||
default:
|
||||
FIXME("opt_name:%x\n", optname);
|
||||
return SOCKET_ERROR;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#ifdef SO_SNDTIMEO
|
||||
case WS_SO_SNDTIMEO:
|
||||
#endif
|
||||
|
||||
/* Is a privileged and useless operation, so we don't. */
|
||||
if ((optname == WS_SO_DEBUG) && (level == WS_SOL_SOCKET))
|
||||
{
|
||||
FIXME("(%d,SOL_SOCKET,SO_DEBUG,%p(%d)) attempted (is privileged). Ignoring.\n",s,optval,*(const DWORD*)optval);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(optname == WS_SO_DONTLINGER && level == WS_SOL_SOCKET) {
|
||||
/* This is unique to WinSock and takes special conversion */
|
||||
linger.l_onoff = *((const int*)optval) ? 0: 1;
|
||||
linger.l_linger = 0;
|
||||
optname=SO_LINGER;
|
||||
optval = (char*)&linger;
|
||||
optlen = sizeof(struct linger);
|
||||
level = SOL_SOCKET;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!convert_sockopt(&level, &optname)) {
|
||||
ERR("Invalid level (%d) or optname (%d)\n", level, optname);
|
||||
SetLastError(WSAENOPROTOOPT);
|
||||
return SOCKET_ERROR;
|
||||
}
|
||||
if (optname == SO_LINGER && optval) {
|
||||
linger.l_onoff = ((LINGER*)optval)->l_onoff;
|
||||
linger.l_linger = ((LINGER*)optval)->l_linger;
|
||||
/* FIXME: what is documented behavior if SO_LINGER optval
|
||||
is null?? */
|
||||
optval = (char*)&linger;
|
||||
optlen = sizeof(struct linger);
|
||||
}
|
||||
else if (optval && optlen < sizeof(int))
|
||||
{
|
||||
woptval= *((const INT16 *) optval);
|
||||
optval= (char*) &woptval;
|
||||
optlen=sizeof(int);
|
||||
}
|
||||
if (level == SOL_SOCKET && is_timeout_option(optname))
|
||||
{
|
||||
if (optlen == sizeof(UINT32)) {
|
||||
#if defined(SO_RCVTIMEO) || defined(SO_SNDTIMEO)
|
||||
if (optval && optlen == sizeof(UINT32)) {
|
||||
/* WinSock passes miliseconds instead of struct timeval */
|
||||
tval.tv_usec = (*(const UINT32*)optval % 1000) * 1000;
|
||||
tval.tv_sec = *(const UINT32*)optval / 1000;
|
||||
|
@ -2995,15 +2979,105 @@ int WINAPI WS_setsockopt(SOCKET s, int level, int optname,
|
|||
WARN("SO_SND/RCVTIMEO for %d bytes is weird: ignored\n", optlen);
|
||||
return 0;
|
||||
}
|
||||
convert_sockopt(&level, &optname);
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
TRACE("Unknown SOL_SOCKET optname: 0x%08x\n", optname);
|
||||
SetLastError(WSAENOPROTOOPT);
|
||||
return SOCKET_ERROR;
|
||||
}
|
||||
if (level == SOL_SOCKET && optname == SO_RCVBUF && *(const int*)optval < 2048)
|
||||
break; /* case WS_SOL_SOCKET */
|
||||
|
||||
#ifdef HAVE_IPX
|
||||
case NSPROTO_IPX:
|
||||
switch(optname)
|
||||
{
|
||||
WARN("SO_RCVBF for %d bytes is too small: ignored\n", *(const int*)optval );
|
||||
case IPX_PTYPE:
|
||||
fd = get_sock_fd( s, 0, NULL );
|
||||
TRACE("trying to set IPX_PTYPE: %d (fd: %d)\n", *(const int*)optval, fd);
|
||||
|
||||
/* We try to set the ipx type on ipx socket level. */
|
||||
#ifdef SOL_IPX
|
||||
if(setsockopt(fd, SOL_IPX, IPX_TYPE, optval, optlen) == -1)
|
||||
{
|
||||
ERR("IPX: could not set ipx option type; expect weird behaviour\n");
|
||||
release_sock_fd( s, fd );
|
||||
return SOCKET_ERROR;
|
||||
}
|
||||
#else
|
||||
{
|
||||
struct ipx val;
|
||||
/* Should we retrieve val using a getsockopt call and then
|
||||
* set the modified one? */
|
||||
val.ipx_pt = *optval;
|
||||
setsockopt(fd, 0, SO_DEFAULT_HEADERS, &val, sizeof(struct ipx));
|
||||
}
|
||||
#endif
|
||||
release_sock_fd( s, fd );
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
case IPX_FILTERPTYPE:
|
||||
/* Sets the receive filter packet type, at the moment we don't support it */
|
||||
FIXME("IPX_FILTERPTYPE: %x\n", *optval);
|
||||
/* Returning 0 is better for now than returning a SOCKET_ERROR */
|
||||
return 0;
|
||||
|
||||
default:
|
||||
FIXME("opt_name:%x\n", optname);
|
||||
return SOCKET_ERROR;
|
||||
}
|
||||
break; /* case NSPROTO_IPX */
|
||||
#endif
|
||||
|
||||
/* Levels WS_IPPROTO_TCP and WS_IPPROTO_IP convert directly */
|
||||
case WS_IPPROTO_TCP:
|
||||
switch(optname)
|
||||
{
|
||||
case WS_TCP_NODELAY:
|
||||
convert_sockopt(&level, &optname);
|
||||
break;
|
||||
default:
|
||||
FIXME("Unknown IPPROTO_TCP optname 0x%08x\n", optname);
|
||||
return SOCKET_ERROR;
|
||||
}
|
||||
break;
|
||||
|
||||
case WS_IPPROTO_IP:
|
||||
switch(optname)
|
||||
{
|
||||
case WS_IP_ADD_MEMBERSHIP:
|
||||
case WS_IP_DROP_MEMBERSHIP:
|
||||
#ifdef IP_HDRINCL
|
||||
case WS_IP_HDRINCL:
|
||||
#endif
|
||||
case WS_IP_MULTICAST_IF:
|
||||
case WS_IP_MULTICAST_LOOP:
|
||||
case WS_IP_MULTICAST_TTL:
|
||||
case WS_IP_OPTIONS:
|
||||
case WS_IP_TOS:
|
||||
case WS_IP_TTL:
|
||||
convert_sockopt(&level, &optname);
|
||||
break;
|
||||
default:
|
||||
FIXME("Unknown IPPROTO_IP optname 0x%08x\n", optname);
|
||||
return SOCKET_ERROR;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
FIXME("Unknown level: 0x%08x\n", level);
|
||||
return SOCKET_ERROR;
|
||||
} /* end switch(level) */
|
||||
|
||||
/* avoid endianness issues if argument is a 16-bit int */
|
||||
if (optval && optlen < sizeof(int))
|
||||
{
|
||||
woptval= *((const INT16 *) optval);
|
||||
optval= (char*) &woptval;
|
||||
optlen=sizeof(int);
|
||||
}
|
||||
fd = get_sock_fd( s, 0, NULL );
|
||||
if (fd == -1) return SOCKET_ERROR;
|
||||
|
||||
|
@ -3015,6 +3089,7 @@ int WINAPI WS_setsockopt(SOCKET s, int level, int optname,
|
|||
TRACE("Setting socket error, %d\n", wsaErrno());
|
||||
SetLastError(wsaErrno());
|
||||
release_sock_fd( s, fd );
|
||||
|
||||
return SOCKET_ERROR;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue