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