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 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 use quiche::h3::NameValue;
37
38 const MAX_DATAGRAM_SIZE: usize = 1350;
39
40 struct PartialResponse {
41 headers: Option<Vec<quiche::h3::Header>>,
42
43 body: Vec<u8>,
44
45 written: usize,
46 }
47
48 struct Client {
49 conn: quiche::Connection,
50
51 http3_conn: Option<quiche::h3::Connection>,
52
53 partial_responses: HashMap<u64, PartialResponse>,
54 }
55
56 type ClientMap = HashMap<quiche::ConnectionId<'static>, Client>;
57
main()58 fn main() {
59 let mut buf = [0; 65535];
60 let mut out = [0; MAX_DATAGRAM_SIZE];
61
62 let mut args = std::env::args();
63
64 let cmd = &args.next().unwrap();
65
66 if args.len() != 0 {
67 println!("Usage: {cmd}");
68 println!("\nSee tools/apps/ for more complete implementations.");
69 return;
70 }
71
72 // Setup the event loop.
73 let mut poll = mio::Poll::new().unwrap();
74 let mut events = mio::Events::with_capacity(1024);
75
76 // Create the UDP listening socket, and register it with the event loop.
77 let mut socket =
78 mio::net::UdpSocket::bind("127.0.0.1:4433".parse().unwrap()).unwrap();
79 poll.registry()
80 .register(&mut socket, mio::Token(0), mio::Interest::READABLE)
81 .unwrap();
82
83 // Create the configuration for the QUIC connections.
84 let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION).unwrap();
85
86 config
87 .load_cert_chain_from_pem_file("examples/cert.crt")
88 .unwrap();
89 config
90 .load_priv_key_from_pem_file("examples/cert.key")
91 .unwrap();
92
93 config
94 .set_application_protos(quiche::h3::APPLICATION_PROTOCOL)
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 h3_config = quiche::h3::Config::new().unwrap();
110
111 let rng = SystemRandom::new();
112 let conn_id_seed =
113 ring::hmac::Key::generate(ring::hmac::HMAC_SHA256, &rng).unwrap();
114
115 let mut clients = ClientMap::new();
116
117 let local_addr = socket.local_addr().unwrap();
118
119 loop {
120 // Find the shorter timeout from all the active connections.
121 //
122 // TODO: use event loop that properly supports timers
123 let timeout = clients.values().filter_map(|c| c.conn.timeout()).min();
124
125 poll.poll(&mut events, timeout).unwrap();
126
127 // Read incoming UDP packets from the socket and feed them to quiche,
128 // until there are no more packets to read.
129 'read: loop {
130 // If the event loop reported no events, it means that the timeout
131 // has expired, so handle it without attempting to read packets. We
132 // will then proceed with the send loop.
133 if events.is_empty() {
134 debug!("timed out");
135
136 clients.values_mut().for_each(|c| c.conn.on_timeout());
137
138 break 'read;
139 }
140
141 let (len, from) = match socket.recv_from(&mut buf) {
142 Ok(v) => v,
143
144 Err(e) => {
145 // There are no more UDP packets to read, so end the read
146 // loop.
147 if e.kind() == std::io::ErrorKind::WouldBlock {
148 debug!("recv() would block");
149 break 'read;
150 }
151
152 panic!("recv() failed: {:?}", e);
153 },
154 };
155
156 debug!("got {} bytes", len);
157
158 let pkt_buf = &mut buf[..len];
159
160 // Parse the QUIC packet's header.
161 let hdr = match quiche::Header::from_slice(
162 pkt_buf,
163 quiche::MAX_CONN_ID_LEN,
164 ) {
165 Ok(v) => v,
166
167 Err(e) => {
168 error!("Parsing packet header failed: {:?}", e);
169 continue 'read;
170 },
171 };
172
173 trace!("got packet {:?}", hdr);
174
175 let conn_id = ring::hmac::sign(&conn_id_seed, &hdr.dcid);
176 let conn_id = &conn_id.as_ref()[..quiche::MAX_CONN_ID_LEN];
177 let conn_id = conn_id.to_vec().into();
178
179 // Lookup a connection based on the packet's connection ID. If there
180 // is no connection matching, create a new one.
181 let client = if !clients.contains_key(&hdr.dcid) &&
182 !clients.contains_key(&conn_id)
183 {
184 if hdr.ty != quiche::Type::Initial {
185 error!("Packet is not Initial");
186 continue 'read;
187 }
188
189 if !quiche::version_is_supported(hdr.version) {
190 warn!("Doing version negotiation");
191
192 let len =
193 quiche::negotiate_version(&hdr.scid, &hdr.dcid, &mut out)
194 .unwrap();
195
196 let out = &out[..len];
197
198 if let Err(e) = socket.send_to(out, from) {
199 if e.kind() == std::io::ErrorKind::WouldBlock {
200 debug!("send() would block");
201 break;
202 }
203
204 panic!("send() failed: {:?}", e);
205 }
206 continue 'read;
207 }
208
209 let mut scid = [0; quiche::MAX_CONN_ID_LEN];
210 scid.copy_from_slice(&conn_id);
211
212 let scid = quiche::ConnectionId::from_ref(&scid);
213
214 // Token is always present in Initial packets.
215 let token = hdr.token.as_ref().unwrap();
216
217 // Do stateless retry if the client didn't send a token.
218 if token.is_empty() {
219 warn!("Doing stateless retry");
220
221 let new_token = mint_token(&hdr, &from);
222
223 let len = quiche::retry(
224 &hdr.scid,
225 &hdr.dcid,
226 &scid,
227 &new_token,
228 hdr.version,
229 &mut out,
230 )
231 .unwrap();
232
233 let out = &out[..len];
234
235 if let Err(e) = socket.send_to(out, from) {
236 if e.kind() == std::io::ErrorKind::WouldBlock {
237 debug!("send() would block");
238 break;
239 }
240
241 panic!("send() failed: {:?}", e);
242 }
243 continue 'read;
244 }
245
246 let odcid = validate_token(&from, token);
247
248 // The token was not valid, meaning the retry failed, so
249 // drop the packet.
250 if odcid.is_none() {
251 error!("Invalid address validation token");
252 continue 'read;
253 }
254
255 if scid.len() != hdr.dcid.len() {
256 error!("Invalid destination connection ID");
257 continue 'read;
258 }
259
260 // Reuse the source connection ID we sent in the Retry packet,
261 // instead of changing it again.
262 let scid = hdr.dcid.clone();
263
264 debug!("New connection: dcid={:?} scid={:?}", hdr.dcid, scid);
265
266 let conn = quiche::accept(
267 &scid,
268 odcid.as_ref(),
269 local_addr,
270 from,
271 &mut config,
272 )
273 .unwrap();
274
275 let client = Client {
276 conn,
277 http3_conn: None,
278 partial_responses: HashMap::new(),
279 };
280
281 clients.insert(scid.clone(), client);
282
283 clients.get_mut(&scid).unwrap()
284 } else {
285 match clients.get_mut(&hdr.dcid) {
286 Some(v) => v,
287
288 None => clients.get_mut(&conn_id).unwrap(),
289 }
290 };
291
292 let recv_info = quiche::RecvInfo {
293 to: socket.local_addr().unwrap(),
294 from,
295 };
296
297 // Process potentially coalesced packets.
298 let read = match client.conn.recv(pkt_buf, recv_info) {
299 Ok(v) => v,
300
301 Err(e) => {
302 error!("{} recv failed: {:?}", client.conn.trace_id(), e);
303 continue 'read;
304 },
305 };
306
307 debug!("{} processed {} bytes", client.conn.trace_id(), read);
308
309 // Create a new HTTP/3 connection as soon as the QUIC connection
310 // is established.
311 if (client.conn.is_in_early_data() || client.conn.is_established()) &&
312 client.http3_conn.is_none()
313 {
314 debug!(
315 "{} QUIC handshake completed, now trying HTTP/3",
316 client.conn.trace_id()
317 );
318
319 let h3_conn = match quiche::h3::Connection::with_transport(
320 &mut client.conn,
321 &h3_config,
322 ) {
323 Ok(v) => v,
324
325 Err(e) => {
326 error!("failed to create HTTP/3 connection: {}", e);
327 continue 'read;
328 },
329 };
330
331 // TODO: sanity check h3 connection before adding to map
332 client.http3_conn = Some(h3_conn);
333 }
334
335 if client.http3_conn.is_some() {
336 // Handle writable streams.
337 for stream_id in client.conn.writable() {
338 handle_writable(client, stream_id);
339 }
340
341 // Process HTTP/3 events.
342 loop {
343 let http3_conn = client.http3_conn.as_mut().unwrap();
344
345 match http3_conn.poll(&mut client.conn) {
346 Ok((
347 stream_id,
348 quiche::h3::Event::Headers { list, .. },
349 )) => {
350 handle_request(
351 client,
352 stream_id,
353 &list,
354 "examples/root",
355 );
356 },
357
358 Ok((stream_id, quiche::h3::Event::Data)) => {
359 info!(
360 "{} got data on stream id {}",
361 client.conn.trace_id(),
362 stream_id
363 );
364 },
365
366 Ok((_stream_id, quiche::h3::Event::Finished)) => (),
367
368 Ok((_stream_id, quiche::h3::Event::Reset { .. })) => (),
369
370 Ok((_flow_id, quiche::h3::Event::Datagram)) => (),
371
372 Ok((
373 _prioritized_element_id,
374 quiche::h3::Event::PriorityUpdate,
375 )) => (),
376
377 Ok((_goaway_id, quiche::h3::Event::GoAway)) => (),
378
379 Err(quiche::h3::Error::Done) => {
380 break;
381 },
382
383 Err(e) => {
384 error!(
385 "{} HTTP/3 error {:?}",
386 client.conn.trace_id(),
387 e
388 );
389
390 break;
391 },
392 }
393 }
394 }
395 }
396
397 // Generate outgoing QUIC packets for all active connections and send
398 // them on the UDP socket, until quiche reports that there are no more
399 // packets to be sent.
400 for client in clients.values_mut() {
401 loop {
402 let (write, send_info) = match client.conn.send(&mut out) {
403 Ok(v) => v,
404
405 Err(quiche::Error::Done) => {
406 debug!("{} done writing", client.conn.trace_id());
407 break;
408 },
409
410 Err(e) => {
411 error!("{} send failed: {:?}", client.conn.trace_id(), e);
412
413 client.conn.close(false, 0x1, b"fail").ok();
414 break;
415 },
416 };
417
418 if let Err(e) = socket.send_to(&out[..write], send_info.to) {
419 if e.kind() == std::io::ErrorKind::WouldBlock {
420 debug!("send() would block");
421 break;
422 }
423
424 panic!("send() failed: {:?}", e);
425 }
426
427 debug!("{} written {} bytes", client.conn.trace_id(), write);
428 }
429 }
430
431 // Garbage collect closed connections.
432 clients.retain(|_, ref mut c| {
433 debug!("Collecting garbage");
434
435 if c.conn.is_closed() {
436 info!(
437 "{} connection collected {:?}",
438 c.conn.trace_id(),
439 c.conn.stats()
440 );
441 }
442
443 !c.conn.is_closed()
444 });
445 }
446 }
447
448 /// Generate a stateless retry token.
449 ///
450 /// The token includes the static string `"quiche"` followed by the IP address
451 /// of the client and by the original destination connection ID generated by the
452 /// client.
453 ///
454 /// Note that this function is only an example and doesn't do any cryptographic
455 /// authenticate of the token. *It should not be used in production system*.
mint_token(hdr: &quiche::Header, src: &net::SocketAddr) -> Vec<u8>456 fn mint_token(hdr: &quiche::Header, src: &net::SocketAddr) -> Vec<u8> {
457 let mut token = Vec::new();
458
459 token.extend_from_slice(b"quiche");
460
461 let addr = match src.ip() {
462 std::net::IpAddr::V4(a) => a.octets().to_vec(),
463 std::net::IpAddr::V6(a) => a.octets().to_vec(),
464 };
465
466 token.extend_from_slice(&addr);
467 token.extend_from_slice(&hdr.dcid);
468
469 token
470 }
471
472 /// Validates a stateless retry token.
473 ///
474 /// This checks that the ticket includes the `"quiche"` static string, and that
475 /// the client IP address matches the address stored in the ticket.
476 ///
477 /// Note that this function is only an example and doesn't do any cryptographic
478 /// 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>>479 fn validate_token<'a>(
480 src: &net::SocketAddr, token: &'a [u8],
481 ) -> Option<quiche::ConnectionId<'a>> {
482 if token.len() < 6 {
483 return None;
484 }
485
486 if &token[..6] != b"quiche" {
487 return None;
488 }
489
490 let token = &token[6..];
491
492 let addr = match src.ip() {
493 std::net::IpAddr::V4(a) => a.octets().to_vec(),
494 std::net::IpAddr::V6(a) => a.octets().to_vec(),
495 };
496
497 if token.len() < addr.len() || &token[..addr.len()] != addr.as_slice() {
498 return None;
499 }
500
501 Some(quiche::ConnectionId::from_ref(&token[addr.len()..]))
502 }
503
504 /// Handles incoming HTTP/3 requests.
handle_request( client: &mut Client, stream_id: u64, headers: &[quiche::h3::Header], root: &str, )505 fn handle_request(
506 client: &mut Client, stream_id: u64, headers: &[quiche::h3::Header],
507 root: &str,
508 ) {
509 let conn = &mut client.conn;
510 let http3_conn = &mut client.http3_conn.as_mut().unwrap();
511
512 info!(
513 "{} got request {:?} on stream id {}",
514 conn.trace_id(),
515 hdrs_to_strings(headers),
516 stream_id
517 );
518
519 // We decide the response based on headers alone, so stop reading the
520 // request stream so that any body is ignored and pointless Data events
521 // are not generated.
522 conn.stream_shutdown(stream_id, quiche::Shutdown::Read, 0)
523 .unwrap();
524
525 let (headers, body) = build_response(root, headers);
526
527 match http3_conn.send_response(conn, stream_id, &headers, false) {
528 Ok(v) => v,
529
530 Err(quiche::h3::Error::StreamBlocked) => {
531 let response = PartialResponse {
532 headers: Some(headers),
533 body,
534 written: 0,
535 };
536
537 client.partial_responses.insert(stream_id, response);
538 return;
539 },
540
541 Err(e) => {
542 error!("{} stream send failed {:?}", conn.trace_id(), e);
543 return;
544 },
545 }
546
547 let written = match http3_conn.send_body(conn, stream_id, &body, true) {
548 Ok(v) => v,
549
550 Err(quiche::h3::Error::Done) => 0,
551
552 Err(e) => {
553 error!("{} stream send failed {:?}", conn.trace_id(), e);
554 return;
555 },
556 };
557
558 if written < body.len() {
559 let response = PartialResponse {
560 headers: None,
561 body,
562 written,
563 };
564
565 client.partial_responses.insert(stream_id, response);
566 }
567 }
568
569 /// Builds an HTTP/3 response given a request.
build_response( root: &str, request: &[quiche::h3::Header], ) -> (Vec<quiche::h3::Header>, Vec<u8>)570 fn build_response(
571 root: &str, request: &[quiche::h3::Header],
572 ) -> (Vec<quiche::h3::Header>, Vec<u8>) {
573 let mut file_path = std::path::PathBuf::from(root);
574 let mut path = std::path::Path::new("");
575 let mut method = None;
576
577 // Look for the request's path and method.
578 for hdr in request {
579 match hdr.name() {
580 b":path" =>
581 path = std::path::Path::new(
582 std::str::from_utf8(hdr.value()).unwrap(),
583 ),
584
585 b":method" => method = Some(hdr.value()),
586
587 _ => (),
588 }
589 }
590
591 let (status, body) = match method {
592 Some(b"GET") => {
593 for c in path.components() {
594 if let std::path::Component::Normal(v) = c {
595 file_path.push(v)
596 }
597 }
598
599 match std::fs::read(file_path.as_path()) {
600 Ok(data) => (200, data),
601
602 Err(_) => (404, b"Not Found!".to_vec()),
603 }
604 },
605
606 _ => (405, Vec::new()),
607 };
608
609 let headers = vec![
610 quiche::h3::Header::new(b":status", status.to_string().as_bytes()),
611 quiche::h3::Header::new(b"server", b"quiche"),
612 quiche::h3::Header::new(
613 b"content-length",
614 body.len().to_string().as_bytes(),
615 ),
616 ];
617
618 (headers, body)
619 }
620
621 /// Handles newly writable streams.
handle_writable(client: &mut Client, stream_id: u64)622 fn handle_writable(client: &mut Client, stream_id: u64) {
623 let conn = &mut client.conn;
624 let http3_conn = &mut client.http3_conn.as_mut().unwrap();
625
626 debug!("{} stream {} is writable", conn.trace_id(), stream_id);
627
628 if !client.partial_responses.contains_key(&stream_id) {
629 return;
630 }
631
632 let resp = client.partial_responses.get_mut(&stream_id).unwrap();
633
634 if let Some(ref headers) = resp.headers {
635 match http3_conn.send_response(conn, stream_id, headers, false) {
636 Ok(_) => (),
637
638 Err(quiche::h3::Error::StreamBlocked) => {
639 return;
640 },
641
642 Err(e) => {
643 error!("{} stream send failed {:?}", conn.trace_id(), e);
644 return;
645 },
646 }
647 }
648
649 resp.headers = None;
650
651 let body = &resp.body[resp.written..];
652
653 let written = match http3_conn.send_body(conn, stream_id, body, true) {
654 Ok(v) => v,
655
656 Err(quiche::h3::Error::Done) => 0,
657
658 Err(e) => {
659 client.partial_responses.remove(&stream_id);
660
661 error!("{} stream send failed {:?}", conn.trace_id(), e);
662 return;
663 },
664 };
665
666 resp.written += written;
667
668 if resp.written == resp.body.len() {
669 client.partial_responses.remove(&stream_id);
670 }
671 }
672
hdrs_to_strings(hdrs: &[quiche::h3::Header]) -> Vec<(String, String)>673 pub fn hdrs_to_strings(hdrs: &[quiche::h3::Header]) -> Vec<(String, String)> {
674 hdrs.iter()
675 .map(|h| {
676 let name = String::from_utf8_lossy(h.name()).to_string();
677 let value = String::from_utf8_lossy(h.value()).to_string();
678
679 (name, value)
680 })
681 .collect()
682 }
683