mumble_server_runtime_gateway/
serve.rs1use std::sync::Arc;
8
9use anyhow::{Context, Result};
10use tokio::net::{TcpListener, UdpSocket};
11use tokio_rustls::TlsAcceptor;
12
13use crate::config::GatewayConfig;
14use crate::connection;
15use crate::router::ConnectionRouter;
16use crate::runtime::{Runtime, RuntimeHandle};
17use crate::tls::{self, Identity};
18use crate::voice::VoicePlane;
19
20pub struct Gateway {
26 runtime: Runtime,
27 config: Arc<GatewayConfig>,
28 listener: TcpListener,
29 udp: Arc<UdpSocket>,
30 acceptor: TlsAcceptor,
31}
32
33impl Gateway {
34 pub async fn bind(config: GatewayConfig, identity: Identity) -> Result<Gateway> {
40 tls::install_crypto_provider();
41 let acceptor = TlsAcceptor::from(tls::server_config(identity)?);
42 let (listener, udp) = bind_both(config.bind).await?;
43
44 let mut config = config;
45 config.bind = listener.local_addr().context("TCP local address")?;
46
47 Ok(Gateway {
48 runtime: Runtime::start(),
49 config: Arc::new(config),
50 listener,
51 udp: Arc::new(udp),
52 acceptor,
53 })
54 }
55
56 #[must_use]
58 pub fn address(&self) -> std::net::SocketAddr {
59 self.config.bind
60 }
61
62 #[must_use]
63 pub fn runtime(&self) -> RuntimeHandle {
64 self.runtime.handle()
65 }
66
67 pub async fn serve<R: ConnectionRouter>(self, router: R) -> Result<()> {
74 let router = Arc::new(router);
75 let runtime = self.runtime.handle();
76 let voice = Arc::new(VoicePlane::new(
77 Arc::clone(&self.udp),
78 Arc::clone(runtime.peers()),
79 (*self.config).clone(),
80 ));
81
82 let plane = {
85 let voice = Arc::clone(&voice);
86 tokio::spawn(async move {
87 if let Err(error) = voice.run().await {
88 eprintln!("mumble-server-runtime-gateway: the voice plane stopped: {error}");
89 }
90 })
91 };
92
93 let accepting = async {
94 loop {
95 let (tcp, from) = self.listener.accept().await.context("TCP accept")?;
96 let acceptor = self.acceptor.clone();
97 let runtime = runtime.clone();
98 let router = Arc::clone(&router);
99 let voice = Arc::clone(&voice);
100 let udp = Arc::clone(&self.udp);
101 let config = Arc::clone(&self.config);
102
103 tokio::spawn(async move {
107 if let Err(error) =
108 connection::serve(tcp, acceptor, runtime, router, voice, udp, config).await
109 {
110 eprintln!(
111 "mumble-server-runtime-gateway: connection from {from} ended: {error:#}"
112 );
113 }
114 });
115 }
116 };
117
118 let outcome: Result<()> = accepting.await;
119 plane.abort();
120 outcome
121 }
122}
123
124const EPHEMERAL_ATTEMPTS: u32 = 16;
129
130async fn bind_both(address: std::net::SocketAddr) -> Result<(TcpListener, UdpSocket)> {
139 let mut attempts = if address.port() == 0 {
140 EPHEMERAL_ATTEMPTS
141 } else {
142 1
143 };
144
145 loop {
146 attempts = attempts.saturating_sub(1);
147 let listener = TcpListener::bind(address)
148 .await
149 .with_context(|| format!("binding TCP {address}"))?;
150 let bound = listener.local_addr().context("TCP local address")?;
151
152 match UdpSocket::bind(bound).await {
153 Ok(udp) => return Ok((listener, udp)),
154 Err(error) if attempts > 0 => {
155 drop(listener);
158 eprintln!(
159 "mumble-server-runtime-gateway: UDP {bound} was taken ({error}), trying another port"
160 );
161 }
162 Err(error) => {
163 return Err(anyhow::Error::from(error))
164 .with_context(|| format!("binding UDP {bound}"));
165 }
166 }
167 }
168}
169
170pub async fn serve<R: ConnectionRouter>(
181 config: GatewayConfig,
182 identity: Identity,
183 build: impl FnOnce(&RuntimeHandle) -> R,
184) -> Result<()> {
185 let gateway = Gateway::bind(config, identity).await?;
186 let router = build(&gateway.runtime());
187 gateway.serve(router).await
188}