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