1
0
mirror of https://github.com/libuv/libuv synced 2025-03-28 21:13:16 +00:00

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>
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 err;
if (domain == AF_UNSPEC || uv__stream_fd(handle) != -1) {
handle->flags |= flags;
return 0;
}
err = uv__socket(domain, SOCK_STREAM, 0);
if (err < 0)
return err;
@ -48,10 +45,74 @@ static int maybe_new_socket(uv_tcp_t* handle, int domain, int flags) {
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;
}
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 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) {
static int single_accept = -1;
unsigned long flags;
int err;
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)
tcp->flags |= UV_TCP_SINGLE_ACCEPT;
err = maybe_new_socket(tcp, AF_INET, UV_STREAM_READABLE);
if (err)
return err;
#ifdef __MVS__
flags = UV_STREAM_READABLE;
#if defined(__MVS__)
/* on zOS the listen call does not bind automatically
if the socket is unbound. Hence the manual binding to
an arbitrary port is required to be done manually
*/
if (!(tcp->flags & UV_HANDLE_BOUND)) {
struct sockaddr_storage saddr;
socklen_t slen = sizeof(saddr);
memset(&saddr, 0, sizeof(saddr));
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
flags |= UV_HANDLE_BOUND;
#endif
err = maybe_new_socket(tcp, AF_INET, flags);
if (err)
return err;
if (listen(tcp->io_watcher.fd, backlog))
return -errno;

View File

@ -43,6 +43,7 @@ int ipc_send_recv_helper(void);
int ipc_helper_bind_twice(void);
int stdio_over_pipes_helper(void);
int spawn_stdin_stdout(void);
int spawn_tcp_server_helper(void);
static int maybe_run_test(int argc, char **argv);
@ -111,6 +112,10 @@ static int maybe_run_test(int argc, char **argv) {
return 1;
}
if (strcmp(argv[1], "spawn_tcp_server_helper") == 0) {
return spawn_tcp_server_helper();
}
if (strcmp(argv[1], "spawn_helper3") == 0) {
char buffer[256];
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_inherit_streams)
TEST_DECLARE (spawn_quoted_path)
TEST_DECLARE (spawn_tcp_server)
TEST_DECLARE (fs_poll)
TEST_DECLARE (fs_poll_getpath)
TEST_DECLARE (kill)
@ -743,6 +744,7 @@ TASK_LIST_START
TEST_ENTRY (spawn_reads_child_path)
TEST_ENTRY (spawn_inherit_streams)
TEST_ENTRY (spawn_quoted_path)
TEST_ENTRY (spawn_tcp_server)
TEST_ENTRY (fs_poll)
TEST_ENTRY (fs_poll_getpath)
TEST_ENTRY (kill)

View File

@ -50,6 +50,7 @@ static size_t exepath_size = 1024;
static char* args[5];
static int no_term_signal;
static int timer_counter;
static uv_tcp_t tcp_server;
#define OUTPUT_SIZE 1024
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) {
int r;