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:
parent
88d716e126
commit
c5dd2d4218
101
src/unix/tcp.c
101
src/unix/tcp.c
@ -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;
|
||||||
|
|||||||
@ -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));
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
@ -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;
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user