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 
73     struct sockaddr_storage peer_addr;
74     socklen_t peer_addr_len;
75 
76     UT_hash_handle hh;
77 };
78 
79 static quiche_config *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 *) &send_info.to,
110                               send_info.to_len);
111 
112         if (sent != written) {
113             perror("failed to send");
114             return;
115         }
116 
117         fprintf(stderr, "sent %zd bytes\n", sent);
118     }
119 
120     double t = quiche_conn_timeout_as_nanos(conn_io->conn) / 1e9f;
121     conn_io->timer.repeat = t;
122     ev_timer_again(loop, &conn_io->timer);
123 }
124 
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)125 static void mint_token(const uint8_t *dcid, size_t dcid_len,
126                        struct sockaddr_storage *addr, socklen_t addr_len,
127                        uint8_t *token, size_t *token_len) {
128     memcpy(token, "quiche", sizeof("quiche") - 1);
129     memcpy(token + sizeof("quiche") - 1, addr, addr_len);
130     memcpy(token + sizeof("quiche") - 1 + addr_len, dcid, dcid_len);
131 
132     *token_len = sizeof("quiche") - 1 + addr_len + dcid_len;
133 }
134 
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)135 static bool validate_token(const uint8_t *token, size_t token_len,
136                            struct sockaddr_storage *addr, socklen_t addr_len,
137                            uint8_t *odcid, size_t *odcid_len) {
138     if ((token_len < sizeof("quiche") - 1) ||
139          memcmp(token, "quiche", sizeof("quiche") - 1)) {
140         return false;
141     }
142 
143     token += sizeof("quiche") - 1;
144     token_len -= sizeof("quiche") - 1;
145 
146     if ((token_len < addr_len) || memcmp(token, addr, addr_len)) {
147         return false;
148     }
149 
150     token += addr_len;
151     token_len -= addr_len;
152 
153     if (*odcid_len < token_len) {
154         return false;
155     }
156 
157     memcpy(odcid, token, token_len);
158     *odcid_len = token_len;
159 
160     return true;
161 }
162 
gen_cid(uint8_t * cid,size_t cid_len)163 static uint8_t *gen_cid(uint8_t *cid, size_t cid_len) {
164     int rng = open("/dev/urandom", O_RDONLY);
165     if (rng < 0) {
166         perror("failed to open /dev/urandom");
167         return NULL;
168     }
169 
170     ssize_t rand_len = read(rng, cid, cid_len);
171     if (rand_len < 0) {
172         perror("failed to create connection ID");
173         return NULL;
174     }
175 
176     return cid;
177 }
178 
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)179 static struct conn_io *create_conn(uint8_t *scid, size_t scid_len,
180                                    uint8_t *odcid, size_t odcid_len,
181                                    struct sockaddr *local_addr,
182                                    socklen_t local_addr_len,
183                                    struct sockaddr_storage *peer_addr,
184                                    socklen_t peer_addr_len)
185 {
186     struct conn_io *conn_io = calloc(1, sizeof(*conn_io));
187     if (conn_io == NULL) {
188         fprintf(stderr, "failed to allocate connection IO\n");
189         return NULL;
190     }
191 
192     if (scid_len != LOCAL_CONN_ID_LEN) {
193         fprintf(stderr, "failed, scid length too short\n");
194     }
195 
196     memcpy(conn_io->cid, scid, LOCAL_CONN_ID_LEN);
197 
198     quiche_conn *conn = quiche_accept(conn_io->cid, LOCAL_CONN_ID_LEN,
199                                       odcid, odcid_len,
200                                       local_addr,
201                                       local_addr_len,
202                                       (struct sockaddr *) peer_addr,
203                                       peer_addr_len,
204                                       config);
205 
206     if (conn == NULL) {
207         fprintf(stderr, "failed to create connection\n");
208         return NULL;
209     }
210 
211     conn_io->sock = conns->sock;
212     conn_io->conn = conn;
213 
214     memcpy(&conn_io->peer_addr, peer_addr, peer_addr_len);
215     conn_io->peer_addr_len = peer_addr_len;
216 
217     ev_init(&conn_io->timer, timeout_cb);
218     conn_io->timer.data = conn_io;
219 
220     HASH_ADD(hh, conns->h, cid, LOCAL_CONN_ID_LEN, conn_io);
221 
222     fprintf(stderr, "new connection\n");
223 
224     return conn_io;
225 }
226 
recv_cb(EV_P_ ev_io * w,int revents)227 static void recv_cb(EV_P_ ev_io *w, int revents) {
228     struct conn_io *tmp, *conn_io = NULL;
229 
230     static uint8_t buf[65535];
231     static uint8_t out[MAX_DATAGRAM_SIZE];
232 
233     while (1) {
234         struct sockaddr_storage peer_addr;
235         socklen_t peer_addr_len = sizeof(peer_addr);
236         memset(&peer_addr, 0, peer_addr_len);
237 
238         ssize_t read = recvfrom(conns->sock, buf, sizeof(buf), 0,
239                                 (struct sockaddr *) &peer_addr,
240                                 &peer_addr_len);
241 
242         if (read < 0) {
243             if ((errno == EWOULDBLOCK) || (errno == EAGAIN)) {
244                 fprintf(stderr, "recv would block\n");
245                 break;
246             }
247 
248             perror("failed to read");
249             return;
250         }
251 
252         uint8_t type;
253         uint32_t version;
254 
255         uint8_t scid[QUICHE_MAX_CONN_ID_LEN];
256         size_t scid_len = sizeof(scid);
257 
258         uint8_t dcid[QUICHE_MAX_CONN_ID_LEN];
259         size_t dcid_len = sizeof(dcid);
260 
261         uint8_t odcid[QUICHE_MAX_CONN_ID_LEN];
262         size_t odcid_len = sizeof(odcid);
263 
264         uint8_t token[MAX_TOKEN_LEN];
265         size_t token_len = sizeof(token);
266 
267         int rc = quiche_header_info(buf, read, LOCAL_CONN_ID_LEN, &version,
268                                     &type, scid, &scid_len, dcid, &dcid_len,
269                                     token, &token_len);
270         if (rc < 0) {
271             fprintf(stderr, "failed to parse header: %d\n", rc);
272             continue;
273         }
274 
275         HASH_FIND(hh, conns->h, dcid, dcid_len, conn_io);
276 
277         if (conn_io == NULL) {
278             if (!quiche_version_is_supported(version)) {
279                 fprintf(stderr, "version negotiation\n");
280 
281                 ssize_t written = quiche_negotiate_version(scid, scid_len,
282                                                            dcid, dcid_len,
283                                                            out, sizeof(out));
284 
285                 if (written < 0) {
286                     fprintf(stderr, "failed to create vneg packet: %zd\n",
287                             written);
288                     continue;
289                 }
290 
291                 ssize_t sent = sendto(conns->sock, out, written, 0,
292                                       (struct sockaddr *) &peer_addr,
293                                       peer_addr_len);
294                 if (sent != written) {
295                     perror("failed to send");
296                     continue;
297                 }
298 
299                 fprintf(stderr, "sent %zd bytes\n", sent);
300                 continue;
301             }
302 
303             if (token_len == 0) {
304                 fprintf(stderr, "stateless retry\n");
305 
306                 mint_token(dcid, dcid_len, &peer_addr, peer_addr_len,
307                            token, &token_len);
308 
309                 uint8_t new_cid[LOCAL_CONN_ID_LEN];
310 
311                 if (gen_cid(new_cid, LOCAL_CONN_ID_LEN) == NULL) {
312                     continue;
313                 }
314 
315                 ssize_t written = quiche_retry(scid, scid_len,
316                                                dcid, dcid_len,
317                                                new_cid, LOCAL_CONN_ID_LEN,
318                                                token, token_len,
319                                                version, out, sizeof(out));
320 
321                 if (written < 0) {
322                     fprintf(stderr, "failed to create retry packet: %zd\n",
323                             written);
324                     continue;
325                 }
326 
327                 ssize_t sent = sendto(conns->sock, out, written, 0,
328                                       (struct sockaddr *) &peer_addr,
329                                       peer_addr_len);
330                 if (sent != written) {
331                     perror("failed to send");
332                     continue;
333                 }
334 
335                 fprintf(stderr, "sent %zd bytes\n", sent);
336                 continue;
337             }
338 
339 
340             if (!validate_token(token, token_len, &peer_addr, peer_addr_len,
341                                odcid, &odcid_len)) {
342                 fprintf(stderr, "invalid address validation token\n");
343                 continue;
344             }
345 
346             conn_io = create_conn(dcid, dcid_len, odcid, odcid_len,
347                                   conns->local_addr, conns->local_addr_len,
348                                   &peer_addr, peer_addr_len);
349 
350             if (conn_io == NULL) {
351                 continue;
352             }
353         }
354 
355         quiche_recv_info recv_info = {
356             (struct sockaddr *)&peer_addr,
357             peer_addr_len,
358 
359             conns->local_addr,
360             conns->local_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             uint64_t s = 0;
374 
375             quiche_stream_iter *readable = quiche_conn_readable(conn_io->conn);
376 
377             while (quiche_stream_iter_next(readable, &s)) {
378                 fprintf(stderr, "stream %" PRIu64 " is readable\n", s);
379 
380                 bool fin = false;
381                 ssize_t recv_len = quiche_conn_stream_recv(conn_io->conn, s,
382                                                            buf, sizeof(buf),
383                                                            &fin);
384                 if (recv_len < 0) {
385                     break;
386                 }
387 
388                 if (fin) {
389                     static const char *resp = "byez\n";
390                     quiche_conn_stream_send(conn_io->conn, s, (uint8_t *) resp,
391                                             5, true);
392                 }
393             }
394 
395             quiche_stream_iter_free(readable);
396         }
397     }
398 
399     HASH_ITER(hh, conns->h, conn_io, tmp) {
400         flush_egress(loop, conn_io);
401 
402         if (quiche_conn_is_closed(conn_io->conn)) {
403             quiche_stats stats;
404             quiche_path_stats path_stats;
405 
406             quiche_conn_stats(conn_io->conn, &stats);
407             quiche_conn_path_stats(conn_io->conn, 0, &path_stats);
408 
409             fprintf(stderr, "connection closed, recv=%zu sent=%zu lost=%zu rtt=%" PRIu64 "ns cwnd=%zu\n",
410                     stats.recv, stats.sent, stats.lost, path_stats.rtt, path_stats.cwnd);
411 
412             HASH_DELETE(hh, conns->h, conn_io);
413 
414             ev_timer_stop(loop, &conn_io->timer);
415             quiche_conn_free(conn_io->conn);
416             free(conn_io);
417         }
418     }
419 }
420 
timeout_cb(EV_P_ ev_timer * w,int revents)421 static void timeout_cb(EV_P_ ev_timer *w, int revents) {
422     struct conn_io *conn_io = w->data;
423     quiche_conn_on_timeout(conn_io->conn);
424 
425     fprintf(stderr, "timeout\n");
426 
427     flush_egress(loop, conn_io);
428 
429     if (quiche_conn_is_closed(conn_io->conn)) {
430         quiche_stats stats;
431         quiche_path_stats path_stats;
432 
433         quiche_conn_stats(conn_io->conn, &stats);
434         quiche_conn_path_stats(conn_io->conn, 0, &path_stats);
435 
436         fprintf(stderr, "connection closed, recv=%zu sent=%zu lost=%zu rtt=%" PRIu64 "ns cwnd=%zu\n",
437                 stats.recv, stats.sent, stats.lost, path_stats.rtt, path_stats.cwnd);
438 
439         HASH_DELETE(hh, conns->h, conn_io);
440 
441         ev_timer_stop(loop, &conn_io->timer);
442         quiche_conn_free(conn_io->conn);
443         free(conn_io);
444 
445         return;
446     }
447 }
448 
main(int argc,char * argv[])449 int main(int argc, char *argv[]) {
450     const char *host = argv[1];
451     const char *port = argv[2];
452 
453     const struct addrinfo hints = {
454         .ai_family = PF_UNSPEC,
455         .ai_socktype = SOCK_DGRAM,
456         .ai_protocol = IPPROTO_UDP
457     };
458 
459     quiche_enable_debug_logging(debug_log, NULL);
460 
461     struct addrinfo *local;
462     if (getaddrinfo(host, port, &hints, &local) != 0) {
463         perror("failed to resolve host");
464         return -1;
465     }
466 
467     int sock = socket(local->ai_family, SOCK_DGRAM, 0);
468     if (sock < 0) {
469         perror("failed to create socket");
470         return -1;
471     }
472 
473     if (fcntl(sock, F_SETFL, O_NONBLOCK) != 0) {
474         perror("failed to make socket non-blocking");
475         return -1;
476     }
477 
478     if (bind(sock, local->ai_addr, local->ai_addrlen) < 0) {
479         perror("failed to connect socket");
480         return -1;
481     }
482 
483     config = quiche_config_new(QUICHE_PROTOCOL_VERSION);
484     if (config == NULL) {
485         fprintf(stderr, "failed to create config\n");
486         return -1;
487     }
488 
489     quiche_config_load_cert_chain_from_pem_file(config, "./cert.crt");
490     quiche_config_load_priv_key_from_pem_file(config, "./cert.key");
491 
492     quiche_config_set_application_protos(config,
493         (uint8_t *) "\x0ahq-interop\x05hq-29\x05hq-28\x05hq-27\x08http/0.9", 38);
494 
495     quiche_config_set_max_idle_timeout(config, 5000);
496     quiche_config_set_max_recv_udp_payload_size(config, MAX_DATAGRAM_SIZE);
497     quiche_config_set_max_send_udp_payload_size(config, MAX_DATAGRAM_SIZE);
498     quiche_config_set_initial_max_data(config, 10000000);
499     quiche_config_set_initial_max_stream_data_bidi_local(config, 1000000);
500     quiche_config_set_initial_max_stream_data_bidi_remote(config, 1000000);
501     quiche_config_set_initial_max_streams_bidi(config, 100);
502     quiche_config_set_cc_algorithm(config, QUICHE_CC_RENO);
503 
504     struct connections c;
505     c.sock = sock;
506     c.h = NULL;
507     c.local_addr = local->ai_addr;
508     c.local_addr_len = local->ai_addrlen;
509 
510     conns = &c;
511 
512     ev_io watcher;
513 
514     struct ev_loop *loop = ev_default_loop(0);
515 
516     ev_io_init(&watcher, recv_cb, sock, EV_READ);
517     ev_io_start(loop, &watcher);
518     watcher.data = &c;
519 
520     ev_loop(loop, 0);
521 
522     freeaddrinfo(local);
523 
524     quiche_config_free(config);
525 
526     return 0;
527 }
528