1use std::collections::{BTreeMap, VecDeque};
5use std::net::SocketAddr;
6use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
7use std::sync::{Arc, Mutex, MutexGuard, OnceLock};
8use std::time::{Duration, Instant};
9
10use bytes::{Bytes, BytesMut};
11use prost::Message;
12use rustls::pki_types::ServerName;
13use tokio::io::{AsyncReadExt, AsyncWrite, AsyncWriteExt, ReadHalf};
14use tokio::net::TcpStream;
15use tokio::sync::{Notify, mpsc, oneshot, watch};
16use tokio_rustls::TlsConnector;
17use tokio_rustls::client::TlsStream;
18
19use crate::error::{CommandError, ConnectError, PermissionDenied, ProtocolError, RejectReason};
20use crate::framing::{self, Frame, MessageType};
21use crate::proto;
22use crate::ratelimit::{self, LOW_PRIORITY_MAX_WAIT, LeakyBucket, Limit};
23use crate::state::{Channel, CryptSetup, ServerInfo, ServerState, User, UserChange};
24use crate::tap::{PacketTap, TapDirection, TapPacing, TappedFrame};
25use crate::tls::{self, CertVerifier, ClientIdentity, ServerCert};
26use crate::version::MumbleVersion;
27use crate::voice::CryptStats;
28
29const PING_INTERVAL: Duration = Duration::from_secs(5);
31const READ_TIMEOUT: Duration = Duration::from_secs(30);
33const DRAIN_TIMEOUT: Duration = Duration::from_secs(2);
37
38pub struct ConnectConfig {
40 pub host: String,
41 pub port: u16,
42 pub username: String,
43 pub password: Option<String>,
45 pub tokens: Vec<String>,
47 pub identity: Option<ClientIdentity>,
49 pub verify_cert: CertVerifier,
51 pub login_timeout: Duration,
53}
54
55#[derive(Debug, Clone, Default, PartialEq, Eq)]
57pub struct TextTarget {
58 pub sessions: Vec<u32>,
60 pub channels: Vec<u32>,
61 pub trees: Vec<u32>,
63}
64
65impl TextTarget {
66 pub fn channel(id: u32) -> Self {
67 Self {
68 channels: vec![id],
69 ..Default::default()
70 }
71 }
72
73 pub fn user(session: u32) -> Self {
74 Self {
75 sessions: vec![session],
76 ..Default::default()
77 }
78 }
79}
80
81#[derive(Debug, Clone, PartialEq)]
83pub struct TextMessage {
84 pub actor: Option<u32>,
86 pub sender: Option<User>,
90 pub target: TextTarget,
91 pub message: String,
92}
93
94pub const PLUGIN_DATA_MAX: usize = 1000;
96
97#[derive(Debug, Clone, PartialEq, Eq)]
99pub struct PluginData {
100 pub sender: Option<u32>,
102 pub data_id: String,
104 pub data: Bytes,
105}
106
107#[derive(Debug, Clone, Copy, PartialEq, Eq)]
109pub enum LowOutcome {
110 Sent,
112 Expired,
114 Disconnected,
116}
117
118#[derive(Debug)]
120pub struct PluginTicket(oneshot::Receiver<LowOutcome>);
121
122impl PluginTicket {
123 pub async fn outcome(self) -> LowOutcome {
125 self.0.await.unwrap_or(LowOutcome::Disconnected)
127 }
128}
129
130#[derive(Debug, Clone, PartialEq, Eq)]
132pub enum DisconnectReason {
133 Requested,
135 Kicked {
137 actor: Option<u32>,
138 reason: Option<String>,
139 ban: bool,
140 },
141 Closed,
143 Timeout,
145 Error(String),
147}
148
149#[derive(Debug, Clone, PartialEq)]
151pub enum Event {
152 ChannelUpdated(Channel),
153 ChannelRemoved(Channel),
154 UserUpdated(UserChange),
155 UserRemoved {
156 user: User,
157 actor: Option<u32>,
158 reason: Option<String>,
159 ban: bool,
160 },
161 TextMessage(TextMessage),
162 PermissionDenied(PermissionDenied),
164 PermissionsUpdated {
166 channel_id: Option<u32>,
167 flush: bool,
170 },
171 Acl(Box<proto::Acl>),
175 UsersQueried(proto::QueryUsers),
178 BanList(Vec<crate::ban::Ban>),
183 UserList(Vec<crate::registered::RegisteredUser>),
188 ServerConfigUpdated(ServerInfo),
189 CryptSetup(CryptSetup),
192 UdpTunnel(Bytes),
194 PluginData(PluginData),
196 UserStats(Box<proto::UserStats>),
199 Disconnected(DisconnectReason),
201}
202
203pub struct Connection {
205 pub handle: Handle,
206 pub events: mpsc::UnboundedReceiver<Event>,
208 pub server_cert: Option<ServerCert>,
210}
211
212enum Outgoing {
213 Frame(Frame),
214 LowPluginData(Frame, oneshot::Sender<LowOutcome>),
217 Flush(oneshot::Sender<()>),
220 Disconnect,
222 Abort,
224}
225
226struct Shared {
227 state: Mutex<ServerState>,
228 shutdown: Notify,
229 closing: AtomicBool,
231 pong: watch::Sender<u64>,
234 reason: Mutex<Option<DisconnectReason>>,
235 connected: AtomicBool,
236 ping: Mutex<PingReport>,
237 tap: Option<Arc<dyn PacketTap>>,
238 low_expired: AtomicU64,
240 tokens: Mutex<Vec<String>>,
242}
243
244impl Shared {
245 fn state(&self) -> MutexGuard<'_, ServerState> {
246 self.state
247 .lock()
248 .unwrap_or_else(|poisoned| poisoned.into_inner())
249 }
250
251 fn ping(&self) -> MutexGuard<'_, PingReport> {
252 self.ping.lock().unwrap_or_else(|p| p.into_inner())
253 }
254
255 fn shut_down(&self, reason: DisconnectReason) {
257 let mut slot = self.reason.lock().unwrap_or_else(|p| p.into_inner());
258 if slot.is_none() {
259 *slot = Some(reason);
260 }
261 self.shutdown.notify_one();
262 }
263
264 fn tap(&self, direction: TapDirection, frame: &Frame, pacing: Option<TapPacing>) {
265 if let Some(tap) = self.tap.as_ref().filter(|tap| tap.enabled()) {
266 tap.on_frame(TappedFrame::new(
267 direction,
268 frame.kind,
269 &frame.payload,
270 pacing,
271 ));
272 }
273 }
274}
275
276struct PingReport {
280 started: Instant,
281 tcp_packets: u32,
282 tcp_avg_ms: f64,
283 tcp_m2: f64,
285 udp: Option<UdpPing>,
286 crypt: CryptStats,
288}
289
290#[derive(Debug, Clone, Copy, PartialEq)]
292pub struct UdpPing {
293 pub avg_ms: f32,
294 pub packets: u32,
296}
297
298impl Default for PingReport {
299 fn default() -> Self {
300 Self {
301 started: Instant::now(),
302 tcp_packets: 0,
303 tcp_avg_ms: 0.0,
304 tcp_m2: 0.0,
305 udp: None,
306 crypt: CryptStats::default(),
307 }
308 }
309}
310
311impl PingReport {
312 fn now_us(&self) -> u64 {
314 self.started.elapsed().as_micros() as u64
315 }
316
317 fn on_reply(&mut self, sent_us: u64) {
318 let Some(rtt_us) = self.now_us().checked_sub(sent_us) else {
319 return;
320 };
321 let rtt_ms = rtt_us as f64 / 1000.0;
322 self.tcp_packets = self.tcp_packets.saturating_add(1);
323 let delta = rtt_ms - self.tcp_avg_ms;
324 self.tcp_avg_ms += delta / f64::from(self.tcp_packets);
325 self.tcp_m2 += delta * (rtt_ms - self.tcp_avg_ms);
326 }
327
328 fn message(&self) -> proto::Ping {
329 let tcp = self.tcp_packets > 0;
330 proto::Ping {
331 timestamp: Some(self.now_us()),
332 tcp_packets: tcp.then_some(self.tcp_packets),
333 tcp_ping_avg: tcp.then_some(self.tcp_avg_ms as f32),
334 tcp_ping_var: tcp.then(|| (self.tcp_m2 / f64::from(self.tcp_packets)) as f32),
335 udp_packets: self.udp.map(|u| u.packets),
336 udp_ping_avg: self.udp.map(|u| u.avg_ms),
337 good: Some(self.crypt.good),
338 late: Some(self.crypt.late),
339 lost: Some(self.crypt.lost),
340 resync: Some(self.crypt.resync),
341 ..Default::default()
342 }
343 }
344}
345
346#[derive(Clone)]
351pub struct Handle {
352 tx: mpsc::UnboundedSender<Outgoing>,
353 shared: Arc<Shared>,
354}
355
356pub async fn connect(config: ConnectConfig) -> Result<Connection, ConnectError> {
358 connect_with_progress(config, None).await
359}
360
361pub type SyncProgress = Arc<dyn Fn(usize) + Send + Sync>;
363
364pub async fn connect_with_progress(
366 config: ConnectConfig,
367 progress: Option<SyncProgress>,
368) -> Result<Connection, ConnectError> {
369 connect_with_tap(config, progress, None).await
370}
371
372pub async fn connect_with_tap(
375 config: ConnectConfig,
376 progress: Option<SyncProgress>,
377 tap: Option<Arc<dyn PacketTap>>,
378) -> Result<Connection, ConnectError> {
379 let limit = config.login_timeout;
380 tokio::time::timeout(limit, connect_inner(config, progress, tap))
381 .await
382 .map_err(|_| ConnectError::Timeout)?
383}
384
385async fn connect_inner(
386 config: ConnectConfig,
387 progress: Option<SyncProgress>,
388 tap: Option<Arc<dyn PacketTap>>,
389) -> Result<Connection, ConnectError> {
390 let server_name = ServerName::try_from(config.host.clone())
391 .map_err(|_| ConnectError::InvalidAddress(config.host.clone()))?;
392 let (tls_config, seen_cert) = tls::client_config(config.identity, config.verify_cert)?;
393 let addrs: Vec<SocketAddr> = tokio::net::lookup_host((config.host.as_str(), config.port))
395 .await
396 .map_err(ConnectError::Resolve)?
397 .collect();
398 if addrs.is_empty() {
399 return Err(ConnectError::Resolve(std::io::Error::new(
400 std::io::ErrorKind::NotFound,
401 "no addresses found",
402 )));
403 }
404 let tcp = TcpStream::connect(addrs.as_slice()).await?;
405 tcp.set_nodelay(true)?;
407 let mut stream = match TlsConnector::from(tls_config)
408 .connect(server_name, tcp)
409 .await
410 {
411 Ok(stream) => stream,
412 Err(err) => {
413 let tls_err = err
414 .get_ref()
415 .and_then(|e| e.downcast_ref::<rustls::Error>())
416 .cloned();
417 return Err(match (tls_err, seen_cert.take()) {
418 (
419 Some(rustls::Error::InvalidCertificate(
420 rustls::CertificateError::ApplicationVerificationFailure,
421 )),
422 Some(cert),
423 ) => ConnectError::CertificateRejected(Box::new(cert)),
424 (Some(e), _) => ConnectError::Tls(e),
425 (None, _) => ConnectError::Io(err),
426 });
427 }
428 };
429 let server_cert = seen_cert.take();
431 let mut handshake = BytesMut::new();
437 let tokens = config.tokens.clone();
438 for frame in handshake_frames(config.username, config.password, config.tokens) {
439 frame.encode(&mut handshake);
440 }
441 async {
442 stream.write_all(&handshake).await?;
443 stream.flush().await
444 }
445 .await?;
446 let (mut reader, writer) = tokio::io::split(stream);
447
448 let (tx, rx) = mpsc::unbounded_channel();
449 let (events_tx, events) = mpsc::unbounded_channel();
450 let shared = Arc::new(Shared {
451 state: Mutex::new(ServerState::default()),
452 shutdown: Notify::new(),
453 closing: AtomicBool::new(false),
454 pong: watch::Sender::new(0),
455 reason: Mutex::new(None),
456 connected: AtomicBool::new(true),
457 low_expired: AtomicU64::new(0),
458 tokens: Mutex::new(tokens),
459 ping: Mutex::default(),
460 tap,
461 });
462 tokio::spawn(write_loop(writer, rx, shared.clone()));
463 let handle = Handle {
464 tx,
465 shared: shared.clone(),
466 };
467
468 let mut dispatcher = Dispatcher {
469 shared: shared.clone(),
470 events: events_tx,
471 synced: false,
472 };
473 let mut buf = BytesMut::with_capacity(16 * 1024);
474 let mut reported = 0;
475 loop {
476 if reader
477 .read_buf(&mut buf)
478 .await
479 .inspect_err(|_| handle.close())?
480 == 0
481 {
482 handle.close();
483 return Err(ConnectError::Closed);
484 }
485 while let Some(frame) = framing::decode(&mut buf).inspect_err(|_| handle.close())? {
486 match dispatcher.handle(frame) {
487 Control::Continue => {
488 if let Some(progress) = &progress {
489 let count = handle.with_state(|s| s.channels.len());
490 if count != reported {
491 reported = count;
492 progress(count);
493 }
494 }
495 }
496 Control::Synced => {
497 tokio::spawn(read_loop(reader, buf, dispatcher, handle.tx.downgrade()));
498 return Ok(Connection {
499 handle,
500 events,
501 server_cert,
502 });
503 }
504 Control::Rejected(reason, message) => {
505 handle.close();
506 return Err(ConnectError::Rejected { reason, message });
507 }
508 Control::Kicked(_) => {
509 handle.close();
510 return Err(ConnectError::Closed);
511 }
512 }
513 }
514 }
515}
516
517static CLIENT_RELEASE: OnceLock<String> = OnceLock::new();
519
520pub fn set_client_release(release: impl Into<String>) {
524 let _ = CLIENT_RELEASE.set(release.into());
526}
527
528fn client_release() -> String {
529 CLIENT_RELEASE
530 .get()
531 .cloned()
532 .unwrap_or_else(|| format!("Mumcord {}", env!("CARGO_PKG_VERSION")))
533}
534
535fn handshake_frames(username: String, password: Option<String>, tokens: Vec<String>) -> [Frame; 2] {
537 let version = proto::Version {
538 version_v1: Some(MumbleVersion::CLIENT.to_v1()),
539 version_v2: Some(MumbleVersion::CLIENT.to_v2()),
540 release: Some(client_release()),
541 os: Some(std::env::consts::OS.to_owned()),
542 os_version: None,
543 };
544 let auth = proto::Authenticate {
545 username: Some(username),
546 password,
547 tokens,
548 opus: Some(true),
549 ..Default::default()
550 };
551 [
552 Frame::from_message(MessageType::Version, &version),
553 Frame::from_message(MessageType::Authenticate, &auth),
554 ]
555}
556
557fn access_tokens_message(tokens: Vec<String>) -> proto::Authenticate {
558 proto::Authenticate {
559 tokens,
560 ..Default::default()
561 }
562}
563
564async fn write_loop<W: AsyncWrite + Unpin>(
565 writer: W,
566 mut rx: mpsc::UnboundedReceiver<Outgoing>,
567 shared: Arc<Shared>,
568) {
569 let mut ping =
570 tokio::time::interval_at(tokio::time::Instant::now() + PING_INTERVAL, PING_INTERVAL);
571 let mut pong = shared.pong.subscribe();
572 let mut out = Writer::new(writer, shared.clone(), tokio::time::Instant::now());
573 let mut closing: Option<tokio::time::Instant> = None;
575 let mut drained: Option<oneshot::Receiver<()>> = None;
576 let mut handles_gone = false;
577 let result = loop {
578 let now = tokio::time::Instant::now();
579 let messages_ready_at = out.messages.ready_at(now);
580 let plugin_data_ready_at = out.plugin_data.ready_at(now);
581 let step = tokio::select! {
582 msg = rx.recv(), if !handles_gone => match msg {
583 Some(Outgoing::Frame(frame)) => match ratelimit::limit_of(frame.kind) {
584 Some(Limit::Messages) => {
585 out.messages.push(frame, now);
586 Ok(())
587 }
588 Some(Limit::PluginData) => {
589 out.plugin_data.push(frame, now);
590 Ok(())
591 }
592 None => out.write(&frame, None).await,
593 },
594 Some(Outgoing::LowPluginData(frame, sent)) => {
595 out.plugin_data.push_low(frame, sent, now);
596 Ok(())
597 }
598 Some(Outgoing::Flush(done)) => {
599 out.flushes.push(out.flush_marker(done));
600 out.confirm_written().await
601 }
602 Some(Outgoing::Disconnect) | None if closing.is_none() => {
603 handles_gone = msg.is_none();
604 closing = Some(tokio::time::Instant::now() + DRAIN_TIMEOUT);
605 let (done, wait) = oneshot::channel();
606 drained = Some(wait);
607 out.flushes.push(out.flush_marker(done));
608 out.confirm_written().await
609 }
610 Some(Outgoing::Disconnect) => Ok(()),
611 None => {
612 handles_gone = true;
613 Ok(())
614 }
615 Some(Outgoing::Abort) => break Ok(()),
616 },
617 () = sleep_until_some(messages_ready_at) => match out.messages.pop() {
618 Some((frame, _)) => out.write_paced(&frame, None).await,
619 None => Ok(()),
620 },
621 () = sleep_until_some(plugin_data_ready_at) => {
622 out.expire_low(tokio::time::Instant::now());
623 match out.plugin_data.pop() {
624 Some((frame, pacing)) => out.write_paced(&frame, Some(pacing)).await,
625 None => match out.plugin_data.pop_low() {
626 Some((frame, pacing, sent)) => {
627 let written = out.write(&frame, Some(pacing)).await;
628 if written.is_ok() {
629 let _ = sent.send(LowOutcome::Sent);
631 }
632 written
633 }
634 None => Ok(()),
635 },
636 }
637 }
638 Ok(()) = pong.changed() => {
639 out.confirm_answered(*pong.borrow_and_update());
640 Ok(())
641 }
642 () = done_or_pending(&mut drained) => break Ok(()),
643 () = sleep_until_some(closing) => {
644 tracing::warn!("disconnect timed out before the queue was sent");
645 break Ok(());
646 }
647 _ = ping.tick() => {
648 let frame = Frame::from_message(MessageType::Ping, &shared.ping().message());
649 out.write(&frame, None).await
650 }
651 };
652 if let Err(err) = step {
653 break Err(err);
654 }
655 };
656 out.log_dropped();
657 match result {
658 Ok(()) => {
659 let _ = out.writer.shutdown().await;
661 shared.shut_down(DisconnectReason::Requested);
662 }
663 Err(err) => {
664 tracing::warn!(error = %err, "control channel write failed");
665 shared.shut_down(DisconnectReason::Error(err.to_string()));
666 }
667 }
668}
669
670struct Writer<W> {
673 writer: W,
674 buf: BytesMut,
675 shared: Arc<Shared>,
676 messages: Paced,
677 plugin_data: Paced,
678 flushes: Vec<FlushMarker>,
680 unanswered: Vec<(u64, oneshot::Sender<()>)>,
682}
683
684struct FlushMarker {
686 messages: u64,
687 plugin_data: u64,
688 done: oneshot::Sender<()>,
689}
690
691impl<W: AsyncWrite + Unpin> Writer<W> {
692 fn new(writer: W, shared: Arc<Shared>, now: tokio::time::Instant) -> Self {
693 Self {
694 writer,
695 buf: BytesMut::new(),
696 shared,
697 messages: Paced::new(Limit::Messages, now),
698 plugin_data: Paced::new(Limit::PluginData, now),
699 flushes: Vec::new(),
700 unanswered: Vec::new(),
701 }
702 }
703
704 async fn write(&mut self, frame: &Frame, pacing: Option<TapPacing>) -> std::io::Result<()> {
705 self.buf.clear();
706 frame.encode(&mut self.buf);
707 self.writer.write_all(&self.buf).await?;
708 self.writer.flush().await?;
709 self.shared.tap(TapDirection::Sent, frame, pacing);
710 Ok(())
711 }
712
713 async fn write_paced(
714 &mut self,
715 frame: &Frame,
716 pacing: Option<TapPacing>,
717 ) -> std::io::Result<()> {
718 self.write(frame, pacing).await?;
719 self.confirm_written().await
720 }
721
722 fn flush_marker(&self, done: oneshot::Sender<()>) -> FlushMarker {
723 FlushMarker {
724 messages: self.messages.pushed,
725 plugin_data: self.plugin_data.pushed,
726 done,
727 }
728 }
729
730 async fn confirm_written(&mut self) -> std::io::Result<()> {
732 let (sent, waiting): (Vec<_>, Vec<_>) = std::mem::take(&mut self.flushes)
733 .into_iter()
734 .partition(|f| {
735 self.messages.popped >= f.messages && self.plugin_data.popped >= f.plugin_data
736 });
737 self.flushes = waiting;
738 if sent.is_empty() {
739 return Ok(());
740 }
741 let ping = self.shared.ping().message();
742 let stamp = ping.timestamp.unwrap_or_default();
743 self.unanswered
744 .extend(sent.into_iter().map(|f| (stamp, f.done)));
745 self.write(&Frame::from_message(MessageType::Ping, &ping), None)
746 .await
747 }
748
749 fn confirm_answered(&mut self, answered: u64) {
751 let (done, waiting): (Vec<_>, Vec<_>) = std::mem::take(&mut self.unanswered)
752 .into_iter()
753 .partition(|(stamp, _)| *stamp <= answered);
754 self.unanswered = waiting;
755 for (_, flush) in done {
756 let _ = flush.send(());
758 }
759 }
760
761 fn expire_low(&mut self, now: tokio::time::Instant) {
763 let expired = self.plugin_data.expire_low(now);
764 if expired > 0 {
765 self.shared
766 .low_expired
767 .fetch_add(expired, Ordering::Relaxed);
768 tracing::debug!(
769 expired,
770 "low-priority plugin data dropped after waiting too long"
771 );
772 }
773 }
774
775 fn log_dropped(&self) {
777 let mut dropped: BTreeMap<u16, usize> = BTreeMap::new();
778 let low = self.plugin_data.low.iter().map(|l| &l.frame);
779 for frame in self
780 .messages
781 .queue
782 .iter()
783 .chain(&self.plugin_data.queue)
784 .map(|(frame, _)| frame)
785 .chain(low)
786 {
787 *dropped.entry(frame.kind).or_default() += 1;
788 }
789 for (kind, count) in dropped {
790 let kind = MessageType::from_u16(kind);
791 tracing::warn!(?kind, count, "queued messages dropped on disconnect");
792 }
793 }
794}
795
796struct Paced {
798 bucket: LeakyBucket,
799 queue: VecDeque<(Frame, tokio::time::Instant)>,
801 low: VecDeque<LowFrame>,
803 pushed: u64,
805 popped: u64,
806}
807
808impl Paced {
809 fn new(limit: Limit, now: tokio::time::Instant) -> Self {
810 Self {
811 bucket: LeakyBucket::new(limit, now),
812 queue: VecDeque::new(),
813 low: VecDeque::new(),
814 pushed: 0,
815 popped: 0,
816 }
817 }
818
819 fn push(&mut self, frame: Frame, now: tokio::time::Instant) {
820 self.queue.push_back((frame, now));
821 self.pushed += 1;
822 }
823
824 fn push_low(
825 &mut self,
826 frame: Frame,
827 sent: oneshot::Sender<LowOutcome>,
828 now: tokio::time::Instant,
829 ) {
830 self.low.push_back(LowFrame {
831 frame,
832 queued: now,
833 sent,
834 });
835 }
836
837 fn ready_at(&self, now: tokio::time::Instant) -> Option<tokio::time::Instant> {
840 (!self.queue.is_empty() || !self.low.is_empty()).then(|| now + self.bucket.wait_time(now))
841 }
842
843 fn pop(&mut self) -> Option<(Frame, TapPacing)> {
845 let (frame, queued) = self.queue.pop_front()?;
846 self.popped += 1;
847 Some((frame, self.take(queued)))
848 }
849
850 fn pop_low(&mut self) -> Option<(Frame, TapPacing, oneshot::Sender<LowOutcome>)> {
852 let LowFrame {
853 frame,
854 queued,
855 sent,
856 } = self.low.pop_front()?;
857 Some((frame, self.take(queued), sent))
858 }
859
860 fn take(&mut self, queued: tokio::time::Instant) -> TapPacing {
861 let now = tokio::time::Instant::now();
862 self.bucket.take(now);
863 TapPacing {
864 delay: now.saturating_duration_since(queued),
865 tokens_left: self.bucket.tokens_left(now),
866 }
867 }
868
869 fn expire_low(&mut self, now: tokio::time::Instant) -> u64 {
871 let mut expired = 0;
872 while self
874 .low
875 .front()
876 .is_some_and(|l| now.saturating_duration_since(l.queued) > LOW_PRIORITY_MAX_WAIT)
877 {
878 if let Some(l) = self.low.pop_front() {
879 let _ = l.sent.send(LowOutcome::Expired);
881 expired += 1;
882 }
883 }
884 expired
885 }
886}
887
888struct LowFrame {
889 frame: Frame,
890 queued: tokio::time::Instant,
891 sent: oneshot::Sender<LowOutcome>,
892}
893
894async fn done_or_pending(rx: &mut Option<oneshot::Receiver<()>>) {
896 match rx {
897 Some(rx) => {
898 let _ = rx.await;
899 }
900 None => std::future::pending().await,
901 }
902}
903
904async fn sleep_until_some(at: Option<tokio::time::Instant>) {
906 match at {
907 Some(at) => tokio::time::sleep_until(at).await,
908 None => std::future::pending().await,
909 }
910}
911
912async fn read_loop(
913 mut reader: ReadHalf<TlsStream<TcpStream>>,
914 mut buf: BytesMut,
915 mut dispatcher: Dispatcher,
916 writer: mpsc::WeakUnboundedSender<Outgoing>,
917) {
918 let shared = dispatcher.shared.clone();
919 let reason = 'outer: loop {
920 loop {
922 match framing::decode(&mut buf) {
923 Ok(Some(frame)) => {
924 if let Control::Kicked(reason) = dispatcher.handle(frame) {
925 break 'outer reason;
926 }
927 }
928 Ok(None) => break,
929 Err(err) => break 'outer DisconnectReason::Error(err.to_string()),
930 }
931 }
932 tokio::select! {
933 read = tokio::time::timeout(READ_TIMEOUT, reader.read_buf(&mut buf)) => match read {
934 Err(_) => break DisconnectReason::Timeout,
935 Ok(Ok(0)) => break DisconnectReason::Closed,
936 Ok(Ok(_)) => {}
937 Ok(Err(err)) => break DisconnectReason::Error(err.to_string()),
938 },
939 () = shared.shutdown.notified() => {
940 let stored = shared.reason.lock().unwrap_or_else(|p| p.into_inner()).take();
941 break stored.unwrap_or(DisconnectReason::Requested);
942 }
943 }
944 };
945 shared.connected.store(false, Ordering::Release);
946 if let Some(tx) = writer.upgrade() {
948 let _ = tx.send(Outgoing::Abort);
949 }
950 tracing::info!(?reason, "control channel closed");
951 let _ = dispatcher.events.send(Event::Disconnected(reason));
952}
953
954enum Control {
955 Continue,
956 Synced,
957 Rejected(RejectReason, String),
958 Kicked(DisconnectReason),
959}
960
961struct Dispatcher {
962 shared: Arc<Shared>,
963 events: mpsc::UnboundedSender<Event>,
964 synced: bool,
967}
968
969fn decode<M: Message + Default>(kind: &'static str, payload: Bytes) -> Result<M, ProtocolError> {
970 M::decode(payload).map_err(|source| ProtocolError::Decode { kind, source })
971}
972
973impl Dispatcher {
974 fn emit(&self, event: Event) {
975 let _ = self.events.send(event);
977 }
978
979 fn emit_synced(&self, event: Event) {
980 if self.synced {
981 self.emit(event);
982 }
983 }
984
985 fn handle(&mut self, frame: Frame) -> Control {
986 self.shared.tap(TapDirection::Received, &frame, None);
987 let Some(kind) = frame.message_type() else {
988 tracing::debug!(kind = frame.kind, "ignoring unknown control message");
989 return Control::Continue;
990 };
991 match self.handle_known(kind, frame.payload) {
992 Ok(control) => control,
993 Err(err) => {
994 tracing::warn!(error = %err, "skipping malformed control message");
996 Control::Continue
997 }
998 }
999 }
1000
1001 fn handle_known(
1002 &mut self,
1003 kind: MessageType,
1004 payload: Bytes,
1005 ) -> Result<Control, ProtocolError> {
1006 let shared = self.shared.clone();
1007 match kind {
1008 MessageType::UdpTunnel => self.emit(Event::UdpTunnel(payload)),
1009 MessageType::Version => {
1010 let m: proto::Version = decode("Version", payload)?;
1011 let mut state = shared.state();
1012 state.server.version = MumbleVersion::from_wire(m.version_v1, m.version_v2);
1013 state.server.release = m.release;
1014 state.server.os = m.os;
1015 }
1016 MessageType::Reject => {
1017 let m: proto::Reject = decode("Reject", payload)?;
1018 let reason = RejectReason::from_wire(m.r#type);
1019 return Ok(Control::Rejected(reason, m.reason.unwrap_or_default()));
1020 }
1021 MessageType::ServerSync => {
1022 let m: proto::ServerSync = decode("ServerSync", payload)?;
1023 shared.state().apply_server_sync(&m);
1024 self.synced = true;
1025 return Ok(Control::Synced);
1026 }
1027 MessageType::ChannelState => {
1028 let m: proto::ChannelState = decode("ChannelState", payload)?;
1029 let channel = shared.state().apply_channel_state(m).cloned();
1030 if let Some(channel) = channel {
1031 self.emit_synced(Event::ChannelUpdated(channel));
1032 }
1033 }
1034 MessageType::ChannelRemove => {
1035 let m: proto::ChannelRemove = decode("ChannelRemove", payload)?;
1036 let removed = shared.state().apply_channel_remove(m.channel_id);
1037 if let Some(channel) = removed {
1038 self.emit_synced(Event::ChannelRemoved(channel));
1039 }
1040 }
1041 MessageType::UserState => {
1042 let m: proto::UserState = decode("UserState", payload)?;
1043 let change = shared.state().apply_user_state(m);
1044 if let Some(change) = change {
1045 self.emit_synced(Event::UserUpdated(change));
1046 }
1047 }
1048 MessageType::UserRemove => {
1049 let m: proto::UserRemove = decode("UserRemove", payload)?;
1050 let ban = m.ban.unwrap_or(false);
1051 let (removed, own) = {
1052 let mut state = shared.state();
1053 (
1054 state.apply_user_remove(m.session),
1055 state.session == Some(m.session),
1056 )
1057 };
1058 if own {
1059 return Ok(Control::Kicked(DisconnectReason::Kicked {
1060 actor: m.actor,
1061 reason: m.reason,
1062 ban,
1063 }));
1064 }
1065 if let Some(user) = removed {
1066 self.emit_synced(Event::UserRemoved {
1067 user,
1068 actor: m.actor,
1069 reason: m.reason,
1070 ban,
1071 });
1072 }
1073 }
1074 MessageType::TextMessage => {
1075 let m: proto::TextMessage = decode("TextMessage", payload)?;
1076 let sender = m.actor.and_then(|actor| {
1077 shared.state().users.get(&actor).map(|user| User {
1078 comment: None,
1079 texture: None,
1080 ..user.clone()
1081 })
1082 });
1083 self.emit(Event::TextMessage(TextMessage {
1084 actor: m.actor,
1085 sender,
1086 target: TextTarget {
1087 sessions: m.session,
1088 channels: m.channel_id,
1089 trees: m.tree_id,
1090 },
1091 message: m.message,
1092 }));
1093 }
1094 MessageType::PluginDataTransmission => {
1095 let m: proto::PluginDataTransmission = decode("PluginDataTransmission", payload)?;
1096 self.emit(Event::PluginData(PluginData {
1097 sender: m.sender_session,
1098 data_id: m.data_id.unwrap_or_default(),
1099 data: m.data.map(Bytes::from).unwrap_or_default(),
1100 }));
1101 }
1102 MessageType::UserStats => {
1103 let m: proto::UserStats = decode("UserStats", payload)?;
1104 self.emit(Event::UserStats(Box::new(m)));
1105 }
1106 MessageType::PermissionDenied => {
1107 let m: proto::PermissionDenied = decode("PermissionDenied", payload)?;
1108 self.emit(Event::PermissionDenied(m.into()));
1109 }
1110 MessageType::CryptSetup => {
1111 let m: proto::CryptSetup = decode("CryptSetup", payload)?;
1112 let crypt = CryptSetup::from(m);
1113 {
1114 let mut state = shared.state();
1115 match (&mut state.crypt, crypt.key.is_some()) {
1117 (Some(current), false) => {
1118 if crypt.server_nonce.is_some() {
1119 current.server_nonce.clone_from(&crypt.server_nonce);
1120 }
1121 }
1122 (slot, _) => *slot = Some(crypt.clone()),
1123 }
1124 }
1125 self.emit(Event::CryptSetup(crypt));
1126 }
1127 MessageType::PermissionQuery => {
1128 let m: proto::PermissionQuery = decode("PermissionQuery", payload)?;
1129 shared.state().apply_permission_query(&m);
1130 self.emit_synced(Event::PermissionsUpdated {
1131 channel_id: m.channel_id,
1132 flush: m.flush == Some(true),
1133 });
1134 }
1135 MessageType::Acl => {
1136 let m: proto::Acl = decode("Acl", payload)?;
1137 self.emit(Event::Acl(Box::new(m)));
1138 }
1139 MessageType::QueryUsers => {
1140 let m: proto::QueryUsers = decode("QueryUsers", payload)?;
1141 self.emit(Event::UsersQueried(m));
1142 }
1143 MessageType::BanList => {
1144 let m: proto::BanList = decode("BanList", payload)?;
1145 self.emit(Event::BanList(crate::ban::from_message(&m)));
1146 }
1147 MessageType::UserList => {
1148 let m: proto::UserList = decode("UserList", payload)?;
1149 self.emit(Event::UserList(crate::registered::from_message(&m)));
1150 }
1151 MessageType::ServerConfig => {
1152 let m: proto::ServerConfig = decode("ServerConfig", payload)?;
1153 let info = {
1154 let mut state = shared.state();
1155 state.apply_server_config(m);
1156 state.server.clone()
1157 };
1158 self.emit_synced(Event::ServerConfigUpdated(info));
1159 }
1160 MessageType::Ping => {
1161 let m: proto::Ping = decode("Ping", payload)?;
1162 if let Some(sent) = m.timestamp {
1163 self.shared.ping().on_reply(sent);
1164 self.shared.pong.send_if_modified(|latest| {
1165 let newer = sent > *latest;
1166 if newer {
1167 *latest = sent;
1168 }
1169 newer
1170 });
1171 }
1172 }
1173 _ => tracing::trace!(?kind, "ignoring control message"),
1174 }
1175 Ok(Control::Continue)
1176 }
1177}
1178
1179impl Handle {
1180 fn send(&self, frame: Frame) -> Result<(), CommandError> {
1181 self.queue(Outgoing::Frame(frame))
1182 }
1183
1184 fn queue(&self, out: Outgoing) -> Result<(), CommandError> {
1185 if !self.is_connected() || self.shared.closing.load(Ordering::Acquire) {
1186 return Err(CommandError::NotConnected);
1187 }
1188 self.tx.send(out).map_err(|_| CommandError::NotConnected)
1189 }
1190
1191 fn close(&self) {
1193 let _ = self.tx.send(Outgoing::Abort);
1194 }
1195
1196 fn send_user_state(&self, mut m: proto::UserState) -> Result<(), CommandError> {
1197 m.session = self.shared.state().session;
1198 self.send(Frame::from_message(MessageType::UserState, &m))
1199 }
1200
1201 fn require(&self, feature: &'static str, required: MumbleVersion) -> Result<(), CommandError> {
1202 let server = self.server_version().unwrap_or(MumbleVersion::new(0, 0, 0));
1203 if server >= required {
1204 Ok(())
1205 } else {
1206 Err(CommandError::Unsupported {
1207 feature,
1208 required,
1209 server,
1210 })
1211 }
1212 }
1213
1214 pub fn is_connected(&self) -> bool {
1215 self.shared.connected.load(Ordering::Acquire)
1216 }
1217
1218 pub fn state(&self) -> ServerState {
1220 self.shared.state().clone()
1221 }
1222
1223 pub fn with_state<R>(&self, f: impl FnOnce(&ServerState) -> R) -> R {
1225 f(&self.shared.state())
1226 }
1227
1228 pub fn server_version(&self) -> Option<MumbleVersion> {
1229 self.shared.state().server.version
1230 }
1231
1232 pub fn join_channel(&self, channel_id: u32) -> Result<(), CommandError> {
1233 self.send_user_state(proto::UserState {
1234 channel_id: Some(channel_id),
1235 ..Default::default()
1236 })
1237 }
1238
1239 pub fn move_user(&self, session: u32, channel_id: u32) -> Result<(), CommandError> {
1242 let m = proto::UserState {
1243 session: Some(session),
1244 channel_id: Some(channel_id),
1245 ..Default::default()
1246 };
1247 self.send(Frame::from_message(MessageType::UserState, &m))
1248 }
1249
1250 pub fn set_self_mute(&self, mute: bool) -> Result<(), CommandError> {
1251 self.send_user_state(proto::UserState {
1252 self_mute: Some(mute),
1253 ..Default::default()
1254 })
1255 }
1256
1257 pub fn set_self_deaf(&self, deaf: bool) -> Result<(), CommandError> {
1259 let mut m = proto::UserState {
1260 self_deaf: Some(deaf),
1261 ..Default::default()
1262 };
1263 if deaf {
1264 m.self_mute = Some(true);
1265 }
1266 self.send_user_state(m)
1267 }
1268
1269 pub fn listen(&self, channels: &[u32]) -> Result<(), CommandError> {
1271 self.require("listening", MumbleVersion::LISTENING)?;
1272 self.send_user_state(proto::UserState {
1273 listening_channel_add: channels.to_vec(),
1274 ..Default::default()
1275 })
1276 }
1277
1278 pub fn unlisten(&self, channels: &[u32]) -> Result<(), CommandError> {
1279 self.require("listening", MumbleVersion::LISTENING)?;
1280 self.send_user_state(proto::UserState {
1281 listening_channel_remove: channels.to_vec(),
1282 ..Default::default()
1283 })
1284 }
1285
1286 pub fn set_listener_volume(&self, channel_id: u32, volume: f32) -> Result<(), CommandError> {
1289 self.require("listener volume", MumbleVersion::LISTENER_VOLUME)?;
1290 self.send_user_state(proto::UserState {
1291 listening_volume_adjustment: vec![proto::user_state::VolumeAdjustment {
1292 listening_channel: Some(channel_id),
1293 volume_adjustment: Some(volume),
1294 }],
1295 ..Default::default()
1296 })
1297 }
1298
1299 pub fn set_texture(&self, image: Vec<u8>) -> Result<(), CommandError> {
1302 self.send_user_state(proto::UserState {
1303 texture: Some(image),
1304 ..Default::default()
1305 })
1306 }
1307
1308 pub fn set_plugin_context(
1312 &self,
1313 context: Vec<u8>,
1314 identity: String,
1315 ) -> Result<(), CommandError> {
1316 self.send_user_state(proto::UserState {
1317 plugin_context: Some(context),
1318 plugin_identity: Some(identity),
1319 ..Default::default()
1320 })
1321 }
1322
1323 pub fn set_comment(&self, comment: String) -> Result<(), CommandError> {
1325 self.send_user_state(proto::UserState {
1326 comment: Some(comment),
1327 ..Default::default()
1328 })
1329 }
1330
1331 pub fn register_self(&self) -> Result<(), CommandError> {
1334 self.send_user_state(proto::UserState {
1335 user_id: Some(0),
1336 ..Default::default()
1337 })
1338 }
1339
1340 pub fn send_text(&self, target: &TextTarget, message: &str) -> Result<(), CommandError> {
1342 let m = proto::TextMessage {
1343 actor: None,
1344 session: target.sessions.clone(),
1345 channel_id: target.channels.clone(),
1346 tree_id: target.trees.clone(),
1347 message: message.to_owned(),
1348 };
1349 self.send(Frame::from_message(MessageType::TextMessage, &m))
1350 }
1351
1352 pub fn send_plugin_data(
1356 &self,
1357 receivers: &[u32],
1358 data_id: &str,
1359 data: Vec<u8>,
1360 ) -> Result<(), CommandError> {
1361 self.require("plugin data", MumbleVersion::PLUGIN_DATA)?;
1362 self.send(self.plugin_data_frame(receivers, data_id, data))
1363 }
1364
1365 pub fn send_plugin_data_low(
1369 &self,
1370 receivers: &[u32],
1371 data_id: &str,
1372 data: Vec<u8>,
1373 ) -> Result<PluginTicket, CommandError> {
1374 self.require("plugin data", MumbleVersion::PLUGIN_DATA)?;
1375 let (tx, rx) = oneshot::channel();
1376 self.queue(Outgoing::LowPluginData(
1377 self.plugin_data_frame(receivers, data_id, data),
1378 tx,
1379 ))?;
1380 Ok(PluginTicket(rx))
1381 }
1382
1383 pub fn plugin_data_expired(&self) -> u64 {
1385 self.shared.low_expired.load(Ordering::Relaxed)
1386 }
1387
1388 fn plugin_data_frame(&self, receivers: &[u32], data_id: &str, data: Vec<u8>) -> Frame {
1389 let m = proto::PluginDataTransmission {
1390 sender_session: self.shared.state().session,
1391 receiver_sessions: receivers.to_vec(),
1392 data: Some(data),
1393 data_id: Some(data_id.to_owned()),
1394 };
1395 Frame::from_message(MessageType::PluginDataTransmission, &m)
1396 }
1397
1398 pub fn request_blobs(
1400 &self,
1401 textures: &[u32],
1402 comments: &[u32],
1403 channel_descriptions: &[u32],
1404 ) -> Result<(), CommandError> {
1405 let m = proto::RequestBlob {
1406 session_texture: textures.to_vec(),
1407 session_comment: comments.to_vec(),
1408 channel_description: channel_descriptions.to_vec(),
1409 };
1410 self.send(Frame::from_message(MessageType::RequestBlob, &m))
1411 }
1412
1413 pub fn request_user_stats(&self, session: u32, stats_only: bool) -> Result<(), CommandError> {
1416 let m = proto::UserStats {
1417 session: Some(session),
1418 stats_only: Some(stats_only),
1419 ..Default::default()
1420 };
1421 self.send(Frame::from_message(MessageType::UserStats, &m))
1422 }
1423
1424 pub fn report_voice_link(&self, ping: Option<UdpPing>, crypt: CryptStats) {
1427 let mut report = self.shared.ping();
1428 report.udp = ping;
1429 report.crypt = crypt;
1430 }
1431
1432 pub fn set_access_tokens(&self, tokens: Vec<String>) -> Result<(), CommandError> {
1436 self.send(Frame::from_message(
1437 MessageType::Authenticate,
1438 &access_tokens_message(tokens.clone()),
1439 ))?;
1440 *self.shared.tokens.lock().unwrap_or_else(|p| p.into_inner()) = tokens;
1441 Ok(())
1442 }
1443
1444 pub fn access_tokens(&self) -> Vec<String> {
1446 self.shared
1447 .tokens
1448 .lock()
1449 .unwrap_or_else(|p| p.into_inner())
1450 .clone()
1451 }
1452
1453 pub fn query_permissions(&self, channel_id: u32) -> Result<(), CommandError> {
1455 let m = proto::PermissionQuery {
1456 channel_id: Some(channel_id),
1457 ..Default::default()
1458 };
1459 self.send(Frame::from_message(MessageType::PermissionQuery, &m))
1460 }
1461
1462 pub fn query_acl(&self, channel_id: u32) -> Result<(), CommandError> {
1470 let m = proto::Acl {
1471 channel_id,
1472 query: Some(true),
1473 ..Default::default()
1474 };
1475 self.send(Frame::from_message(MessageType::Acl, &m))
1476 }
1477
1478 pub fn remove_user(
1485 &self,
1486 session: u32,
1487 reason: Option<&str>,
1488 ban: bool,
1489 ) -> Result<(), CommandError> {
1490 let m = proto::UserRemove {
1491 session,
1492 actor: None,
1493 reason: reason.filter(|r| !r.is_empty()).map(|r| r.to_owned()),
1494 ban: Some(ban),
1495 };
1496 self.send(Frame::from_message(MessageType::UserRemove, &m))
1497 }
1498
1499 pub fn query_ban_list(&self) -> Result<(), CommandError> {
1503 self.send(Frame::from_message(
1504 MessageType::BanList,
1505 &crate::ban::query_message(),
1506 ))
1507 }
1508
1509 pub fn send_ban_list(&self, bans: &[crate::ban::Ban]) -> Result<(), CommandError> {
1513 self.send(Frame::from_message(
1514 MessageType::BanList,
1515 &crate::ban::to_message(bans),
1516 ))
1517 }
1518
1519 pub fn query_user_list(&self) -> Result<(), CommandError> {
1524 self.send(Frame::from_message(
1525 MessageType::UserList,
1526 &crate::registered::query_message(),
1527 ))
1528 }
1529
1530 pub fn change_registered_user(
1536 &self,
1537 user_id: u32,
1538 name: Option<&str>,
1539 ) -> Result<(), CommandError> {
1540 self.send(Frame::from_message(
1541 MessageType::UserList,
1542 &crate::registered::change_message(&crate::registered::RegisteredUser {
1543 user_id,
1544 name: name.map(str::to_owned),
1545 ..Default::default()
1546 }),
1547 ))
1548 }
1549
1550 pub fn send_acl(&self, acl: proto::Acl) -> Result<(), CommandError> {
1553 self.send(Frame::from_message(MessageType::Acl, &acl))
1554 }
1555
1556 pub fn send_admin_user_state(&self, m: proto::UserState) -> Result<(), CommandError> {
1560 self.send(Frame::from_message(MessageType::UserState, &m))
1561 }
1562
1563 pub fn send_channel_state(&self, state: proto::ChannelState) -> Result<(), CommandError> {
1566 self.send(Frame::from_message(MessageType::ChannelState, &state))
1567 }
1568
1569 pub fn query_users(&self, ids: Vec<u32>) -> Result<(), CommandError> {
1572 let m = proto::QueryUsers {
1573 ids,
1574 names: Vec::new(),
1575 };
1576 self.send(Frame::from_message(MessageType::QueryUsers, &m))
1577 }
1578
1579 pub fn send_channel_remove(&self, channel_id: u32) -> Result<(), CommandError> {
1582 let m = proto::ChannelRemove { channel_id };
1583 self.send(Frame::from_message(MessageType::ChannelRemove, &m))
1584 }
1585
1586 pub fn send_voice_target(&self, target: &proto::VoiceTarget) -> Result<(), CommandError> {
1592 self.send(Frame::from_message(MessageType::VoiceTarget, target))
1593 }
1594
1595 pub fn send_tunnel(&self, packet: Bytes) -> Result<(), CommandError> {
1597 self.send(Frame {
1598 kind: MessageType::UdpTunnel as u16,
1599 payload: packet,
1600 })
1601 }
1602
1603 pub fn send_crypt_setup(&self, crypt: CryptSetup) -> Result<(), CommandError> {
1606 let m = proto::CryptSetup::from(crypt);
1607 self.send(Frame::from_message(MessageType::CryptSetup, &m))
1608 }
1609
1610 pub async fn flush(&self) -> Result<(), CommandError> {
1615 let (done, wait) = oneshot::channel();
1616 self.queue(Outgoing::Flush(done))?;
1617 wait.await.map_err(|_| CommandError::NotConnected)
1618 }
1619
1620 pub fn disconnect(&self) {
1625 self.shared.closing.store(true, Ordering::Release);
1626 let _ = self.tx.send(Outgoing::Disconnect);
1627 }
1628
1629 pub async fn ping(&self) -> Option<Duration> {
1633 let mut pong = self.shared.pong.subscribe();
1634 let sent_us = self.shared.ping().now_us();
1635 let frame = Frame::from_message(MessageType::Ping, &self.shared.ping().message());
1636 self.queue(Outgoing::Frame(frame)).ok()?;
1637 let started = Instant::now();
1638 loop {
1639 if *pong.borrow_and_update() > sent_us {
1640 return Some(started.elapsed());
1641 }
1642 pong.changed().await.ok()?;
1643 }
1644 }
1645
1646 pub fn closed(&self) -> impl Future<Output = ()> + Send + use<> {
1649 let tx = self.tx.clone();
1650 async move { tx.closed().await }
1651 }
1652}
1653
1654#[cfg(test)]
1655mod tests {
1656 use prost::Message;
1657
1658 use super::*;
1659 use crate::permissions;
1660
1661 #[test]
1662 fn pings_report_the_own_tcp_and_udp_figures() {
1663 let mut report = PingReport {
1664 started: Instant::now() - Duration::from_secs(1),
1665 ..Default::default()
1666 };
1667 assert_eq!(report.message().tcp_ping_avg, None);
1668 let now = report.now_us();
1669 report.on_reply(now - 30_000);
1670 report.on_reply(now - 10_000);
1671 report.udp = Some(UdpPing {
1672 avg_ms: 12.5,
1673 packets: 40,
1674 });
1675 let m = report.message();
1676 assert_eq!(m.tcp_packets, Some(2));
1677 let avg = m.tcp_ping_avg.expect("tcp ping");
1678 assert!((20.0..21.0).contains(&avg), "{avg}");
1679 assert!(m.tcp_ping_var.expect("var") > 90.0);
1680 assert_eq!((m.udp_packets, m.udp_ping_avg), (Some(40), Some(12.5)));
1681 assert_eq!(m.good, Some(0));
1682 }
1683
1684 #[test]
1685 fn send_acl_queues_an_acl_frame() {
1686 let (tx, mut rx) = mpsc::unbounded_channel();
1687 let handle = Handle {
1688 tx,
1689 shared: Arc::new(Shared {
1690 state: Mutex::new(ServerState::default()),
1691 shutdown: Notify::new(),
1692 closing: AtomicBool::new(false),
1693 pong: watch::Sender::new(0),
1694 reason: Mutex::new(None),
1695 connected: AtomicBool::new(true),
1696 ping: Mutex::default(),
1697 tap: None,
1698 low_expired: AtomicU64::new(0),
1699 tokens: Mutex::default(),
1700 }),
1701 };
1702 let acl = proto::Acl {
1703 channel_id: 3,
1704 inherit_acls: Some(true),
1705 ..Default::default()
1706 };
1707 handle.send_acl(acl.clone()).expect("queued");
1708 let Ok(Outgoing::Frame(frame)) = rx.try_recv() else {
1709 panic!("expected a frame");
1710 };
1711 assert_eq!(frame.kind, MessageType::Acl as u16);
1712 assert_eq!(proto::Acl::decode(frame.payload).expect("decode"), acl);
1713 }
1714
1715 fn test_shared(state: ServerState) -> Arc<Shared> {
1716 Arc::new(Shared {
1717 state: Mutex::new(state),
1718 shutdown: Notify::new(),
1719 closing: AtomicBool::new(false),
1720 pong: watch::Sender::new(0),
1721 reason: Mutex::new(None),
1722 connected: AtomicBool::new(true),
1723 ping: Mutex::default(),
1724 tap: None,
1725 low_expired: AtomicU64::new(0),
1726 tokens: Mutex::default(),
1727 })
1728 }
1729
1730 #[test]
1731 fn acl_query_sends_a_query_and_the_answer_becomes_an_event() {
1732 let (tx, mut rx) = mpsc::unbounded_channel();
1733 let shared = test_shared(ServerState::default());
1734 let handle = Handle {
1735 tx: tx.clone(),
1736 shared: shared.clone(),
1737 };
1738 handle.query_acl(2).expect("queued");
1739 let Ok(Outgoing::Frame(frame)) = rx.try_recv() else {
1740 panic!("expected a frame");
1741 };
1742 assert_eq!(frame.kind, MessageType::Acl as u16);
1743 let m = proto::Acl::decode(frame.payload).expect("decode");
1744 assert_eq!((m.channel_id, m.query), (2, Some(true)));
1745
1746 let (events, mut events_rx) = mpsc::unbounded_channel();
1748 let mut dispatcher = Dispatcher {
1749 shared,
1750 events,
1751 synced: true,
1752 };
1753 let acl = proto::Acl {
1754 channel_id: 2,
1755 inherit_acls: Some(true),
1756 groups: vec![proto::acl::ChanGroup {
1757 name: "editors".into(),
1758 add: vec![7],
1759 ..Default::default()
1760 }],
1761 acls: vec![proto::acl::ChanAcl {
1762 grant: Some(permissions::WRITE),
1763 group: Some("editors".into()),
1764 ..Default::default()
1765 }],
1766 query: Some(false),
1767 };
1768 assert!(matches!(
1769 dispatcher.handle(Frame::from_message(MessageType::Acl, &acl)),
1770 Control::Continue
1771 ));
1772 assert!(rx.try_recv().is_err(), "the query is only sent once");
1773 assert_eq!(events_rx.try_recv(), Ok(Event::Acl(Box::new(acl))));
1774 }
1775
1776 #[test]
1777 fn a_text_message_keeps_its_sender_after_the_sender_left() {
1778 let mut state = ServerState::default();
1779 state.users.insert(
1780 9,
1781 User {
1782 session: 9,
1783 name: "Mallory".into(),
1784 cert_hash: Some("abc".into()),
1785 comment: Some("long comment".into()),
1786 texture: Some(vec![1, 2, 3]),
1787 ..Default::default()
1788 },
1789 );
1790 let (events, mut events_rx) = mpsc::unbounded_channel();
1791 let mut dispatcher = Dispatcher {
1792 shared: test_shared(state),
1793 events,
1794 synced: true,
1795 };
1796 let text = proto::TextMessage {
1797 actor: Some(9),
1798 channel_id: vec![0],
1799 message: "hi".into(),
1800 ..Default::default()
1801 };
1802 dispatcher.handle(Frame::from_message(MessageType::TextMessage, &text));
1803 let remove = proto::UserRemove {
1805 session: 9,
1806 ..Default::default()
1807 };
1808 dispatcher.handle(Frame::from_message(MessageType::UserRemove, &remove));
1809 let Ok(Event::TextMessage(message)) = events_rx.try_recv() else {
1810 panic!("expected the text message first");
1811 };
1812 let sender = message
1813 .sender
1814 .expect("the sender as it was when the message came");
1815 assert_eq!(sender.name, "Mallory");
1816 assert_eq!(sender.cert_hash.as_deref(), Some("abc"));
1817 assert_eq!((sender.comment, sender.texture), (None, None));
1818 }
1819
1820 #[test]
1821 fn plugin_data_needs_a_1_4_server_and_names_the_receivers() {
1822 let (tx, mut rx) = mpsc::unbounded_channel();
1823 let state = ServerState {
1824 session: Some(7),
1825 ..Default::default()
1826 };
1827 let handle = Handle {
1828 tx,
1829 shared: Arc::new(Shared {
1830 state: Mutex::new(state),
1831 shutdown: Notify::new(),
1832 closing: AtomicBool::new(false),
1833 pong: watch::Sender::new(0),
1834 reason: Mutex::new(None),
1835 connected: AtomicBool::new(true),
1836 ping: Mutex::default(),
1837 tap: None,
1838 low_expired: AtomicU64::new(0),
1839 tokens: Mutex::default(),
1840 }),
1841 };
1842 assert!(matches!(
1843 handle.send_plugin_data(&[3], "id", vec![1]),
1844 Err(CommandError::Unsupported { .. })
1845 ));
1846 handle.shared.state().server.version = Some(MumbleVersion::new(1, 4, 0));
1847 handle
1848 .send_plugin_data(&[3, 4], "mumcord.test", b"{}".to_vec())
1849 .expect("queued");
1850 let Ok(Outgoing::Frame(frame)) = rx.try_recv() else {
1851 panic!("expected a frame");
1852 };
1853 assert_eq!(frame.kind, MessageType::PluginDataTransmission as u16);
1854 let m = proto::PluginDataTransmission::decode(frame.payload).expect("decode");
1855 assert_eq!(m.sender_session, Some(7));
1856 assert_eq!(m.receiver_sessions, vec![3, 4]);
1857 assert_eq!(m.data_id.as_deref(), Some("mumcord.test"));
1858 assert_eq!(m.data.as_deref(), Some(&b"{}"[..]));
1859 }
1860}
1861
1862#[cfg(test)]
1863mod drain_tests {
1864 use tokio::io::{AsyncReadExt, DuplexStream};
1865 use tokio::task::JoinHandle;
1866
1867 use super::*;
1868
1869 fn shared() -> Arc<Shared> {
1870 Arc::new(Shared {
1871 state: Mutex::new(ServerState::default()),
1872 shutdown: Notify::new(),
1873 closing: AtomicBool::new(false),
1874 pong: watch::Sender::new(0),
1875 reason: Mutex::new(None),
1876 connected: AtomicBool::new(true),
1877 ping: Mutex::default(),
1878 tap: None,
1879 low_expired: AtomicU64::new(0),
1880 tokens: Mutex::default(),
1881 })
1882 }
1883
1884 fn text(i: u32) -> Frame {
1885 let m = proto::TextMessage {
1886 message: format!("message {i}"),
1887 ..Default::default()
1888 };
1889 Frame::from_message(MessageType::TextMessage, &m)
1890 }
1891
1892 fn server(
1895 mut stream: DuplexStream,
1896 shared: Arc<Shared>,
1897 answer: bool,
1898 ) -> JoinHandle<Vec<String>> {
1899 tokio::spawn(async move {
1900 let mut buf = BytesMut::new();
1901 let mut texts = Vec::new();
1902 while stream.read_buf(&mut buf).await.expect("read") > 0 {
1903 while let Some(frame) = framing::decode(&mut buf).expect("frame") {
1904 if frame.kind == MessageType::TextMessage as u16 {
1905 let m = proto::TextMessage::decode(frame.payload).expect("text");
1906 texts.push(m.message);
1907 } else if frame.kind == MessageType::PluginDataTransmission as u16 {
1908 let m = proto::PluginDataTransmission::decode(frame.payload)
1909 .expect("plugin data");
1910 texts.push(m.data_id.expect("data id"));
1911 } else if frame.kind == MessageType::Ping as u16 && answer {
1912 let m = proto::Ping::decode(frame.payload).expect("ping");
1913 shared.pong.send_replace(m.timestamp.expect("timestamp"));
1914 }
1915 }
1916 }
1917 texts
1918 })
1919 }
1920
1921 fn start(answer: bool) -> (Handle, JoinHandle<Vec<String>>) {
1922 start_with(answer, shared())
1923 }
1924
1925 fn start_with(answer: bool, shared: Arc<Shared>) -> (Handle, JoinHandle<Vec<String>>) {
1926 let (client, server_side) = tokio::io::duplex(64 * 1024);
1927 let (tx, rx) = mpsc::unbounded_channel();
1928 tokio::spawn(write_loop(client, rx, shared.clone()));
1929 let server = server(server_side, shared.clone(), answer);
1930 (Handle { tx, shared }, server)
1931 }
1932
1933 fn expected(count: u32) -> Vec<String> {
1934 (0..count).map(|i| format!("message {i}")).collect()
1935 }
1936
1937 #[tokio::test(start_paused = true)]
1938 async fn disconnect_sends_the_queue_beyond_the_burst_first() {
1939 let (handle, server) = start(true);
1940 let started = tokio::time::Instant::now();
1941 for i in 0..5 {
1942 handle.send(text(i)).expect("queued");
1943 }
1944 handle.disconnect();
1945 assert_eq!(handle.send(text(9)), Err(CommandError::NotConnected));
1946 handle.closed().await;
1947 assert_eq!(server.await.expect("server"), expected(5));
1948 assert!(started.elapsed() >= Duration::from_millis(1200));
1950 assert_eq!(
1951 *handle.shared.reason.lock().expect("reason"),
1952 Some(DisconnectReason::Requested)
1953 );
1954 }
1955
1956 #[tokio::test(start_paused = true)]
1957 async fn disconnect_drops_what_does_not_fit_in_the_timeout() {
1958 let (handle, server) = start(true);
1959 let started = tokio::time::Instant::now();
1960 for i in 0..8 {
1961 handle.send(text(i)).expect("queued");
1962 }
1963 handle.disconnect();
1964 handle.closed().await;
1965 assert_eq!(server.await.expect("server"), expected(5));
1967 assert_eq!(started.elapsed(), DRAIN_TIMEOUT);
1968 }
1969
1970 #[tokio::test(start_paused = true)]
1971 async fn disconnect_waits_for_the_server_to_confirm() {
1972 let (handle, server) = start(false);
1973 let started = tokio::time::Instant::now();
1974 handle.send(text(0)).expect("queued");
1975 handle.disconnect();
1976 handle.closed().await;
1977 assert_eq!(server.await.expect("server"), expected(1));
1978 assert_eq!(started.elapsed(), DRAIN_TIMEOUT);
1979 }
1980
1981 #[tokio::test(start_paused = true)]
1982 async fn ping_returns_the_round_trip_of_the_answer() {
1983 let (handle, server) = start(true);
1984 assert!(handle.ping().await.is_some());
1985 handle.disconnect();
1987 assert_eq!(server.await.expect("server"), Vec::<String>::new());
1988 }
1989
1990 #[tokio::test(start_paused = true)]
1991 async fn ping_gives_up_when_the_server_never_answers() {
1992 let (handle, _server) = start(false);
1993 let waited = tokio::time::timeout(Duration::from_secs(1), handle.ping()).await;
1994 assert!(waited.is_err(), "no answer means no result");
1995 }
1996
1997 #[tokio::test(start_paused = true)]
1998 async fn ping_gives_up_when_the_connection_ends() {
1999 let (handle, server) = start(true);
2000 drop(server);
2001 let _ = handle.ping().await;
2002 }
2003
2004 #[tokio::test(start_paused = true)]
2005 async fn flush_completes_once_the_queue_is_sent_and_confirmed() {
2006 let (handle, server) = start(true);
2007 let started = tokio::time::Instant::now();
2008 for i in 0..6 {
2009 handle.send(text(i)).expect("queued");
2010 }
2011 handle.flush().await.expect("flushed");
2012 assert_eq!(started.elapsed(), Duration::from_millis(2400));
2014 handle.disconnect();
2015 assert_eq!(server.await.expect("server"), expected(6));
2016 }
2017
2018 #[derive(Default)]
2019 struct Recorder(Mutex<Vec<u16>>);
2020
2021 impl PacketTap for Recorder {
2022 fn enabled(&self) -> bool {
2023 true
2024 }
2025
2026 fn on_frame(&self, frame: TappedFrame) {
2027 assert_eq!(frame.direction, TapDirection::Sent);
2028 self.0.lock().expect("recorder").push(frame.kind);
2029 }
2030 }
2031
2032 #[tokio::test(start_paused = true)]
2033 async fn frames_sent_while_draining_reach_the_tap() {
2034 let recorder = Arc::new(Recorder::default());
2035 let mut shared = shared();
2036 Arc::get_mut(&mut shared).expect("unshared").tap = Some(recorder.clone());
2037 let (handle, server) = start_with(true, shared);
2038 for i in 0..6 {
2039 handle.send(text(i)).expect("queued");
2040 }
2041 handle.disconnect();
2042 handle.closed().await;
2043 assert_eq!(server.await.expect("server"), expected(5));
2044 let texts = recorder
2045 .0
2046 .lock()
2047 .expect("recorder")
2048 .iter()
2049 .filter(|kind| **kind == MessageType::TextMessage as u16)
2050 .count();
2051 assert_eq!(texts, 5);
2052 }
2053
2054 fn start_plugin_data() -> (Handle, JoinHandle<Vec<String>>) {
2055 let shared = shared();
2056 shared.state().server.version = Some(MumbleVersion::new(1, 4, 0));
2057 start_with(true, shared)
2058 }
2059
2060 fn send_core(handle: &Handle, ids: std::ops::Range<u32>) {
2061 for i in ids {
2062 handle
2063 .send_plugin_data(&[2], &format!("core {i}"), vec![0])
2064 .expect("queued");
2065 }
2066 }
2067
2068 fn send_low(handle: &Handle, i: u32) -> PluginTicket {
2069 handle
2070 .send_plugin_data_low(&[2], &format!("low {i}"), vec![0])
2071 .expect("queued")
2072 }
2073
2074 #[tokio::test(start_paused = true)]
2075 async fn core_plugin_data_overtakes_low_priority_data() {
2076 let (handle, server) = start_plugin_data();
2077 send_core(&handle, 0..12);
2079 let tickets: Vec<_> = (0..3).map(|i| send_low(&handle, i)).collect();
2080 send_core(&handle, 12..15);
2081 for ticket in tickets {
2082 assert_eq!(ticket.outcome().await, LowOutcome::Sent);
2083 }
2084 handle.disconnect();
2085 let mut want: Vec<_> = (0..15).map(|i| format!("core {i}")).collect();
2086 want.extend((0..3).map(|i| format!("low {i}")));
2087 assert_eq!(server.await.expect("server"), want);
2088 assert_eq!(handle.plugin_data_expired(), 0);
2089 }
2090
2091 #[tokio::test(start_paused = true)]
2092 async fn low_priority_data_goes_at_once_when_nothing_else_waits() {
2093 let (handle, server) = start_plugin_data();
2094 let started = tokio::time::Instant::now();
2095 assert_eq!(send_low(&handle, 0).outcome().await, LowOutcome::Sent);
2096 assert_eq!(started.elapsed(), Duration::ZERO);
2097 handle.disconnect();
2098 assert_eq!(server.await.expect("server"), ["low 0"]);
2099 }
2100
2101 #[tokio::test(start_paused = true)]
2102 async fn low_priority_data_expires_after_ten_seconds_behind_core_data() {
2103 let (handle, server) = start_plugin_data();
2104 let started = tokio::time::Instant::now();
2105 send_core(&handle, 0..52);
2107 let ticket = send_low(&handle, 0);
2108 assert_eq!(ticket.outcome().await, LowOutcome::Expired);
2109 let waited = started.elapsed();
2110 assert!(waited > LOW_PRIORITY_MAX_WAIT, "{waited:?}");
2111 assert!(
2112 waited <= LOW_PRIORITY_MAX_WAIT + Duration::from_millis(300),
2113 "{waited:?}"
2114 );
2115 assert_eq!(handle.plugin_data_expired(), 1);
2116 handle.close();
2117 let sent = server.await.expect("server");
2118 assert!(!sent.iter().any(|id| id.starts_with("low")), "{sent:?}");
2119 }
2120
2121 #[tokio::test(start_paused = true)]
2122 async fn queued_low_priority_data_is_reported_when_the_connection_ends() {
2123 let (handle, server) = start_plugin_data();
2124 send_core(&handle, 0..12);
2125 let ticket = send_low(&handle, 0);
2126 handle.close();
2127 assert_eq!(ticket.outcome().await, LowOutcome::Disconnected);
2128 let sent = server.await.expect("server");
2129 assert!(!sent.iter().any(|id| id.starts_with("low")), "{sent:?}");
2130 assert_eq!(handle.plugin_data_expired(), 0);
2131 }
2132
2133 #[test]
2134 fn low_priority_plugin_data_needs_server_support() {
2135 let (tx, _rx) = mpsc::unbounded_channel();
2136 let handle = Handle {
2137 tx,
2138 shared: shared(),
2139 };
2140 assert!(matches!(
2141 handle.send_plugin_data_low(&[3], "id", vec![1]),
2142 Err(CommandError::Unsupported { .. })
2143 ));
2144 }
2145
2146 #[tokio::test(start_paused = true)]
2147 async fn abort_drops_the_queue() {
2148 let (handle, server) = start(true);
2149 for i in 0..6 {
2150 handle.send(text(i)).expect("queued");
2151 }
2152 handle.close();
2153 handle.closed().await;
2154 let sent = server.await.expect("server");
2156 assert!(sent.len() <= 4, "{sent:?}");
2157 }
2158}
2159
2160#[cfg(test)]
2161mod handshake_tests {
2162 use super::*;
2163
2164 #[test]
2165 fn version_precedes_authenticate_with_both_encodings() {
2166 let [version, auth] = handshake_frames("alice".to_owned(), None, vec!["secret".into()]);
2167 assert_eq!(version.message_type(), Some(MessageType::Version));
2168 assert_eq!(auth.message_type(), Some(MessageType::Authenticate));
2169
2170 let v = proto::Version::decode(version.payload).expect("version");
2171 assert_eq!(v.version_v2, Some(0x0001_0005_0000_0000));
2173 assert_eq!(v.version_v1, Some(0x0001_0500));
2174 assert!(
2175 MumbleVersion::from_wire(v.version_v1, v.version_v2)
2176 .is_some_and(|v| v.supports_listening())
2177 );
2178 assert!(v.release.is_some_and(|r| r.starts_with("Mumcord ")));
2179
2180 let a = proto::Authenticate::decode(auth.payload).expect("authenticate");
2181 assert_eq!(a.username.as_deref(), Some("alice"));
2182 assert_eq!(a.opus, Some(true));
2183 assert_eq!(a.tokens, ["secret"]);
2184 }
2185
2186 #[test]
2187 fn the_app_sets_the_release_name_servers_show() {
2188 set_client_release("Mumcord dev~a1b2c3d");
2190 set_client_release("Mumcord ignored");
2191 let [version, _] = handshake_frames("alice".to_owned(), None, Vec::new());
2192 let v = proto::Version::decode(version.payload).expect("version");
2193 assert_eq!(v.release.as_deref(), Some("Mumcord dev~a1b2c3d"));
2194 }
2195
2196 #[test]
2197 fn the_handle_remembers_the_tokens_it_sent() {
2198 let (tx, _rx) = mpsc::unbounded_channel();
2199 let handle = Handle {
2200 tx,
2201 shared: Arc::new(Shared {
2202 state: Mutex::new(ServerState::default()),
2203 shutdown: Notify::new(),
2204 closing: AtomicBool::new(false),
2205 pong: watch::Sender::new(0),
2206 reason: Mutex::new(None),
2207 connected: AtomicBool::new(true),
2208 ping: Mutex::default(),
2209 tap: None,
2210 low_expired: AtomicU64::new(0),
2211 tokens: Mutex::new(vec!["login".into()]),
2212 }),
2213 };
2214 assert_eq!(handle.access_tokens(), ["login"]);
2215 handle
2216 .set_access_tokens(vec!["one".into(), "two".into()])
2217 .expect("queued");
2218 assert_eq!(handle.access_tokens(), ["one", "two"]);
2219 }
2220
2221 #[test]
2222 fn access_tokens_resend_carries_only_the_tokens() {
2223 let frame = Frame::from_message(
2224 MessageType::Authenticate,
2225 &access_tokens_message(vec!["one".into(), "two".into()]),
2226 );
2227 let a = proto::Authenticate::decode(frame.payload).expect("authenticate");
2228 assert_eq!(a.tokens, ["one", "two"]);
2229 assert_eq!(a.username, None);
2230 assert_eq!(a.password, None);
2231 }
2232}