• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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