• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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