Commit a9ae1e04 authored by Paul Gofman's avatar Paul Gofman Committed by Alexandre Julliard

server: Support SO_EXCLUSIVEADDRUSE socket option.

parent 003ee620
......@@ -2043,19 +2043,18 @@ static void test_reuseaddr(void)
}
s[2];
int error;
BOOL todo;
}
tests_exclusive[] =
{
{
{{ AF_INET, (struct sockaddr *)&saddr_in_any, sizeof(saddr_in_any), TRUE, },
{ AF_INET, (struct sockaddr *)&saddr_in_loopback, sizeof(saddr_in_loopback), FALSE, }},
WSAEACCES, TRUE,
WSAEACCES,
},
{
{{ AF_INET6, (struct sockaddr *)&saddr_in6_any, sizeof(saddr_in6_any), TRUE, },
{ AF_INET, (struct sockaddr *)&saddr_in_loopback, sizeof(saddr_in_loopback), FALSE, }},
WSAEACCES, TRUE,
WSAEACCES,
},
{
{{ AF_INET, (struct sockaddr *)&saddr_in_any, sizeof(saddr_in_any), FALSE, },
......@@ -2065,7 +2064,7 @@ static void test_reuseaddr(void)
{
{{ AF_INET, (struct sockaddr *)&saddr_in_any, sizeof(saddr_in_any), TRUE, },
{ AF_INET, (struct sockaddr *)&saddr_in_loopback, sizeof(saddr_in_loopback), TRUE, }},
WSAEACCES, TRUE,
WSAEACCES,
},
{
{{ AF_INET, (struct sockaddr *)&saddr_in_loopback, sizeof(saddr_in_loopback), TRUE, },
......@@ -2075,12 +2074,12 @@ static void test_reuseaddr(void)
{
{{ AF_INET, (struct sockaddr *)&saddr_in_loopback, sizeof(saddr_in_loopback), FALSE, },
{ AF_INET, (struct sockaddr *)&saddr_in_any, sizeof(saddr_in_any), TRUE, }},
WSAEADDRINUSE, TRUE,
WSAEADDRINUSE,
},
{
{{ AF_INET, (struct sockaddr *)&saddr_in_loopback, sizeof(saddr_in_loopback), FALSE, },
{ AF_INET6, (struct sockaddr *)&saddr_in6_any, sizeof(saddr_in6_any), TRUE, }},
WSAEADDRINUSE, TRUE,
WSAEADDRINUSE,
},
{
{{ AF_INET6, (struct sockaddr *)&saddr_in6_loopback, sizeof(saddr_in6_loopback), FALSE, },
......@@ -2095,17 +2094,17 @@ static void test_reuseaddr(void)
{
{{ AF_INET6, (struct sockaddr *)&saddr_in6_loopback_v4mapped, sizeof(saddr_in6_loopback_v4mapped), FALSE, },
{ AF_INET, (struct sockaddr *)&saddr_in_any, sizeof(saddr_in_any), TRUE, }},
WSAEADDRINUSE, TRUE,
WSAEADDRINUSE,
},
{
{{ AF_INET6, (struct sockaddr *)&saddr_in6_any, sizeof(saddr_in6_any), TRUE, },
{ AF_INET, (struct sockaddr *)&saddr_in_any, sizeof(saddr_in_any), FALSE, }},
WSAEACCES, TRUE,
WSAEACCES,
},
{
{{ AF_INET6, (struct sockaddr *)&saddr_in6_any, sizeof(saddr_in6_any), FALSE, },
{ AF_INET, (struct sockaddr *)&saddr_in_any, sizeof(saddr_in_any), TRUE, }},
NOERROR, TRUE,
NOERROR,
},
{
{{ AF_INET, (struct sockaddr *)&saddr_in_any, sizeof(saddr_in_any), FALSE, },
......@@ -2120,7 +2119,7 @@ static void test_reuseaddr(void)
{
{{ AF_INET6, (struct sockaddr *)&saddr_in6_any, sizeof(saddr_in6_any), FALSE, },
{ AF_INET, (struct sockaddr *)&saddr_in_any, sizeof(saddr_in_any), FALSE, }},
NOERROR, TRUE,
NOERROR,
},
{
{{ AF_INET6, (struct sockaddr *)&saddr_in6_any_v4mapped, sizeof(saddr_in6_any_v4mapped), FALSE, },
......@@ -2398,14 +2397,11 @@ static void test_reuseaddr(void)
rc = bind(s[1], tests_exclusive[i].s[1].addr, tests_exclusive[i].s[1].addrlen);
todo_wine_if(tests_exclusive[i].todo)
{
if (tests_exclusive[i].error)
ok(rc == SOCKET_ERROR && WSAGetLastError() == tests_exclusive[i].error,
"got rc %d, error %d, expected error %d.\n", rc, WSAGetLastError(), tests_exclusive[i].error);
else
ok(!rc, "got error %d.\n", WSAGetLastError());
}
closesocket(s[0]);
closesocket(s[1]);
......
......@@ -187,6 +187,7 @@ struct bound_addr
{
struct rb_entry entry;
union unix_sockaddr addr;
int match_any_addr;
int reuse_count;
};
......@@ -276,7 +277,8 @@ static int addr_compare( const void *key, const struct wine_rb_entry *entry )
{
if (addr->addr.in.sin_port != bound_addr->addr.in.sin_port)
return addr->addr.in.sin_port < bound_addr->addr.in.sin_port ? -1 : 1;
if (addr->addr.in.sin_addr.s_addr == bound_addr->addr.in.sin_addr.s_addr)
if (bound_addr->match_any_addr || addr->match_any_addr
|| addr->addr.in.sin_addr.s_addr == bound_addr->addr.in.sin_addr.s_addr)
return 0;
return addr->addr.in.sin_addr.s_addr < bound_addr->addr.in.sin_addr.s_addr ? -1 : 1;
}
......@@ -284,14 +286,16 @@ static int addr_compare( const void *key, const struct wine_rb_entry *entry )
assert( addr->addr.addr.sa_family == AF_INET6 );
if (addr->addr.in6.sin6_port != bound_addr->addr.in6.sin6_port)
return addr->addr.in6.sin6_port < bound_addr->addr.in6.sin6_port ? -1 : 1;
if (bound_addr->match_any_addr || addr->match_any_addr) return 0;
return memcmp( &addr->addr.in6.sin6_addr, &bound_addr->addr.in6.sin6_addr, sizeof(addr->addr.in6.sin6_addr) );
}
static int ipv4addr_from_v6( union unix_sockaddr *v4addr, const struct sockaddr_in6 *in6 )
static int ipv4addr_from_v6( union unix_sockaddr *v4addr, const struct sockaddr_in6 *in6, int map_unspecified )
{
v4addr->in.sin_family = AF_INET;
v4addr->in.sin_port = in6->sin6_port;
if (IN6_IS_ADDR_UNSPECIFIED(&in6->sin6_addr))
if (map_unspecified && IN6_IS_ADDR_UNSPECIFIED(&in6->sin6_addr))
{
v4addr->in.sin_addr.s_addr = htonl( INADDR_ANY );
return 1;
......@@ -318,6 +322,15 @@ static int should_track_conflicts_for_addr( struct sock *sock, const union unix_
return 0;
}
static int is_any_addr( const union unix_sockaddr *addr )
{
if (addr->addr.sa_family == AF_INET && addr->in.sin_addr.s_addr == htonl( INADDR_ANY ))
return 1;
if (addr->addr.sa_family == AF_INET6 && IN6_IS_ADDR_UNSPECIFIED(&addr->in6.sin6_addr))
return 1;
return 0;
}
static int check_addr_usage( struct sock *sock, const union unix_sockaddr *addr, int v6only )
{
struct bound_addr *bound_addr, search_addr;
......@@ -326,26 +339,30 @@ static int check_addr_usage( struct sock *sock, const union unix_sockaddr *addr,
if (!should_track_conflicts_for_addr( sock, addr )) return 0;
search_addr.addr = *addr;
search_addr.match_any_addr = sock->exclusiveaddruse && is_any_addr( addr );
if ((entry = rb_get( &bound_addresses_tree, &search_addr )))
{
bound_addr = WINE_RB_ENTRY_VALUE(entry, struct bound_addr, entry);
if (bound_addr->reuse_count == -1 || !sock->reuseaddr)
{
set_error( sock->reuseaddr ? STATUS_ACCESS_DENIED : STATUS_SHARING_VIOLATION );
set_error( sock->reuseaddr || bound_addr->match_any_addr
? STATUS_ACCESS_DENIED : STATUS_SHARING_VIOLATION );
return 1;
}
}
if (sock->family != WS_AF_INET6 || v6only) return 0;
if (!ipv4addr_from_v6( &search_addr.addr, &addr->in6 )) return 0;
if (!ipv4addr_from_v6( &search_addr.addr, &addr->in6, sock->exclusiveaddruse )) return 0;
search_addr.match_any_addr = sock->exclusiveaddruse && is_any_addr( &search_addr.addr );
if ((entry = rb_get( &bound_addresses_tree, &search_addr )))
{
bound_addr = WINE_RB_ENTRY_VALUE(entry, struct bound_addr, entry);
if (bound_addr->reuse_count == -1 || !sock->reuseaddr)
{
set_error( sock->reuseaddr ? STATUS_ACCESS_DENIED : STATUS_SHARING_VIOLATION );
set_error( sock->reuseaddr || bound_addr->match_any_addr
? STATUS_ACCESS_DENIED : STATUS_SHARING_VIOLATION );
return 1;
}
}
......@@ -360,6 +377,7 @@ static struct bound_addr *register_bound_address( struct sock *sock, const union
return NULL;
bound_addr->addr = *addr;
bound_addr->match_any_addr = sock->exclusiveaddruse && is_any_addr( addr );
if (rb_put( &bound_addresses_tree, bound_addr, &bound_addr->entry ))
{
......@@ -392,7 +410,8 @@ static void update_addr_usage( struct sock *sock, const union unix_sockaddr *add
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;
if (!ipv4addr_from_v6( &v4addr, &addr->in6, sock->exclusiveaddruse )) return;
sock->bound_addr[1] = register_bound_address( sock, &v4addr );
}
......
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