unix: keep track of bound sockets sent via spawn

We use the UV_HANDLE_BOUND flag to mark a socket as bound to a
port. We need to do this for sockets that are sent from another
process as well as sockets that created by the process itself.
First check if the port number is non-zero. If yes then mark
it as bound.

PR-URL: https://github.com/libuv/libuv/pull/1348
Reviewed-By: Santiago Gimeno <santiago.gimeno@gmail.com>
This commit is contained in:
John Barboza 2017-05-15 11:32:33 -04:00 committed by Santiago Gimeno
parent 88d716e126
commit c5dd2d4218
No known key found for this signature in database
GPG Key ID: F28C3C8DA33C03BE
4 changed files with 158 additions and 26 deletions

View File

@ -28,15 +28,12 @@
#include <errno.h> #include <errno.h>
static int maybe_new_socket(uv_tcp_t* handle, int domain, int flags) { static int new_socket(uv_tcp_t* handle, int domain, unsigned long flags) {
struct sockaddr_storage saddr;
socklen_t slen;
int sockfd; int sockfd;
int err; int err;
if (domain == AF_UNSPEC || uv__stream_fd(handle) != -1) {
handle->flags |= flags;
return 0;
}
err = uv__socket(domain, SOCK_STREAM, 0); err = uv__socket(domain, SOCK_STREAM, 0);
if (err < 0) if (err < 0)
return err; return err;
@ -48,10 +45,74 @@ static int maybe_new_socket(uv_tcp_t* handle, int domain, int flags) {
return err; return err;
} }
if (flags & UV_HANDLE_BOUND) {
/* Bind this new socket to an arbitrary port */
slen = sizeof(saddr);
memset(&saddr, 0, sizeof(saddr));
err = getsockname(uv__stream_fd(handle), (struct sockaddr*) &saddr, &slen);
if (err) {
uv__close(sockfd);
return err;
}
err = bind(uv__stream_fd(handle), (struct sockaddr*) &saddr, slen);
if (err) {
uv__close(sockfd);
return err;
}
}
return 0; return 0;
} }
static int maybe_new_socket(uv_tcp_t* handle, int domain, unsigned long flags) {
struct sockaddr_storage saddr;
socklen_t slen;
if (domain == AF_UNSPEC) {
handle->flags |= flags;
return 0;
}
if (uv__stream_fd(handle) != -1) {
if (flags & UV_HANDLE_BOUND) {
if (handle->flags & UV_HANDLE_BOUND) {
/* It is already bound to a port. */
handle->flags |= flags;
return 0;
}
/* Query to see if tcp socket is bound. */
slen = sizeof(saddr);
memset(&saddr, 0, sizeof(saddr));
if (getsockname(uv__stream_fd(handle), (struct sockaddr*) &saddr, &slen))
return -errno;
if ((saddr.ss_family == AF_INET6 &&
((struct sockaddr_in6*) &saddr)->sin6_port != 0) ||
(saddr.ss_family == AF_INET &&
((struct sockaddr_in*) &saddr)->sin_port != 0)) {
/* Handle is already bound to a port. */
handle->flags |= flags;
return 0;
}
/* Bind to arbitrary port */
if (bind(uv__stream_fd(handle), (struct sockaddr*) &saddr, slen))
return -errno;
}
handle->flags |= flags;
return 0;
}
return new_socket(handle, domain, flags);
}
int uv_tcp_init_ex(uv_loop_t* loop, uv_tcp_t* tcp, unsigned int flags) { int uv_tcp_init_ex(uv_loop_t* loop, uv_tcp_t* tcp, unsigned int flags) {
int domain; int domain;
@ -260,6 +321,7 @@ int uv_tcp_getpeername(const uv_tcp_t* handle,
int uv_tcp_listen(uv_tcp_t* tcp, int backlog, uv_connection_cb cb) { int uv_tcp_listen(uv_tcp_t* tcp, int backlog, uv_connection_cb cb) {
static int single_accept = -1; static int single_accept = -1;
unsigned long flags;
int err; int err;
if (tcp->delayed_error) if (tcp->delayed_error)
@ -273,30 +335,17 @@ int uv_tcp_listen(uv_tcp_t* tcp, int backlog, uv_connection_cb cb) {
if (single_accept) if (single_accept)
tcp->flags |= UV_TCP_SINGLE_ACCEPT; tcp->flags |= UV_TCP_SINGLE_ACCEPT;
err = maybe_new_socket(tcp, AF_INET, UV_STREAM_READABLE); flags = UV_STREAM_READABLE;
if (err) #if defined(__MVS__)
return err;
#ifdef __MVS__
/* on zOS the listen call does not bind automatically /* on zOS the listen call does not bind automatically
if the socket is unbound. Hence the manual binding to if the socket is unbound. Hence the manual binding to
an arbitrary port is required to be done manually an arbitrary port is required to be done manually
*/ */
flags |= UV_HANDLE_BOUND;
if (!(tcp->flags & UV_HANDLE_BOUND)) { #endif
struct sockaddr_storage saddr; err = maybe_new_socket(tcp, AF_INET, flags);
socklen_t slen = sizeof(saddr); if (err)
memset(&saddr, 0, sizeof(saddr)); return err;
if (getsockname(tcp->io_watcher.fd, (struct sockaddr*) &saddr, &slen))
return -errno;
if (bind(tcp->io_watcher.fd, (struct sockaddr*) &saddr, slen))
return -errno;
tcp->flags |= UV_HANDLE_BOUND;
}
#endif
if (listen(tcp->io_watcher.fd, backlog)) if (listen(tcp->io_watcher.fd, backlog))
return -errno; return -errno;

View File

@ -43,6 +43,7 @@ int ipc_send_recv_helper(void);
int ipc_helper_bind_twice(void); int ipc_helper_bind_twice(void);
int stdio_over_pipes_helper(void); int stdio_over_pipes_helper(void);
int spawn_stdin_stdout(void); int spawn_stdin_stdout(void);
int spawn_tcp_server_helper(void);
static int maybe_run_test(int argc, char **argv); static int maybe_run_test(int argc, char **argv);
@ -111,6 +112,10 @@ static int maybe_run_test(int argc, char **argv) {
return 1; return 1;
} }
if (strcmp(argv[1], "spawn_tcp_server_helper") == 0) {
return spawn_tcp_server_helper();
}
if (strcmp(argv[1], "spawn_helper3") == 0) { if (strcmp(argv[1], "spawn_helper3") == 0) {
char buffer[256]; char buffer[256];
ASSERT(buffer == fgets(buffer, sizeof(buffer) - 1, stdin)); ASSERT(buffer == fgets(buffer, sizeof(buffer) - 1, stdin));

View File

@ -260,6 +260,7 @@ TEST_DECLARE (spawn_closed_process_io)
TEST_DECLARE (spawn_reads_child_path) TEST_DECLARE (spawn_reads_child_path)
TEST_DECLARE (spawn_inherit_streams) TEST_DECLARE (spawn_inherit_streams)
TEST_DECLARE (spawn_quoted_path) TEST_DECLARE (spawn_quoted_path)
TEST_DECLARE (spawn_tcp_server)
TEST_DECLARE (fs_poll) TEST_DECLARE (fs_poll)
TEST_DECLARE (fs_poll_getpath) TEST_DECLARE (fs_poll_getpath)
TEST_DECLARE (kill) TEST_DECLARE (kill)
@ -743,6 +744,7 @@ TASK_LIST_START
TEST_ENTRY (spawn_reads_child_path) TEST_ENTRY (spawn_reads_child_path)
TEST_ENTRY (spawn_inherit_streams) TEST_ENTRY (spawn_inherit_streams)
TEST_ENTRY (spawn_quoted_path) TEST_ENTRY (spawn_quoted_path)
TEST_ENTRY (spawn_tcp_server)
TEST_ENTRY (fs_poll) TEST_ENTRY (fs_poll)
TEST_ENTRY (fs_poll_getpath) TEST_ENTRY (fs_poll_getpath)
TEST_ENTRY (kill) TEST_ENTRY (kill)

View File

@ -50,6 +50,7 @@ static size_t exepath_size = 1024;
static char* args[5]; static char* args[5];
static int no_term_signal; static int no_term_signal;
static int timer_counter; static int timer_counter;
static uv_tcp_t tcp_server;
#define OUTPUT_SIZE 1024 #define OUTPUT_SIZE 1024
static char output[OUTPUT_SIZE]; static char output[OUTPUT_SIZE];
@ -622,6 +623,81 @@ TEST_IMPL(spawn_stdio_greater_than_3) {
} }
int spawn_tcp_server_helper(void) {
uv_tcp_t tcp;
uv_os_sock_t handle;
int r;
r = uv_tcp_init(uv_default_loop(), &tcp);
ASSERT(r == 0);
#ifdef _WIN32
handle = _get_osfhandle(3);
#else
handle = 3;
#endif
r = uv_tcp_open(&tcp, handle);
ASSERT(r == 0);
/* Make sure that we can listen on a socket that was
* passed down from the parent process
*/
r = uv_listen((uv_stream_t*)&tcp, SOMAXCONN, NULL);
ASSERT(r == 0);
return 1;
}
TEST_IMPL(spawn_tcp_server) {
uv_stdio_container_t stdio[4];
struct sockaddr_in addr;
int fd;
int r;
init_process_options("spawn_tcp_server_helper", exit_cb);
ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr));
fd = -1;
r = uv_tcp_init_ex(uv_default_loop(), &tcp_server, AF_INET);
ASSERT(r == 0);
r = uv_tcp_bind(&tcp_server, (const struct sockaddr*) &addr, 0);
ASSERT(r == 0);
#ifdef _WIN32
r = uv_fileno((uv_handle_t*)&tcp_server, &handle);
fd = _open_osfhandle((intptr_t) handle, 0);
#else
r = uv_fileno((uv_handle_t*)&tcp_server, &fd);
#endif
ASSERT(r == 0);
ASSERT(fd > 0);
options.stdio = stdio;
options.stdio[0].flags = UV_INHERIT_FD;
options.stdio[0].data.fd = 0;
options.stdio[1].flags = UV_INHERIT_FD;
options.stdio[1].data.fd = 1;
options.stdio[2].flags = UV_INHERIT_FD;
options.stdio[2].data.fd = 2;
options.stdio[3].flags = UV_INHERIT_FD;
options.stdio[3].data.fd = fd;
options.stdio_count = 4;
r = uv_spawn(uv_default_loop(), &process, &options);
ASSERT(r == 0);
r = uv_run(uv_default_loop(), UV_RUN_DEFAULT);
ASSERT(r == 0);
ASSERT(exit_cb_called == 1);
ASSERT(close_cb_called == 1);
MAKE_VALGRIND_HAPPY();
return 0;
}
TEST_IMPL(spawn_ignored_stdio) { TEST_IMPL(spawn_ignored_stdio) {
int r; int r;