1#[macro_use]
28extern crate log;
29
30use std::io;
31
32use std::net;
33
34use std::io::prelude::*;
35
36use std::collections::HashMap;
37
38use std::convert::TryFrom;
39
40use std::rc::Rc;
41
42use std::cell::RefCell;
43
44use ring::rand::*;
45
46use quiche_apps::args::*;
47
48use quiche_apps::common::*;
49
50use quiche_apps::sendto::*;
51
52const MAX_BUF_SIZE: usize = 65507;
53
54const MAX_DATAGRAM_SIZE: usize = 1350;
55
56fn main() {
57 let mut buf = [0; MAX_BUF_SIZE];
58 let mut out = [0; MAX_BUF_SIZE];
59 let mut pacing = false;
60
61 env_logger::builder().format_timestamp_nanos().init();
62
63 let docopt = docopt::Docopt::new(SERVER_USAGE).unwrap();
65 let conn_args = CommonArgs::with_docopt(&docopt);
66 let args = ServerArgs::with_docopt(&docopt);
67
68 let mut poll = mio::Poll::new().unwrap();
70 let mut events = mio::Events::with_capacity(1024);
71
72 let mut socket =
74 mio::net::UdpSocket::bind(args.listen.parse().unwrap()).unwrap();
75
76 if !args.disable_pacing {
79 match set_txtime_sockopt(&socket) {
80 Ok(_) => {
81 pacing = true;
82 debug!("successfully set SO_TXTIME socket option");
83 },
84 Err(e) => debug!("setsockopt failed {e:?}"),
85 };
86 }
87
88 info!("listening on {:}", socket.local_addr().unwrap());
89
90 poll.registry()
91 .register(&mut socket, mio::Token(0), mio::Interest::READABLE)
92 .unwrap();
93
94 let max_datagram_size = MAX_DATAGRAM_SIZE;
95 let enable_gso = if args.disable_gso {
96 false
97 } else {
98 detect_gso(&socket, max_datagram_size)
99 };
100
101 trace!("GSO detected: {enable_gso}");
102
103 let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION).unwrap();
105
106 config.load_cert_chain_from_pem_file(&args.cert).unwrap();
107 config.load_priv_key_from_pem_file(&args.key).unwrap();
108
109 config.set_application_protos(&conn_args.alpns).unwrap();
110
111 config.discover_pmtu(args.enable_pmtud);
112 config.set_initial_rtt(conn_args.initial_rtt);
113 config.set_max_idle_timeout(conn_args.idle_timeout);
114 config.set_max_recv_udp_payload_size(max_datagram_size);
115 config.set_max_send_udp_payload_size(max_datagram_size);
116 config.set_initial_max_data(conn_args.max_data);
117 config.set_initial_max_stream_data_bidi_local(conn_args.max_stream_data);
118 config.set_initial_max_stream_data_bidi_remote(conn_args.max_stream_data);
119 config.set_initial_max_stream_data_uni(conn_args.max_stream_data);
120 config.set_initial_max_streams_bidi(conn_args.max_streams_bidi);
121 config.set_initial_max_streams_uni(conn_args.max_streams_uni);
122 config.set_disable_active_migration(!conn_args.enable_active_migration);
123 config.set_active_connection_id_limit(conn_args.max_active_cids);
124 config.set_initial_congestion_window_packets(
125 usize::try_from(conn_args.initial_cwnd_packets).unwrap(),
126 );
127
128 config.set_max_connection_window(conn_args.max_window);
129 config.set_max_stream_window(conn_args.max_stream_window);
130
131 config.enable_pacing(pacing);
132
133 let mut keylog = None;
134
135 if let Some(keylog_path) = std::env::var_os("SSLKEYLOGFILE") {
136 let file = std::fs::OpenOptions::new()
137 .create(true)
138 .append(true)
139 .open(keylog_path)
140 .unwrap();
141
142 keylog = Some(file);
143
144 config.log_keys();
145 }
146
147 if conn_args.early_data {
148 config.enable_early_data();
149 }
150
151 if conn_args.no_grease {
152 config.grease(false);
153 }
154
155 config
156 .set_cc_algorithm_name(&conn_args.cc_algorithm)
157 .unwrap();
158
159 if conn_args.disable_hystart {
160 config.enable_hystart(false);
161 }
162
163 if conn_args.dgrams_enabled {
164 config.enable_dgram(true, 1000, 1000);
165 }
166
167 let rng = SystemRandom::new();
168 let conn_id_seed =
169 ring::hmac::Key::generate(ring::hmac::HMAC_SHA256, &rng).unwrap();
170
171 let mut next_client_id = 0;
172 let mut clients_ids = ClientIdMap::new();
173 let mut clients = ClientMap::new();
174
175 let mut pkt_count = 0;
176
177 let mut continue_write = false;
178
179 let local_addr = socket.local_addr().unwrap();
180
181 loop {
182 let timeout = match continue_write {
186 true => Some(std::time::Duration::from_secs(0)),
187
188 false => clients.values().filter_map(|c| c.conn.timeout()).min(),
189 };
190
191 let mut poll_res = poll.poll(&mut events, timeout);
192 while let Err(e) = poll_res.as_ref() {
193 if e.kind() == std::io::ErrorKind::Interrupted {
194 trace!("mio poll() call failed, retrying: {e:?}");
195 poll_res = poll.poll(&mut events, timeout);
196 } else {
197 panic!("mio poll() call failed fatally: {e:?}");
198 }
199 }
200
201 'read: loop {
204 if events.is_empty() && !continue_write {
208 trace!("timed out");
209
210 clients.values_mut().for_each(|c| c.conn.on_timeout());
211
212 break 'read;
213 }
214
215 let (len, from) = match socket.recv_from(&mut buf) {
216 Ok(v) => v,
217
218 Err(e) => {
219 if e.kind() == std::io::ErrorKind::WouldBlock {
222 trace!("recv() would block");
223 break 'read;
224 }
225
226 panic!("recv() failed: {e:?}");
227 },
228 };
229
230 trace!("got {len} bytes from {from} to {local_addr}");
231
232 let pkt_buf = &mut buf[..len];
233
234 if let Some(target_path) = conn_args.dump_packet_path.as_ref() {
235 let path = format!("{target_path}/{pkt_count}.pkt");
236
237 if let Ok(f) = std::fs::File::create(path) {
238 let mut f = std::io::BufWriter::new(f);
239 f.write_all(pkt_buf).ok();
240 }
241 }
242
243 pkt_count += 1;
244
245 let hdr = match quiche::Header::from_slice(
247 pkt_buf,
248 quiche::MAX_CONN_ID_LEN,
249 ) {
250 Ok(v) => v,
251
252 Err(e) => {
253 error!("Parsing packet header failed: {e:?}");
254 continue 'read;
255 },
256 };
257
258 trace!("got packet {hdr:?}");
259
260 let conn_id = if !cfg!(feature = "fuzzing") {
261 let conn_id = ring::hmac::sign(&conn_id_seed, &hdr.dcid);
262 let conn_id = &conn_id.as_ref()[..quiche::MAX_CONN_ID_LEN];
263 conn_id.to_vec().into()
264 } else {
265 [0; quiche::MAX_CONN_ID_LEN].to_vec().into()
267 };
268
269 let client = if !clients_ids.contains_key(&hdr.dcid) &&
272 !clients_ids.contains_key(&conn_id)
273 {
274 if hdr.ty != quiche::Type::Initial {
275 error!("Packet is not Initial");
276 continue 'read;
277 }
278
279 if !quiche::version_is_supported(hdr.version) {
280 warn!("Doing version negotiation");
281
282 let len =
283 quiche::negotiate_version(&hdr.scid, &hdr.dcid, &mut out)
284 .unwrap();
285
286 let out = &out[..len];
287
288 if let Err(e) = socket.send_to(out, from) {
289 if e.kind() == std::io::ErrorKind::WouldBlock {
290 trace!("send() would block");
291 break;
292 }
293
294 panic!("send() failed: {e:?}");
295 }
296 continue 'read;
297 }
298
299 let mut scid = [0; quiche::MAX_CONN_ID_LEN];
300 scid.copy_from_slice(&conn_id);
301
302 let mut odcid = None;
303
304 if !args.no_retry {
305 let token = hdr.token.as_ref().unwrap();
307
308 if token.is_empty() {
310 warn!("Doing stateless retry");
311
312 let scid = quiche::ConnectionId::from_ref(&scid);
313 let new_token = mint_token(&hdr, &from);
314
315 let len = quiche::retry(
316 &hdr.scid,
317 &hdr.dcid,
318 &scid,
319 &new_token,
320 hdr.version,
321 &mut out,
322 )
323 .unwrap();
324
325 let out = &out[..len];
326
327 if let Err(e) = socket.send_to(out, from) {
328 if e.kind() == std::io::ErrorKind::WouldBlock {
329 trace!("send() would block");
330 break;
331 }
332
333 panic!("send() failed: {e:?}");
334 }
335 continue 'read;
336 }
337
338 odcid = validate_token(&from, token);
339
340 if odcid.is_none() {
343 error!("Invalid address validation token");
344 continue;
345 }
346
347 if scid.len() != hdr.dcid.len() {
348 error!("Invalid destination connection ID");
349 continue 'read;
350 }
351
352 scid.copy_from_slice(&hdr.dcid);
355 }
356
357 let scid = quiche::ConnectionId::from_vec(scid.to_vec());
358
359 debug!("New connection: dcid={:?} scid={:?}", hdr.dcid, scid);
360
361 #[allow(unused_mut)]
362 let mut conn = quiche::accept(
363 &scid,
364 odcid.as_ref(),
365 local_addr,
366 from,
367 &mut config,
368 )
369 .unwrap();
370
371 if let Some(keylog) = &mut keylog {
372 if let Ok(keylog) = keylog.try_clone() {
373 conn.set_keylog(Box::new(keylog));
374 }
375 }
376
377 #[cfg(feature = "qlog")]
379 {
380 if let Some(dir) = std::env::var_os("QLOGDIR") {
381 let id = format!("{:?}", &scid);
382 let writer = make_qlog_writer(&dir, "server", &id);
383
384 conn.set_qlog(
385 std::boxed::Box::new(writer),
386 "quiche-server qlog".to_string(),
387 format!("{} id={}", "quiche-server qlog", id),
388 );
389 }
390 }
391
392 let client_id = next_client_id;
393
394 let client = Client {
395 conn,
396 http_conn: None,
397 client_id,
398 partial_requests: HashMap::new(),
399 partial_responses: HashMap::new(),
400 app_proto_selected: false,
401 max_datagram_size,
402 loss_rate: 0.0,
403 max_send_burst: MAX_BUF_SIZE,
404 };
405
406 clients.insert(client_id, client);
407 clients_ids.insert(scid.clone(), client_id);
408
409 next_client_id += 1;
410
411 clients.get_mut(&client_id).unwrap()
412 } else {
413 let cid = match clients_ids.get(&hdr.dcid) {
414 Some(v) => v,
415
416 None => clients_ids.get(&conn_id).unwrap(),
417 };
418
419 clients.get_mut(cid).unwrap()
420 };
421
422 let recv_info = quiche::RecvInfo {
423 to: local_addr,
424 from,
425 };
426
427 let read = match client.conn.recv(pkt_buf, recv_info) {
429 Ok(v) => v,
430
431 Err(e) => {
432 error!("{} recv failed: {:?}", client.conn.trace_id(), e);
433 continue 'read;
434 },
435 };
436
437 trace!("{} processed {} bytes", client.conn.trace_id(), read);
438
439 if !client.app_proto_selected &&
442 (client.conn.is_in_early_data() ||
443 client.conn.is_established())
444 {
445 let app_proto = client.conn.application_proto();
453
454 #[allow(clippy::box_default)]
455 if alpns::HTTP_09.contains(&app_proto) {
456 client.http_conn = Some(Box::<Http09Conn>::default());
457
458 client.app_proto_selected = true;
459 } else if alpns::HTTP_3.contains(&app_proto) {
460 let dgram_sender = if conn_args.dgrams_enabled {
461 Some(Http3DgramSender::new(
462 conn_args.dgram_count,
463 conn_args.dgram_data.clone(),
464 1,
465 ))
466 } else {
467 None
468 };
469
470 client.http_conn = match Http3Conn::with_conn(
471 &mut client.conn,
472 conn_args.max_field_section_size,
473 conn_args.qpack_max_table_capacity,
474 conn_args.qpack_blocked_streams,
475 dgram_sender,
476 Rc::new(RefCell::new(stdout_sink)),
477 ) {
478 Ok(v) => Some(v),
479
480 Err(e) => {
481 trace!("{} {}", client.conn.trace_id(), e);
482 None
483 },
484 };
485
486 client.app_proto_selected = true;
487 }
488
489 client.max_datagram_size =
491 client.conn.max_send_udp_payload_size();
492 }
493
494 if client.http_conn.is_some() {
495 let conn = &mut client.conn;
496 let http_conn = client.http_conn.as_mut().unwrap();
497 let partial_responses = &mut client.partial_responses;
498
499 for stream_id in writable_response_streams(conn) {
502 http_conn.handle_writable(conn, partial_responses, stream_id);
503 }
504
505 if http_conn
506 .handle_requests(
507 conn,
508 &mut client.partial_requests,
509 partial_responses,
510 &args.root,
511 &args.index,
512 &mut buf,
513 )
514 .is_err()
515 {
516 continue 'read;
517 }
518 }
519
520 handle_path_events(client);
521
522 while let Some(retired_scid) = client.conn.retired_scid_next() {
524 info!("Retiring source CID {retired_scid:?}");
525 clients_ids.remove(&retired_scid);
526 }
527
528 while client.conn.scids_left() > 0 {
530 let (scid, reset_token) = generate_cid_and_reset_token(&rng);
531 if client.conn.new_scid(&scid, reset_token, false).is_err() {
532 break;
533 }
534
535 clients_ids.insert(scid, client.client_id);
536 }
537 }
538
539 continue_write = false;
543 for client in clients.values_mut() {
544 let loss_rate =
546 client.conn.stats().lost as f64 / client.conn.stats().sent as f64;
547 if loss_rate > client.loss_rate + 0.001 {
548 client.max_send_burst = client.max_send_burst / 4 * 3;
549 client.max_send_burst =
551 client.max_send_burst.max(client.max_datagram_size * 10);
552 client.loss_rate = loss_rate;
553 }
554
555 let max_send_burst =
556 client.conn.send_quantum().min(client.max_send_burst) /
557 client.max_datagram_size *
558 client.max_datagram_size;
559 let mut total_write = 0;
560 let mut dst_info = None;
561
562 while total_write < max_send_burst {
563 let (write, send_info) = match client
564 .conn
565 .send(&mut out[total_write..max_send_burst])
566 {
567 Ok(v) => v,
568
569 Err(quiche::Error::Done) => {
570 trace!("{} done writing", client.conn.trace_id());
571 break;
572 },
573
574 Err(e) => {
575 error!("{} send failed: {:?}", client.conn.trace_id(), e);
576
577 client.conn.close(false, 0x1, b"fail").ok();
578 break;
579 },
580 };
581
582 total_write += write;
583
584 let _ = dst_info.get_or_insert(send_info);
586
587 if write < client.max_datagram_size {
588 continue_write = true;
589 break;
590 }
591 }
592
593 if total_write == 0 || dst_info.is_none() {
594 continue;
595 }
596
597 if let Err(e) = send_to(
598 &socket,
599 &out[..total_write],
600 &dst_info.unwrap(),
601 client.max_datagram_size,
602 pacing,
603 enable_gso,
604 ) {
605 if e.kind() == std::io::ErrorKind::WouldBlock {
606 trace!("send() would block");
607 break;
608 }
609
610 panic!("send_to() failed: {e:?}");
611 }
612
613 trace!(
614 "{} written {total_write} bytes with {dst_info:?}",
615 client.conn.trace_id()
616 );
617
618 if total_write >= max_send_burst {
619 trace!("{} pause writing", client.conn.trace_id(),);
620 continue_write = true;
621 break;
622 }
623 }
624
625 clients.retain(|_, ref mut c| {
627 trace!("Collecting garbage");
628
629 if c.conn.is_closed() {
630 info!(
631 "{} connection collected {:?} {:?}",
632 c.conn.trace_id(),
633 c.conn.stats(),
634 c.conn.path_stats().collect::<Vec<quiche::PathStats>>()
635 );
636
637 for id in c.conn.source_ids() {
638 let id_owned = id.clone().into_owned();
639 clients_ids.remove(&id_owned);
640 }
641 }
642
643 !c.conn.is_closed()
644 });
645 }
646}
647
648fn mint_token(hdr: &quiche::Header, src: &net::SocketAddr) -> Vec<u8> {
657 let mut token = Vec::new();
658
659 token.extend_from_slice(b"quiche");
660
661 let addr = match src.ip() {
662 std::net::IpAddr::V4(a) => a.octets().to_vec(),
663 std::net::IpAddr::V6(a) => a.octets().to_vec(),
664 };
665
666 token.extend_from_slice(&addr);
667 token.extend_from_slice(&hdr.dcid);
668
669 token
670}
671
672fn validate_token<'a>(
680 src: &net::SocketAddr, token: &'a [u8],
681) -> Option<quiche::ConnectionId<'a>> {
682 if token.len() < 6 {
683 return None;
684 }
685
686 if &token[..6] != b"quiche" {
687 return None;
688 }
689
690 let token = &token[6..];
691
692 let addr = match src.ip() {
693 std::net::IpAddr::V4(a) => a.octets().to_vec(),
694 std::net::IpAddr::V6(a) => a.octets().to_vec(),
695 };
696
697 if token.len() < addr.len() || &token[..addr.len()] != addr.as_slice() {
698 return None;
699 }
700
701 Some(quiche::ConnectionId::from_ref(&token[addr.len()..]))
702}
703
704fn handle_path_events(client: &mut Client) {
705 while let Some(qe) = client.conn.path_event_next() {
706 match qe {
707 quiche::PathEvent::New(local_addr, peer_addr) => {
708 info!(
709 "{} Seen new path ({}, {})",
710 client.conn.trace_id(),
711 local_addr,
712 peer_addr
713 );
714
715 client
717 .conn
718 .probe_path(local_addr, peer_addr)
719 .expect("cannot probe");
720 },
721
722 quiche::PathEvent::Validated(local_addr, peer_addr) => {
723 info!(
724 "{} Path ({}, {}) is now validated",
725 client.conn.trace_id(),
726 local_addr,
727 peer_addr
728 );
729 },
730
731 quiche::PathEvent::FailedValidation(local_addr, peer_addr) => {
732 info!(
733 "{} Path ({}, {}) failed validation",
734 client.conn.trace_id(),
735 local_addr,
736 peer_addr
737 );
738 },
739
740 quiche::PathEvent::Closed(local_addr, peer_addr) => {
741 info!(
742 "{} Path ({}, {}) is now closed and unusable",
743 client.conn.trace_id(),
744 local_addr,
745 peer_addr
746 );
747 },
748
749 quiche::PathEvent::ReusedSourceConnectionId(cid_seq, old, new) => {
750 info!(
751 "{} Peer reused cid seq {} (initially {:?}) on {:?}",
752 client.conn.trace_id(),
753 cid_seq,
754 old,
755 new
756 );
757 },
758
759 quiche::PathEvent::PeerMigrated(local_addr, peer_addr) => {
760 info!(
761 "{} Connection migrated to ({}, {})",
762 client.conn.trace_id(),
763 local_addr,
764 peer_addr
765 );
766 },
767 }
768 }
769}
770
771#[cfg(target_os = "linux")]
778fn set_txtime_sockopt(sock: &mio::net::UdpSocket) -> io::Result<()> {
779 use nix::sys::socket::setsockopt;
780 use nix::sys::socket::sockopt::TxTime;
781 use std::os::unix::io::AsRawFd;
782
783 let config = nix::libc::sock_txtime {
784 clockid: libc::CLOCK_MONOTONIC,
785 flags: 0,
786 };
787
788 setsockopt(sock.as_raw_fd(), TxTime, &config)?;
789
790 Ok(())
791}
792
793#[cfg(not(target_os = "linux"))]
794fn set_txtime_sockopt(_: &mio::net::UdpSocket) -> io::Result<()> {
795 use std::io::Error;
796
797 Err(Error::other("Not supported on this platform"))
798}