[PATCH 0/9] MR1196: winsock: Implement SO_EXCLUSIVEADDR.
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. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/1196
From: Paul Gofman <pgofman(a)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(); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/1196
From: Paul Gofman <pgofman(a)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()) -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/1196
From: Paul Gofman <pgofman(a)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; } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/1196
From: Paul Gofman <pgofman(a)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; -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/1196
From: Paul Gofman <pgofman(a)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) { -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/1196
From: Paul Gofman <pgofman(a)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)) -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/1196
From: Paul Gofman <pgofman(a)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) ); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/1196
From: Paul Gofman <pgofman(a)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 ); } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/1196
From: Paul Gofman <pgofman(a)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); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/1196
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. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/1196
participants (4)
-
Marvin -
Paul Gofman -
Paul Gofman (@gofman) -
Zebediah Figura (@zfigura)