1 // Copyright (C) 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
43 #include <quiche.h>
44
45 #define LOCAL_CONN_ID_LEN 16
46
47 #define MAX_DATAGRAM_SIZE 1350
48
49 struct conn_io {
50 ev_timer timer;
51
52 const char *host;
53
54 int sock;
55
56 struct sockaddr_storage local_addr;
57 socklen_t local_addr_len;
58
59 quiche_conn *conn;
60
61 quiche_h3_conn *http3;
62 };
63
debug_log(const char * line,void * argp)64 static void debug_log(const char *line, void *argp) {
65 fprintf(stderr, "%s\n", line);
66 }
67
flush_egress(struct ev_loop * loop,struct conn_io * conn_io)68 static void flush_egress(struct ev_loop *loop, struct conn_io *conn_io) {
69 static uint8_t out[MAX_DATAGRAM_SIZE];
70
71 quiche_send_info send_info;
72
73 while (1) {
74 ssize_t written = quiche_conn_send(conn_io->conn, out, sizeof(out),
75 &send_info);
76
77 if (written == QUICHE_ERR_DONE) {
78 fprintf(stderr, "done writing\n");
79 break;
80 }
81
82 if (written < 0) {
83 fprintf(stderr, "failed to create packet: %zd\n", written);
84 return;
85 }
86
87 ssize_t sent = sendto(conn_io->sock, out, written, 0,
88 (struct sockaddr *) &send_info.to,
89 send_info.to_len);
90
91 if (sent != written) {
92 perror("failed to send");
93 return;
94 }
95
96 fprintf(stderr, "sent %zd bytes\n", sent);
97 }
98
99 double t = quiche_conn_timeout_as_nanos(conn_io->conn) / 1e9f;
100 conn_io->timer.repeat = t;
101 ev_timer_again(loop, &conn_io->timer);
102 }
103
for_each_setting(uint64_t identifier,uint64_t value,void * argp)104 static int for_each_setting(uint64_t identifier, uint64_t value,
105 void *argp) {
106 fprintf(stderr, "got HTTP/3 SETTING: %" PRIu64 "=%" PRIu64 "\n",
107 identifier, value);
108
109 return 0;
110 }
111
for_each_header(uint8_t * name,size_t name_len,uint8_t * value,size_t value_len,void * argp)112 static int for_each_header(uint8_t *name, size_t name_len,
113 uint8_t *value, size_t value_len,
114 void *argp) {
115 fprintf(stderr, "got HTTP header: %.*s=%.*s\n",
116 (int) name_len, name, (int) value_len, value);
117
118 return 0;
119 }
120
recv_cb(EV_P_ ev_io * w,int revents)121 static void recv_cb(EV_P_ ev_io *w, int revents) {
122 static bool req_sent = false;
123 static bool settings_received = false;
124
125 struct conn_io *conn_io = w->data;
126
127 static uint8_t buf[65535];
128
129 while (1) {
130 struct sockaddr_storage peer_addr;
131 socklen_t peer_addr_len = sizeof(peer_addr);
132 memset(&peer_addr, 0, peer_addr_len);
133
134 ssize_t read = recvfrom(conn_io->sock, buf, sizeof(buf), 0,
135 (struct sockaddr *) &peer_addr,
136 &peer_addr_len);
137
138 if (read < 0) {
139 if ((errno == EWOULDBLOCK) || (errno == EAGAIN)) {
140 fprintf(stderr, "recv would block\n");
141 break;
142 }
143
144 perror("failed to read");
145 return;
146 }
147
148 quiche_recv_info recv_info = {
149 (struct sockaddr *) &peer_addr,
150 peer_addr_len,
151
152 (struct sockaddr *) &conn_io->local_addr,
153 conn_io->local_addr_len,
154 };
155
156 ssize_t done = quiche_conn_recv(conn_io->conn, buf, read, &recv_info);
157
158 if (done < 0) {
159 fprintf(stderr, "failed to process packet: %zd\n", done);
160 continue;
161 }
162
163 fprintf(stderr, "recv %zd bytes\n", done);
164 }
165
166 fprintf(stderr, "done reading\n");
167
168 if (quiche_conn_is_closed(conn_io->conn)) {
169 fprintf(stderr, "connection closed\n");
170
171 ev_break(EV_A_ EVBREAK_ONE);
172 return;
173 }
174
175 if (quiche_conn_is_established(conn_io->conn) && !req_sent) {
176 const uint8_t *app_proto;
177 size_t app_proto_len;
178
179 quiche_conn_application_proto(conn_io->conn, &app_proto, &app_proto_len);
180
181 fprintf(stderr, "connection established: %.*s\n",
182 (int) app_proto_len, app_proto);
183
184 quiche_h3_config *config = quiche_h3_config_new();
185 if (config == NULL) {
186 fprintf(stderr, "failed to create HTTP/3 config\n");
187 return;
188 }
189
190 conn_io->http3 = quiche_h3_conn_new_with_transport(conn_io->conn, config);
191 if (conn_io->http3 == NULL) {
192 fprintf(stderr, "failed to create HTTP/3 connection\n");
193 return;
194 }
195
196 quiche_h3_config_free(config);
197
198 quiche_h3_header headers[] = {
199 {
200 .name = (const uint8_t *) ":method",
201 .name_len = sizeof(":method") - 1,
202
203 .value = (const uint8_t *) "GET",
204 .value_len = sizeof("GET") - 1,
205 },
206
207 {
208 .name = (const uint8_t *) ":scheme",
209 .name_len = sizeof(":scheme") - 1,
210
211 .value = (const uint8_t *) "https",
212 .value_len = sizeof("https") - 1,
213 },
214
215 {
216 .name = (const uint8_t *) ":authority",
217 .name_len = sizeof(":authority") - 1,
218
219 .value = (const uint8_t *) conn_io->host,
220 .value_len = strlen(conn_io->host),
221 },
222
223 {
224 .name = (const uint8_t *) ":path",
225 .name_len = sizeof(":path") - 1,
226
227 .value = (const uint8_t *) "/",
228 .value_len = sizeof("/") - 1,
229 },
230
231 {
232 .name = (const uint8_t *) "user-agent",
233 .name_len = sizeof("user-agent") - 1,
234
235 .value = (const uint8_t *) "quiche",
236 .value_len = sizeof("quiche") - 1,
237 },
238 };
239
240 int64_t stream_id = quiche_h3_send_request(conn_io->http3,
241 conn_io->conn,
242 headers, 5, true);
243
244 fprintf(stderr, "sent HTTP request %" PRId64 "\n", stream_id);
245
246 req_sent = true;
247 }
248
249 if (quiche_conn_is_established(conn_io->conn)) {
250 quiche_h3_event *ev;
251
252 while (1) {
253 int64_t s = quiche_h3_conn_poll(conn_io->http3,
254 conn_io->conn,
255 &ev);
256
257 if (s < 0) {
258 break;
259 }
260
261 if (!settings_received) {
262 int rc = quiche_h3_for_each_setting(conn_io->http3,
263 for_each_setting,
264 NULL);
265
266 if (rc == 0) {
267 settings_received = true;
268 }
269 }
270
271 switch (quiche_h3_event_type(ev)) {
272 case QUICHE_H3_EVENT_HEADERS: {
273 int rc = quiche_h3_event_for_each_header(ev, for_each_header,
274 NULL);
275
276 if (rc != 0) {
277 fprintf(stderr, "failed to process headers");
278 }
279
280 break;
281 }
282
283 case QUICHE_H3_EVENT_DATA: {
284 for (;;) {
285 ssize_t len = quiche_h3_recv_body(conn_io->http3,
286 conn_io->conn, s,
287 buf, sizeof(buf));
288
289 if (len <= 0) {
290 break;
291 }
292
293 printf("%.*s", (int) len, buf);
294 }
295
296 break;
297 }
298
299 case QUICHE_H3_EVENT_FINISHED:
300 if (quiche_conn_close(conn_io->conn, true, 0, NULL, 0) < 0) {
301 fprintf(stderr, "failed to close connection\n");
302 }
303 break;
304
305 case QUICHE_H3_EVENT_RESET:
306 fprintf(stderr, "request was reset\n");
307
308 if (quiche_conn_close(conn_io->conn, true, 0, NULL, 0) < 0) {
309 fprintf(stderr, "failed to close connection\n");
310 }
311 break;
312
313 case QUICHE_H3_EVENT_PRIORITY_UPDATE:
314 break;
315
316 case QUICHE_H3_EVENT_DATAGRAM:
317 break;
318
319 case QUICHE_H3_EVENT_GOAWAY: {
320 fprintf(stderr, "got GOAWAY\n");
321 break;
322 }
323 }
324
325 quiche_h3_event_free(ev);
326 }
327 }
328
329 flush_egress(loop, conn_io);
330 }
331
timeout_cb(EV_P_ ev_timer * w,int revents)332 static void timeout_cb(EV_P_ ev_timer *w, int revents) {
333 struct conn_io *conn_io = w->data;
334 quiche_conn_on_timeout(conn_io->conn);
335
336 fprintf(stderr, "timeout\n");
337
338 flush_egress(loop, conn_io);
339
340 if (quiche_conn_is_closed(conn_io->conn)) {
341 quiche_stats stats;
342 quiche_path_stats path_stats;
343
344 quiche_conn_stats(conn_io->conn, &stats);
345 quiche_conn_path_stats(conn_io->conn, 0, &path_stats);
346
347 fprintf(stderr, "connection closed, recv=%zu sent=%zu lost=%zu rtt=%" PRIu64 "ns\n",
348 stats.recv, stats.sent, stats.lost, path_stats.rtt);
349
350 ev_break(EV_A_ EVBREAK_ONE);
351 return;
352 }
353 }
354
main(int argc,char * argv[])355 int main(int argc, char *argv[]) {
356 const char *host = argv[1];
357 const char *port = argv[2];
358
359 const struct addrinfo hints = {
360 .ai_family = PF_UNSPEC,
361 .ai_socktype = SOCK_DGRAM,
362 .ai_protocol = IPPROTO_UDP
363 };
364
365 quiche_enable_debug_logging(debug_log, NULL);
366
367 struct addrinfo *peer;
368 if (getaddrinfo(host, port, &hints, &peer) != 0) {
369 perror("failed to resolve host");
370 return -1;
371 }
372
373 int sock = socket(peer->ai_family, SOCK_DGRAM, 0);
374 if (sock < 0) {
375 perror("failed to create socket");
376 return -1;
377 }
378
379 if (fcntl(sock, F_SETFL, O_NONBLOCK) != 0) {
380 perror("failed to make socket non-blocking");
381 return -1;
382 }
383
384 quiche_config *config = quiche_config_new(0xbabababa);
385 if (config == NULL) {
386 fprintf(stderr, "failed to create config\n");
387 return -1;
388 }
389
390 quiche_config_set_application_protos(config,
391 (uint8_t *) QUICHE_H3_APPLICATION_PROTOCOL,
392 sizeof(QUICHE_H3_APPLICATION_PROTOCOL) - 1);
393
394 quiche_config_set_max_idle_timeout(config, 5000);
395 quiche_config_set_max_recv_udp_payload_size(config, MAX_DATAGRAM_SIZE);
396 quiche_config_set_max_send_udp_payload_size(config, MAX_DATAGRAM_SIZE);
397 quiche_config_set_initial_max_data(config, 10000000);
398 quiche_config_set_initial_max_stream_data_bidi_local(config, 1000000);
399 quiche_config_set_initial_max_stream_data_bidi_remote(config, 1000000);
400 quiche_config_set_initial_max_stream_data_uni(config, 1000000);
401 quiche_config_set_initial_max_streams_bidi(config, 100);
402 quiche_config_set_initial_max_streams_uni(config, 100);
403 quiche_config_set_disable_active_migration(config, true);
404
405 if (getenv("SSLKEYLOGFILE")) {
406 quiche_config_log_keys(config);
407 }
408
409 // ABC: old config creation here
410
411 uint8_t scid[LOCAL_CONN_ID_LEN];
412 int rng = open("/dev/urandom", O_RDONLY);
413 if (rng < 0) {
414 perror("failed to open /dev/urandom");
415 return -1;
416 }
417
418 ssize_t rand_len = read(rng, &scid, sizeof(scid));
419 if (rand_len < 0) {
420 perror("failed to create connection ID");
421 return -1;
422 }
423
424 struct conn_io *conn_io = malloc(sizeof(*conn_io));
425 if (conn_io == NULL) {
426 fprintf(stderr, "failed to allocate connection IO\n");
427 return -1;
428 }
429
430 conn_io->local_addr_len = sizeof(conn_io->local_addr);
431 if (getsockname(sock, (struct sockaddr *)&conn_io->local_addr,
432 &conn_io->local_addr_len) != 0)
433 {
434 perror("failed to get local address of socket");
435 return -1;
436 };
437
438 quiche_conn *conn = quiche_connect(host, (const uint8_t *) scid, sizeof(scid),
439 (struct sockaddr *) &conn_io->local_addr,
440 conn_io->local_addr_len,
441 peer->ai_addr, peer->ai_addrlen, config);
442
443 if (conn == NULL) {
444 fprintf(stderr, "failed to create connection\n");
445 return -1;
446 }
447
448 conn_io->sock = sock;
449 conn_io->conn = conn;
450 conn_io->host = host;
451
452 ev_io watcher;
453
454 struct ev_loop *loop = ev_default_loop(0);
455
456 ev_io_init(&watcher, recv_cb, conn_io->sock, EV_READ);
457 ev_io_start(loop, &watcher);
458 watcher.data = conn_io;
459
460 ev_init(&conn_io->timer, timeout_cb);
461 conn_io->timer.data = conn_io;
462
463 flush_egress(loop, conn_io);
464
465 ev_loop(loop, 0);
466
467 freeaddrinfo(peer);
468
469 quiche_h3_conn_free(conn_io->http3);
470
471 quiche_conn_free(conn);
472
473 quiche_config_free(config);
474
475 return 0;
476 }
477