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 let Some(http_conn) = client.http_conn.as_mut() {
495 let conn = &mut client.conn;
496 let partial_responses = &mut client.partial_responses;
497
498 for stream_id in writable_response_streams(conn) {
501 http_conn.handle_writable(conn, partial_responses, stream_id);
502 }
503
504 if http_conn
505 .handle_requests(
506 conn,
507 &mut client.partial_requests,
508 partial_responses,
509 &args.root,
510 &args.index,
511 &mut buf,
512 )
513 .is_err()
514 {
515 continue 'read;
516 }
517 }
518
519 handle_path_events(client);
520
521 while let Some(retired_scid) = client.conn.retired_scid_next() {
523 info!("Retiring source CID {retired_scid:?}");
524 clients_ids.remove(&retired_scid);
525 }
526
527 while client.conn.scids_left() > 0 {
529 let (scid, reset_token) = generate_cid_and_reset_token(&rng);
530 if client.conn.new_scid(&scid, reset_token, false).is_err() {
531 break;
532 }
533
534 clients_ids.insert(scid, client.client_id);
535 }
536 }
537
538 continue_write = false;
542 for client in clients.values_mut() {
543 let loss_rate =
545 client.conn.stats().lost as f64 / client.conn.stats().sent as f64;
546 if loss_rate > client.loss_rate + 0.001 {
547 client.max_send_burst = client.max_send_burst / 4 * 3;
548 client.max_send_burst =
550 client.max_send_burst.max(client.max_datagram_size * 10);
551 client.loss_rate = loss_rate;
552 }
553
554 let max_send_burst =
555 client.conn.send_quantum().min(client.max_send_burst) /
556 client.max_datagram_size *
557 client.max_datagram_size;
558 let mut total_write = 0;
559 let mut dst_info = None;
560
561 while total_write < max_send_burst {
562 let (write, send_info) = match client
563 .conn
564 .send(&mut out[total_write..max_send_burst])
565 {
566 Ok(v) => v,
567
568 Err(quiche::Error::Done) => {
569 trace!("{} done writing", client.conn.trace_id());
570 break;
571 },
572
573 Err(e) => {
574 error!("{} send failed: {:?}", client.conn.trace_id(), e);
575
576 client.conn.close(false, 0x1, b"fail").ok();
577 break;
578 },
579 };
580
581 total_write += write;
582
583 let _ = dst_info.get_or_insert(send_info);
585
586 if write < client.max_datagram_size {
587 continue_write = true;
588 break;
589 }
590 }
591
592 if total_write == 0 || dst_info.is_none() {
593 continue;
594 }
595
596 if let Err(e) = send_to(
597 &socket,
598 &out[..total_write],
599 &dst_info.unwrap(),
600 client.max_datagram_size,
601 pacing,
602 enable_gso,
603 ) {
604 if e.kind() == std::io::ErrorKind::WouldBlock {
605 trace!("send() would block");
606 break;
607 }
608
609 panic!("send_to() failed: {e:?}");
610 }
611
612 trace!(
613 "{} written {total_write} bytes with {dst_info:?}",
614 client.conn.trace_id()
615 );
616
617 if total_write >= max_send_burst {
618 trace!("{} pause writing", client.conn.trace_id(),);
619 continue_write = true;
620 break;
621 }
622 }
623
624 clients.retain(|_, ref mut c| {
626 trace!("Collecting garbage");
627
628 if c.conn.is_closed() {
629 info!(
630 "{} connection collected {:?} {:?}",
631 c.conn.trace_id(),
632 c.conn.stats(),
633 c.conn.path_stats().collect::<Vec<quiche::PathStats>>()
634 );
635
636 for id in c.conn.source_ids() {
637 let id_owned = id.clone().into_owned();
638 clients_ids.remove(&id_owned);
639 }
640 }
641
642 !c.conn.is_closed()
643 });
644 }
645}
646
647fn mint_token(hdr: &quiche::Header, src: &net::SocketAddr) -> Vec<u8> {
656 let mut token = Vec::new();
657
658 token.extend_from_slice(b"quiche");
659
660 let addr = match src.ip() {
661 std::net::IpAddr::V4(a) => a.octets().to_vec(),
662 std::net::IpAddr::V6(a) => a.octets().to_vec(),
663 };
664
665 token.extend_from_slice(&addr);
666 token.extend_from_slice(&hdr.dcid);
667
668 token
669}
670
671fn validate_token<'a>(
679 src: &net::SocketAddr, token: &'a [u8],
680) -> Option<quiche::ConnectionId<'a>> {
681 if token.len() < 6 {
682 return None;
683 }
684
685 if &token[..6] != b"quiche" {
686 return None;
687 }
688
689 let token = &token[6..];
690
691 let addr = match src.ip() {
692 std::net::IpAddr::V4(a) => a.octets().to_vec(),
693 std::net::IpAddr::V6(a) => a.octets().to_vec(),
694 };
695
696 if token.len() < addr.len() || &token[..addr.len()] != addr.as_slice() {
697 return None;
698 }
699
700 Some(quiche::ConnectionId::from_ref(&token[addr.len()..]))
701}
702
703fn handle_path_events(client: &mut Client) {
704 while let Some(qe) = client.conn.path_event_next() {
705 match qe {
706 quiche::PathEvent::New(local_addr, peer_addr) => {
707 info!(
708 "{} Seen new path ({}, {})",
709 client.conn.trace_id(),
710 local_addr,
711 peer_addr
712 );
713
714 client
716 .conn
717 .probe_path(local_addr, peer_addr)
718 .expect("cannot probe");
719 },
720
721 quiche::PathEvent::Validated(local_addr, peer_addr) => {
722 info!(
723 "{} Path ({}, {}) is now validated",
724 client.conn.trace_id(),
725 local_addr,
726 peer_addr
727 );
728 },
729
730 quiche::PathEvent::FailedValidation(local_addr, peer_addr) => {
731 info!(
732 "{} Path ({}, {}) failed validation",
733 client.conn.trace_id(),
734 local_addr,
735 peer_addr
736 );
737 },
738
739 quiche::PathEvent::Closed(local_addr, peer_addr) => {
740 info!(
741 "{} Path ({}, {}) is now closed and unusable",
742 client.conn.trace_id(),
743 local_addr,
744 peer_addr
745 );
746 },
747
748 quiche::PathEvent::ReusedSourceConnectionId(cid_seq, old, new) => {
749 info!(
750 "{} Peer reused cid seq {} (initially {:?}) on {:?}",
751 client.conn.trace_id(),
752 cid_seq,
753 old,
754 new
755 );
756 },
757
758 quiche::PathEvent::PeerMigrated(local_addr, peer_addr) => {
759 info!(
760 "{} Connection migrated to ({}, {})",
761 client.conn.trace_id(),
762 local_addr,
763 peer_addr
764 );
765 },
766 }
767 }
768}
769
770#[cfg(target_os = "linux")]
777fn set_txtime_sockopt(sock: &mio::net::UdpSocket) -> io::Result<()> {
778 use nix::sys::socket::setsockopt;
779 use nix::sys::socket::sockopt::TxTime;
780 use std::os::unix::io::AsFd;
781
782 let config = nix::libc::sock_txtime {
783 clockid: libc::CLOCK_MONOTONIC,
784 flags: 0,
785 };
786
787 setsockopt(&sock.as_fd(), TxTime, &config)?;
788
789 Ok(())
790}
791
792#[cfg(not(target_os = "linux"))]
793fn set_txtime_sockopt(_: &mio::net::UdpSocket) -> io::Result<()> {
794 use std::io::Error;
795
796 Err(Error::other("Not supported on this platform"))
797}