blob: 36c8d2856ce65d90b0430213b4671c3ad2c8b2a9 [file] [log] [blame]
Craig Tiller34cf2f32015-04-08 16:10:00 -07001/*
2 *
Craig Tiller6169d5f2016-03-31 07:46:18 -07003 * Copyright 2015, Google Inc.
Craig Tiller34cf2f32015-04-08 16:10:00 -07004 * 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
34#include <grpc/support/port_platform.h>
35#include "test/core/util/test_config.h"
36#if defined(GPR_WINSOCK_SOCKET) && defined(GRPC_TEST_PICK_PORT)
37
38#include "test/core/util/port.h"
39
Craig Tillerf40df232016-03-25 13:38:14 -070040#include <errno.h>
Craig Tillerf7262052015-04-08 16:13:58 -070041#include <process.h>
Craig Tiller34cf2f32015-04-08 16:10:00 -070042#include <stdio.h>
Craig Tiller34cf2f32015-04-08 16:10:00 -070043#include <string.h>
Craig Tiller34cf2f32015-04-08 16:10:00 -070044
Craig Tillerb0298592015-08-27 07:38:01 -070045#include <grpc/grpc.h>
46#include <grpc/support/alloc.h>
Craig Tiller34cf2f32015-04-08 16:10:00 -070047#include <grpc/support/log.h>
48
Craig Tiller9533d042016-03-25 17:11:06 -070049#include "src/core/lib/http/httpcli.h"
50#include "src/core/lib/iomgr/sockaddr_utils.h"
51#include "src/core/lib/support/env.h"
Craig Tiller4cc04f82016-03-17 08:56:20 -070052#include "test/core/util/port_server_client.h"
Craig Tillerb0298592015-08-27 07:38:01 -070053
Nicolas "Pixel" Noblec4b18a52016-04-15 04:53:54 +020054#if GPR_GETPID_IN_UNISTD_H
55#include <sys/unistd.h>
56static int _getpid() { return getpid(); }
57#endif
58
Craig Tiller34cf2f32015-04-08 16:10:00 -070059#define NUM_RANDOM_PORTS_TO_PICK 100
60
Craig Tiller4326f732015-09-02 12:36:25 -070061static int *chosen_ports = NULL;
62static size_t num_chosen_ports = 0;
63
Craig Tillera82950e2015-09-22 12:33:20 -070064static int has_port_been_chosen(int port) {
Craig Tiller4326f732015-09-02 12:36:25 -070065 size_t i;
Craig Tillera82950e2015-09-22 12:33:20 -070066 for (i = 0; i < num_chosen_ports; i++) {
67 if (chosen_ports[i] == port) {
68 return 1;
Craig Tiller4326f732015-09-02 12:36:25 -070069 }
Craig Tillera82950e2015-09-22 12:33:20 -070070 }
Craig Tiller4326f732015-09-02 12:36:25 -070071 return 0;
72}
73
David Klempner2754c912016-05-13 13:11:28 -070074static int free_chosen_port(int port) {
75 size_t i;
76 int found = 0;
77 size_t found_at = 0;
78 char *env = gpr_getenv("GRPC_TEST_PORT_SERVER");
79 if (env != NULL) {
80 /* Find the port and erase it from the list, then tell the server it can be
81 freed. */
82 for (i = 0; i < num_chosen_ports; i++) {
83 if (chosen_ports[i] == port) {
84 GPR_ASSERT(found == 0);
85 found = 1;
86 found_at = i;
87 }
88 }
89 if (found) {
90 chosen_ports[found_at] = chosen_ports[num_chosen_ports - 1];
91 grpc_free_port_using_server(env, port);
92 num_chosen_ports--;
David Klempner2754c912016-05-13 13:11:28 -070093 }
94 }
95 return found;
96}
97
Craig Tiller4cc04f82016-03-17 08:56:20 -070098static void free_chosen_ports(void) {
99 char *env = gpr_getenv("GRPC_TEST_PORT_SERVER");
100 if (env != NULL) {
101 size_t i;
102 for (i = 0; i < num_chosen_ports; i++) {
103 grpc_free_port_using_server(env, chosen_ports[i]);
104 }
105 gpr_free(env);
106 }
107
108 gpr_free(chosen_ports);
109}
Craig Tiller4326f732015-09-02 12:36:25 -0700110
Craig Tillera82950e2015-09-22 12:33:20 -0700111static void chose_port(int port) {
112 if (chosen_ports == NULL) {
113 atexit(free_chosen_ports);
114 }
Craig Tiller4326f732015-09-02 12:36:25 -0700115 num_chosen_ports++;
Craig Tillera82950e2015-09-22 12:33:20 -0700116 chosen_ports = gpr_realloc(chosen_ports, sizeof(int) * num_chosen_ports);
Craig Tiller4326f732015-09-02 12:36:25 -0700117 chosen_ports[num_chosen_ports - 1] = port;
118}
119
Craig Tillera82950e2015-09-22 12:33:20 -0700120static int is_port_available(int *port, int is_tcp) {
Craig Tiller34cf2f32015-04-08 16:10:00 -0700121 const int proto = is_tcp ? IPPROTO_TCP : 0;
Craig Tillera82950e2015-09-22 12:33:20 -0700122 const SOCKET fd = socket(AF_INET, is_tcp ? SOCK_STREAM : SOCK_DGRAM, proto);
Craig Tiller34cf2f32015-04-08 16:10:00 -0700123 int one = 1;
124 struct sockaddr_in addr;
Craig Tillera82950e2015-09-22 12:33:20 -0700125 socklen_t alen = sizeof(addr);
Craig Tiller34cf2f32015-04-08 16:10:00 -0700126 int actual_port;
127
Craig Tillera82950e2015-09-22 12:33:20 -0700128 GPR_ASSERT(*port >= 0);
129 GPR_ASSERT(*port <= 65535);
130 if (INVALID_SOCKET == fd) {
131 gpr_log(GPR_ERROR, "socket() failed: %s", strerror(errno));
132 return 0;
133 }
Craig Tiller34cf2f32015-04-08 16:10:00 -0700134
135 /* Reuseaddr lets us start up a server immediately after it exits */
Craig Tillera82950e2015-09-22 12:33:20 -0700136 if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const char *)&one,
137 sizeof(one)) < 0) {
138 gpr_log(GPR_ERROR, "setsockopt() failed: %s", strerror(errno));
139 closesocket(fd);
140 return 0;
141 }
Craig Tiller34cf2f32015-04-08 16:10:00 -0700142
143 /* Try binding to port */
144 addr.sin_family = AF_INET;
145 addr.sin_addr.s_addr = INADDR_ANY;
Nicolas "Pixel" Noblec4b18a52016-04-15 04:53:54 +0200146 addr.sin_port = htons((u_short)*port);
Craig Tillera82950e2015-09-22 12:33:20 -0700147 if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
148 gpr_log(GPR_DEBUG, "bind(port=%d) failed: %s", *port, strerror(errno));
149 closesocket(fd);
150 return 0;
151 }
Craig Tiller34cf2f32015-04-08 16:10:00 -0700152
153 /* Get the bound port number */
Craig Tillera82950e2015-09-22 12:33:20 -0700154 if (getsockname(fd, (struct sockaddr *)&addr, &alen) < 0) {
155 gpr_log(GPR_ERROR, "getsockname() failed: %s", strerror(errno));
156 closesocket(fd);
157 return 0;
158 }
Nicolas "Pixel" Noblec4b18a52016-04-15 04:53:54 +0200159 GPR_ASSERT(alen <= (socklen_t)sizeof(addr));
Craig Tillera82950e2015-09-22 12:33:20 -0700160 actual_port = ntohs(addr.sin_port);
161 GPR_ASSERT(actual_port > 0);
162 if (*port == 0) {
163 *port = actual_port;
164 } else {
165 GPR_ASSERT(*port == actual_port);
166 }
Craig Tiller34cf2f32015-04-08 16:10:00 -0700167
Craig Tillera82950e2015-09-22 12:33:20 -0700168 closesocket(fd);
Craig Tiller34cf2f32015-04-08 16:10:00 -0700169 return 1;
170}
171
Craig Tillera82950e2015-09-22 12:33:20 -0700172int grpc_pick_unused_port(void) {
Craig Tiller34cf2f32015-04-08 16:10:00 -0700173 /* We repeatedly pick a port and then see whether or not it is
174 available for use both as a TCP socket and a UDP socket. First, we
175 pick a random large port number. For subsequent
176 iterations, we bind to an anonymous port and let the OS pick the
177 port number. The random port picking reduces the probability of
178 races with other processes on kernels that want to reuse the same
179 port numbers over and over. */
180
Craig Tillerb0298592015-08-27 07:38:01 -0700181 /* In alternating iterations we trial UDP ports before TCP ports UDP
Craig Tiller34cf2f32015-04-08 16:10:00 -0700182 ports -- it could be the case that this machine has been using up
183 UDP ports and they are scarcer. */
184
185 /* Type of port to first pick in next iteration */
186 int is_tcp = 1;
Craig Tillerb0298592015-08-27 07:38:01 -0700187 int trial = 0;
188
Craig Tillera82950e2015-09-22 12:33:20 -0700189 char *env = gpr_getenv("GRPC_TEST_PORT_SERVER");
190 if (env) {
Craig Tiller4cc04f82016-03-17 08:56:20 -0700191 int port = grpc_pick_port_using_server(env);
Craig Tillera82950e2015-09-22 12:33:20 -0700192 gpr_free(env);
193 if (port != 0) {
Craig Tillerb0298592015-08-27 07:38:01 -0700194 return port;
195 }
Craig Tillera82950e2015-09-22 12:33:20 -0700196 }
197
198 for (;;) {
199 int port;
200 trial++;
201 if (trial == 1) {
202 port = _getpid() % (65536 - 30000) + 30000;
203 } else if (trial <= NUM_RANDOM_PORTS_TO_PICK) {
204 port = rand() % (65536 - 30000) + 30000;
205 } else {
206 port = 0;
207 }
208
209 if (has_port_been_chosen(port)) {
210 continue;
211 }
212
213 if (!is_port_available(&port, is_tcp)) {
214 continue;
215 }
216
217 GPR_ASSERT(port > 0);
218 /* Check that the port # is free for the other type of socket also */
219 if (!is_port_available(&port, !is_tcp)) {
220 /* In the next iteration trial to bind to the other type first
221 because perhaps it is more rare. */
222 is_tcp = !is_tcp;
223 continue;
224 }
225
226 /* TODO(ctiller): consider caching this port in some structure, to avoid
227 handing it out again */
228
229 chose_port(port);
230 return port;
231 }
Craig Tiller34cf2f32015-04-08 16:10:00 -0700232
233 /* The port iterator reached the end without finding a suitable port. */
234 return 0;
235}
236
Craig Tillera82950e2015-09-22 12:33:20 -0700237int grpc_pick_unused_port_or_die(void) {
238 int port = grpc_pick_unused_port();
239 GPR_ASSERT(port > 0);
Craig Tiller34cf2f32015-04-08 16:10:00 -0700240 return port;
241}
242
David Klempner2754c912016-05-13 13:11:28 -0700243void grpc_recycle_unused_port(int port) {
244 GPR_ASSERT(free_chosen_port(port));
245}
246
Craig Tiller34cf2f32015-04-08 16:10:00 -0700247#endif /* GPR_WINSOCK_SOCKET && GRPC_TEST_PICK_PORT */