diff options
-rw-r--r-- | test/core/util/port_posix.c | 100 |
1 files changed, 58 insertions, 42 deletions
diff --git a/test/core/util/port_posix.c b/test/core/util/port_posix.c index 60537b4946..1cb4a8c05f 100644 --- a/test/core/util/port_posix.c +++ b/test/core/util/port_posix.c @@ -39,6 +39,7 @@ #include <errno.h> #include <netinet/in.h> +#include <stdbool.h> #include <stdio.h> #include <string.h> #include <sys/socket.h> @@ -50,6 +51,8 @@ #include <grpc/support/string_util.h> #include "src/core/lib/http/httpcli.h" +#include "src/core/lib/iomgr/resolve_address.h" +#include "src/core/lib/iomgr/sockaddr_utils.h" #include "src/core/lib/support/env.h" #include "test/core/util/port_server_client.h" @@ -115,55 +118,68 @@ static void chose_port(int port) { chosen_ports[num_chosen_ports - 1] = port; } -static int is_port_available(int *port, int is_tcp) { - const int proto = is_tcp ? IPPROTO_TCP : 0; - const int fd = socket(AF_INET, is_tcp ? SOCK_STREAM : SOCK_DGRAM, proto); - int one = 1; - struct sockaddr_in addr; - socklen_t alen = sizeof(addr); - int actual_port; - +static bool is_port_available(int *port, bool is_tcp) { GPR_ASSERT(*port >= 0); GPR_ASSERT(*port <= 65535); - if (fd < 0) { - gpr_log(GPR_ERROR, "socket() failed: %s", strerror(errno)); - return 0; - } - /* Reuseaddr lets us start up a server immediately after it exits */ - if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) < 0) { - gpr_log(GPR_ERROR, "setsockopt() failed: %s", strerror(errno)); - close(fd); - return 0; - } + /* For a port to be considered available, the kernel must support + at least one of (IPv6, IPv4), and the port must be available + on each supported family. */ + bool got_socket = false; + for (int is_ipv6 = 1; is_ipv6 >= 0; is_ipv6--) { + const int fd = socket(is_ipv6 ? AF_INET6 : AF_INET, + is_tcp ? SOCK_STREAM : SOCK_DGRAM, + is_tcp ? IPPROTO_TCP : 0); + if (fd >= 0) { + got_socket = true; + } else { + continue; + } - /* Try binding to port */ - addr.sin_family = AF_INET; - addr.sin_addr.s_addr = INADDR_ANY; - addr.sin_port = htons((uint16_t)*port); - if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { - gpr_log(GPR_DEBUG, "bind(port=%d) failed: %s", *port, strerror(errno)); - close(fd); - return 0; - } + /* Reuseaddr lets us start up a server immediately after it exits */ + const int one = 1; + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) < 0) { + gpr_log(GPR_ERROR, "setsockopt() failed: %s", strerror(errno)); + close(fd); + return false; + } + + /* Try binding to port */ + grpc_resolved_address addr; + if (is_ipv6) { + grpc_sockaddr_make_wildcard6(*port, &addr); /* [::]:port */ + } else { + grpc_sockaddr_make_wildcard4(*port, &addr); /* 0.0.0.0:port */ + } + if (bind(fd, (struct sockaddr *)addr.addr, (socklen_t)addr.len) < 0) { + gpr_log(GPR_DEBUG, "bind(port=%d) failed: %s", *port, strerror(errno)); + close(fd); + return false; + } + + /* Get the bound port number */ + if (getsockname(fd, (struct sockaddr *)addr.addr, + (socklen_t *)&addr.len) < 0) { + gpr_log(GPR_ERROR, "getsockname() failed: %s", strerror(errno)); + close(fd); + return false; + } + GPR_ASSERT(addr.len <= sizeof(addr.addr)); + const int actual_port = grpc_sockaddr_get_port(&addr); + GPR_ASSERT(actual_port > 0); + if (*port == 0) { + *port = actual_port; + } else { + GPR_ASSERT(*port == actual_port); + } - /* Get the bound port number */ - if (getsockname(fd, (struct sockaddr *)&addr, &alen) < 0) { - gpr_log(GPR_ERROR, "getsockname() failed: %s", strerror(errno)); close(fd); - return 0; } - GPR_ASSERT(alen <= sizeof(addr)); - actual_port = ntohs(addr.sin_port); - GPR_ASSERT(actual_port > 0); - if (*port == 0) { - *port = actual_port; - } else { - GPR_ASSERT(*port == actual_port); + if (!got_socket) { + gpr_log(GPR_ERROR, "socket() failed: %s", strerror(errno)); + return false; } - - close(fd); - return 1; + return true; } int grpc_pick_unused_port(void) { @@ -180,7 +196,7 @@ int grpc_pick_unused_port(void) { UDP ports and they are scarcer. */ /* Type of port to first pick in next iteration */ - int is_tcp = 1; + bool is_tcp = true; int trial = 0; char *env = gpr_getenv("GRPC_TEST_PORT_SERVER"); |