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