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