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