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