1
0
Fork 0
mirror of synced 2025-03-07 03:53:26 +01:00

server: Set Unix SO_REUSEADDR on all the TCP sockets.

Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=50955
This commit is contained in:
Paul Gofman 2022-10-18 16:00:20 -05:00 committed by Alexandre Julliard
parent 382ec78db3
commit ef7de1fc1f
2 changed files with 197 additions and 20 deletions

View file

@ -1397,7 +1397,7 @@ static void test_v2_bound_port(void)
ok(!ret, "Failed to bind to port\n");
swprintf(url, ARRAY_SIZE(url), L"http://localhost:%u/", port);
ret = pHttpAddUrlToUrlGroup(group, url, 0xdeadbeef, 0);
ok(ret == ERROR_SHARING_VIOLATION, "Unexpected failure adding %s, error %u.\n", debugstr_w(url), ret);
todo_wine ok(ret == ERROR_SHARING_VIOLATION, "Unexpected failure adding %s, error %u.\n", debugstr_w(url), ret);
shutdown(s2, SD_BOTH);
closesocket(s2);
@ -1407,8 +1407,8 @@ static void test_v2_bound_port(void)
s = socket(AF_INET, SOCK_STREAM, 0);
ret = connect(s, (struct sockaddr *)&sockaddr, sizeof(sockaddr));
ok(ret, "Connecting to socket succeeded, %lu.\n", GetLastError());
ok(GetLastError() == WSAECONNREFUSED, "Unexpected error connecting to socket, %lu.\n", GetLastError());
todo_wine ok(ret, "Connecting to socket succeeded, %lu.\n", GetLastError());
todo_wine ok(GetLastError() == WSAECONNREFUSED, "Unexpected error connecting to socket, %lu.\n", GetLastError());
closesocket(s);
ret = pHttpCloseRequestQueue(dummy_queue);

View file

