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