blob: 7467c2f9ea4099ce668af67228cb081eb1eb1300 [file] [log] [blame]
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -08001/*
2 *
Craig Tiller06059952015-02-18 08:34:56 -08003 * Copyright 2015, Google Inc.
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -08004 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are
8 * met:
9 *
10 * * Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above
13 * copyright notice, this list of conditions and the following disclaimer
14 * in the documentation and/or other materials provided with the
15 * distribution.
16 * * Neither the name of Google Inc. nor the names of its
17 * contributors may be used to endorse or promote products derived from
18 * this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 *
32 */
33
jtattermusch97fb3f62014-12-08 15:13:41 -080034#include <grpc/support/port_platform.h>
Yang Gao5a5032d2015-04-01 13:54:44 -070035#include "test/core/util/test_config.h"
36#if defined(GPR_POSIX_SOCKET) && defined(GRPC_TEST_PICK_PORT)
jtattermusch97fb3f62014-12-08 15:13:41 -080037
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080038#include "test/core/util/port.h"
39
40#include <netinet/in.h>
41#include <sys/socket.h>
42#include <stdio.h>
43#include <errno.h>
44#include <string.h>
45#include <unistd.h>
46
47#include <grpc/support/log.h>
48
49#define NUM_RANDOM_PORTS_TO_PICK 100
50
51static int is_port_available(int *port, int is_tcp) {
52 const int proto = is_tcp ? IPPROTO_TCP : 0;
53 const int fd = socket(AF_INET, is_tcp ? SOCK_STREAM : SOCK_DGRAM, proto);
54 int one = 1;
55 struct sockaddr_in addr;
56 socklen_t alen = sizeof(addr);
57 int actual_port;
58
59 GPR_ASSERT(*port >= 0);
60 GPR_ASSERT(*port <= 65535);
61 if (fd < 0) {
62 gpr_log(GPR_ERROR, "socket() failed: %s", strerror(errno));
63 return 0;
64 }
65
66 /* Reuseaddr lets us start up a server immediately after it exits */
67 if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) < 0) {
68 gpr_log(GPR_ERROR, "setsockopt() failed: %s", strerror(errno));
69 close(fd);
70 return 0;
71 }
72
73 /* Try binding to port */
74 addr.sin_family = AF_INET;
75 addr.sin_addr.s_addr = INADDR_ANY;
76 addr.sin_port = htons(*port);
77 if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
78 gpr_log(GPR_DEBUG, "bind(port=%d) failed: %s", *port, strerror(errno));
79 close(fd);
80 return 0;
81 }
82
83 /* Get the bound port number */
84 if (getsockname(fd, (struct sockaddr *)&addr, &alen) < 0) {
85 gpr_log(GPR_ERROR, "getsockname() failed: %s", strerror(errno));
86 close(fd);
87 return 0;
88 }
89 GPR_ASSERT(alen <= sizeof(addr));
90 actual_port = ntohs(addr.sin_port);
91 GPR_ASSERT(actual_port > 0);
92 if (*port == 0) {
93 *port = actual_port;
94 } else {
95 GPR_ASSERT(*port == actual_port);
96 }
97
98 close(fd);
99 return 1;
100}
101
Craig Tiller32946d32015-01-15 11:37:30 -0800102int grpc_pick_unused_port(void) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800103 /* We repeatedly pick a port and then see whether or not it is
104 available for use both as a TCP socket and a UDP socket. First, we
105 pick a random large port number. For subsequent
106 iterations, we bind to an anonymous port and let the OS pick the
107 port number. The random port picking reduces the probability of
108 races with other processes on kernels that want to reuse the same
109 port numbers over and over. */
110
111 /* In alternating iterations we try UDP ports before TCP ports UDP
112 ports -- it could be the case that this machine has been using up
113 UDP ports and they are scarcer. */
114
115 /* Type of port to first pick in next iteration */
116 int is_tcp = 1;
Craig Tiller73509a52015-02-26 17:43:49 -0800117 int try = 0;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800118
119 for (;;) {
Craig Tiller73509a52015-02-26 17:43:49 -0800120 int port;
Craig Tiller7b5eb712015-02-27 07:52:09 -0800121 try++;
122 if (try == 1) {
Craig Tiller73509a52015-02-26 17:43:49 -0800123 port = getpid() % (65536 - 30000) + 30000;
Craig Tiller7b5eb712015-02-27 07:52:09 -0800124 } else if (try <= NUM_RANDOM_PORTS_TO_PICK) {
Craig Tiller73509a52015-02-26 17:43:49 -0800125 port = rand() % (65536 - 30000) + 30000;
126 } else {
127 port = 0;
128 }
Yang Gao5a5032d2015-04-01 13:54:44 -0700129
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800130 if (!is_port_available(&port, is_tcp)) {
131 continue;
132 }
Craig Tiller73509a52015-02-26 17:43:49 -0800133
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800134 GPR_ASSERT(port > 0);
135 /* Check that the port # is free for the other type of socket also */
136 if (!is_port_available(&port, !is_tcp)) {
137 /* In the next iteration try to bind to the other type first
138 because perhaps it is more rare. */
139 is_tcp = !is_tcp;
140 continue;
141 }
142
143 /* TODO(ctiller): consider caching this port in some structure, to avoid
144 handing it out again */
145
146 return port;
147 }
148
149 /* The port iterator reached the end without finding a suitable port. */
150 return 0;
151}
152
Craig Tiller32946d32015-01-15 11:37:30 -0800153int grpc_pick_unused_port_or_die(void) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800154 int port = grpc_pick_unused_port();
155 GPR_ASSERT(port > 0);
156 return port;
157}
jtattermusch97fb3f62014-12-08 15:13:41 -0800158
Yang Gao5a5032d2015-04-01 13:54:44 -0700159#endif /* GPR_POSIX_SOCKET && GRPC_TEST_PICK_PORT */