Merge pull request #8835 from pmarks-net/master

port_posix: use IPv4+IPv6 sockets to check for a free port.
pull/8846/head^2
Paul Marks 8 years ago committed by GitHub
commit b0568043ef
  1. 100
      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");

Loading…
Cancel
Save