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