win: map 0.0.0.0 and :: addresses to localhost

On Linux when connecting IP addresses 0.0.0.0 and :: are automatically
converted to localhost. This adds same functionality to Windows.

PR-URL: https://github.com/libuv/libuv/pull/1515
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
This commit is contained in:
Bartosz Sosnowski 2017-12-28 10:42:11 +01:00
parent cde132631b
commit 2b32e77bb6
10 changed files with 127 additions and 7 deletions

View File

@ -159,6 +159,7 @@ test_run_tests_SOURCES = test/blackhole-server.c \
test/test-close-fd.c \
test/test-close-order.c \
test/test-condvar.c \
test/test-connect-unspecified.c \
test/test-connection-fail.c \
test/test-cwd-and-chdir.c \
test/test-default-loop-close.c \

View File

@ -102,7 +102,14 @@ API
and an uninitialized :c:type:`uv_connect_t`. `addr` should point to an
initialized ``struct sockaddr_in`` or ``struct sockaddr_in6``.
On Windows if the `addr` is initialized to point to an unspecified address
(``0.0.0.0`` or ``::``) it will be changed to point to ``localhost``.
This is done to match the behavior of Linux systems.
The callback is made when the connection has been established or when a
connection error happened.
.. versionchanged:: 1.19.0 added ``0.0.0.0`` and ``::`` to ``localhost``
mapping
.. seealso:: The :c:type:`uv_stream_t` API functions also apply.

View File

@ -243,6 +243,10 @@ API
with :c:func:`uv_udp_bind` it will be bound to 0.0.0.0
(the "all interfaces" IPv4 address) and a random port number.
On Windows if the `addr` is initialized to point to an unspecified address
(``0.0.0.0`` or ``::``) it will be changed to point to ``localhost``.
This is done to match the behavior of Linux systems.
:param req: UDP request handle. Need not be initialized.
:param handle: UDP handle. Should have been initialized with
@ -259,6 +263,9 @@ API
:returns: 0 on success, or an error code < 0 on failure.
.. versionchanged:: 1.19.0 added ``0.0.0.0`` and ``::`` to ``localhost``
mapping
.. c:function:: int uv_udp_try_send(uv_udp_t* handle, const uv_buf_t bufs[], unsigned int nbufs, const struct sockaddr* addr)
Same as :c:func:`uv_udp_send`, but won't queue a send request if it can't

View File

