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