Commit 829e6f14 authored by Kai Blin's avatar Kai Blin Committed by Alexandre Julliard

ws2_32: Rewrite setsockopt to be more readable.

parent a10c40df
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
* *
* Copyright (C) 1993,1994,1996,1997 John Brezak, Erik Bos, Alex Korobka. * Copyright (C) 1993,1994,1996,1997 John Brezak, Erik Bos, Alex Korobka.
* Copyright (C) 2005 Marcus Meissner * Copyright (C) 2005 Marcus Meissner
* Copyright (C) 2006 Kai Blin
* *
* This library is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public * 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, ...@@ -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", TRACE("socket: %04x, level %d, name %d, ptr %p, len %d\n",
s, level, optname, optval, optlen); s, level, optname, optval, optlen);
/* SO_OPENTYPE does not require a valid socket handle. */ switch(level)
if (level == WS_SOL_SOCKET && optname == WS_SO_OPENTYPE)
{ {
if (optlen < sizeof(int) || !optval) case WS_SOL_SOCKET:
switch(optname)
{ {
SetLastError(WSAEFAULT); /* Some options need some conversion before they can be sent to
return SOCKET_ERROR; * setsockopt. The conversions are done here, then they will fall though
} * to the general case. Special options that are not passed to
get_per_thread_data()->opentype = *(const int *)optval; * setsockopt follow below that.*/
TRACE("setting global SO_OPENTYPE to 0x%x\n", *(const 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");
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));
}
#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;
}
#endif
/* Is a privileged and useless operation, so we don't. */ case WS_SO_DONTLINGER:
if ((optname == WS_SO_DEBUG) && (level == WS_SOL_SOCKET)) linger.l_onoff = *((const int*)optval) ? 0: 1;
{ linger.l_linger = 0;
FIXME("(%d,SOL_SOCKET,SO_DEBUG,%p(%d)) attempted (is privileged). Ignoring.\n",s,optval,*(const DWORD*)optval); level = SOL_SOCKET;
return 0; optname = SO_LINGER;
} optval = (char*)&linger;
optlen = sizeof(struct linger);
break;
if(optname == WS_SO_DONTLINGER && level == WS_SOL_SOCKET) { case WS_SO_LINGER:
/* 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_onoff = ((LINGER*)optval)->l_onoff;
linger.l_linger = ((LINGER*)optval)->l_linger; linger.l_linger = ((LINGER*)optval)->l_linger;
/* FIXME: what is documented behavior if SO_LINGER optval /* FIXME: what is documented behavior if SO_LINGER optval
is null?? */ is null?? */
level = SOL_SOCKET;
optname = SO_LINGER;
optval = (char*)&linger; optval = (char*)&linger;
optlen = sizeof(struct linger); optlen = sizeof(struct linger);
} break;
else if (optval && optlen < sizeof(int))
{ case WS_SO_RCVBUF:
woptval= *((const INT16 *) optval); if (*(const int*)optval < 2048)
optval= (char*) &woptval; {
optlen=sizeof(int); WARN("SO_RCVBF for %d bytes is too small: ignored\n", *(const int*)optval );
} return 0;
if (level == SOL_SOCKET && is_timeout_option(optname)) }
{ /* Fall through */
if (optlen == sizeof(UINT32)) {
/* 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 = 0x%x\n", *((int*)optval) );
return 0;
/* 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;
#ifdef SO_RCVTIMEO
case WS_SO_RCVTIMEO:
#endif
#ifdef SO_SNDTIMEO
case WS_SO_SNDTIMEO:
#endif
#if defined(SO_RCVTIMEO) || defined(SO_SNDTIMEO)
if (optval && optlen == sizeof(UINT32)) {
/* WinSock passes miliseconds instead of struct timeval */ /* WinSock passes miliseconds instead of struct timeval */
tval.tv_usec = (*(const UINT32*)optval % 1000) * 1000; tval.tv_usec = (*(const UINT32*)optval % 1000) * 1000;
tval.tv_sec = *(const UINT32*)optval / 1000; tval.tv_sec = *(const UINT32*)optval / 1000;
...@@ -2995,15 +2979,105 @@ int WINAPI WS_setsockopt(SOCKET s, int level, int optname, ...@@ -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); WARN("SO_SND/RCVTIMEO for %d bytes is weird: ignored\n", optlen);
return 0; 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; 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 ); fd = get_sock_fd( s, 0, NULL );
if (fd == -1) return SOCKET_ERROR; if (fd == -1) return SOCKET_ERROR;
...@@ -3015,6 +3089,7 @@ int WINAPI WS_setsockopt(SOCKET s, int level, int optname, ...@@ -3015,6 +3089,7 @@ int WINAPI WS_setsockopt(SOCKET s, int level, int optname,
TRACE("Setting socket error, %d\n", wsaErrno()); TRACE("Setting socket error, %d\n", wsaErrno());
SetLastError(wsaErrno()); SetLastError(wsaErrno());
release_sock_fd( s, fd ); release_sock_fd( s, fd );
return SOCKET_ERROR; return SOCKET_ERROR;
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment