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,39 +2871,127 @@ int WINAPI WS_setsockopt(SOCKET s, int level, int optname, ...@@ -2870,39 +2871,127 @@ 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 );
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; 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 /* For some reason the game GrandPrixLegends does set SO_DONTROUTE on its
* socket. According to MSDN, this option is silently ignored.*/ * socket. According to MSDN, this option is silently ignored.*/
if ( level==WS_SOL_SOCKET && optname==WS_SO_DONTROUTE ) case WS_SO_DONTROUTE:
{ TRACE("Ignoring SO_DONTROUTE\n");
TRACE("Ignoring SO_DONTROUTE.\n");
return 0; return 0;
}
/* SO_EXCLUSIVEADDRUSE stops applications from binding to a port that is /* Stops two sockets from being bound to the same port. Always happens
* already used. This never works on Unix systems, so just ignore this * on unix systems, so just drop it. */
* setting*/ case WS_SO_EXCLUSIVEADDRUSE:
if ( level == WS_SOL_SOCKET && optname == 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)
{ {
TRACE("Ignoring SO_EXCLUSIVEADDRUSE\n"); 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 */
tval.tv_usec = (*(const UINT32*)optval % 1000) * 1000;
tval.tv_sec = *(const UINT32*)optval / 1000;
/* min of 500 milisec */
if (tval.tv_sec == 0 && tval.tv_usec < 500000)
tval.tv_usec = 500000;
optlen = sizeof(struct timeval);
optval = (char*)&tval;
} else if (optlen == sizeof(struct timeval)) {
WARN("SO_SND/RCVTIMEO for %d bytes: assuming unixism\n", optlen);
} else {
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;
}
break; /* case WS_SOL_SOCKET */
#ifdef HAVE_IPX #ifdef HAVE_IPX
if(level == NSPROTO_IPX) case NSPROTO_IPX:
{
switch(optname) switch(optname)
{ {
case IPX_PTYPE: case IPX_PTYPE:
...@@ -2914,6 +3003,7 @@ int WINAPI WS_setsockopt(SOCKET s, int level, int optname, ...@@ -2914,6 +3003,7 @@ int WINAPI WS_setsockopt(SOCKET s, int level, int optname,
if(setsockopt(fd, SOL_IPX, IPX_TYPE, optval, optlen) == -1) if(setsockopt(fd, SOL_IPX, IPX_TYPE, optval, optlen) == -1)
{ {
ERR("IPX: could not set ipx option type; expect weird behaviour\n"); ERR("IPX: could not set ipx option type; expect weird behaviour\n");
release_sock_fd( s, fd );
return SOCKET_ERROR; return SOCKET_ERROR;
} }
#else #else
...@@ -2927,83 +3017,67 @@ int WINAPI WS_setsockopt(SOCKET s, int level, int optname, ...@@ -2927,83 +3017,67 @@ int WINAPI WS_setsockopt(SOCKET s, int level, int optname,
#endif #endif
release_sock_fd( s, fd ); release_sock_fd( s, fd );
return 0; return 0;
case IPX_FILTERPTYPE: case IPX_FILTERPTYPE:
/* Sets the receive filter packet type, at the moment we don't support it */ /* Sets the receive filter packet type, at the moment we don't support it */
FIXME("IPX_FILTERPTYPE: %x\n", *optval); FIXME("IPX_FILTERPTYPE: %x\n", *optval);
/* Returning 0 is better for now than returning a SOCKET_ERROR */ /* Returning 0 is better for now than returning a SOCKET_ERROR */
return 0; return 0;
default: default:
FIXME("opt_name:%x\n", optname); FIXME("opt_name:%x\n", optname);
return SOCKET_ERROR; return SOCKET_ERROR;
} }
return 0; break; /* case NSPROTO_IPX */
}
#endif #endif
/* Is a privileged and useless operation, so we don't. */ /* Levels WS_IPPROTO_TCP and WS_IPPROTO_IP convert directly */
if ((optname == WS_SO_DEBUG) && (level == WS_SOL_SOCKET)) case WS_IPPROTO_TCP:
switch(optname)
{ {
FIXME("(%d,SOL_SOCKET,SO_DEBUG,%p(%d)) attempted (is privileged). Ignoring.\n",s,optval,*(const DWORD*)optval); case WS_TCP_NODELAY:
return 0; convert_sockopt(&level, &optname);
break;
default:
FIXME("Unknown IPPROTO_TCP optname 0x%08x\n", optname);
return SOCKET_ERROR;
} }
break;
if(optname == WS_SO_DONTLINGER && level == WS_SOL_SOCKET) { case WS_IPPROTO_IP:
/* This is unique to WinSock and takes special conversion */ switch(optname)
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)) { case WS_IP_ADD_MEMBERSHIP:
ERR("Invalid level (%d) or optname (%d)\n", level, optname); case WS_IP_DROP_MEMBERSHIP:
SetLastError(WSAENOPROTOOPT); #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; return SOCKET_ERROR;
} }
if (optname == SO_LINGER && optval) { break;
linger.l_onoff = ((LINGER*)optval)->l_onoff;
linger.l_linger = ((LINGER*)optval)->l_linger; default:
/* FIXME: what is documented behavior if SO_LINGER optval FIXME("Unknown level: 0x%08x\n", level);
is null?? */ return SOCKET_ERROR;
optval = (char*)&linger; } /* end switch(level) */
optlen = sizeof(struct linger);
} /* avoid endianness issues if argument is a 16-bit int */
else if (optval && optlen < sizeof(int)) if (optval && optlen < sizeof(int))
{ {
woptval= *((const INT16 *) optval); woptval= *((const INT16 *) optval);
optval= (char*) &woptval; optval= (char*) &woptval;
optlen=sizeof(int); optlen=sizeof(int);
} }
if (level == SOL_SOCKET && is_timeout_option(optname))
{
if (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;
/* min of 500 milisec */
if (tval.tv_sec == 0 && tval.tv_usec < 500000)
tval.tv_usec = 500000;
optlen = sizeof(struct timeval);
optval = (char*)&tval;
} else if (optlen == sizeof(struct timeval)) {
WARN("SO_SND/RCVTIMEO for %d bytes: assuming unixism\n", optlen);
} else {
WARN("SO_SND/RCVTIMEO for %d bytes is weird: ignored\n", optlen);
return 0;
}
}
if (level == SOL_SOCKET && optname == SO_RCVBUF && *(const int*)optval < 2048)
{
WARN("SO_RCVBF for %d bytes is too small: ignored\n", *(const int*)optval );
return 0;
}
}
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