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_max_idle_timeout(conn_args.idle_timeout);
113 config.set_max_recv_udp_payload_size(max_datagram_size);
114 config.set_max_send_udp_payload_size(max_datagram_size);
115 config.set_initial_max_data(conn_args.max_data);
116 config.set_initial_max_stream_data_bidi_local(conn_args.max_stream_data);
117 config.set_initial_max_stream_data_bidi_remote(conn_args.max_stream_data);
118 config.set_initial_max_stream_data_uni(conn_args.max_stream_data);
119 config.set_initial_max_streams_bidi(conn_args.max_streams_bidi);
120 config.set_initial_max_streams_uni(conn_args.max_streams_uni);
121 config.set_disable_active_migration(!conn_args.enable_active_migration);
122 config.set_active_connection_id_limit(conn_args.max_active_cids);
123 config.set_initial_congestion_window_packets(
124 usize::try_from(conn_args.initial_cwnd_packets).unwrap(),
125 );
126
127 config.set_max_connection_window(conn_args.max_window);
128 config.set_max_stream_window(conn_args.max_stream_window);
129
130 config.enable_pacing(pacing);
131
132 let mut keylog = None;
133
134 if let Some(keylog_path) = std::env::var_os("SSLKEYLOGFILE") {
135 let file = std::fs::OpenOptions::new()
136 .create(true)
137 .append(true)
138 .open(keylog_path)
139 .unwrap();
140
141 keylog = Some(file);
142
143 config.log_keys();
144 }
145
146 if conn_args.early_data {
147 config.enable_early_data();
148 }
149
150 if conn_args.no_grease {
151 config.grease(false);
152 }
153
154 config
155 .set_cc_algorithm_name(&conn_args.cc_algorithm)
156 .unwrap();
157
158 if conn_args.disable_hystart {
159 config.enable_hystart(false);
160 }
161
162 if conn_args.dgrams_enabled {
163 config.enable_dgram(true, 1000, 1000);
164 }
165
166 let rng = SystemRandom::new();
167 let conn_id_seed =
168 ring::hmac::Key::generate(ring::hmac::HMAC_SHA256, &rng).unwrap();
169
170 let mut next_client_id = 0;
171 let mut clients_ids = ClientIdMap::new();
172 let mut clients = ClientMap::new();
173
174 let mut pkt_count = 0;
175
176 let mut continue_write = false;
177
178 let local_addr = socket.local_addr().unwrap();
179
180 loop {
181 let timeout = match continue_write {
185 true => Some(std::time::Duration::from_secs(0)),
186
187 false => clients.values().filter_map(|c| c.conn.timeout()).min(),
188 };
189
190 let mut poll_res = poll.poll(&mut events, timeout);
191 while let Err(e) = poll_res.as_ref() {
192 if e.kind() == std::io::ErrorKind::Interrupted {
193 trace!("mio poll() call failed, retrying: {:?}", e);
194 poll_res = poll.poll(&mut events, timeout);
195 } else {
196 panic!("mio poll() call failed fatally: {:?}", e);
197 }
198 }
199
200 'read: loop {
203 if events.is_empty() && !continue_write {
207 trace!("timed out");
208
209 clients.values_mut().for_each(|c| c.conn.on_timeout());
210
211 break 'read;
212 }
213
214 let (len, from) = match socket.recv_from(&mut buf) {
215 Ok(v) => v,
216
217 Err(e) => {
218 if e.kind() == std::io::ErrorKind::WouldBlock {
221 trace!("recv() would block");
222 break 'read;
223 }
224
225 panic!("recv() failed: {:?}", e);
226 },
227 };
228
229 trace!("got {len} bytes from {from} to {local_addr}");
230
231 let pkt_buf = &mut buf[..len];
232
233 if let Some(target_path) = conn_args.dump_packet_path.as_ref() {
234 let path = format!("{target_path}/{pkt_count}.pkt");
235
236 if let Ok(f) = std::fs::File::create(path) {
237 let mut f = std::io::BufWriter::new(f);
238 f.write_all(pkt_buf).ok();
239 }
240 }
241
242 pkt_count += 1;
243
244 let hdr = match quiche::Header::from_slice(
246 pkt_buf,
247 quiche::MAX_CONN_ID_LEN,
248 ) {
249 Ok(v) => v,
250
251 Err(e) => {
252 error!("Parsing packet header failed: {:?}", e);
253 continue 'read;
254 },
255 };
256
257 trace!("got packet {:?}", hdr);
258
259 let conn_id = if !cfg!(feature = "fuzzing") {
260 let conn_id = ring::hmac::sign(&conn_id_seed, &hdr.dcid);
261 let conn_id = &conn_id.as_ref()[..quiche::MAX_CONN_ID_LEN];
262 conn_id.to_vec().into()
263 } else {
264 [0; quiche::MAX_CONN_ID_LEN].to_vec().into()
266 };
267
268 let client = if !clients_ids.contains_key(&hdr.dcid) &&
271 !clients_ids.contains_key(&conn_id)
272 {
273 if hdr.ty != quiche::Type::Initial {
274 error!("Packet is not Initial");
275 continue 'read;
276 }
277
278 if !quiche::version_is_supported(hdr.version) {
279 warn!("Doing version negotiation");
280
281 let len =
282 quiche::negotiate_version(&hdr.scid, &hdr.dcid, &mut out)
283 .unwrap();
284
285 let out = &out[..len];
286
287 if let Err(e) = socket.send_to(out, from) {
288 if e.kind() == std::io::ErrorKind::WouldBlock {
289 trace!("send() would block");
290 break;
291 }
292
293 panic!("send() failed: {:?}", e);
294 }
295 continue 'read;
296 }
297
298 let mut scid = [0; quiche::MAX_CONN_ID_LEN];
299 scid.copy_from_slice(&conn_id);
300
301 let mut odcid = None;
302
303 if !args.no_retry {
304 let token = hdr.token.as_ref().unwrap();
306
307 if token.is_empty() {
309 warn!("Doing stateless retry");
310
311 let scid = quiche::ConnectionId::from_ref(&scid);
312 let new_token = mint_token(&hdr, &from);
313
314 let len = quiche::retry(
315 &hdr.scid,
316 &hdr.dcid,
317 &scid,
318 &new_token,
319 hdr.version,
320 &mut out,
321 )
322 .unwrap();
323
324 let out = &out[..len];
325
326 if let Err(e) = socket.send_to(out, from) {
327 if e.kind() == std::io::ErrorKind::WouldBlock {
328 trace!("send() would block");
329 break;
330 }
331
332 panic!("send() failed: {:?}", e);
333 }
334 continue 'read;
335 }
336
337 odcid = validate_token(&from, token);
338
339 if odcid.is_none() {
342 error!("Invalid address validation token");
343 continue;
344 }
345
346 if scid.len() != hdr.dcid.len() {
347 error!("Invalid destination connection ID");
348 continue 'read;
349 }
350
351 scid.copy_from_slice(&hdr.dcid);
354 }
355
356 let scid = quiche::ConnectionId::from_vec(scid.to_vec());
357
358 debug!("New connection: dcid={:?} scid={:?}", hdr.dcid, scid);
359
360 #[allow(unused_mut)]
361 let mut conn = quiche::accept(
362 &scid,
363 odcid.as_ref(),
364 local_addr,
365 from,
366 &mut config,
367 )
368 .unwrap();
369
370 if let Some(keylog) = &mut keylog {
371 if let Ok(keylog) = keylog.try_clone() {
372 conn.set_keylog(Box::new(keylog));
373 }
374 }
375
376 #[cfg(feature = "qlog")]
378 {
379 if let Some(dir) = std::env::var_os("QLOGDIR") {
380 let id = format!("{:?}", &scid);
381 let writer = make_qlog_writer(&dir, "server", &id);
382
383 conn.set_qlog(
384 std::boxed::Box::new(writer),
385 "quiche-server qlog".to_string(),
386 format!("{} id={}", "quiche-server qlog", id),
387 );
388 }
389 }
390
391 let client_id = next_client_id;
392
393 let client = Client {
394 conn,
395 http_conn: None,
396 client_id,
397 partial_requests: HashMap::new(),
398 partial_responses: HashMap::new(),
399 app_proto_selected: false,
400 max_datagram_size,
401 loss_rate: 0.0,
402 max_send_burst: MAX_BUF_SIZE,
403 };
404
405 clients.insert(client_id, client);
406 clients_ids.insert(scid.clone(), client_id);
407
408 next_client_id += 1;
409
410 clients.get_mut(&client_id).unwrap()
411 } else {
412 let cid = match clients_ids.get(&hdr.dcid) {
413 Some(v) => v,
414
415 None => clients_ids.get(&conn_id).unwrap(),
416 };
417
418 clients.get_mut(cid).unwrap()
419 };
420
421 let recv_info = quiche::RecvInfo {
422 to: local_addr,
423 from,
424 };
425
426 let read = match client.conn.recv(pkt_buf, recv_info) {
428 Ok(v) => v,
429
430 Err(e) => {
431 error!("{} recv failed: {:?}", client.conn.trace_id(), e);
432 continue 'read;
433 },
434 };
435
436 trace!("{} processed {} bytes", client.conn.trace_id(), read);
437
438 if !client.app_proto_selected &&
441 (client.conn.is_in_early_data() ||
442 client.conn.is_established())
443 {
444 let app_proto = client.conn.application_proto();
452
453 #[allow(clippy::box_default)]
454 if alpns::HTTP_09.contains(&app_proto) {
455 client.http_conn = Some(Box::<Http09Conn>::default());
456
457 client.app_proto_selected = true;
458 } else if alpns::HTTP_3.contains(&app_proto) {
459 let dgram_sender = if conn_args.dgrams_enabled {
460 Some(Http3DgramSender::new(
461 conn_args.dgram_count,
462 conn_args.dgram_data.clone(),
463 1,
464 ))
465 } else {
466 None
467 };
468
469 client.http_conn = match Http3Conn::with_conn(
470 &mut client.conn,
471 conn_args.max_field_section_size,
472 conn_args.qpack_max_table_capacity,
473 conn_args.qpack_blocked_streams,
474 dgram_sender,
475 Rc::new(RefCell::new(stdout_sink)),
476 ) {
477 Ok(v) => Some(v),
478
479 Err(e) => {
480 trace!("{} {}", client.conn.trace_id(), e);
481 None
482 },
483 };
484
485 client.app_proto_selected = true;
486 }
487
488 client.max_datagram_size =
490 client.conn.max_send_udp_payload_size();
491 }
492
493 if client.http_conn.is_some() {
494 let conn = &mut client.conn;
495 let http_conn = client.http_conn.as_mut().unwrap();
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::AsRawFd;
781
782 let config = nix::libc::sock_txtime {
783 clockid: libc::CLOCK_MONOTONIC,
784 flags: 0,
785 };
786
787 setsockopt(sock.as_raw_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}