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