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 
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     int sock;
53 
54     struct sockaddr_storage local_addr;
55     socklen_t local_addr_len;
56 
57     quiche_conn *conn;
58 };
59 
debug_log(const char * line,void * argp)60 static void debug_log(const char *line, void *argp) {
61     fprintf(stderr, "%s\n", line);
62 }
63 
flush_egress(struct ev_loop * loop,struct conn_io * conn_io)64 static void flush_egress(struct ev_loop *loop, struct conn_io *conn_io) {
65     static uint8_t out[MAX_DATAGRAM_SIZE];
66 
67     quiche_send_info send_info;
68 
69     while (1) {
70         ssize_t written = quiche_conn_send(conn_io->conn, out, sizeof(out),
71                                            &send_info);
72 
73         if (written == QUICHE_ERR_DONE) {
74             fprintf(stderr, "done writing\n");
75             break;
76         }
77 
78         if (written < 0) {
79             fprintf(stderr, "failed to create packet: %zd\n", written);
80             return;
81         }
82 
83         ssize_t sent = sendto(conn_io->sock, out, written, 0,
84                               (struct sockaddr *) &send_info.to,
85                               send_info.to_len);
86 
87         if (sent != written) {
88             perror("failed to send");
89             return;
90         }
91 
92         fprintf(stderr, "sent %zd bytes\n", sent);
93     }
94 
95     double t = quiche_conn_timeout_as_nanos(conn_io->conn) / 1e9f;
96     conn_io->timer.repeat = t;
97     ev_timer_again(loop, &conn_io->timer);
98 }
99 
recv_cb(EV_P_ ev_io * w,int revents)100 static void recv_cb(EV_P_ ev_io *w, int revents) {
101     static bool req_sent = false;
102 
103     struct conn_io *conn_io = w->data;
104 
105     static uint8_t buf[65535];
106 
107     while (1) {
108         struct sockaddr_storage peer_addr;
109         socklen_t peer_addr_len = sizeof(peer_addr);
110         memset(&peer_addr, 0, peer_addr_len);
111 
112         ssize_t read = recvfrom(conn_io->sock, buf, sizeof(buf), 0,
113                                 (struct sockaddr *) &peer_addr,
114                                 &peer_addr_len);
115 
116         if (read < 0) {
117             if ((errno == EWOULDBLOCK) || (errno == EAGAIN)) {
118                 fprintf(stderr, "recv would block\n");
119                 break;
120             }
121 
122             perror("failed to read");
123             return;
124         }
125 
126         quiche_recv_info recv_info = {
127             (struct sockaddr *) &peer_addr,
128             peer_addr_len,
129 
130             (struct sockaddr *) &conn_io->local_addr,
131             conn_io->local_addr_len,
132         };
133 
134         ssize_t done = quiche_conn_recv(conn_io->conn, buf, read, &recv_info);
135 
136         if (done < 0) {
137             fprintf(stderr, "failed to process packet\n");
138             continue;
139         }
140 
141         fprintf(stderr, "recv %zd bytes\n", done);
142     }
143 
144     fprintf(stderr, "done reading\n");
145 
146     if (quiche_conn_is_closed(conn_io->conn)) {
147         fprintf(stderr, "connection closed\n");
148 
149         ev_break(EV_A_ EVBREAK_ONE);
150         return;
151     }
152 
153     if (quiche_conn_is_established(conn_io->conn) && !req_sent) {
154         const uint8_t *app_proto;
155         size_t app_proto_len;
156 
157         quiche_conn_application_proto(conn_io->conn, &app_proto, &app_proto_len);
158 
159         fprintf(stderr, "connection established: %.*s\n",
160                 (int) app_proto_len, app_proto);
161 
162         const static uint8_t r[] = "GET /index.html\r\n";
163         if (quiche_conn_stream_send(conn_io->conn, 4, r, sizeof(r), true) < 0) {
164             fprintf(stderr, "failed to send HTTP request\n");
165             return;
166         }
167 
168         fprintf(stderr, "sent HTTP request\n");
169 
170         req_sent = true;
171     }
172 
173     if (quiche_conn_is_established(conn_io->conn)) {
174         uint64_t s = 0;
175 
176         quiche_stream_iter *readable = quiche_conn_readable(conn_io->conn);
177 
178         while (quiche_stream_iter_next(readable, &s)) {
179             fprintf(stderr, "stream %" PRIu64 " is readable\n", s);
180 
181             bool fin = false;
182             ssize_t recv_len = quiche_conn_stream_recv(conn_io->conn, s,
183                                                        buf, sizeof(buf),
184                                                        &fin);
185             if (recv_len < 0) {
186                 break;
187             }
188 
189             printf("%.*s", (int) recv_len, buf);
190 
191             if (fin) {
192                 if (quiche_conn_close(conn_io->conn, true, 0, NULL, 0) < 0) {
193                     fprintf(stderr, "failed to close connection\n");
194                 }
195             }
196         }
197 
198         quiche_stream_iter_free(readable);
199     }
200 
201     flush_egress(loop, conn_io);
202 }
203 
timeout_cb(EV_P_ ev_timer * w,int revents)204 static void timeout_cb(EV_P_ ev_timer *w, int revents) {
205     struct conn_io *conn_io = w->data;
206     quiche_conn_on_timeout(conn_io->conn);
207 
208     fprintf(stderr, "timeout\n");
209 
210     flush_egress(loop, conn_io);
211 
212     if (quiche_conn_is_closed(conn_io->conn)) {
213         quiche_stats stats;
214         quiche_path_stats path_stats;
215 
216         quiche_conn_stats(conn_io->conn, &stats);
217         quiche_conn_path_stats(conn_io->conn, 0, &path_stats);
218 
219         fprintf(stderr, "connection closed, recv=%zu sent=%zu lost=%zu rtt=%" PRIu64 "ns\n",
220                 stats.recv, stats.sent, stats.lost, path_stats.rtt);
221 
222         ev_break(EV_A_ EVBREAK_ONE);
223         return;
224     }
225 }
226 
main(int argc,char * argv[])227 int main(int argc, char *argv[]) {
228     const char *host = argv[1];
229     const char *port = argv[2];
230 
231     const struct addrinfo hints = {
232         .ai_family = PF_UNSPEC,
233         .ai_socktype = SOCK_DGRAM,
234         .ai_protocol = IPPROTO_UDP
235     };
236 
237     quiche_enable_debug_logging(debug_log, NULL);
238 
239     struct addrinfo *peer;
240     if (getaddrinfo(host, port, &hints, &peer) != 0) {
241         perror("failed to resolve host");
242         return -1;
243     }
244 
245     int sock = socket(peer->ai_family, SOCK_DGRAM, 0);
246     if (sock < 0) {
247         perror("failed to create socket");
248         return -1;
249     }
250 
251     if (fcntl(sock, F_SETFL, O_NONBLOCK) != 0) {
252         perror("failed to make socket non-blocking");
253         return -1;
254     }
255 
256     quiche_config *config = quiche_config_new(0xbabababa);
257     if (config == NULL) {
258         fprintf(stderr, "failed to create config\n");
259         return -1;
260     }
261 
262     quiche_config_set_application_protos(config,
263         (uint8_t *) "\x0ahq-interop\x05hq-29\x05hq-28\x05hq-27\x08http/0.9", 38);
264 
265     quiche_config_set_max_idle_timeout(config, 5000);
266     quiche_config_set_max_recv_udp_payload_size(config, MAX_DATAGRAM_SIZE);
267     quiche_config_set_max_send_udp_payload_size(config, MAX_DATAGRAM_SIZE);
268     quiche_config_set_initial_max_data(config, 10000000);
269     quiche_config_set_initial_max_stream_data_bidi_local(config, 1000000);
270     quiche_config_set_initial_max_stream_data_uni(config, 1000000);
271     quiche_config_set_initial_max_streams_bidi(config, 100);
272     quiche_config_set_initial_max_streams_uni(config, 100);
273     quiche_config_set_disable_active_migration(config, true);
274 
275     if (getenv("SSLKEYLOGFILE")) {
276       quiche_config_log_keys(config);
277     }
278 
279     uint8_t scid[LOCAL_CONN_ID_LEN];
280     int rng = open("/dev/urandom", O_RDONLY);
281     if (rng < 0) {
282         perror("failed to open /dev/urandom");
283         return -1;
284     }
285 
286     ssize_t rand_len = read(rng, &scid, sizeof(scid));
287     if (rand_len < 0) {
288         perror("failed to create connection ID");
289         return -1;
290     }
291 
292     struct conn_io *conn_io = malloc(sizeof(*conn_io));
293     if (conn_io == NULL) {
294         fprintf(stderr, "failed to allocate connection IO\n");
295         return -1;
296     }
297 
298     conn_io->local_addr_len = sizeof(conn_io->local_addr);
299     if (getsockname(sock, (struct sockaddr *)&conn_io->local_addr,
300                     &conn_io->local_addr_len) != 0)
301     {
302         perror("failed to get local address of socket");
303         return -1;
304     };
305 
306     quiche_conn *conn = quiche_connect(host, (const uint8_t *) scid, sizeof(scid),
307                                        (struct sockaddr *) &conn_io->local_addr,
308                                        conn_io->local_addr_len,
309                                        peer->ai_addr, peer->ai_addrlen, config);
310 
311     if (conn == NULL) {
312         fprintf(stderr, "failed to create connection\n");
313         return -1;
314     }
315 
316     conn_io->sock = sock;
317     conn_io->conn = conn;
318 
319     ev_io watcher;
320 
321     struct ev_loop *loop = ev_default_loop(0);
322 
323     ev_io_init(&watcher, recv_cb, conn_io->sock, EV_READ);
324     ev_io_start(loop, &watcher);
325     watcher.data = conn_io;
326 
327     ev_init(&conn_io->timer, timeout_cb);
328     conn_io->timer.data = conn_io;
329 
330     flush_egress(loop, conn_io);
331 
332     ev_loop(loop, 0);
333 
334     freeaddrinfo(peer);
335 
336     quiche_conn_free(conn);
337 
338     quiche_config_free(config);
339 
340     return 0;
341 }
342