While adding tests for this I also find a couple of things related to previous MR 1111 which I am also fixing here: - it turns out IPV6 loopback (as in "::1") should not be mapped to ipv4-mapped loopback, ipv6 and ipv4 loopbacks are different addresses. The mapping of that was also missing htonl() for INADDR_LOOPBACK so essentially didn't break anything; - basically the same is for INADDR_ANY (ipv6 "any" doesn't prevent binding to ipv4 "any"), although that is different when SO_EXCLUSIVEADDRUSE is used: in that case ipv6 wildcard address actually blocks ipv4 INADDR_ANY.
From: Paul Gofman pgofman@codeweavers.com
--- dlls/ws2_32/tests/sock.c | 208 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 204 insertions(+), 4 deletions(-)
diff --git a/dlls/ws2_32/tests/sock.c b/dlls/ws2_32/tests/sock.c index 5460bc621d3..852cdd88dc7 100644 --- a/dlls/ws2_32/tests/sock.c +++ b/dlls/ws2_32/tests/sock.c @@ -2013,9 +2013,10 @@ static void test_set_getsockopt(void) } }
-static void test_so_reuseaddr(void) +static void test_reuseaddr(void) { static struct sockaddr_in6 saddr_in6_any, saddr_in6_loopback; + static struct sockaddr_in6 saddr_in6_any_v4mapped, saddr_in6_loopback_v4mapped; static struct sockaddr_in saddr_in_any, saddr_in_loopback;
static const struct @@ -2030,10 +2031,132 @@ static void test_so_reuseaddr(void) { AF_INET, (struct sockaddr *)&saddr_in_any, (struct sockaddr *)&saddr_in_loopback, sizeof(saddr_in_any) }, { AF_INET6, (struct sockaddr *)&saddr_in6_any, (struct sockaddr *)&saddr_in6_loopback, sizeof(saddr_in6_any) }, }; - unsigned int rc, reuse; + static const struct + { + struct + { + int domain; + struct sockaddr *addr; + socklen_t addrlen; + BOOL exclusive; + } + 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, + }, + { + {{ 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, + }, + { + {{ AF_INET, (struct sockaddr *)&saddr_in_any, sizeof(saddr_in_any), FALSE, }, + { AF_INET, (struct sockaddr *)&saddr_in_loopback, sizeof(saddr_in_loopback), TRUE, }}, + NOERROR, + }, + { + {{ 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, + }, + { + {{ AF_INET, (struct sockaddr *)&saddr_in_loopback, sizeof(saddr_in_loopback), TRUE, }, + { AF_INET, (struct sockaddr *)&saddr_in_any, sizeof(saddr_in_any), FALSE, }}, + NOERROR, + }, + { + {{ 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, + }, + { + {{ 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, + }, + { + {{ AF_INET6, (struct sockaddr *)&saddr_in6_loopback, sizeof(saddr_in6_loopback), FALSE, }, + { AF_INET, (struct sockaddr *)&saddr_in_any, sizeof(saddr_in_any), TRUE, }}, + NOERROR, + }, + { + {{ AF_INET6, (struct sockaddr *)&saddr_in6_loopback, sizeof(saddr_in6_loopback), TRUE, }, + { AF_INET, (struct sockaddr *)&saddr_in_loopback, sizeof(saddr_in_loopback), TRUE, }}, + NOERROR, + }, + { + {{ 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, + }, + { + {{ 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, + }, + { + {{ 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, + }, + { + {{ AF_INET, (struct sockaddr *)&saddr_in_any, sizeof(saddr_in_any), FALSE, }, + { AF_INET, (struct sockaddr *)&saddr_in_any, sizeof(saddr_in_any), TRUE, }}, + WSAEADDRINUSE, + }, + { + {{ AF_INET, (struct sockaddr *)&saddr_in_any, sizeof(saddr_in_any), FALSE, }, + { AF_INET, (struct sockaddr *)&saddr_in_any, sizeof(saddr_in_any), FALSE, }}, + WSAEADDRINUSE, + }, + { + {{ 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, + }, + { + {{ AF_INET6, (struct sockaddr *)&saddr_in6_any_v4mapped, sizeof(saddr_in6_any_v4mapped), FALSE, }, + { AF_INET, (struct sockaddr *)&saddr_in_any, sizeof(saddr_in_any), FALSE, }}, + WSAEADDRINUSE, + }, + { + {{ AF_INET6, (struct sockaddr *)&saddr_in6_loopback_v4mapped, sizeof(saddr_in6_loopback_v4mapped), FALSE, }, + { AF_INET, (struct sockaddr *)&saddr_in_loopback, sizeof(saddr_in_loopback), FALSE, }}, + WSAEADDRINUSE, + }, + { + {{ AF_INET, (struct sockaddr *)&saddr_in_loopback, sizeof(saddr_in_loopback), FALSE, }, + { AF_INET6, (struct sockaddr *)&saddr_in6_loopback_v4mapped, sizeof(saddr_in6_loopback_v4mapped), FALSE, }}, + WSAEADDRINUSE, + }, + { + {{ AF_INET6, (struct sockaddr *)&saddr_in6_loopback, sizeof(saddr_in6_loopback), TRUE, }, + { AF_INET, (struct sockaddr *)&saddr_in_loopback, sizeof(saddr_in_loopback), TRUE, }}, + NOERROR, + }, + { + {{ AF_INET, (struct sockaddr *)&saddr_in_loopback, sizeof(saddr_in_loopback), TRUE, }, + { AF_INET, (struct sockaddr *)&saddr_in_loopback, sizeof(saddr_in_loopback), FALSE, }}, + WSAEADDRINUSE, + }, + { + {{ AF_INET, (struct sockaddr *)&saddr_in_loopback, sizeof(saddr_in_loopback), FALSE, }, + { AF_INET, (struct sockaddr *)&saddr_in_loopback, sizeof(saddr_in_loopback), TRUE, }}, + WSAEADDRINUSE, + }, + }; + + unsigned int rc, reuse, value; struct sockaddr saddr; SOCKET s1, s2, s3, s4; - unsigned int i; + unsigned int i, j; int size;
saddr_in_any.sin_family = AF_INET; @@ -2048,6 +2171,14 @@ static void test_so_reuseaddr(void) saddr_in6_loopback = saddr_in6_any; inet_pton(AF_INET6, "::1", &saddr_in6_loopback.sin6_addr);
+ saddr_in6_loopback_v4mapped = saddr_in6_any; + rc = inet_pton(AF_INET6, "::ffff:127.0.0.1", &saddr_in6_loopback_v4mapped.sin6_addr); + ok(rc, "got error %d.\n", WSAGetLastError()); + + saddr_in6_any_v4mapped = saddr_in6_any; + rc = inet_pton(AF_INET6, "::ffff:0.0.0.0", &saddr_in6_any_v4mapped.sin6_addr); + ok(rc, "got error %d.\n", WSAGetLastError()); + for (i = 0; i < ARRAY_SIZE(tests); ++i) { winetest_push_context("test %u", i); @@ -2210,6 +2341,75 @@ static void test_so_reuseaddr(void)
winetest_pop_context(); } + + /* SO_REUSEADDR and SO_EXCLUSIVEADDRUSE are mutually exclusive. */ + s1 = socket(AF_INET, SOCK_STREAM, 0); + ok(s1 != INVALID_SOCKET, "got error %d.\n", WSAGetLastError()); + + value = 1; + rc = setsockopt(s1, SOL_SOCKET, SO_REUSEADDR, (char*)&value, sizeof(value)); + ok(!rc, "got error %d.\n", WSAGetLastError()); + + value = 1; + rc = setsockopt(s1, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (char*)&value, sizeof(value)); + todo_wine ok(rc == SOCKET_ERROR && WSAGetLastError() == WSAEINVAL, "got rc %d, error %d.\n", rc, WSAGetLastError()); + + value = 0; + rc = setsockopt(s1, SOL_SOCKET, SO_REUSEADDR, (char*)&value, sizeof(value)); + + value = 1; + rc = setsockopt(s1, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (char*)&value, sizeof(value)); + ok(!rc, "got error %d.\n", WSAGetLastError()); + + value = 1; + rc = setsockopt(s1, SOL_SOCKET, SO_REUSEADDR, (char*)&value, sizeof(value)); + todo_wine ok(rc == SOCKET_ERROR && WSAGetLastError() == WSAEINVAL, "got rc %d, error %d.\n", rc, WSAGetLastError()); + + closesocket(s1); + + /* Test SO_EXCLUSIVEADDRUSE. */ + for (i = 0; i < ARRAY_SIZE(tests_exclusive); ++i) + { + SOCKET s[2]; + + winetest_push_context("test %u", i); + + for (j = 0; j < 2; ++j) + { + s[j] = socket(tests_exclusive[i].s[j].domain, SOCK_STREAM, 0); + ok(s[j] != INVALID_SOCKET, "got error %d.\n", WSAGetLastError()); + + if (tests_exclusive[i].s[j].exclusive) + { + value = 1; + rc = setsockopt(s[j], SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (char*)&value, sizeof(value)); + ok(!rc, "got error %d.\n", WSAGetLastError()); + } + if (tests_exclusive[i].s[j].domain == AF_INET6) + { + value = 0; + rc = setsockopt(s[j], IPPROTO_IPV6, IPV6_V6ONLY, (char*)&value, sizeof(value)); + ok(!rc, "got error %d.\n", WSAGetLastError()); + } + } + rc = bind(s[0], tests_exclusive[i].s[0].addr, tests_exclusive[i].s[0].addrlen); + ok(!rc, "got error %d.\n", WSAGetLastError()); + + 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]); + winetest_pop_context(); + } }
#define IP_PKTINFO_LEN (sizeof(WSACMSGHDR) + WSA_CMSG_ALIGN(sizeof(struct in_pktinfo))) @@ -13389,7 +13589,7 @@ START_TEST( sock ) Init();
test_set_getsockopt(); - test_so_reuseaddr(); + test_reuseaddr(); test_ip_pktinfo(); test_ipv4_cmsg(); test_ipv6_cmsg();
From: Paul Gofman pgofman@codeweavers.com
--- dlls/ws2_32/socket.c | 20 ++++++++++++++------ dlls/ws2_32/tests/sock.c | 1 + include/wine/afd.h | 2 ++ server/sock.c | 32 ++++++++++++++++++++++++++++++++ 4 files changed, 49 insertions(+), 6 deletions(-)
diff --git a/dlls/ws2_32/socket.c b/dlls/ws2_32/socket.c index 2f115f02e48..9e447f29d8c 100644 --- a/dlls/ws2_32/socket.c +++ b/dlls/ws2_32/socket.c @@ -1818,6 +1818,11 @@ int WINAPI getsockopt( SOCKET s, int level, int optname, char *optval, int *optl if (!ret && *optlen < sizeof(DWORD)) *optlen = 1; return ret;
+ case SO_EXCLUSIVEADDRUSE: + ret = server_getsockopt( s, IOCTL_AFD_WINE_GET_SO_EXCLUSIVEADDRUSE, optval, optlen ); + if (!ret && *optlen < sizeof(DWORD)) *optlen = 1; + return ret; + case SO_SNDBUF: if (*optlen < sizeof(DWORD) || !optval) { @@ -3215,6 +3220,15 @@ int WINAPI setsockopt( SOCKET s, int level, int optname, const char *optval, int memcpy( &value, optval, min( optlen, sizeof(value) )); return server_setsockopt( s, IOCTL_AFD_WINE_SET_SO_REUSEADDR, (char *)&value, sizeof(value) );
+ case SO_EXCLUSIVEADDRUSE: + if (!optval) + { + SetLastError( WSAEFAULT ); + return SOCKET_ERROR; + } + memcpy( &value, optval, min( optlen, sizeof(value) )); + return server_setsockopt( s, IOCTL_AFD_WINE_SET_SO_EXCLUSIVEADDRUSE, (char *)&value, sizeof(value) ); + case SO_SNDBUF: if (optlen < 0) optlen = 4; return server_setsockopt( s, IOCTL_AFD_WINE_SET_SO_SNDBUF, optval, optlen ); @@ -3240,12 +3254,6 @@ int WINAPI setsockopt( SOCKET s, int level, int optname, const char *optval, int SetLastError( ERROR_SUCCESS ); return 0;
- /* Stops two sockets from being bound to the same port. Always happens - * on unix systems, so just drop it. */ - case SO_EXCLUSIVEADDRUSE: - TRACE("Ignoring SO_EXCLUSIVEADDRUSE, is always set.\n"); - return 0; - /* After a ConnectEx call succeeds, the socket can't be used with half of the * normal winsock functions on windows. We don't have that problem. */ case SO_UPDATE_CONNECT_CONTEXT: diff --git a/dlls/ws2_32/tests/sock.c b/dlls/ws2_32/tests/sock.c index 852cdd88dc7..00f4327bb3b 100644 --- a/dlls/ws2_32/tests/sock.c +++ b/dlls/ws2_32/tests/sock.c @@ -1209,6 +1209,7 @@ static void test_set_getsockopt(void) {AF_INET, SOCK_STREAM, SOL_SOCKET, SO_DONTROUTE, TRUE, {1, 1, 1}, {0}, TRUE}, {AF_INET, SOCK_STREAM, SOL_SOCKET, SO_RCVTIMEO, FALSE, {1, 2, 4}, {0}, TRUE}, {AF_INET, SOCK_STREAM, SOL_SOCKET, SO_REUSEADDR, TRUE, {1, 1, 4}, {0, 0xdead0001, 0}, TRUE, TRUE}, + {AF_INET, SOCK_STREAM, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, TRUE, {1, 1, 4}, {0, 0xdead0001, 0}, TRUE, TRUE}, {AF_INET, SOCK_STREAM, SOL_SOCKET, SO_SNDBUF, FALSE, {1, 2, 4}, {0xdeadbe00, 0xdead0000}, TRUE}, {AF_INET, SOCK_STREAM, SOL_SOCKET, SO_SNDTIMEO, FALSE, {1, 2, 4}, {0}, TRUE}, {AF_INET, SOCK_STREAM, SOL_SOCKET, SO_OPENTYPE, FALSE, {1, 2, 4}, {0}, TRUE}, diff --git a/include/wine/afd.h b/include/wine/afd.h index 993730cacdb..b34a7b7318e 100644 --- a/include/wine/afd.h +++ b/include/wine/afd.h @@ -283,6 +283,8 @@ C_ASSERT( sizeof(struct afd_get_events_params) == 56 ); #define IOCTL_AFD_WINE_SET_IP_RECVTTL WINE_AFD_IOC(294) #define IOCTL_AFD_WINE_GET_IP_RECVTOS WINE_AFD_IOC(295) #define IOCTL_AFD_WINE_SET_IP_RECVTOS WINE_AFD_IOC(296) +#define IOCTL_AFD_WINE_GET_SO_EXCLUSIVEADDRUSE WINE_AFD_IOC(297) +#define IOCTL_AFD_WINE_SET_SO_EXCLUSIVEADDRUSE WINE_AFD_IOC(298)
struct afd_iovec { diff --git a/server/sock.c b/server/sock.c index 2156a1a12c2..88215650757 100644 --- a/server/sock.c +++ b/server/sock.c @@ -256,6 +256,7 @@ struct sock unsigned int bound : 1; /* is the socket bound? */ unsigned int reset : 1; /* did we get a TCP reset? */ unsigned int reuseaddr : 1; /* winsock SO_REUSEADDR option value */ + unsigned int exclusiveaddruse : 1; /* winsock SO_EXCLUSIVEADDRUSE option value */ };
static int is_tcp_socket( struct sock *sock ) @@ -1697,6 +1698,7 @@ static struct sock *create_socket(void) sock->bound = 0; sock->reset = 0; sock->reuseaddr = 0; + sock->exclusiveaddruse = 0; sock->rcvbuf = 0; sock->sndbuf = 0; sock->rcvtimeo = 0; @@ -3103,6 +3105,21 @@ static void sock_ioctl( struct fd *fd, ioctl_code_t code, struct async *async ) return; }
+ case IOCTL_AFD_WINE_SET_SO_EXCLUSIVEADDRUSE: + { + int exclusive; + + if (get_req_data_size() < sizeof(exclusive)) + { + set_error( STATUS_BUFFER_TOO_SMALL ); + return; + } + + exclusive = *(int *)get_req_data(); + sock->exclusiveaddruse = !!exclusive; + return; + } + case IOCTL_AFD_WINE_GET_SO_SNDBUF: { int sndbuf = sock->sndbuf; @@ -3205,6 +3222,21 @@ static void sock_ioctl( struct fd *fd, ioctl_code_t code, struct async *async ) return; }
+ case IOCTL_AFD_WINE_GET_SO_EXCLUSIVEADDRUSE: + { + int exclusive; + + if (!get_reply_max_size()) + { + set_error( STATUS_BUFFER_TOO_SMALL ); + return; + } + + exclusive = sock->exclusiveaddruse; + set_reply_data( &exclusive, min( sizeof(exclusive), get_reply_max_size() )); + return; + } + case IOCTL_AFD_POLL: { if (get_reply_max_size() < get_req_data_size())
From: Paul Gofman pgofman@codeweavers.com
--- dlls/ws2_32/tests/sock.c | 4 ++-- server/sock.c | 12 ++++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-)
diff --git a/dlls/ws2_32/tests/sock.c b/dlls/ws2_32/tests/sock.c index 00f4327bb3b..44a1691950a 100644 --- a/dlls/ws2_32/tests/sock.c +++ b/dlls/ws2_32/tests/sock.c @@ -2353,7 +2353,7 @@ static void test_reuseaddr(void)
value = 1; rc = setsockopt(s1, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (char*)&value, sizeof(value)); - todo_wine ok(rc == SOCKET_ERROR && WSAGetLastError() == WSAEINVAL, "got rc %d, error %d.\n", rc, WSAGetLastError()); + ok(rc == SOCKET_ERROR && WSAGetLastError() == WSAEINVAL, "got rc %d, error %d.\n", rc, WSAGetLastError());
value = 0; rc = setsockopt(s1, SOL_SOCKET, SO_REUSEADDR, (char*)&value, sizeof(value)); @@ -2364,7 +2364,7 @@ static void test_reuseaddr(void)
value = 1; rc = setsockopt(s1, SOL_SOCKET, SO_REUSEADDR, (char*)&value, sizeof(value)); - todo_wine ok(rc == SOCKET_ERROR && WSAGetLastError() == WSAEINVAL, "got rc %d, error %d.\n", rc, WSAGetLastError()); + ok(rc == SOCKET_ERROR && WSAGetLastError() == WSAEINVAL, "got rc %d, error %d.\n", rc, WSAGetLastError());
closesocket(s1);
diff --git a/server/sock.c b/server/sock.c index 88215650757..141c5ea6d0a 100644 --- a/server/sock.c +++ b/server/sock.c @@ -3091,6 +3091,13 @@ static void sock_ioctl( struct fd *fd, ioctl_code_t code, struct async *async ) }
reuse = *(int *)get_req_data(); + + if (reuse && sock->exclusiveaddruse) + { + set_error( STATUS_INVALID_PARAMETER ); + return; + } + if (is_tcp_socket( sock )) ret = 0; else @@ -3116,6 +3123,11 @@ static void sock_ioctl( struct fd *fd, ioctl_code_t code, struct async *async ) }
exclusive = *(int *)get_req_data(); + if (exclusive && sock->reuseaddr) + { + set_error( STATUS_INVALID_PARAMETER ); + return; + } sock->exclusiveaddruse = !!exclusive; return; }
From: Paul Gofman pgofman@codeweavers.com
--- server/sock.c | 48 ++++++++++++++++++++++++++---------------------- 1 file changed, 26 insertions(+), 22 deletions(-)
diff --git a/server/sock.c b/server/sock.c index 141c5ea6d0a..5ed62ae700f 100644 --- a/server/sock.c +++ b/server/sock.c @@ -267,24 +267,24 @@ static int is_tcp_socket( struct sock *sock ) 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; + const struct bound_addr *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.addr.sa_family != bound_addr->addr.addr.sa_family) + return addr->addr.addr.sa_family < bound_addr->addr.addr.sa_family ? -1 : 1;
- if (addr->addr.sa_family == AF_INET) + if (addr->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) + 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) return 0; - return addr->in.sin_addr.s_addr < bound_addr->addr.in.sin_addr.s_addr ? -1 : 1; + return addr->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) ); + 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; + 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 ) @@ -325,21 +325,23 @@ static int should_track_conflicts_for_addr( struct sock *sock, const union unix_
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 bound_addr *bound_addr, search_addr; struct rb_entry *entry;
if (!should_track_conflicts_for_addr( sock, addr )) return 0;
- if ((entry = rb_get( &bound_addresses_tree, addr ))) + search_addr.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) 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 ))) + if (!ipv4addr_from_v6( &search_addr.addr, &addr->in6 )) return 0; + + 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) return 1; @@ -349,15 +351,18 @@ static int check_addr_usage( struct sock *sock, const union unix_sockaddr *addr,
static struct bound_addr *register_bound_address( struct sock *sock, const union unix_sockaddr *addr ) { - struct bound_addr *bound_addr; + struct bound_addr *bound_addr, *temp;
if (!(bound_addr = mem_alloc( sizeof(*bound_addr) ))) return NULL;
- if (rb_put( &bound_addresses_tree, addr, &bound_addr->entry )) + bound_addr->addr = *addr; + + if (rb_put( &bound_addresses_tree, bound_addr, &bound_addr->entry )) { - free( bound_addr ); - bound_addr = WINE_RB_ENTRY_VALUE(rb_get( &bound_addresses_tree, addr ), struct bound_addr, entry); + temp = bound_addr; + bound_addr = WINE_RB_ENTRY_VALUE(rb_get( &bound_addresses_tree, temp ), struct bound_addr, entry); + free( temp ); if (bound_addr->reuse_count == -1) { if (debug_level) @@ -368,7 +373,6 @@ static struct bound_addr *register_bound_address( struct sock *sock, const union } else { - bound_addr->addr = *addr; bound_addr->reuse_count = sock->reuseaddr ? 1 : -1; } return bound_addr;
From: Paul Gofman pgofman@codeweavers.com
--- server/sock.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-)
diff --git a/server/sock.c b/server/sock.c index 5ed62ae700f..3bb04c5925a 100644 --- a/server/sock.c +++ b/server/sock.c @@ -335,7 +335,11 @@ static int check_addr_usage( struct sock *sock, const union unix_sockaddr *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) return 1; + if (bound_addr->reuse_count == -1 || !sock->reuseaddr) + { + set_error( sock->reuseaddr ? STATUS_ACCESS_DENIED : STATUS_SHARING_VIOLATION ); + return 1; + } }
if (sock->family != WS_AF_INET6 || v6only) return 0; @@ -344,7 +348,11 @@ static int check_addr_usage( struct sock *sock, const union unix_sockaddr *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) return 1; + if (bound_addr->reuse_count == -1 || !sock->reuseaddr) + { + set_error( sock->reuseaddr ? STATUS_ACCESS_DENIED : STATUS_SHARING_VIOLATION ); + return 1; + } } return 0; } @@ -2895,10 +2903,7 @@ static void sock_ioctl( struct fd *fd, ioctl_code_t code, struct async *async ) #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) {
From: Paul Gofman pgofman@codeweavers.com
--- server/sock.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/server/sock.c b/server/sock.c index 3bb04c5925a..e9e4640b3b8 100644 --- a/server/sock.c +++ b/server/sock.c @@ -293,7 +293,7 @@ static int ipv4addr_from_v6( union unix_sockaddr *v4addr, const struct sockaddr_ v4addr->in.sin_port = in6->sin6_port; if (IN6_IS_ADDR_UNSPECIFIED(&in6->sin6_addr)) { - v4addr->in.sin_addr.s_addr = INADDR_ANY; + v4addr->in.sin_addr.s_addr = htonl( INADDR_ANY ); return 1; } if (IN6_IS_ADDR_LOOPBACK(&in6->sin6_addr))
From: Paul Gofman pgofman@codeweavers.com
--- server/sock.c | 5 ----- 1 file changed, 5 deletions(-)
diff --git a/server/sock.c b/server/sock.c index e9e4640b3b8..d1c63dc47c3 100644 --- a/server/sock.c +++ b/server/sock.c @@ -296,11 +296,6 @@ static int ipv4addr_from_v6( union unix_sockaddr *v4addr, const struct sockaddr_ v4addr->in.sin_addr.s_addr = htonl( 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) );
From: Paul Gofman pgofman@codeweavers.com
--- dlls/ws2_32/tests/sock.c | 22 +++++++++------------- server/sock.c | 33 ++++++++++++++++++++++++++------- 2 files changed, 35 insertions(+), 20 deletions(-)
diff --git a/dlls/ws2_32/tests/sock.c b/dlls/ws2_32/tests/sock.c index 44a1691950a..9b39ecff3c5 100644 --- a/dlls/ws2_32/tests/sock.c +++ b/dlls/ws2_32/tests/sock.c @@ -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]); diff --git a/server/sock.c b/server/sock.c index d1c63dc47c3..c130821b7f4 100644 --- a/server/sock.c +++ b/server/sock.c @@ -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 ); }
From: Paul Gofman pgofman@codeweavers.com
--- dlls/http.sys/http.c | 4 +++- dlls/httpapi/tests/httpapi.c | 6 +++--- 2 files changed, 6 insertions(+), 4 deletions(-)
diff --git a/dlls/http.sys/http.c b/dlls/http.sys/http.c index e16805ccbdb..680ef30730c 100644 --- a/dlls/http.sys/http.c +++ b/dlls/http.sys/http.c @@ -715,7 +715,7 @@ static NTSTATUS http_add_url(struct request_queue *queue, IRP *irp) struct listening_socket *listening_sock; char *url, *endptr; size_t queue_url_len, new_url_len; - ULONG true = 1; + ULONG true = 1, value; SOCKET s = INVALID_SOCKET;
TRACE("host %s, context %s.\n", debugstr_a(params->url), wine_dbgstr_longlong(params->context)); @@ -781,6 +781,8 @@ static NTSTATUS http_add_url(struct request_queue *queue, IRP *irp)
addr.sin_family = AF_INET; addr.sin_addr.S_un.S_addr = INADDR_ANY; + value = 1; + setsockopt(s, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (char *)&value, sizeof(value)); if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) == -1) { LeaveCriticalSection(&http_cs); diff --git a/dlls/httpapi/tests/httpapi.c b/dlls/httpapi/tests/httpapi.c index ec1be5bc82a..1d00aa22c50 100644 --- a/dlls/httpapi/tests/httpapi.c +++ b/dlls/httpapi/tests/httpapi.c @@ -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); - todo_wine ok(ret == ERROR_SHARING_VIOLATION, "Unexpected failure adding %s, error %u.\n", debugstr_w(url), ret); + 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)); - todo_wine ok(ret, "Connecting to socket succeeded, %lu.\n", GetLastError()); - todo_wine ok(GetLastError() == WSAECONNREFUSED, "Unexpected error connecting to socket, %lu.\n", GetLastError()); + ok(ret, "Connecting to socket succeeded, %lu.\n", GetLastError()); + ok(GetLastError() == WSAECONNREFUSED, "Unexpected error connecting to socket, %lu.\n", GetLastError());
closesocket(s); ret = pHttpCloseRequestQueue(dummy_queue);
Hi,
It looks like your patch introduced the new failures shown below. Please investigate and fix them before resubmitting your patch. If they are not new, fixing them anyway would help a lot. Otherwise please ask for the known failures list to be updated.
The full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=125587
Your paranoid android.
=== build (build log) ===
error: patch failed: dlls/ws2_32/tests/sock.c:2013 error: patch failed: server/sock.c:256 error: patch failed: dlls/ws2_32/tests/sock.c:2353 error: patch failed: server/sock.c:3091 error: patch failed: server/sock.c:267 error: patch failed: server/sock.c:335 error: patch failed: server/sock.c:293 error: patch failed: server/sock.c:296 error: patch failed: dlls/ws2_32/tests/sock.c:2043 error: patch failed: server/sock.c:187 error: patch failed: dlls/httpapi/tests/httpapi.c:1397 Task: Patch failed to apply
=== debian11 (build log) ===
error: patch failed: dlls/ws2_32/tests/sock.c:2013 error: patch failed: server/sock.c:256 error: patch failed: dlls/ws2_32/tests/sock.c:2353 error: patch failed: server/sock.c:3091 error: patch failed: server/sock.c:267 error: patch failed: server/sock.c:335 error: patch failed: server/sock.c:293 error: patch failed: server/sock.c:296 error: patch failed: dlls/ws2_32/tests/sock.c:2043 error: patch failed: server/sock.c:187 error: patch failed: dlls/httpapi/tests/httpapi.c:1397 Task: Patch failed to apply
=== debian11b (build log) ===
error: patch failed: dlls/ws2_32/tests/sock.c:2013 error: patch failed: server/sock.c:256 error: patch failed: dlls/ws2_32/tests/sock.c:2353 error: patch failed: server/sock.c:3091 error: patch failed: server/sock.c:267 error: patch failed: server/sock.c:335 error: patch failed: server/sock.c:293 error: patch failed: server/sock.c:296 error: patch failed: dlls/ws2_32/tests/sock.c:2043 error: patch failed: server/sock.c:187 error: patch failed: dlls/httpapi/tests/httpapi.c:1397 Task: Patch failed to apply
This merge request was approved by Zebediah Figura.