1 /*
2 * Copyright (c) 2014 DeNA Co., Ltd.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to
6 * deal in the Software without restriction, including without limitation the
7 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8 * sell copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
20 * IN THE SOFTWARE.
21 */
22 #include <errno.h>
23 #include <getopt.h>
24 #include <inttypes.h>
25 #include <netdb.h>
26 #include <netinet/in.h>
27 #include <stdio.h>
28 #include <sys/socket.h>
29 #include <sys/types.h>
30 #include <openssl/err.h>
31 #include <openssl/ssl.h>
32 #include "h2o/socket.h"
33 #include "h2o/string_.h"
34
35 /* configuration */
36 static char *host, *port;
37 static SSL_CTX *ssl_ctx;
38 static int mode_server, server_flag_received;
39 static h2o_socket_latency_optimization_conditions_t latopt_cond = {.min_rtt = 50, .max_additional_delay = 10, .max_cwnd = 65535};
40 size_t write_block_size = 65536;
41
42 /* globals */
43 static h2o_loop_t *loop;
44 static h2o_socket_t *sock;
45 static struct {
46 uint64_t resp_start_at;
47 uint64_t sig_received_at;
48 uint64_t bytes_received;
49 uint64_t bytes_before_sig;
50 } client_stats;
51
52 static void server_write(h2o_socket_t *sock);
53
prepare_write_buf(void)54 static h2o_iovec_t prepare_write_buf(void)
55 {
56 static h2o_iovec_t buf;
57 if (buf.base == NULL) {
58 buf.base = h2o_mem_alloc(write_block_size);
59 buf.len = write_block_size;
60 memset(buf.base, '0', buf.len);
61 }
62 return buf;
63 }
64
server_on_write_ready(h2o_socket_t * sock,const char * err)65 static void server_on_write_ready(h2o_socket_t *sock, const char *err)
66 {
67 if (err != NULL) {
68 fprintf(stderr, "socket unexpected closed by peer:%s\n", err);
69 exit(1);
70 return;
71 }
72 server_write(sock);
73 }
74
server_on_write_complete(h2o_socket_t * sock,const char * err)75 static void server_on_write_complete(h2o_socket_t *sock, const char *err)
76 {
77 if (err != NULL) {
78 fprintf(stderr, "write failed:%s\n", err);
79 exit(1);
80 return;
81 }
82 h2o_socket_notify_write(sock, server_on_write_ready);
83 }
84
server_write(h2o_socket_t * sock)85 void server_write(h2o_socket_t *sock)
86 {
87 size_t sz = h2o_socket_prepare_for_latency_optimized_write(sock, &latopt_cond);
88 h2o_iovec_t buf = prepare_write_buf();
89
90 if (server_flag_received)
91 buf.base[0] = '1';
92 if (sz < buf.len)
93 buf.len = sz;
94
95 fprintf(stderr, "writing %zu bytes\n", buf.len);
96 h2o_socket_write(sock, &buf, 1, server_on_write_complete);
97 }
98
server_on_read_second(h2o_socket_t * sock,const char * err)99 static void server_on_read_second(h2o_socket_t *sock, const char *err)
100 {
101 if (err != NULL) {
102 fprintf(stderr, "connection closed unexpectedly:%s\n", err);
103 exit(1);
104 return;
105 }
106
107 fprintf(stderr, "received the flag\n");
108 server_flag_received = 1;
109 }
110
server_on_read_first(h2o_socket_t * sock,const char * err)111 static void server_on_read_first(h2o_socket_t *sock, const char *err)
112 {
113 if (err != NULL) {
114 fprintf(stderr, "connection closed unexpectedly:%s\n", err);
115 exit(1);
116 return;
117 }
118
119 server_write(sock);
120 h2o_socket_read_start(sock, server_on_read_second);
121 }
122
client_on_write_complete(h2o_socket_t * sock,const char * err)123 static void client_on_write_complete(h2o_socket_t *sock, const char *err)
124 {
125 if (err == NULL)
126 return;
127 /* handle error */
128 fprintf(stderr, "write failed:%s\n", err);
129 h2o_socket_close(sock);
130 exit(1);
131 }
132
client_on_read_second(h2o_socket_t * sock,const char * err)133 static void client_on_read_second(h2o_socket_t *sock, const char *err)
134 {
135 size_t i;
136
137 if (err != NULL) {
138 fprintf(stderr, "connection closed unexpectedly:%s\n", err);
139 exit(1);
140 return;
141 }
142
143 if (client_stats.sig_received_at == 0) {
144 for (i = 0; i != sock->input->size; ++i) {
145 if (sock->input->bytes[i] != '0') {
146 client_stats.sig_received_at = h2o_now(h2o_socket_get_loop(sock));
147 break;
148 }
149 ++client_stats.bytes_before_sig;
150 }
151 }
152 client_stats.bytes_received += sock->input->size;
153 h2o_buffer_consume(&sock->input, sock->input->size);
154
155 if (client_stats.bytes_received >= 1024 * 1024) {
156 uint64_t now = h2o_now(h2o_socket_get_loop(sock));
157 printf("Delay: %" PRIu64 " octets, %" PRIu64 " ms\n", client_stats.bytes_before_sig,
158 client_stats.sig_received_at - client_stats.resp_start_at);
159 printf("Total: %" PRIu64 " octets, %" PRIu64 " ms\n", client_stats.bytes_received, now - client_stats.resp_start_at);
160 exit(0);
161 }
162 }
163
client_on_read_first(h2o_socket_t * sock,const char * err)164 static void client_on_read_first(h2o_socket_t *sock, const char *err)
165 {
166 if (err != NULL) {
167 fprintf(stderr, "connection closed unexpectedly:%s\n", err);
168 exit(1);
169 return;
170 }
171
172 client_stats.resp_start_at = h2o_now(h2o_socket_get_loop(sock));
173 client_stats.bytes_before_sig = sock->input->size;
174 client_stats.bytes_received = sock->input->size;
175 h2o_buffer_consume(&sock->input, sock->input->size);
176
177 h2o_iovec_t data = {H2O_STRLIT("!")};
178 h2o_socket_write(sock, &data, 1, client_on_write_complete);
179 h2o_socket_read_start(sock, client_on_read_second);
180 }
181
on_handshake_complete(h2o_socket_t * sock,const char * err)182 static void on_handshake_complete(h2o_socket_t *sock, const char *err)
183 {
184 if (err != NULL && err != h2o_socket_error_ssl_cert_name_mismatch) {
185 /* TLS handshake failed */
186 fprintf(stderr, "TLS handshake failure:%s\n", err);
187 ERR_print_errors_fp(stderr);
188 h2o_socket_close(sock);
189 exit(1);
190 return;
191 }
192
193 if (mode_server) {
194 h2o_socket_read_start(sock, server_on_read_first);
195 } else {
196 h2o_iovec_t buf = {H2O_STRLIT("0")};
197 h2o_socket_write(sock, &buf, 1, client_on_write_complete);
198 h2o_socket_read_start(sock, client_on_read_first);
199 }
200 }
201
on_connect(h2o_socket_t * sock,const char * err)202 static void on_connect(h2o_socket_t *sock, const char *err)
203 {
204 if (err != NULL) {
205 /* connection failed */
206 fprintf(stderr, "failed to connect to host:%s\n", err);
207 h2o_socket_close(sock);
208 exit(1);
209 return;
210 }
211
212 if (ssl_ctx != NULL) {
213 h2o_socket_ssl_handshake(sock, ssl_ctx, mode_server ? NULL : "blahblah", h2o_iovec_init(NULL, 0), on_handshake_complete);
214 } else {
215 on_handshake_complete(sock, NULL);
216 }
217 }
218
on_accept(h2o_socket_t * listener,const char * err)219 static void on_accept(h2o_socket_t *listener, const char *err)
220 {
221 if (err != NULL)
222 return;
223
224 if ((sock = h2o_evloop_socket_accept(listener)) != NULL) {
225 h2o_socket_close(listener);
226 if (ssl_ctx != NULL) {
227 h2o_socket_ssl_handshake(sock, ssl_ctx, mode_server ? NULL : "blahblah", h2o_iovec_init(NULL, 0),
228 on_handshake_complete);
229 } else {
230 on_handshake_complete(sock, NULL);
231 }
232 }
233 }
234
usage(const char * cmd)235 static void usage(const char *cmd)
236 {
237 fprintf(stderr,
238 "Usage: %s [opts] [<host>:]<port>\n"
239 "Options: --listen if set, waits for incoming connection. Otherwise,\n"
240 " connects to the server running at given address\n"
241 " --reverse-role if set, reverses the role bet. server and the\n"
242 " client once the connection is established\n"
243 " --tls use TLS\n"
244 " --block-size=octets default write block size\n"
245 " --min-rtt=ms minimum RTT to enable latency optimization\n"
246 " --max-cwnd=octets maximum size of CWND to enable latency\n"
247 " optimization\n",
248 cmd);
249 exit(1);
250 }
251
main(int argc,char ** argv)252 int main(int argc, char **argv)
253 {
254 static const struct option longopts[] = {{"listen", no_argument, NULL, 'l'},
255 {"reverse-role", no_argument, NULL, 'r'},
256 {"tls", no_argument, NULL, 't'},
257 {"block-size", no_argument, NULL, 'b'},
258 {"min-rtt", required_argument, NULL, 'R'},
259 {"max-cwnd", required_argument, NULL, 'c'},
260 {}};
261 int opt_ch, mode_listen = 0, mode_reverse_role = 0, mode_tls = 0;
262 struct addrinfo hints, *res = NULL;
263 int err;
264
265 while ((opt_ch = getopt_long(argc, argv, "lrtb:R:c:", longopts, NULL)) != -1) {
266 switch (opt_ch) {
267 case 'l':
268 mode_listen = 1;
269 break;
270 case 'r':
271 mode_reverse_role = 1;
272 break;
273 case 't':
274 mode_tls = 1;
275 break;
276 case 'b':
277 if (sscanf(optarg, "%zu", &write_block_size) != 1) {
278 fprintf(stderr, "write block size (-b) must be a non-negative number of octets\n");
279 exit(1);
280 }
281 break;
282 case 'R':
283 if (sscanf(optarg, "%u", &latopt_cond.min_rtt) != 1) {
284 fprintf(stderr, "min RTT (-m) must be a non-negative number in milliseconds\n");
285 exit(1);
286 }
287 break;
288 case 'c':
289 if (sscanf(optarg, "%u", &latopt_cond.max_cwnd) != 1) {
290 fprintf(stderr, "max CWND size must be a non-negative number of octets\n");
291 exit(1);
292 }
293 break;
294 default:
295 usage(argv[0]);
296 break;
297 }
298 }
299 mode_server = mode_listen;
300 if (mode_reverse_role)
301 mode_server = !mode_server;
302
303 if (argc == optind) {
304 usage(argv[0]);
305 } else {
306 char *hostport = argv[optind], *colon;
307 if ((colon = strchr(hostport, ':')) != NULL) {
308 hostport = argv[optind];
309 host = strdup(hostport);
310 host[colon - hostport] = '\0';
311 port = colon + 1;
312 } else {
313 host = "0.0.0.0";
314 port = argv[optind];
315 }
316 }
317
318 if (mode_tls) {
319 SSL_load_error_strings();
320 SSL_library_init();
321 OpenSSL_add_all_algorithms();
322 if (mode_server) {
323 ssl_ctx = SSL_CTX_new(SSLv23_server_method());
324 SSL_CTX_use_certificate_chain_file(ssl_ctx, "examples/h2o/server.crt");
325 SSL_CTX_use_PrivateKey_file(ssl_ctx, "examples/h2o/server.key", SSL_FILETYPE_PEM);
326 } else {
327 ssl_ctx = SSL_CTX_new(SSLv23_client_method());
328 }
329 int nid = NID_X9_62_prime256v1;
330 EC_KEY *key = EC_KEY_new_by_curve_name(nid);
331 if (key == NULL) {
332 fprintf(stderr, "Failed to create curve \"%s\"\n", OBJ_nid2sn(nid));
333 exit(1);
334 }
335 SSL_CTX_set_tmp_ecdh(ssl_ctx, key);
336 EC_KEY_free(key);
337 SSL_CTX_set_options(ssl_ctx, SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
338 SSL_CTX_set_cipher_list(ssl_ctx, "ECDHE-RSA-AES128-GCM-SHA256");
339 }
340
341 #if H2O_USE_LIBUV
342 loop = uv_loop_new();
343 #else
344 loop = h2o_evloop_create();
345 #endif
346
347 /* resolve host:port (FIXME use the function supplied by the loop) */
348 memset(&hints, 0, sizeof(hints));
349 hints.ai_socktype = SOCK_STREAM;
350 hints.ai_protocol = IPPROTO_TCP;
351 hints.ai_flags = AI_ADDRCONFIG;
352 if ((err = getaddrinfo(host, port, &hints, &res)) != 0) {
353 fprintf(stderr, "failed to resolve %s:%s:%s\n", host, port, gai_strerror(err));
354 exit(1);
355 }
356
357 if (mode_listen) {
358 int fd, reuseaddr_flag = 1;
359 if ((fd = socket(AF_INET, SOCK_STREAM, 0)) == -1 ||
360 setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuseaddr_flag, sizeof(reuseaddr_flag)) != 0 ||
361 bind(fd, res->ai_addr, res->ai_addrlen) != 0 || listen(fd, SOMAXCONN) != 0) {
362 fprintf(stderr, "failed to listen to %s:%s:%s\n", host, port, strerror(errno));
363 exit(1);
364 }
365 h2o_socket_t *listen_sock = h2o_evloop_socket_create(loop, fd, H2O_SOCKET_FLAG_DONT_READ);
366 h2o_socket_read_start(listen_sock, on_accept);
367 } else {
368 if ((sock = h2o_socket_connect(loop, res->ai_addr, res->ai_addrlen, on_connect, NULL)) == NULL) {
369 fprintf(stderr, "failed to create socket:%s\n", strerror(errno));
370 exit(1);
371 }
372 }
373
374 while (1) {
375 #if H2O_USE_LIBUV
376 uv_run(loop, UV_RUN_DEFAULT);
377 #else
378 h2o_evloop_run(loop, INT32_MAX);
379 #endif
380 }
381
382 return 0;
383 }
384