@ -747,10 +747,15 @@ static int uv_tcp_try_connect(uv_connect_t* req,
uv_connect_cb cb) {
uv_loop_t* loop = handle->loop;
const struct sockaddr* bind_addr;
struct sockaddr_storage converted;
BOOL success;
DWORD bytes;
int err;
err = uv__convert_to_localhost_if_unspecified(addr, &converted);
if (err)
return err;
if (handle->delayed_error) {
return handle->delayed_error;
}
@ -782,12 +787,12 @@ static int uv_tcp_try_connect(uv_connect_t* req,
memset(&req->u.io.overlapped, 0, sizeof(req->u.io.overlapped));
success = handle->tcp.conn.func_connectex(handle->socket,
addr,
addrlen,
NULL,
0,
&bytes,
&req->u.io.overlapped);
(const struct sockaddr*) &converted,
addrlen,
NULL,
0,
&bytes,
&req->u.io.overlapped);
if (UV_SUCCEEDED_WITHOUT_IOCP(success)) {
/* Process the req without IOCP. */

View File

@ -923,10 +923,15 @@ int uv__udp_try_send(uv_udp_t* handle,
unsigned int addrlen) {
DWORD bytes;
const struct sockaddr* bind_addr;
struct sockaddr_storage converted;
int err;
assert(nbufs > 0);
err = uv__convert_to_localhost_if_unspecified(addr, &converted);
if (err)
return err;
/* Already sending a message.*/
if (handle->send_queue_count != 0)
return UV_EAGAIN;
@ -948,7 +953,7 @@ int uv__udp_try_send(uv_udp_t* handle,
nbufs,
&bytes,
0,
addr,
(const struct sockaddr*) &converted,
addrlen,
NULL,
NULL);

View File

@ -559,3 +559,31 @@ int WSAAPI uv_msafd_poll(SOCKET socket, AFD_POLL_INFO* info_in,
return SOCKET_ERROR;
}
}
int uv__convert_to_localhost_if_unspecified(const struct sockaddr* addr,
struct sockaddr_storage* storage) {
struct sockaddr_in* dest4;
struct sockaddr_in6* dest6;
if (addr == NULL)
return UV_EINVAL;
switch (addr->sa_family) {
case AF_INET:
dest4 = (struct sockaddr_in*) storage;
memcpy(dest4, addr, sizeof(*dest4));
if (dest4->sin_addr.s_addr == 0)
dest4->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
return 0;
case AF_INET6:
dest6 = (struct sockaddr_in6*) storage;
memcpy(dest6, addr, sizeof(*dest6));
if (memcmp(&dest6->sin6_addr,
&uv_addr_ip6_any_.sin6_addr,
sizeof(uv_addr_ip6_any_.sin6_addr)) == 0)
dest6->sin6_addr = (struct in6_addr) IN6ADDR_LOOPBACK_INIT;
return 0;
default:
return UV_EINVAL;
}
}

View File

@ -187,4 +187,7 @@ typedef struct _IP_ADAPTER_UNICAST_ADDRESS_LH {
#endif
int uv__convert_to_localhost_if_unspecified(const struct sockaddr* addr,
struct sockaddr_storage* storage);
#endif /* UV_WIN_WINSOCK_H_ */

View File

@ -0,0 +1,61 @@
/* Copyright libuv project contributors. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include "uv.h"
#include "task.h"
static void connect_4(uv_connect_t* req, int status) {
ASSERT(status != UV_EADDRNOTAVAIL);
}
static void connect_6(uv_connect_t* req, int status) {
ASSERT(status != UV_EADDRNOTAVAIL);
}
TEST_IMPL(connect_unspecified) {
uv_loop_t* loop;
uv_tcp_t socket4;
struct sockaddr_in addr4;
uv_connect_t connect4;
uv_tcp_t socket6;
struct sockaddr_in6 addr6;
uv_connect_t connect6;
loop = uv_default_loop();
ASSERT(uv_tcp_init(loop, &socket4) == 0);
ASSERT(uv_ip4_addr("0.0.0.0", TEST_PORT, &addr4) == 0);
ASSERT(uv_tcp_connect(&connect4,
&socket4,
(const struct sockaddr*) &addr4,
connect_4) == 0);
ASSERT(uv_tcp_init(loop, &socket6) == 0);
ASSERT(uv_ip6_addr("::", TEST_PORT, &addr6) == 0);
ASSERT(uv_tcp_connect(&connect6,
&socket6,
(const struct sockaddr*) &addr6,
connect_6) == 0);
ASSERT(uv_run(loop, UV_RUN_DEFAULT) == 0);
return 0;
}

View File

@ -55,6 +55,7 @@ TEST_DECLARE (tty_file)
TEST_DECLARE (tty_pty)
TEST_DECLARE (stdio_over_pipes)
TEST_DECLARE (ip6_pton)
TEST_DECLARE (connect_unspecified)
TEST_DECLARE (ipc_listen_before_write)
TEST_DECLARE (ipc_listen_after_write)
#ifndef _WIN32
@ -468,6 +469,7 @@ TASK_LIST_START
TEST_ENTRY (tty_pty)
TEST_ENTRY (stdio_over_pipes)
TEST_ENTRY (ip6_pton)
TEST_ENTRY (connect_unspecified)
TEST_ENTRY (ipc_listen_before_write)
TEST_ENTRY (ipc_listen_after_write)
#ifndef _WIN32

1
uv.gyp
View File

@ -366,6 +366,7 @@
'test/test-callback-order.c',
'test/test-close-fd.c',
'test/test-close-order.c',
'test/test-connect-unspecified.c',
'test/test-connection-fail.c',
'test/test-cwd-and-chdir.c',
'test/test-default-loop-close.c',