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