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 conn_io *h;
59 };
60
61 struct conn_io {
62 ev_timer timer;
63
64 int sock;
65
66 uint8_t cid[LOCAL_CONN_ID_LEN];
67
68 quiche_conn *conn;
69 quiche_h3_conn *http3;
70
71 struct sockaddr_storage peer_addr;
72 socklen_t peer_addr_len;
73
74 UT_hash_handle hh;
75 };
76
77 static quiche_config *config = NULL;
78
79 static quiche_h3_config *http3_config = NULL;
80
81 static struct connections *conns = NULL;
82
83 static void timeout_cb(EV_P_ ev_timer *w, int revents);
84
debug_log(const char * line,void * argp)85 static void debug_log(const char *line, void *argp) {
86 fprintf(stderr, "%s\n", line);
87 }
88
flush_egress(struct ev_loop * loop,struct conn_io * conn_io)89 static void flush_egress(struct ev_loop *loop, struct conn_io *conn_io) {
90 static uint8_t out[MAX_DATAGRAM_SIZE];
91
92 quiche_send_info send_info;
93
94 while (1) {
95 ssize_t written = quiche_conn_send(conn_io->conn, out, sizeof(out),
96 &send_info);
97
98 if (written == QUICHE_ERR_DONE) {
99 fprintf(stderr, "done writing\n");
100 break;
101 }
102
103 if (written < 0) {
104 fprintf(stderr, "failed to create packet: %zd\n", written);
105 return;
106 }
107
108 ssize_t sent = sendto(conn_io->sock, out, written, 0,
109 (struct sockaddr *) &conn_io->peer_addr,
110 conn_io->peer_addr_len);
111 if (sent != written) {
112 perror("failed to send");
113 return;
114 }
115
116 fprintf(stderr, "sent %zd bytes\n", sent);
117 }
118
119 double t = quiche_conn_timeout_as_nanos(conn_io->conn) / 1e9f;
120 conn_io->timer.repeat = t;
121 ev_timer_again(loop, &conn_io->timer);
122 }
123
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)124 static void mint_token(const uint8_t *dcid, size_t dcid_len,
125 struct sockaddr_storage *addr, socklen_t addr_len,
126 uint8_t *token, size_t *token_len) {
127 memcpy(token, "quiche", sizeof("quiche") - 1);
128 memcpy(token + sizeof("quiche") - 1, addr, addr_len);
129 memcpy(token + sizeof("quiche") - 1 + addr_len, dcid, dcid_len);
130
131 *token_len = sizeof("quiche") - 1 + addr_len + dcid_len;
132 }
133
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)134 static bool validate_token(const uint8_t *token, size_t token_len,
135 struct sockaddr_storage *addr, socklen_t addr_len,
136 uint8_t *odcid, size_t *odcid_len) {
137 if ((token_len < sizeof("quiche") - 1) ||
138 memcmp(token, "quiche", sizeof("quiche") - 1)) {
139 return false;
140 }
141
142 token += sizeof("quiche") - 1;
143 token_len -= sizeof("quiche") - 1;
144
145 if ((token_len < addr_len) || memcmp(token, addr, addr_len)) {
146 return false;
147 }
148
149 token += addr_len;
150 token_len -= addr_len;
151
152 if (*odcid_len < token_len) {
153 return false;
154 }
155
156 memcpy(odcid, token, token_len);
157 *odcid_len = token_len;
158
159 return true;
160 }
161
gen_cid(uint8_t * cid,size_t cid_len)162 static uint8_t *gen_cid(uint8_t *cid, size_t cid_len) {
163 int rng = open("/dev/urandom", O_RDONLY);
164 if (rng < 0) {
165 perror("failed to open /dev/urandom");
166 return NULL;
167 }
168
169 ssize_t rand_len = read(rng, cid, cid_len);
170 if (rand_len < 0) {
171 perror("failed to create connection ID");
172 return NULL;
173 }
174
175 return cid;
176 }
177
create_conn(uint8_t * scid,size_t scid_len,uint8_t * odcid,size_t odcid_len,struct sockaddr_storage * peer_addr,socklen_t peer_addr_len)178 static struct conn_io *create_conn(uint8_t *scid, size_t scid_len,
179 uint8_t *odcid, size_t odcid_len,
180 struct sockaddr_storage *peer_addr,
181 socklen_t peer_addr_len) {
182 struct conn_io *conn_io = calloc(1, sizeof(*conn_io));
183 if (conn_io == NULL) {
184 fprintf(stderr, "failed to allocate connection IO\n");
185 return NULL;
186 }
187
188 if (scid_len != LOCAL_CONN_ID_LEN) {
189 fprintf(stderr, "failed, scid length too short\n");
190 }
191
192 memcpy(conn_io->cid, scid, LOCAL_CONN_ID_LEN);
193
194 quiche_conn *conn = quiche_accept(conn_io->cid, LOCAL_CONN_ID_LEN,
195 odcid, odcid_len,
196 (struct sockaddr *) peer_addr,
197 peer_addr_len,
198 config);
199
200 if (conn == NULL) {
201 fprintf(stderr, "failed to create connection\n");
202 return NULL;
203 }
204
205 conn_io->sock = conns->sock;
206 conn_io->conn = conn;
207
208 memcpy(&conn_io->peer_addr, &peer_addr, peer_addr_len);
209 conn_io->peer_addr_len = peer_addr_len;
210
211 ev_init(&conn_io->timer, timeout_cb);
212 conn_io->timer.data = conn_io;
213
214 HASH_ADD(hh, conns->h, cid, LOCAL_CONN_ID_LEN, conn_io);
215
216 fprintf(stderr, "new connection\n");
217
218 return conn_io;
219 }
220
for_each_header(uint8_t * name,size_t name_len,uint8_t * value,size_t value_len,void * argp)221 static int for_each_header(uint8_t *name, size_t name_len,
222 uint8_t *value, size_t value_len,
223 void *argp) {
224 fprintf(stderr, "got HTTP header: %.*s=%.*s\n",
225 (int) name_len, name, (int) value_len, value);
226
227 return 0;
228 }
229
recv_cb(EV_P_ ev_io * w,int revents)230 static void recv_cb(EV_P_ ev_io *w, int revents) {
231 struct conn_io *tmp, *conn_io = NULL;
232
233 static uint8_t buf[65535];
234 static uint8_t out[MAX_DATAGRAM_SIZE];
235
236 while (1) {
237 struct sockaddr_storage peer_addr;
238 socklen_t peer_addr_len = sizeof(peer_addr);
239 memset(&peer_addr, 0, peer_addr_len);
240
241 ssize_t read = recvfrom(conns->sock, buf, sizeof(buf), 0,
242 (struct sockaddr *) &peer_addr,
243 &peer_addr_len);
244
245 if (read < 0) {
246 if ((errno == EWOULDBLOCK) || (errno == EAGAIN)) {
247 fprintf(stderr, "recv would block\n");
248 break;
249 }
250
251 perror("failed to read");
252 return;
253 }
254
255 uint8_t type;
256 uint32_t version;
257
258 uint8_t scid[QUICHE_MAX_CONN_ID_LEN];
259 size_t scid_len = sizeof(scid);
260
261 uint8_t dcid[QUICHE_MAX_CONN_ID_LEN];
262 size_t dcid_len = sizeof(dcid);
263
264 uint8_t odcid[QUICHE_MAX_CONN_ID_LEN];
265 size_t odcid_len = sizeof(odcid);
266
267 uint8_t token[MAX_TOKEN_LEN];
268 size_t token_len = sizeof(token);
269
270 int rc = quiche_header_info(buf, read, LOCAL_CONN_ID_LEN, &version,
271 &type, scid, &scid_len, dcid, &dcid_len,
272 token, &token_len);
273 if (rc < 0) {
274 fprintf(stderr, "failed to parse header: %d\n", rc);
275 return;
276 }
277
278 HASH_FIND(hh, conns->h, dcid, dcid_len, conn_io);
279
280 if (conn_io == NULL) {
281 if (!quiche_version_is_supported(version)) {
282 fprintf(stderr, "version negotiation\n");
283
284 ssize_t written = quiche_negotiate_version(scid, scid_len,
285 dcid, dcid_len,
286 out, sizeof(out));
287
288 if (written < 0) {
289 fprintf(stderr, "failed to create vneg packet: %zd\n",
290 written);
291 continue;
292 }
293
294 ssize_t sent = sendto(conns->sock, out, written, 0,
295 (struct sockaddr *) &peer_addr,
296 peer_addr_len);
297 if (sent != written) {
298 perror("failed to send");
299 continue;
300 }
301
302 fprintf(stderr, "sent %zd bytes\n", sent);
303 continue;
304 }
305
306 if (token_len == 0) {
307 fprintf(stderr, "stateless retry\n");
308
309 mint_token(dcid, dcid_len, &peer_addr, peer_addr_len,
310 token, &token_len);
311
312 uint8_t new_cid[LOCAL_CONN_ID_LEN];
313
314 if (gen_cid(new_cid, LOCAL_CONN_ID_LEN) == NULL) {
315 continue;
316 }
317
318 ssize_t written = quiche_retry(scid, scid_len,
319 dcid, dcid_len,
320 new_cid, LOCAL_CONN_ID_LEN,
321 token, token_len,
322 version, out, sizeof(out));
323
324 if (written < 0) {
325 fprintf(stderr, "failed to create retry packet: %zd\n",
326 written);
327 continue;
328 }
329
330 ssize_t sent = sendto(conns->sock, out, written, 0,
331 (struct sockaddr *) &peer_addr,
332 peer_addr_len);
333 if (sent != written) {
334 perror("failed to send");
335 continue;
336 }
337
338 fprintf(stderr, "sent %zd bytes\n", sent);
339 continue;
340 }
341
342
343 if (!validate_token(token, token_len, &peer_addr, peer_addr_len,
344 odcid, &odcid_len)) {
345 fprintf(stderr, "invalid address validation token\n");
346 continue;
347 }
348
349 conn_io = create_conn(dcid, dcid_len, odcid, odcid_len,
350 &peer_addr, peer_addr_len);
351
352 if (conn_io == NULL) {
353 continue;
354 }
355 }
356
357 quiche_recv_info recv_info = {
358 (struct sockaddr *) &peer_addr,
359
360 peer_addr_len,
361 };
362
363 ssize_t done = quiche_conn_recv(conn_io->conn, buf, read, &recv_info);
364
365 if (done < 0) {
366 fprintf(stderr, "failed to process packet: %zd\n", done);
367 continue;
368 }
369
370 fprintf(stderr, "recv %zd bytes\n", done);
371
372 if (quiche_conn_is_established(conn_io->conn)) {
373 quiche_h3_event *ev;
374
375 if (conn_io->http3 == NULL) {
376 conn_io->http3 = quiche_h3_conn_new_with_transport(conn_io->conn,
377 http3_config);
378 if (conn_io->http3 == NULL) {
379 fprintf(stderr, "failed to create HTTP/3 connection\n");
380 continue;
381 }
382 }
383
384 while (1) {
385 int64_t s = quiche_h3_conn_poll(conn_io->http3,
386 conn_io->conn,
387 &ev);
388
389 if (s < 0) {
390 break;
391 }
392
393 switch (quiche_h3_event_type(ev)) {
394 case QUICHE_H3_EVENT_HEADERS: {
395 int rc = quiche_h3_event_for_each_header(ev,
396 for_each_header,
397 NULL);
398
399 if (rc != 0) {
400 fprintf(stderr, "failed to process headers\n");
401 }
402
403 quiche_h3_header headers[] = {
404 {
405 .name = (const uint8_t *) ":status",
406 .name_len = sizeof(":status") - 1,
407
408 .value = (const uint8_t *) "200",
409 .value_len = sizeof("200") - 1,
410 },
411
412 {
413 .name = (const uint8_t *) "server",
414 .name_len = sizeof("server") - 1,
415
416 .value = (const uint8_t *) "quiche",
417 .value_len = sizeof("quiche") - 1,
418 },
419
420 {
421 .name = (const uint8_t *) "content-length",
422 .name_len = sizeof("content-length") - 1,
423
424 .value = (const uint8_t *) "5",
425 .value_len = sizeof("5") - 1,
426 },
427 };
428
429 quiche_h3_send_response(conn_io->http3, conn_io->conn,
430 s, headers, 3, false);
431
432 quiche_h3_send_body(conn_io->http3, conn_io->conn,
433 s, (uint8_t *) "byez\n", 5, true);
434 break;
435 }
436
437 case QUICHE_H3_EVENT_DATA: {
438 fprintf(stderr, "got HTTP data\n");
439 break;
440 }
441
442 case QUICHE_H3_EVENT_FINISHED:
443 break;
444
445 case QUICHE_H3_EVENT_DATAGRAM:
446 break;
447
448 case QUICHE_H3_EVENT_GOAWAY: {
449 fprintf(stderr, "got GOAWAY\n");
450 break;
451 }
452 }
453
454 quiche_h3_event_free(ev);
455 }
456 }
457 }
458
459 HASH_ITER(hh, conns->h, conn_io, tmp) {
460 flush_egress(loop, conn_io);
461
462 if (quiche_conn_is_closed(conn_io->conn)) {
463 quiche_stats stats;
464
465 quiche_conn_stats(conn_io->conn, &stats);
466 fprintf(stderr, "connection closed, recv=%zu sent=%zu lost=%zu rtt=%" PRIu64 "ns cwnd=%zu\n",
467 stats.recv, stats.sent, stats.lost, stats.rtt, stats.cwnd);
468
469 HASH_DELETE(hh, conns->h, conn_io);
470
471 ev_timer_stop(loop, &conn_io->timer);
472
473 quiche_conn_free(conn_io->conn);
474 free(conn_io);
475 }
476 }
477 }
478
timeout_cb(EV_P_ ev_timer * w,int revents)479 static void timeout_cb(EV_P_ ev_timer *w, int revents) {
480 struct conn_io *conn_io = w->data;
481 quiche_conn_on_timeout(conn_io->conn);
482
483 fprintf(stderr, "timeout\n");
484
485 flush_egress(loop, conn_io);
486
487 if (quiche_conn_is_closed(conn_io->conn)) {
488 quiche_stats stats;
489
490 quiche_conn_stats(conn_io->conn, &stats);
491 fprintf(stderr, "connection closed, recv=%zu sent=%zu lost=%zu rtt=%" PRIu64 "ns cwnd=%zu\n",
492 stats.recv, stats.sent, stats.lost, stats.rtt, stats.cwnd);
493
494 HASH_DELETE(hh, conns->h, conn_io);
495
496 ev_timer_stop(loop, &conn_io->timer);
497 quiche_conn_free(conn_io->conn);
498 free(conn_io);
499
500 return;
501 }
502 }
503
main(int argc,char * argv[])504 int main(int argc, char *argv[]) {
505 const char *host = argv[1];
506 const char *port = argv[2];
507
508 const struct addrinfo hints = {
509 .ai_family = PF_UNSPEC,
510 .ai_socktype = SOCK_DGRAM,
511 .ai_protocol = IPPROTO_UDP
512 };
513
514 quiche_enable_debug_logging(debug_log, NULL);
515
516 struct addrinfo *local;
517 if (getaddrinfo(host, port, &hints, &local) != 0) {
518 perror("failed to resolve host");
519 return -1;
520 }
521
522 int sock = socket(local->ai_family, SOCK_DGRAM, 0);
523 if (sock < 0) {
524 perror("failed to create socket");
525 return -1;
526 }
527
528 if (fcntl(sock, F_SETFL, O_NONBLOCK) != 0) {
529 perror("failed to make socket non-blocking");
530 return -1;
531 }
532
533 if (bind(sock, local->ai_addr, local->ai_addrlen) < 0) {
534 perror("failed to connect socket");
535 return -1;
536 }
537
538 config = quiche_config_new(QUICHE_PROTOCOL_VERSION);
539 if (config == NULL) {
540 fprintf(stderr, "failed to create config\n");
541 return -1;
542 }
543
544 quiche_config_load_cert_chain_from_pem_file(config, "./cert.crt");
545 quiche_config_load_priv_key_from_pem_file(config, "./cert.key");
546
547 quiche_config_set_application_protos(config,
548 (uint8_t *) QUICHE_H3_APPLICATION_PROTOCOL,
549 sizeof(QUICHE_H3_APPLICATION_PROTOCOL) - 1);
550
551 quiche_config_set_max_idle_timeout(config, 5000);
552 quiche_config_set_max_recv_udp_payload_size(config, MAX_DATAGRAM_SIZE);
553 quiche_config_set_max_send_udp_payload_size(config, MAX_DATAGRAM_SIZE);
554 quiche_config_set_initial_max_data(config, 10000000);
555 quiche_config_set_initial_max_stream_data_bidi_local(config, 1000000);
556 quiche_config_set_initial_max_stream_data_bidi_remote(config, 1000000);
557 quiche_config_set_initial_max_stream_data_uni(config, 1000000);
558 quiche_config_set_initial_max_streams_bidi(config, 100);
559 quiche_config_set_initial_max_streams_uni(config, 100);
560 quiche_config_set_disable_active_migration(config, true);
561 quiche_config_set_cc_algorithm(config, QUICHE_CC_RENO);
562
563 http3_config = quiche_h3_config_new();
564 if (http3_config == NULL) {
565 fprintf(stderr, "failed to create HTTP/3 config\n");
566 return -1;
567 }
568
569 struct connections c;
570 c.sock = sock;
571 c.h = NULL;
572
573 conns = &c;
574
575 ev_io watcher;
576
577 struct ev_loop *loop = ev_default_loop(0);
578
579 ev_io_init(&watcher, recv_cb, sock, EV_READ);
580 ev_io_start(loop, &watcher);
581 watcher.data = &c;
582
583 ev_loop(loop, 0);
584
585 freeaddrinfo(local);
586
587 quiche_h3_config_free(http3_config);
588
589 quiche_config_free(config);
590
591 return 0;
592 }
593