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