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