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