@ -91,6 +91,7 @@
#include "wsipx.h"
#include "af_irda.h"
#include "wine/afd.h"
#include "wine/rbtree.h"
#include "process.h"
#include "file.h"
@ -114,6 +115,19 @@ union win_sockaddr
SOCKADDR_IRDA irda;
};
union unix_sockaddr
{
struct sockaddr addr;
struct sockaddr_in in;
struct sockaddr_in6 in6;
#ifdef HAS_IPX
struct sockaddr_ipx ipx;
#endif
#ifdef HAS_IRDA
struct sockaddr_irda irda;
#endif
};
static struct list poll_list = LIST_INIT( poll_list );
struct poll_req
@ -169,6 +183,13 @@ enum connection_state
SOCK_CONNECTIONLESS,
};
struct bound_addr
{
struct rb_entry entry;
union unix_sockaddr addr;
int reuse_count;
};
#define MAX_ICMP_HISTORY_LENGTH 8
struct sock
@ -224,6 +245,7 @@ struct sock
unsigned short icmp_seq;
}
icmp_fixup_data[MAX_ICMP_HISTORY_LENGTH]; /* Sent ICMP packets history used to fixup reply id. */
struct bound_addr *bound_addr[2]; /* Links to the entries in bound addresses tree. */
unsigned int icmp_fixup_data_len; /* Sent ICMP packets history length. */
unsigned int rd_shutdown : 1; /* is the read end shut down? */
unsigned int wr_shutdown : 1; /* is the write end shut down? */
@ -236,6 +258,137 @@ struct sock
unsigned int reuseaddr : 1; /* winsock SO_REUSEADDR option value */
};
static int is_tcp_socket( struct sock *sock )
{
return sock->type == WS_SOCK_STREAM && (sock->family == WS_AF_INET || sock->family == WS_AF_INET6);
}
static int addr_compare( const void *key, const struct wine_rb_entry *entry )
{
const struct bound_addr *bound_addr = RB_ENTRY_VALUE(entry, struct bound_addr, entry);
const union unix_sockaddr *addr = key;
if (addr->addr.sa_family != bound_addr->addr.addr.sa_family)
return addr->addr.sa_family < bound_addr->addr.addr.sa_family ? -1 : 1;
if (addr->addr.sa_family == AF_INET)
{
if (addr->in.sin_port != bound_addr->addr.in.sin_port)
return addr->in.sin_port < bound_addr->addr.in.sin_port ? -1 : 1;
if (addr->in.sin_addr.s_addr == bound_addr->addr.in.sin_addr.s_addr)
return 0;
return addr->in.sin_addr.s_addr < bound_addr->addr.in.sin_addr.s_addr ? -1 : 1;
}
assert( addr->addr.sa_family == AF_INET6 );
if (addr->in6.sin6_port != bound_addr->addr.in6.sin6_port)
return addr->in6.sin6_port < bound_addr->addr.in6.sin6_port ? -1 : 1;
return memcmp( &addr->in6.sin6_addr, &bound_addr->addr.in6.sin6_addr, sizeof(addr->in6.sin6_addr) );
}
static int ipv4addr_from_v6( union unix_sockaddr *v4addr, const struct sockaddr_in6 *in6 )
{
v4addr->in.sin_family = AF_INET;
v4addr->in.sin_port = in6->sin6_port;
if (IN6_IS_ADDR_UNSPECIFIED(&in6->sin6_addr))
{
v4addr->in.sin_addr.s_addr = INADDR_ANY;
return 1;
}
if (IN6_IS_ADDR_LOOPBACK(&in6->sin6_addr))
{
v4addr->in.sin_addr.s_addr = INADDR_LOOPBACK;
return 1;
}
if (IN6_IS_ADDR_V4COMPAT(&in6->sin6_addr) || IN6_IS_ADDR_V4MAPPED(&in6->sin6_addr))
{
memcpy( &v4addr->in.sin_addr.s_addr, &in6->sin6_addr.s6_addr[12], sizeof(v4addr->in.sin_addr.s_addr) );
return 1;
}
return 0;
}
static struct rb_tree bound_addresses_tree = { addr_compare };
static int should_track_conflicts_for_addr( struct sock *sock, const union unix_sockaddr *addr )
{
if (!is_tcp_socket( sock )) return 0;
if (sock->family == WS_AF_INET && addr->addr.sa_family == AF_INET && addr->in.sin_port)
return 1;
else if (sock->family == WS_AF_INET6 && addr->addr.sa_family == AF_INET6 && addr->in6.sin6_port)
return 1;
return 0;
}
static int check_addr_usage( struct sock *sock, const union unix_sockaddr *addr, int v6only )
{
struct bound_addr *bound_addr;
union unix_sockaddr v4addr;
struct rb_entry *entry;
if (!should_track_conflicts_for_addr( sock, addr )) return 0;
if ((entry = rb_get( &bound_addresses_tree, addr )))
{
bound_addr = WINE_RB_ENTRY_VALUE(entry, struct bound_addr, entry);
if (bound_addr->reuse_count == -1 || !sock->reuseaddr) return 1;
}
if (sock->family != WS_AF_INET6 || v6only) return 0;
if (!ipv4addr_from_v6( &v4addr, &addr->in6 )) return 0;
if ((entry = rb_get( &bound_addresses_tree, &v4addr )))
{
bound_addr = WINE_RB_ENTRY_VALUE(entry, struct bound_addr, entry);
if (bound_addr->reuse_count == -1 || !sock->reuseaddr) return 1;
}
return 0;
}
static struct bound_addr *register_bound_address( struct sock *sock, const union unix_sockaddr *addr )
{
struct bound_addr *bound_addr;
if (!(bound_addr = mem_alloc( sizeof(*bound_addr) )))
return NULL;
if (rb_put( &bound_addresses_tree, addr, &bound_addr->entry ))
{
free( bound_addr );
bound_addr = WINE_RB_ENTRY_VALUE(rb_get( &bound_addresses_tree, addr ), struct bound_addr, entry);
if (bound_addr->reuse_count == -1)
{
if (debug_level)
fprintf( stderr, "register_bound_address: address being updated is already exclusively bound\n" );
return NULL;
}
++bound_addr->reuse_count;
}
else
{
bound_addr->addr = *addr;
bound_addr->reuse_count = sock->reuseaddr ? 1 : -1;
}
return bound_addr;
}
static void update_addr_usage( struct sock *sock, const union unix_sockaddr *addr, int v6only )
{
union unix_sockaddr v4addr;
assert( !sock->bound_addr[0] && !sock->bound_addr[1] );
if (!should_track_conflicts_for_addr( sock, addr )) return;
sock->bound_addr[0] = register_bound_address( sock, addr );
if (sock->family != WS_AF_INET6 || v6only) return;
if (!ipv4addr_from_v6( &v4addr, &addr->in6 )) return;
sock->bound_addr[1] = register_bound_address( sock, &v4addr );
}
static void sock_dump( struct object *obj, int verbose );
static struct fd *sock_get_fd( struct object *obj );
static int sock_close_handle( struct object *obj, struct process *process, obj_handle_t handle );
@ -297,19 +450,6 @@ static const struct fd_ops sock_fd_ops =
sock_reselect_async /* reselect_async */
};
union unix_sockaddr
{
struct sockaddr addr;
struct sockaddr_in in;
struct sockaddr_in6 in6;
#ifdef HAS_IPX
struct sockaddr_ipx ipx;
#endif
#ifdef HAS_IRDA
struct sockaddr_irda irda;
#endif
};
static int sockaddr_from_unix( const union unix_sockaddr *uaddr, struct WS_sockaddr *wsaddr, socklen_t wsaddrlen )
{
memset( wsaddr, 0, wsaddrlen );
@ -1493,11 +1633,21 @@ static int sock_close_handle( struct object *obj, struct process *process, obj_h
static void sock_destroy( struct object *obj )
{
struct sock *sock = (struct sock *)obj;
unsigned int i;
assert( obj->ops == &sock_ops );
/* FIXME: special socket shutdown stuff? */
for (i = 0; i < 2; ++i)
{
if (sock->bound_addr[i] && --sock->bound_addr[i]->reuse_count <= 0)
{
rb_remove( &bound_addresses_tree, &sock->bound_addr[i]->entry );
free( sock->bound_addr[i] );
}
}
if ( sock->deferred )
release_object( sock->deferred );
@ -1552,6 +1702,7 @@ static struct sock *create_socket(void)
sock->rcvtimeo = 0;
sock->sndtimeo = 0;
sock->icmp_fixup_data_len = 0;
sock->bound_addr[0] = sock->bound_addr[1] = NULL;
init_async_queue( &sock->read_q );
init_async_queue( &sock->write_q );
init_async_queue( &sock->ifchange_q );
@ -1740,6 +1891,13 @@ static int init_socket( struct sock *sock, int family, int type, int protocol )
sock->type = type;
sock->family = family;
if (is_tcp_socket( sock ))
{
int reuse = 1;
setsockopt( sockfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse) );
}
if (sock->fd)
{
options = get_fd_options( sock->fd );
@ -2671,6 +2829,7 @@ static void sock_ioctl( struct fd *fd, ioctl_code_t code, struct async *async )
union unix_sockaddr unix_addr, bind_addr;
data_size_t in_size;
socklen_t unix_len;
int v6only = 1;
/* the ioctl is METHOD_NEITHER, so ntdll gives us the output buffer as
* input */
@ -2720,6 +2879,21 @@ static void sock_ioctl( struct fd *fd, ioctl_code_t code, struct async *async )
set_async_pending( async );
#ifdef IPV6_V6ONLY
if (sock->family == WS_AF_INET6)
{
socklen_t len = sizeof(v6only);
getsockopt( get_unix_fd(sock->fd), IPPROTO_IPV6, IPV6_V6ONLY, &v6only, &len );
}
#endif
if (check_addr_usage( sock, &bind_addr, v6only ))
{
set_error( sock->reuseaddr ? STATUS_ACCESS_DENIED : STATUS_SHARING_VIOLATION );
return;
}
if (bind( unix_fd, &bind_addr.addr, unix_len ) < 0)
{
if (errno == EADDRINUSE && sock->reuseaddr)
@ -2741,6 +2915,8 @@ static void sock_ioctl( struct fd *fd, ioctl_code_t code, struct async *async )
sock->addr_len = sockaddr_from_unix( &bind_addr, &sock->addr.addr, sizeof(sock->addr) );
}
update_addr_usage( sock, &bind_addr, v6only );
if (get_reply_max_size() >= sock->addr_len)
set_reply_data( &sock->addr, sock->addr_len );
return;
@ -2901,9 +3077,7 @@ static void sock_ioctl( struct fd *fd, ioctl_code_t code, struct async *async )
return;
}
/* BSD socket SO_REUSEADDR is not 100% compatible to winsock semantics;
* however, using it the BSD way fixes bug 8513 and seems to be what
* most programmers assume, anyway */
/* BSD socket SO_REUSEADDR is not compatible with winsock semantics. */
case IOCTL_AFD_WINE_SET_SO_REUSEADDR:
{
int reuse, ret;
@ -2915,7 +3089,10 @@ static void sock_ioctl( struct fd *fd, ioctl_code_t code, struct async *async )
}
reuse = *(int *)get_req_data();
ret = setsockopt( unix_fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse) );
if (is_tcp_socket( sock ))
ret = 0;
else
ret = setsockopt( unix_fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse) );
#ifdef __APPLE__
if (!ret) ret = setsockopt( unix_fd, SOL_SOCKET, SO_REUSEPORT, &reuse, sizeof(reuse) );
#endif