1use std::collections::HashMap;
21use std::net::{IpAddr, SocketAddr};
22use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
23use std::sync::{Arc, Mutex, MutexGuard, PoisonError, RwLock, RwLockReadGuard, RwLockWriteGuard};
24use std::time::{Duration, Instant};
25
26use mumble_server_runtime_crypto::CryptState;
27use mumble_server_runtime_shard::{AudioRouting, ConnectionId, OutboundQueue, SessionId, ShardId};
28use tokio::sync::watch;
29
30use crate::limits::{TextBudget, VoiceBudget};
31
32#[derive(Debug, Clone)]
40pub struct ShardPlane {
41 pub shard: ShardId,
42 pub routing: watch::Receiver<Arc<AudioRouting>>,
43}
44
45pub struct Peer {
47 connection: ConnectionId,
48 session: SessionId,
50 host: IpAddr,
53 crypt: Mutex<CryptState>,
54 address: Mutex<Option<SocketAddr>>,
56 udp_mode: AtomicBool,
59 budget: Mutex<VoiceBudget>,
60 text: Mutex<TextBudget>,
63 cursor: Arc<AtomicU64>,
66 queue: Arc<OutboundQueue>,
67 plane: RwLock<ShardPlane>,
68 online_since: Instant,
71 reported: Mutex<ClientReport>,
73}
74
75#[derive(Debug, Clone, Copy, Default, PartialEq)]
87pub struct ClientReport {
88 pub good: u32,
89 pub late: u32,
90 pub lost: u32,
91 pub resync: u32,
92 pub udp_packets: u32,
93 pub tcp_packets: u32,
94 pub udp_ping_avg: f32,
95 pub udp_ping_var: f32,
96 pub tcp_ping_avg: f32,
97 pub tcp_ping_var: f32,
98}
99
100impl std::fmt::Debug for Peer {
103 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
104 formatter
105 .debug_struct("Peer")
106 .field("connection", &self.connection)
107 .field("session", &self.session)
108 .field("host", &self.host)
109 .field("address", &self.proven_address())
110 .field("udp_mode", &self.udp_mode.load(Ordering::Relaxed))
111 .field("shard", &self.shard())
112 .field("cursor", &self.cursor())
113 .finish_non_exhaustive()
114 }
115}
116
117impl Peer {
118 #[must_use]
119 pub fn new(
120 connection: ConnectionId,
121 session: SessionId,
122 host: IpAddr,
123 crypt: CryptState,
124 queue: Arc<OutboundQueue>,
125 plane: ShardPlane,
126 now: Instant,
127 ) -> Peer {
128 Peer {
129 connection,
130 session,
131 host,
132 crypt: Mutex::new(crypt),
133 address: Mutex::new(None),
134 udp_mode: AtomicBool::new(true),
135 budget: Mutex::new(VoiceBudget::new(now)),
136 text: Mutex::new(TextBudget::new(now)),
137 cursor: Arc::new(AtomicU64::new(0)),
140 queue,
141 plane: RwLock::new(plane),
142 online_since: now,
143 reported: Mutex::new(ClientReport::default()),
144 }
145 }
146
147 #[must_use]
149 pub fn online_since(&self) -> Instant {
150 self.online_since
151 }
152
153 pub fn record_report(&self, report: ClientReport) {
155 *lock(&self.reported) = report;
156 }
157
158 #[must_use]
159 pub fn reported(&self) -> ClientReport {
160 *lock(&self.reported)
161 }
162
163 #[must_use]
166 pub fn traffic(&self, now: Instant) -> (u32, Duration) {
167 let mut budget = lock(&self.budget);
168 (budget.bandwidth(now), budget.idle(now))
169 }
170
171 pub fn record_activity(&self, now: Instant) {
173 lock(&self.budget).touch(now);
174 }
175
176 #[must_use]
177 pub fn connection(&self) -> ConnectionId {
178 self.connection
179 }
180
181 #[must_use]
182 pub fn session(&self) -> SessionId {
183 self.session
184 }
185
186 #[must_use]
187 pub fn host(&self) -> IpAddr {
188 self.host
189 }
190
191 #[must_use]
192 pub fn queue(&self) -> Arc<OutboundQueue> {
193 Arc::clone(&self.queue)
194 }
195
196 #[must_use]
197 pub fn cursor_cell(&self) -> Arc<AtomicU64> {
198 Arc::clone(&self.cursor)
199 }
200
201 #[must_use]
202 pub fn cursor(&self) -> u64 {
203 self.cursor.load(Ordering::Relaxed)
204 }
205
206 #[must_use]
207 pub fn shard(&self) -> ShardId {
208 read(&self.plane).shard
209 }
210
211 #[must_use]
213 pub fn routing(&self) -> Arc<AudioRouting> {
214 let plane = read(&self.plane);
215 Arc::clone(&plane.routing.borrow())
216 }
217
218 pub fn move_to(&self, plane: ShardPlane) {
220 *write(&self.plane) = plane;
221 }
222
223 #[must_use]
232 pub fn decrypt(&self, datagram: &[u8]) -> Option<Vec<u8>> {
233 lock(&self.crypt).decrypt(datagram)
234 }
235
236 #[must_use]
237 pub fn encrypt(&self, plaintext: &[u8]) -> Option<Vec<u8>> {
238 lock(&self.crypt).encrypt(plaintext)
239 }
240
241 #[must_use]
243 pub fn crypt_counters(&self) -> (u32, u32, u32) {
244 let state = lock(&self.crypt);
245 (state.good, state.late, state.lost)
246 }
247
248 #[must_use]
250 pub fn allow_voice(&self, now: Instant, bytes: usize) -> bool {
251 lock(&self.budget).allow(now, bytes)
252 }
253
254 #[must_use]
256 pub fn allow_text(&self, now: Instant) -> bool {
257 lock(&self.text).allow(now)
258 }
259
260 pub fn set_udp_mode(&self, on: bool) {
266 self.udp_mode.store(on, Ordering::Relaxed);
267 }
268
269 #[must_use]
276 pub fn destination(&self) -> Option<SocketAddr> {
277 if !self.udp_mode.load(Ordering::Relaxed) {
278 return None;
279 }
280 *lock(&self.address)
281 }
282
283 #[must_use]
285 pub fn proven_address(&self) -> Option<SocketAddr> {
286 *lock(&self.address)
287 }
288
289 fn bind(&self, address: SocketAddr) {
290 *lock(&self.address) = Some(address);
291 }
292
293 pub fn close(&self) {
295 self.queue.mark_fatal();
296 }
297}
298
299#[derive(Debug, Default)]
306pub struct Peers {
307 by_connection: RwLock<HashMap<ConnectionId, Arc<Peer>>>,
308 by_session: RwLock<HashMap<SessionId, Arc<Peer>>>,
309 by_address: RwLock<HashMap<SocketAddr, Arc<Peer>>>,
310 by_host: RwLock<HashMap<IpAddr, Vec<Arc<Peer>>>>,
314}
315
316impl Peers {
317 #[must_use]
318 pub fn new() -> Peers {
319 Peers::default()
320 }
321
322 pub fn insert(&self, peer: Arc<Peer>) {
323 write(&self.by_connection).insert(peer.connection(), Arc::clone(&peer));
324 write(&self.by_session).insert(peer.session(), Arc::clone(&peer));
325 write(&self.by_host)
326 .entry(peer.host())
327 .or_default()
328 .push(peer);
329 }
330
331 pub fn remove(&self, connection: ConnectionId) -> Option<Arc<Peer>> {
335 let peer = write(&self.by_connection).remove(&connection)?;
336 write(&self.by_session).remove(&peer.session());
337 if let Some(address) = peer.proven_address() {
338 write(&self.by_address).remove(&address);
339 }
340 let mut hosts = write(&self.by_host);
341 if let Some(list) = hosts.get_mut(&peer.host()) {
342 list.retain(|other| other.connection() != connection);
343 if list.is_empty() {
344 hosts.remove(&peer.host());
345 }
346 }
347 Some(peer)
348 }
349
350 #[must_use]
351 pub fn by_connection(&self, connection: ConnectionId) -> Option<Arc<Peer>> {
352 read(&self.by_connection).get(&connection).cloned()
353 }
354
355 #[must_use]
356 pub fn by_session(&self, session: SessionId) -> Option<Arc<Peer>> {
357 read(&self.by_session).get(&session).cloned()
358 }
359
360 #[must_use]
361 pub fn by_address(&self, address: SocketAddr) -> Option<Arc<Peer>> {
362 read(&self.by_address).get(&address).cloned()
363 }
364
365 #[must_use]
367 pub fn candidates(&self, host: IpAddr) -> Vec<Arc<Peer>> {
368 read(&self.by_host).get(&host).cloned().unwrap_or_default()
369 }
370
371 pub fn bind(&self, address: SocketAddr, peer: &Arc<Peer>) {
377 if let Some(previous) = peer.proven_address()
378 && previous != address
379 {
380 write(&self.by_address).remove(&previous);
381 }
382 peer.bind(address);
383 write(&self.by_address).insert(address, Arc::clone(peer));
384 }
385
386 #[must_use]
388 pub fn on_shard(&self, shard: ShardId) -> Vec<Arc<Peer>> {
389 read(&self.by_connection)
390 .values()
391 .filter(|peer| peer.shard() == shard)
392 .cloned()
393 .collect()
394 }
395
396 #[must_use]
397 pub fn len(&self) -> usize {
398 read(&self.by_connection).len()
399 }
400
401 #[must_use]
402 pub fn is_empty(&self) -> bool {
403 self.len() == 0
404 }
405}
406
407fn lock<T>(mutex: &Mutex<T>) -> MutexGuard<'_, T> {
412 mutex.lock().unwrap_or_else(PoisonError::into_inner)
413}
414
415fn read<T>(lock: &RwLock<T>) -> RwLockReadGuard<'_, T> {
416 lock.read().unwrap_or_else(PoisonError::into_inner)
417}
418
419fn write<T>(lock: &RwLock<T>) -> RwLockWriteGuard<'_, T> {
420 lock.write().unwrap_or_else(PoisonError::into_inner)
421}
422
423#[cfg(test)]
424mod tests {
425 #![allow(clippy::expect_used)]
426
427 use super::*;
428 use mumble_server_runtime_shard::AudioRouting;
429
430 fn plane() -> ShardPlane {
431 let (_sender, routing) = watch::channel(Arc::new(AudioRouting::default()));
432 ShardPlane {
433 shard: ShardId(1),
434 routing,
435 }
436 }
437
438 fn peer(connection: u64, session: u32, host: [u8; 4]) -> Arc<Peer> {
439 let (queue, _writer) = OutboundQueue::new();
442 Arc::new(Peer::new(
443 ConnectionId(connection),
444 SessionId(session),
445 IpAddr::from(host),
446 CryptState::new(&[0u8; 16], &[0u8; 16], &[1u8; 16]),
447 Arc::new(queue),
448 plane(),
449 Instant::now(),
450 ))
451 }
452
453 fn address(port: u16) -> SocketAddr {
454 SocketAddr::from(([127, 0, 0, 1], port))
455 }
456
457 #[test]
458 fn a_removed_connection_releases_its_address() {
459 let peers = Peers::new();
460 let alice = peer(1, 10, [127, 0, 0, 1]);
461 peers.insert(Arc::clone(&alice));
462 peers.bind(address(5000), &alice);
463
464 assert!(peers.by_address(address(5000)).is_some());
465 peers.remove(ConnectionId(1));
466
467 assert!(
468 peers.by_address(address(5000)).is_none(),
469 "an address left bound would deliver to whoever inherits the socket"
470 );
471 assert!(peers.by_session(SessionId(10)).is_none());
472 assert!(peers.candidates(IpAddr::from([127, 0, 0, 1])).is_empty());
473 }
474
475 #[test]
476 fn rebinding_an_address_drops_the_previous_one() {
477 let peers = Peers::new();
478 let alice = peer(1, 10, [127, 0, 0, 1]);
479 peers.insert(Arc::clone(&alice));
480
481 peers.bind(address(5000), &alice);
482 peers.bind(address(5001), &alice);
483
484 assert!(peers.by_address(address(5000)).is_none());
485 assert_eq!(
486 peers
487 .by_address(address(5001))
488 .map(|found| found.connection()),
489 Some(ConnectionId(1))
490 );
491 }
492
493 #[test]
494 fn the_host_index_narrows_the_cold_path() {
495 let peers = Peers::new();
496 peers.insert(peer(1, 10, [10, 0, 0, 1]));
497 peers.insert(peer(2, 20, [10, 0, 0, 1]));
498 peers.insert(peer(3, 30, [10, 0, 0, 2]));
499
500 assert_eq!(peers.candidates(IpAddr::from([10, 0, 0, 1])).len(), 2);
501 assert_eq!(peers.candidates(IpAddr::from([10, 0, 0, 2])).len(), 1);
502 assert!(peers.candidates(IpAddr::from([10, 0, 0, 3])).is_empty());
503 }
504
505 #[test]
506 fn a_peer_on_the_tunnel_has_no_udp_destination() {
507 let peers = Peers::new();
508 let alice = peer(1, 10, [127, 0, 0, 1]);
509 peers.insert(Arc::clone(&alice));
510 peers.bind(address(5000), &alice);
511 assert_eq!(alice.destination(), Some(address(5000)));
512
513 alice.set_udp_mode(false);
514 assert_eq!(
515 alice.destination(),
516 None,
517 "a client that tunnelled its own audio must be answered on the tunnel"
518 );
519 assert_eq!(
520 alice.proven_address(),
521 Some(address(5000)),
522 "falling back must not forget the proof, only stop using it"
523 );
524 }
525}