1 // Copyright (C) 2018-2019, Cloudflare, Inc.
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
6 // met:
7 //
8 // * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 //
11 // * Redistributions in binary form must reproduce the above copyright
12 // notice, this list of conditions and the following disclaimer in the
13 // documentation and/or other materials provided with the distribution.
14 //
15 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
16 // IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
17 // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18 // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
19 // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20 // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22 // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
23 // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
24 // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
27 #include <inttypes.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <stdint.h>
31 #include <stdbool.h>
32 #include <unistd.h>
33
34 #include <fcntl.h>
35 #include <errno.h>
36
37 #include <sys/types.h>
38 #include <sys/socket.h>
39 #include <netdb.h>
40
41 #include <ev.h>
42 #include <uthash.h>
43
44 #include <quiche.h>
45
46 #define LOCAL_CONN_ID_LEN 16
47
48 #define MAX_DATAGRAM_SIZE 1350
49
50 #define MAX_TOKEN_LEN \
51 sizeof("quiche") - 1 + \
52 sizeof(struct sockaddr_storage) + \
53 QUICHE_MAX_CONN_ID_LEN
54
55 struct connections {
56 int sock;
57
58 struct sockaddr *local_addr;
59 socklen_t local_addr_len;
60
61 struct conn_io *h;
62 };
63
64 struct conn_io {
65 ev_timer timer;
66
67 int sock;
68
69 uint8_t cid[LOCAL_CONN_ID_LEN];
70
71 quiche_conn *conn;
72 quiche_h3_conn *http3;
73
74 struct sockaddr_storage peer_addr;
75 socklen_t peer_addr_len;
76
77 UT_hash_handle hh;
78 };
79
80 static quiche_config *config = NULL;
81
82 static quiche_h3_config *http3_config = NULL;
83
84 static struct connections *conns = NULL;
85
86 static void timeout_cb(EV_P_ ev_timer *w, int revents);
87
debug_log(const char * line,void * argp)88 static void debug_log(const char *line, void *argp) {
89 fprintf(stderr, "%s\n", line);
90 }
91
flush_egress(struct ev_loop * loop,struct conn_io * conn_io)92 static void flush_egress(struct ev_loop *loop, struct conn_io *conn_io) {
93 static uint8_t out[MAX_DATAGRAM_SIZE];
94
95 quiche_send_info send_info;
96
97 while (1) {
98 ssize_t written = quiche_conn_send(conn_io->conn, out, sizeof(out),
99 &send_info);
100
101 if (written == QUICHE_ERR_DONE) {
102 fprintf(stderr, "done writing\n");
103 break;
104 }
105
106 if (written < 0) {
107 fprintf(stderr, "failed to create packet: %zd\n", written);
108 return;
109 }
110
111 ssize_t sent = sendto(conn_io->sock, out, written, 0,
112 (struct sockaddr *) &conn_io->peer_addr,
113 conn_io->peer_addr_len);
114 if (sent != written) {
115 perror("failed to send");
116 return;
117 }
118
119 fprintf(stderr, "sent %zd bytes\n", sent);
120 }
121
122 double t = quiche_conn_timeout_as_nanos(conn_io->conn) / 1e9f;
123 conn_io->timer.repeat = t;
124 ev_timer_again(loop, &conn_io->timer);
125 }
126
mint_token(const uint8_t * dcid,size_t dcid_len,struct sockaddr_storage * addr,socklen_t addr_len,uint8_t * token,size_t * token_len)127 static void mint_token(const uint8_t *dcid, size_t dcid_len,
128 struct sockaddr_storage *addr, socklen_t addr_len,
129 uint8_t *token, size_t *token_len) {
130 memcpy(token, "quiche", sizeof("quiche") - 1);
131 memcpy(token + sizeof("quiche") - 1, addr, addr_len);
132 memcpy(token + sizeof("quiche") - 1 + addr_len, dcid, dcid_len);
133
134 *token_len = sizeof("quiche") - 1 + addr_len + dcid_len;
135 }
136
validate_token(const uint8_t * token,size_t token_len,struct sockaddr_storage * addr,socklen_t addr_len,uint8_t * odcid,size_t * odcid_len)137 static bool validate_token(const uint8_t *token, size_t token_len,
138 struct sockaddr_storage *addr, socklen_t addr_len,
139 uint8_t *odcid, size_t *odcid_len) {
140 if ((token_len < sizeof("quiche") - 1) ||
141 memcmp(token, "quiche", sizeof("quiche") - 1)) {
142 return false;
143 }
144
145 token += sizeof("quiche") - 1;
146 token_len -= sizeof("quiche") - 1;
147
148 if ((token_len < addr_len) || memcmp(token, addr, addr_len)) {
149 return false;
150 }
151
152 token += addr_len;
153 token_len -= addr_len;
154
155 if (*odcid_len < token_len) {
156 return false;
157 }
158
159 memcpy(odcid, token, token_len);
160 *odcid_len = token_len;
161
162 return true;
163 }
164
gen_cid(uint8_t * cid,size_t cid_len)165 static uint8_t *gen_cid(uint8_t *cid, size_t cid_len) {
166 int rng = open("/dev/urandom", O_RDONLY);
167 if (rng < 0) {
168 perror("failed to open /dev/urandom");
169 return NULL;
170 }
171
172 ssize_t rand_len = read(rng, cid, cid_len);
173 if (rand_len < 0) {
174 perror("failed to create connection ID");
175 return NULL;
176 }
177
178 return cid;
179 }
180
create_conn(uint8_t * scid,size_t scid_len,uint8_t * odcid,size_t odcid_len,struct sockaddr * local_addr,socklen_t local_addr_len,struct sockaddr_storage * peer_addr,socklen_t peer_addr_len)181 static struct conn_io *create_conn(uint8_t *scid, size_t scid_len,
182 uint8_t *odcid, size_t odcid_len,
183 struct sockaddr *local_addr,
184 socklen_t local_addr_len,
185 struct sockaddr_storage *peer_addr,
186 socklen_t peer_addr_len)
187 {
188 struct conn_io *conn_io = calloc(1, sizeof(*conn_io));
189 if (conn_io == NULL) {
190 fprintf(stderr, "failed to allocate connection IO\n");
191 return NULL;
192 }
193
194 if (scid_len != LOCAL_CONN_ID_LEN) {
195 fprintf(stderr, "failed, scid length too short\n");
196 }
197
198 memcpy(conn_io->cid, scid, LOCAL_CONN_ID_LEN);
199
200 quiche_conn *conn = quiche_accept(conn_io->cid, LOCAL_CONN_ID_LEN,
201 odcid, odcid_len,
202 local_addr,
203 local_addr_len,
204 (struct sockaddr *) peer_addr,
205 peer_addr_len,
206 config);
207
208 if (conn == NULL) {
209 fprintf(stderr, "failed to create connection\n");
210 return NULL;
211 }
212
213 conn_io->sock = conns->sock;
214 conn_io->conn = conn;
215
216 memcpy(&conn_io->peer_addr, peer_addr, peer_addr_len);
217 conn_io->peer_addr_len = peer_addr_len;
218
219 ev_init(&conn_io->timer, timeout_cb);
220 conn_io->timer.data = conn_io;
221
222 HASH_ADD(hh, conns->h, cid, LOCAL_CONN_ID_LEN, conn_io);
223
224 fprintf(stderr, "new connection\n");
225
226 return conn_io;
227 }
228
for_each_header(uint8_t * name,size_t name_len,uint8_t * value,size_t value_len,void * argp)229 static int for_each_header(uint8_t *name, size_t name_len,
230 uint8_t *value, size_t value_len,
231 void *argp) {
232 fprintf(stderr, "got HTTP header: %.*s=%.*s\n",
233 (int) name_len, name, (int) value_len, value);
234
235 return 0;
236 }
237
recv_cb(EV_P_ ev_io * w,int revents)238 static void recv_cb(EV_P_ ev_io *w, int revents) {
239 struct conn_io *tmp, *conn_io = NULL;
240
241 static uint8_t buf[65535];
242 static uint8_t out[MAX_DATAGRAM_SIZE];
243
244 while (1) {
245 struct sockaddr_storage peer_addr;
246 socklen_t peer_addr_len = sizeof(peer_addr);
247 memset(&peer_addr, 0, peer_addr_len);
248
249 ssize_t read = recvfrom(conns->sock, buf, sizeof(buf), 0,
250 (struct sockaddr *) &peer_addr,
251 &peer_addr_len);
252
253 if (read < 0) {
254 if ((errno == EWOULDBLOCK) || (errno == EAGAIN)) {
255 fprintf(stderr, "recv would block\n");
256 break;
257 }
258
259 perror("failed to read");
260 return;
261 }
262
263 uint8_t type;
264 uint32_t version;
265
266 uint8_t scid[QUICHE_MAX_CONN_ID_LEN];
267 size_t scid_len = sizeof(scid);
268
269 uint8_t dcid[QUICHE_MAX_CONN_ID_LEN];
270 size_t dcid_len = sizeof(dcid);
271
272 uint8_t odcid[QUICHE_MAX_CONN_ID_LEN];
273 size_t odcid_len = sizeof(odcid);
274
275 uint8_t token[MAX_TOKEN_LEN];
276 size_t token_len = sizeof(token);
277
278 int rc = quiche_header_info(buf, read, LOCAL_CONN_ID_LEN, &version,
279 &type, scid, &scid_len, dcid, &dcid_len,
280 token, &token_len);
281 if (rc < 0) {
282 fprintf(stderr, "failed to parse header: %d\n", rc);
283 return;
284 }
285
286 HASH_FIND(hh, conns->h, dcid, dcid_len, conn_io);
287
288 if (conn_io == NULL) {
289 if (!quiche_version_is_supported(version)) {
290 fprintf(stderr, "version negotiation\n");
291
292 ssize_t written = quiche_negotiate_version(scid, scid_len,
293 dcid, dcid_len,
294 out, sizeof(out));
295
296 if (written < 0) {
297 fprintf(stderr, "failed to create vneg packet: %zd\n",
298 written);
299 continue;
300 }
301
302 ssize_t sent = sendto(conns->sock, out, written, 0,
303 (struct sockaddr *) &peer_addr,
304 peer_addr_len);
305 if (sent != written) {
306 perror("failed to send");
307 continue;
308 }
309
310 fprintf(stderr, "sent %zd bytes\n", sent);
311 continue;
312 }
313
314 if (token_len == 0) {
315 fprintf(stderr, "stateless retry\n");
316
317 mint_token(dcid, dcid_len, &peer_addr, peer_addr_len,
318 token, &token_len);
319
320 uint8_t new_cid[LOCAL_CONN_ID_LEN];
321
322 if (gen_cid(new_cid, LOCAL_CONN_ID_LEN) == NULL) {
323 continue;
324 }
325
326 ssize_t written = quiche_retry(scid, scid_len,
327 dcid, dcid_len,
328 new_cid, LOCAL_CONN_ID_LEN,
329 token, token_len,
330 version, out, sizeof(out));
331
332 if (written < 0) {
333 fprintf(stderr, "failed to create retry packet: %zd\n",
334 written);
335 continue;
336 }
337
338 ssize_t sent = sendto(conns->sock, out, written, 0,
339 (struct sockaddr *) &peer_addr,
340 peer_addr_len);
341 if (sent != written) {
342 perror("failed to send");
343 continue;
344 }
345
346 fprintf(stderr, "sent %zd bytes\n", sent);
347 continue;
348 }
349
350
351 if (!validate_token(token, token_len, &peer_addr, peer_addr_len,
352 odcid, &odcid_len)) {
353 fprintf(stderr, "invalid address validation token\n");
354 continue;
355 }
356
357 conn_io = create_conn(dcid, dcid_len, odcid, odcid_len,
358 conns->local_addr, conns->local_addr_len,
359 &peer_addr, peer_addr_len);
360
361 if (conn_io == NULL) {
362 continue;
363 }
364 }
365
366 quiche_recv_info recv_info = {
367 (struct sockaddr *)&peer_addr,
368 peer_addr_len,
369
370 conns->local_addr,
371 conns->local_addr_len,
372 };
373
374 ssize_t done = quiche_conn_recv(conn_io->conn, buf, read, &recv_info);
375
376 if (done < 0) {
377 fprintf(stderr, "failed to process packet: %zd\n", done);
378 continue;
379 }
380
381 fprintf(stderr, "recv %zd bytes\n", done);
382
383 if (quiche_conn_is_established(conn_io->conn)) {
384 quiche_h3_event *ev;
385
386 if (conn_io->http3 == NULL) {
387 conn_io->http3 = quiche_h3_conn_new_with_transport(conn_io->conn,
388 http3_config);
389 if (conn_io->http3 == NULL) {
390 fprintf(stderr, "failed to create HTTP/3 connection\n");
391 continue;
392 }
393 }
394
395 while (1) {
396 int64_t s = quiche_h3_conn_poll(conn_io->http3,
397 conn_io->conn,
398 &ev);
399
400 if (s < 0) {
401 break;
402 }
403
404 switch (quiche_h3_event_type(ev)) {
405 case QUICHE_H3_EVENT_HEADERS: {
406 int rc = quiche_h3_event_for_each_header(ev,
407 for_each_header,
408 NULL);
409
410 if (rc != 0) {
411 fprintf(stderr, "failed to process headers\n");
412 }
413
414 quiche_h3_header headers[] = {
415 {
416 .name = (const uint8_t *) ":status",
417 .name_len = sizeof(":status") - 1,
418
419 .value = (const uint8_t *) "200",
420 .value_len = sizeof("200") - 1,
421 },
422
423 {
424 .name = (const uint8_t *) "server",
425 .name_len = sizeof("server") - 1,
426
427 .value = (const uint8_t *) "quiche",
428 .value_len = sizeof("quiche") - 1,
429 },
430
431 {
432 .name = (const uint8_t *) "content-length",
433 .name_len = sizeof("content-length") - 1,
434
435 .value = (const uint8_t *) "5",
436 .value_len = sizeof("5") - 1,
437 },
438 };
439
440 quiche_h3_send_response(conn_io->http3, conn_io->conn,
441 s, headers, 3, false);
442
443 quiche_h3_send_body(conn_io->http3, conn_io->conn,
444 s, (uint8_t *) "byez\n", 5, true);
445 break;
446 }
447
448 case QUICHE_H3_EVENT_DATA: {
449 fprintf(stderr, "got HTTP data\n");
450 break;
451 }
452
453 case QUICHE_H3_EVENT_FINISHED:
454 break;
455
456 case QUICHE_H3_EVENT_RESET:
457 break;
458
459 case QUICHE_H3_EVENT_PRIORITY_UPDATE:
460 break;
461
462 case QUICHE_H3_EVENT_DATAGRAM:
463 break;
464
465 case QUICHE_H3_EVENT_GOAWAY: {
466 fprintf(stderr, "got GOAWAY\n");
467 break;
468 }
469 }
470
471 quiche_h3_event_free(ev);
472 }
473 }
474 }
475
476 HASH_ITER(hh, conns->h, conn_io, tmp) {
477 flush_egress(loop, conn_io);
478
479 if (quiche_conn_is_closed(conn_io->conn)) {
480 quiche_stats stats;
481 quiche_path_stats path_stats;
482
483 quiche_conn_stats(conn_io->conn, &stats);
484 quiche_conn_path_stats(conn_io->conn, 0, &path_stats);
485
486 fprintf(stderr, "connection closed, recv=%zu sent=%zu lost=%zu rtt=%" PRIu64 "ns cwnd=%zu\n",
487 stats.recv, stats.sent, stats.lost, path_stats.rtt, path_stats.cwnd);
488
489 HASH_DELETE(hh, conns->h, conn_io);
490
491 ev_timer_stop(loop, &conn_io->timer);
492
493 quiche_conn_free(conn_io->conn);
494 free(conn_io);
495 }
496 }
497 }
498
timeout_cb(EV_P_ ev_timer * w,int revents)499 static void timeout_cb(EV_P_ ev_timer *w, int revents) {
500 struct conn_io *conn_io = w->data;
501 quiche_conn_on_timeout(conn_io->conn);
502
503 fprintf(stderr, "timeout\n");
504
505 flush_egress(loop, conn_io);
506
507 if (quiche_conn_is_closed(conn_io->conn)) {
508 quiche_stats stats;
509 quiche_path_stats path_stats;
510
511 quiche_conn_stats(conn_io->conn, &stats);
512 quiche_conn_path_stats(conn_io->conn, 0, &path_stats);
513
514 fprintf(stderr, "connection closed, recv=%zu sent=%zu lost=%zu rtt=%" PRIu64 "ns cwnd=%zu\n",
515 stats.recv, stats.sent, stats.lost, path_stats.rtt, path_stats.cwnd);
516
517 HASH_DELETE(hh, conns->h, conn_io);
518
519 ev_timer_stop(loop, &conn_io->timer);
520 quiche_conn_free(conn_io->conn);
521 free(conn_io);
522
523 return;
524 }
525 }
526
main(int argc,char * argv[])527 int main(int argc, char *argv[]) {
528 const char *host = argv[1];
529 const char *port = argv[2];
530
531 const struct addrinfo hints = {
532 .ai_family = PF_UNSPEC,
533 .ai_socktype = SOCK_DGRAM,
534 .ai_protocol = IPPROTO_UDP
535 };
536
537 quiche_enable_debug_logging(debug_log, NULL);
538
539 struct addrinfo *local;
540 if (getaddrinfo(host, port, &hints, &local) != 0) {
541 perror("failed to resolve host");
542 return -1;
543 }
544
545 int sock = socket(local->ai_family, SOCK_DGRAM, 0);
546 if (sock < 0) {
547 perror("failed to create socket");
548 return -1;
549 }
550
551 if (fcntl(sock, F_SETFL, O_NONBLOCK) != 0) {
552 perror("failed to make socket non-blocking");
553 return -1;
554 }
555
556 if (bind(sock, local->ai_addr, local->ai_addrlen) < 0) {
557 perror("failed to connect socket");
558 return -1;
559 }
560
561 config = quiche_config_new(QUICHE_PROTOCOL_VERSION);
562 if (config == NULL) {
563 fprintf(stderr, "failed to create config\n");
564 return -1;
565 }
566
567 quiche_config_load_cert_chain_from_pem_file(config, "./cert.crt");
568 quiche_config_load_priv_key_from_pem_file(config, "./cert.key");
569
570 quiche_config_set_application_protos(config,
571 (uint8_t *) QUICHE_H3_APPLICATION_PROTOCOL,
572 sizeof(QUICHE_H3_APPLICATION_PROTOCOL) - 1);
573
574 quiche_config_set_max_idle_timeout(config, 5000);
575 quiche_config_set_max_recv_udp_payload_size(config, MAX_DATAGRAM_SIZE);
576 quiche_config_set_max_send_udp_payload_size(config, MAX_DATAGRAM_SIZE);
577 quiche_config_set_initial_max_data(config, 10000000);
578 quiche_config_set_initial_max_stream_data_bidi_local(config, 1000000);
579 quiche_config_set_initial_max_stream_data_bidi_remote(config, 1000000);
580 quiche_config_set_initial_max_stream_data_uni(config, 1000000);
581 quiche_config_set_initial_max_streams_bidi(config, 100);
582 quiche_config_set_initial_max_streams_uni(config, 100);
583 quiche_config_set_disable_active_migration(config, true);
584 quiche_config_set_cc_algorithm(config, QUICHE_CC_RENO);
585
586 http3_config = quiche_h3_config_new();
587 if (http3_config == NULL) {
588 fprintf(stderr, "failed to create HTTP/3 config\n");
589 return -1;
590 }
591
592 struct connections c;
593 c.sock = sock;
594 c.h = NULL;
595 c.local_addr = local->ai_addr;
596 c.local_addr_len = local->ai_addrlen;
597
598 conns = &c;
599
600 ev_io watcher;
601
602 struct ev_loop *loop = ev_default_loop(0);
603
604 ev_io_init(&watcher, recv_cb, sock, EV_READ);
605 ev_io_start(loop, &watcher);
606 watcher.data = &c;
607
608 ev_loop(loop, 0);
609
610 freeaddrinfo(local);
611
612 quiche_h3_config_free(http3_config);
613
614 quiche_config_free(config);
615
616 return 0;
617 }
618