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 notice,
9 // 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 #[macro_use]
28 extern crate log;
29
30 use std::net;
31
32 use std::collections::HashMap;
33
34 use ring::rand::*;
35
36 const MAX_DATAGRAM_SIZE: usize = 1350;
37
38 struct PartialResponse {
39 body: Vec<u8>,
40
41 written: usize,
42 }
43
44 struct Client {
45 conn: std::pin::Pin<Box<quiche::Connection>>,
46
47 partial_responses: HashMap<u64, PartialResponse>,
48 }
49
50 type ClientMap = HashMap<quiche::ConnectionId<'static>, Client>;
51
main()52 fn main() {
53 let mut buf = [0; 65535];
54 let mut out = [0; MAX_DATAGRAM_SIZE];
55
56 let mut args = std::env::args();
57
58 let cmd = &args.next().unwrap();
59
60 if args.len() != 0 {
61 println!("Usage: {}", cmd);
62 println!("\nSee tools/apps/ for more complete implementations.");
63 return;
64 }
65
66 // Setup the event loop.
67 let poll = mio::Poll::new().unwrap();
68 let mut events = mio::Events::with_capacity(1024);
69
70 // Create the UDP listening socket, and register it with the event loop.
71 let socket = net::UdpSocket::bind("127.0.0.1:4433").unwrap();
72
73 let socket = mio::net::UdpSocket::from_socket(socket).unwrap();
74 poll.register(
75 &socket,
76 mio::Token(0),
77 mio::Ready::readable(),
78 mio::PollOpt::edge(),
79 )
80 .unwrap();
81
82 // Create the configuration for the QUIC connections.
83 let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION).unwrap();
84
85 config
86 .load_cert_chain_from_pem_file("examples/cert.crt")
87 .unwrap();
88 config
89 .load_priv_key_from_pem_file("examples/cert.key")
90 .unwrap();
91
92 config
93 .set_application_protos(
94 b"\x0ahq-interop\x05hq-29\x05hq-28\x05hq-27\x08http/0.9",
95 )
96 .unwrap();
97
98 config.set_max_idle_timeout(5000);
99 config.set_max_recv_udp_payload_size(MAX_DATAGRAM_SIZE);
100 config.set_max_send_udp_payload_size(MAX_DATAGRAM_SIZE);
101 config.set_initial_max_data(10_000_000);
102 config.set_initial_max_stream_data_bidi_local(1_000_000);
103 config.set_initial_max_stream_data_bidi_remote(1_000_000);
104 config.set_initial_max_stream_data_uni(1_000_000);
105 config.set_initial_max_streams_bidi(100);
106 config.set_initial_max_streams_uni(100);
107 config.set_disable_active_migration(true);
108 config.enable_early_data();
109
110 let rng = SystemRandom::new();
111 let conn_id_seed =
112 ring::hmac::Key::generate(ring::hmac::HMAC_SHA256, &rng).unwrap();
113
114 let mut clients = ClientMap::new();
115
116 loop {
117 // Find the shorter timeout from all the active connections.
118 //
119 // TODO: use event loop that properly supports timers
120 let timeout = clients.values().filter_map(|c| c.conn.timeout()).min();
121
122 poll.poll(&mut events, timeout).unwrap();
123
124 // Read incoming UDP packets from the socket and feed them to quiche,
125 // until there are no more packets to read.
126 'read: loop {
127 // If the event loop reported no events, it means that the timeout
128 // has expired, so handle it without attempting to read packets. We
129 // will then proceed with the send loop.
130 if events.is_empty() {
131 debug!("timed out");
132
133 clients.values_mut().for_each(|c| c.conn.on_timeout());
134
135 break 'read;
136 }
137
138 let (len, from) = match socket.recv_from(&mut buf) {
139 Ok(v) => v,
140
141 Err(e) => {
142 // There are no more UDP packets to read, so end the read
143 // loop.
144 if e.kind() == std::io::ErrorKind::WouldBlock {
145 debug!("recv() would block");
146 break 'read;
147 }
148
149 panic!("recv() failed: {:?}", e);
150 },
151 };
152
153 debug!("got {} bytes", len);
154
155 let pkt_buf = &mut buf[..len];
156
157 // Parse the QUIC packet's header.
158 let hdr = match quiche::Header::from_slice(
159 pkt_buf,
160 quiche::MAX_CONN_ID_LEN,
161 ) {
162 Ok(v) => v,
163
164 Err(e) => {
165 error!("Parsing packet header failed: {:?}", e);
166 continue 'read;
167 },
168 };
169
170 trace!("got packet {:?}", hdr);
171
172 let conn_id = ring::hmac::sign(&conn_id_seed, &hdr.dcid);
173 let conn_id = &conn_id.as_ref()[..quiche::MAX_CONN_ID_LEN];
174 let conn_id = conn_id.to_vec().into();
175
176 // Lookup a connection based on the packet's connection ID. If there
177 // is no connection matching, create a new one.
178 let client = if !clients.contains_key(&hdr.dcid) &&
179 !clients.contains_key(&conn_id)
180 {
181 if hdr.ty != quiche::Type::Initial {
182 error!("Packet is not Initial");
183 continue 'read;
184 }
185
186 if !quiche::version_is_supported(hdr.version) {
187 warn!("Doing version negotiation");
188
189 let len =
190 quiche::negotiate_version(&hdr.scid, &hdr.dcid, &mut out)
191 .unwrap();
192
193 let out = &out[..len];
194
195 if let Err(e) = socket.send_to(out, &from) {
196 if e.kind() == std::io::ErrorKind::WouldBlock {
197 debug!("send() would block");
198 break;
199 }
200
201 panic!("send() failed: {:?}", e);
202 }
203 continue 'read;
204 }
205
206 let mut scid = [0; quiche::MAX_CONN_ID_LEN];
207 scid.copy_from_slice(&conn_id);
208
209 let scid = quiche::ConnectionId::from_ref(&scid);
210
211 // Token is always present in Initial packets.
212 let token = hdr.token.as_ref().unwrap();
213
214 // Do stateless retry if the client didn't send a token.
215 if token.is_empty() {
216 warn!("Doing stateless retry");
217
218 let new_token = mint_token(&hdr, &from);
219
220 let len = quiche::retry(
221 &hdr.scid,
222 &hdr.dcid,
223 &scid,
224 &new_token,
225 hdr.version,
226 &mut out,
227 )
228 .unwrap();
229
230 let out = &out[..len];
231
232 if let Err(e) = socket.send_to(out, &from) {
233 if e.kind() == std::io::ErrorKind::WouldBlock {
234 debug!("send() would block");
235 break;
236 }
237
238 panic!("send() failed: {:?}", e);
239 }
240 continue 'read;
241 }
242
243 let odcid = validate_token(&from, token);
244
245 // The token was not valid, meaning the retry failed, so
246 // drop the packet.
247 if odcid.is_none() {
248 error!("Invalid address validation token");
249 continue 'read;
250 }
251
252 if scid.len() != hdr.dcid.len() {
253 error!("Invalid destination connection ID");
254 continue 'read;
255 }
256
257 // Reuse the source connection ID we sent in the Retry packet,
258 // instead of changing it again.
259 let scid = hdr.dcid.clone();
260
261 debug!("New connection: dcid={:?} scid={:?}", hdr.dcid, scid);
262
263 let conn =
264 quiche::accept(&scid, odcid.as_ref(), from, &mut config)
265 .unwrap();
266
267 let client = Client {
268 conn,
269 partial_responses: HashMap::new(),
270 };
271
272 clients.insert(scid.clone(), client);
273
274 clients.get_mut(&scid).unwrap()
275 } else {
276 match clients.get_mut(&hdr.dcid) {
277 Some(v) => v,
278
279 None => clients.get_mut(&conn_id).unwrap(),
280 }
281 };
282
283 let recv_info = quiche::RecvInfo { from };
284
285 // Process potentially coalesced packets.
286 let read = match client.conn.recv(pkt_buf, recv_info) {
287 Ok(v) => v,
288
289 Err(e) => {
290 error!("{} recv failed: {:?}", client.conn.trace_id(), e);
291 continue 'read;
292 },
293 };
294
295 debug!("{} processed {} bytes", client.conn.trace_id(), read);
296
297 if client.conn.is_in_early_data() || client.conn.is_established() {
298 // Handle writable streams.
299 for stream_id in client.conn.writable() {
300 handle_writable(client, stream_id);
301 }
302
303 // Process all readable streams.
304 for s in client.conn.readable() {
305 while let Ok((read, fin)) =
306 client.conn.stream_recv(s, &mut buf)
307 {
308 debug!(
309 "{} received {} bytes",
310 client.conn.trace_id(),
311 read
312 );
313
314 let stream_buf = &buf[..read];
315
316 debug!(
317 "{} stream {} has {} bytes (fin? {})",
318 client.conn.trace_id(),
319 s,
320 stream_buf.len(),
321 fin
322 );
323
324 handle_stream(client, s, stream_buf, "examples/root");
325 }
326 }
327 }
328 }
329
330 // Generate outgoing QUIC packets for all active connections and send
331 // them on the UDP socket, until quiche reports that there are no more
332 // packets to be sent.
333 for client in clients.values_mut() {
334 loop {
335 let (write, send_info) = match client.conn.send(&mut out) {
336 Ok(v) => v,
337
338 Err(quiche::Error::Done) => {
339 debug!("{} done writing", client.conn.trace_id());
340 break;
341 },
342
343 Err(e) => {
344 error!("{} send failed: {:?}", client.conn.trace_id(), e);
345
346 client.conn.close(false, 0x1, b"fail").ok();
347 break;
348 },
349 };
350
351 if let Err(e) = socket.send_to(&out[..write], &send_info.to) {
352 if e.kind() == std::io::ErrorKind::WouldBlock {
353 debug!("send() would block");
354 break;
355 }
356
357 panic!("send() failed: {:?}", e);
358 }
359
360 debug!("{} written {} bytes", client.conn.trace_id(), write);
361 }
362 }
363
364 // Garbage collect closed connections.
365 clients.retain(|_, ref mut c| {
366 debug!("Collecting garbage");
367
368 if c.conn.is_closed() {
369 info!(
370 "{} connection collected {:?}",
371 c.conn.trace_id(),
372 c.conn.stats()
373 );
374 }
375
376 !c.conn.is_closed()
377 });
378 }
379 }
380
381 /// Generate a stateless retry token.
382 ///
383 /// The token includes the static string `"quiche"` followed by the IP address
384 /// of the client and by the original destination connection ID generated by the
385 /// client.
386 ///
387 /// Note that this function is only an example and doesn't do any cryptographic
388 /// authenticate of the token. *It should not be used in production system*.
mint_token(hdr: &quiche::Header, src: &net::SocketAddr) -> Vec<u8>389 fn mint_token(hdr: &quiche::Header, src: &net::SocketAddr) -> Vec<u8> {
390 let mut token = Vec::new();
391
392 token.extend_from_slice(b"quiche");
393
394 let addr = match src.ip() {
395 std::net::IpAddr::V4(a) => a.octets().to_vec(),
396 std::net::IpAddr::V6(a) => a.octets().to_vec(),
397 };
398
399 token.extend_from_slice(&addr);
400 token.extend_from_slice(&hdr.dcid);
401
402 token
403 }
404
405 /// Validates a stateless retry token.
406 ///
407 /// This checks that the ticket includes the `"quiche"` static string, and that
408 /// the client IP address matches the address stored in the ticket.
409 ///
410 /// Note that this function is only an example and doesn't do any cryptographic
411 /// authenticate of the token. *It should not be used in production system*.
validate_token<'a>( src: &net::SocketAddr, token: &'a [u8], ) -> Option<quiche::ConnectionId<'a>>412 fn validate_token<'a>(
413 src: &net::SocketAddr, token: &'a [u8],
414 ) -> Option<quiche::ConnectionId<'a>> {
415 if token.len() < 6 {
416 return None;
417 }
418
419 if &token[..6] != b"quiche" {
420 return None;
421 }
422
423 let token = &token[6..];
424
425 let addr = match src.ip() {
426 std::net::IpAddr::V4(a) => a.octets().to_vec(),
427 std::net::IpAddr::V6(a) => a.octets().to_vec(),
428 };
429
430 if token.len() < addr.len() || &token[..addr.len()] != addr.as_slice() {
431 return None;
432 }
433
434 Some(quiche::ConnectionId::from_ref(&token[addr.len()..]))
435 }
436
437 /// Handles incoming HTTP/0.9 requests.
handle_stream(client: &mut Client, stream_id: u64, buf: &[u8], root: &str)438 fn handle_stream(client: &mut Client, stream_id: u64, buf: &[u8], root: &str) {
439 let conn = &mut client.conn;
440
441 if buf.len() > 4 && &buf[..4] == b"GET " {
442 let uri = &buf[4..buf.len()];
443 let uri = String::from_utf8(uri.to_vec()).unwrap();
444 let uri = String::from(uri.lines().next().unwrap());
445 let uri = std::path::Path::new(&uri);
446 let mut path = std::path::PathBuf::from(root);
447
448 for c in uri.components() {
449 if let std::path::Component::Normal(v) = c {
450 path.push(v)
451 }
452 }
453
454 info!(
455 "{} got GET request for {:?} on stream {}",
456 conn.trace_id(),
457 path,
458 stream_id
459 );
460
461 let body = std::fs::read(path.as_path())
462 .unwrap_or_else(|_| b"Not Found!\r\n".to_vec());
463
464 info!(
465 "{} sending response of size {} on stream {}",
466 conn.trace_id(),
467 body.len(),
468 stream_id
469 );
470
471 let written = match conn.stream_send(stream_id, &body, true) {
472 Ok(v) => v,
473
474 Err(quiche::Error::Done) => 0,
475
476 Err(e) => {
477 error!("{} stream send failed {:?}", conn.trace_id(), e);
478 return;
479 },
480 };
481
482 if written < body.len() {
483 let response = PartialResponse { body, written };
484 client.partial_responses.insert(stream_id, response);
485 }
486 }
487 }
488
489 /// Handles newly writable streams.
handle_writable(client: &mut Client, stream_id: u64)490 fn handle_writable(client: &mut Client, stream_id: u64) {
491 let conn = &mut client.conn;
492
493 debug!("{} stream {} is writable", conn.trace_id(), stream_id);
494
495 if !client.partial_responses.contains_key(&stream_id) {
496 return;
497 }
498
499 let resp = client.partial_responses.get_mut(&stream_id).unwrap();
500 let body = &resp.body[resp.written..];
501
502 let written = match conn.stream_send(stream_id, &body, true) {
503 Ok(v) => v,
504
505 Err(quiche::Error::Done) => 0,
506
507 Err(e) => {
508 client.partial_responses.remove(&stream_id);
509
510 error!("{} stream send failed {:?}", conn.trace_id(), e);
511 return;
512 },
513 };
514
515 resp.written += written;
516
517 if resp.written == resp.body.len() {
518 client.partial_responses.remove(&stream_id);
519 }
520 }
521