Skip to main content

tuwunel_router/
serve.rs

1mod plain;
2#[cfg(test)]
3mod tests;
4#[cfg(feature = "direct_tls")]
5mod tls;
6mod unix;
7
8#[cfg(unix)]
9use std::{borrow::Cow, os::unix::net::UnixListener};
10use std::{
11	iter::once,
12	net::{Ipv4Addr, SocketAddr, TcpListener},
13	path::Path,
14	sync::{Arc, atomic::Ordering},
15};
16
17use tokio::task::JoinSet;
18use tuwunel_core::{Err, Result, debug_info, error, info};
19#[cfg(unix)]
20use tuwunel_core::{itertools::Itertools, utils::sys::is_ipv6_only};
21use tuwunel_service::Services;
22
23use super::layers;
24use crate::handle::ServerHandle;
25
26/// Serve clients
27pub(super) async fn serve(services: Arc<Services>, handle: ServerHandle) -> Result {
28	let server = &services.server;
29	let config = &server.config;
30
31	let (app, _guard) = layers::build(&services)?;
32
33	let mut join_set = JoinSet::new();
34
35	let socket_path = &config.unix_socket_path;
36
37	#[cfg(unix)]
38	let (passed_tcp_listeners, passed_unix_listeners) = systemd_listeners()?;
39	#[cfg(not(unix))]
40	let passed_tcp_listeners: Vec<TcpListener> = Vec::new();
41
42	let addrs = config.get_bind_addrs();
43
44	#[cfg(unix)]
45	let mut listening = passed_addrs(&passed_tcp_listeners)?;
46	#[cfg(not(unix))]
47	let mut listening: Vec<SocketAddr> = Vec::new();
48
49	#[cfg(unix)]
50	let socket_path = unpassed_path(socket_path.as_deref(), &passed_unix_listeners)?;
51	#[cfg(not(unix))]
52	let socket_path = socket_path.as_deref();
53
54	let defaulted = config.is_address_defaulted();
55	let tcp_listeners = bind_addrs(&addrs, &mut listening, defaulted)?;
56
57	#[cfg(unix)]
58	let log_addrs = make_log_addrs(
59		&tcp_listeners,
60		socket_path,
61		&passed_tcp_listeners,
62		&passed_unix_listeners,
63	)?;
64	#[cfg(not(unix))]
65	let log_addrs = make_log_addrs(&tcp_listeners, socket_path, &passed_tcp_listeners)?;
66
67	let mut futures = vec![];
68
69	#[cfg(unix)]
70	{
71		let socket_perms = config.get_unix_socket_perms()?;
72
73		let unix_futures = unix::serve(
74			&app,
75			&handle.handle_unix,
76			passed_unix_listeners.into_iter(),
77			socket_path,
78			socket_perms,
79		)
80		.await?;
81
82		futures.extend(unix_futures);
83	};
84
85	#[cfg_attr(
86		not(feature = "direct_tls"),
87		expect(clippy::redundant_else, unused_variables)
88	)]
89	if let Some((cert, key)) = config.tls.get_tls_cert_key() {
90		#[cfg(feature = "direct_tls")]
91		{
92			services.globals.init_rustls_provider()?;
93
94			let listeners = tcp_listeners
95				.into_iter()
96				.chain(passed_tcp_listeners);
97
98			let tls_futures = tls::serve(
99				&app,
100				&handle.handle_ip,
101				cert,
102				key,
103				config.tls.dual_protocol,
104				listeners,
105			)
106			.await?;
107
108			futures.extend(tls_futures);
109		}
110
111		#[cfg(not(feature = "direct_tls"))]
112		return tuwunel_core::Err!(Config(
113			"tls",
114			"tuwunel was not built with direct TLS support (\"direct_tls\")"
115		));
116	} else {
117		let listeners = tcp_listeners
118			.into_iter()
119			.chain(passed_tcp_listeners);
120
121		let plain_futures = plain::serve(&app, &handle.handle_ip, listeners)?;
122
123		futures.extend(plain_futures);
124	}
125
126	for future in futures {
127		join_set.spawn_on(future, server.runtime());
128	}
129
130	if join_set.is_empty() {
131		return Err!("at least one listener should be installed");
132	}
133
134	info!("Listening on {log_addrs:?}");
135
136	join_set
137		.join_all()
138		.await
139		.into_iter()
140		.filter_map(Result::err)
141		.for_each(|e| error!("Listener stopped: {e}"));
142
143	let handle_active = server
144		.metrics
145		.requests_handle_active
146		.load(Ordering::Acquire);
147
148	debug_info!(
149		handle_finished = server
150			.metrics
151			.requests_handle_finished
152			.load(Ordering::Acquire),
153		panics = server
154			.metrics
155			.requests_panic
156			.load(Ordering::Acquire),
157		handle_active,
158		"Stopped listening on {log_addrs:?}",
159	);
160
161	debug_assert_eq!(0, handle_active, "active request handles still pending");
162
163	Ok(())
164}
165
166/// Addresses the service manager already listens on for us.
167#[cfg(unix)]
168fn passed_addrs(listeners: &[TcpListener]) -> Result<Vec<SocketAddr>> {
169	listeners
170		.iter()
171		.map(listening_addrs)
172		.flatten_ok()
173		.try_collect()
174}
175
176/// Addresses a listener answers on. An unspecified address stands for every
177/// address of its family, and a dual-stack listener stands for the IPv4
178/// wildcard as well.
179fn listening_addrs(listener: &TcpListener) -> Result<impl Iterator<Item = SocketAddr> + use<>> {
180	let addr = listener.local_addr()?;
181
182	#[cfg(unix)]
183	let dual_stack = addr.is_ipv6() && addr.ip().is_unspecified() && !is_ipv6_only(listener)?;
184	#[cfg(not(unix))]
185	let dual_stack = false;
186
187	let wildcard = dual_stack.then(|| SocketAddr::new(Ipv4Addr::UNSPECIFIED.into(), addr.port()));
188
189	Ok(wildcard.into_iter().chain(once(addr)))
190}
191
192/// Whether a listener on one address answers for another, which is the case
193/// when they name the same socket or the listener holds the wider wildcard.
194fn covers(listening: &SocketAddr, addr: &SocketAddr) -> bool {
195	listening.port() == addr.port()
196		&& (listening.ip() == addr.ip()
197			|| (listening.ip().is_unspecified() && listening.is_ipv6() == addr.is_ipv6()))
198}
199
200/// Drops the configured unix socket when a passed listener already binds that
201/// path, which would otherwise be unlinked and replaced on the way up.
202#[cfg(unix)]
203fn unpassed_path<'a>(
204	path: Option<&'a Path>,
205	listeners: &[UnixListener],
206) -> Result<Option<&'a Path>> {
207	let Some(configured) = path else {
208		return Ok(None);
209	};
210
211	let covered = listeners
212		.iter()
213		.map(UnixListener::local_addr)
214		.process_results(|mut addrs| {
215			addrs.any(|addr| {
216				addr.as_pathname()
217					.is_some_and(|passed| same_path(configured, passed))
218			})
219		})?;
220
221	if covered {
222		info!(?configured, "Not binding: the service manager passed a listener for it.");
223	}
224
225	Ok(path.filter(|_| !covered))
226}
227
228/// Paths naming one socket, however the configuration spelled it.
229#[cfg(unix)]
230fn same_path(configured: &Path, passed: &Path) -> bool {
231	configured == passed
232		|| matches!(
233			(configured.canonicalize(), passed.canonicalize()),
234			(Ok(configured), Ok(passed)) if configured == passed
235		)
236}
237
238/// Binds the addresses nothing answers for yet, extending `listening` as it
239/// goes so a wildcard entry covers the ones behind it.
240///
241/// Failures surface here, before any listener is served. A defaulted address
242/// is skipped instead, since the built-in pair only guesses which loopback
243/// families the host has.
244fn bind_addrs(
245	addrs: &[SocketAddr],
246	listening: &mut Vec<SocketAddr>,
247	defaulted: bool,
248) -> Result<Vec<TcpListener>> {
249	let mut listeners = Vec::with_capacity(addrs.len());
250
251	// A dual-stack IPv6 socket answers for the IPv4 wildcard too, and the kernel
252	// refuses it once that wildcard is bound, so IPv6 goes first whatever order
253	// the configuration lists.
254	let mut addrs = addrs.to_vec();
255	addrs.sort_by_key(SocketAddr::is_ipv4);
256
257	for addr in &addrs {
258		if listening.iter().any(|other| covers(other, addr)) {
259			info!(%addr, "Not binding: a listener already answers for it.");
260			continue;
261		}
262
263		let listener = match TcpListener::bind(addr) {
264			| Ok(listener) => listener,
265			| Err(e) if defaulted => {
266				error!(%addr, %e, "Not binding: the default address is unavailable.");
267				continue;
268			},
269			| Err(e) => return Err!(Config("address", "Failed to bind {addr}: {e}")),
270		};
271
272		listener.set_nonblocking(true)?;
273		listening.extend(listening_addrs(&listener)?);
274		listeners.push(listener);
275	}
276
277	Ok(listeners)
278}
279
280#[cfg(unix)]
281fn make_log_addrs(
282	tcp_listeners: &[TcpListener],
283	unix_path: Option<&Path>,
284	passed_tcp_listeners: &[TcpListener],
285	passed_unix_listeners: &[UnixListener],
286) -> Result<Vec<String>> {
287	let tcp_log_addrs = tcp_listeners
288		.iter()
289		.map(|listener| Ok(format!("tcp:{}", listener.local_addr()?)));
290
291	let unix_log_addr = unix_path.as_ref().map(|socket_path| {
292		let path = socket_path.to_string_lossy();
293
294		format!("unix:{path}")
295	});
296
297	let passed_tcp_log_addrs = passed_tcp_listeners.iter().map(|listener| {
298		let addr = listener.local_addr()?;
299
300		Ok(format!("passed:tcp:{addr}"))
301	});
302
303	let passed_unix_log_addrs = passed_unix_listeners.iter().map(|listener| {
304		let addr = listener.local_addr()?;
305		let log_path = addr
306			.as_pathname()
307			.map_or(Cow::Borrowed("?"), Path::to_string_lossy);
308
309		Ok(format!("passed:unix:{log_path}"))
310	});
311
312	tcp_log_addrs
313		.chain(unix_log_addr.into_iter().map(Ok))
314		.chain(passed_tcp_log_addrs)
315		.chain(passed_unix_log_addrs)
316		.collect()
317}
318
319#[cfg(all(unix, feature = "systemd", target_os = "linux"))]
320fn systemd_listeners() -> Result<(Vec<TcpListener>, Vec<UnixListener>)> {
321	use std::os::fd::FromRawFd;
322
323	use tuwunel_core::utils::sys::{SocketFamily, get_socket_family};
324
325	let mut tcp = vec![];
326	let mut unix = vec![];
327
328	for fd in sd_notify::listen_fds()? {
329		debug_assert!(fd >= 3, "fdno probably not a listener socket");
330
331		let family = get_socket_family(fd)?;
332
333		match family {
334			| SocketFamily::Inet => {
335				// SAFETY: systemd should already take care of providing
336				// the correct TCP socket, so we just use it via raw fd
337				let listener = unsafe { TcpListener::from_raw_fd(fd) };
338
339				listener.set_nonblocking(true)?;
340
341				tcp.push(listener);
342			},
343			| SocketFamily::Unix => {
344				// SAFETY: systemd should already take care of providing
345				// the correct UNIX socket, so we just use it via raw fd
346				let listener = unsafe { UnixListener::from_raw_fd(fd) };
347
348				listener.set_nonblocking(true)?;
349
350				unix.push(listener);
351			},
352		}
353	}
354
355	Ok((tcp, unix))
356}
357
358#[cfg(all(
359	unix,
360	any(not(feature = "systemd"), not(target_os = "linux"))
361))]
362fn systemd_listeners() -> Result<(Vec<TcpListener>, Vec<UnixListener>)> { Ok((vec![], vec![])) }
363
364#[cfg(not(unix))]
365fn make_log_addrs(
366	tcp_listeners: &[TcpListener],
367	unix_path: Option<&Path>,
368	passed_tcp_listeners: &[TcpListener],
369) -> Result<Vec<String>> {
370	let tcp_log_addrs = tcp_listeners
371		.iter()
372		.map(|listener| Ok(format!("tcp:{}", listener.local_addr()?)));
373
374	let unix_log_addr = unix_path.as_ref().map(|socket_path| {
375		let path = socket_path.to_string_lossy();
376		format!("unix:{path}")
377	});
378
379	let passed_tcp_log_addrs = passed_tcp_listeners.iter().map(|listener| {
380		let addr = listener.local_addr()?;
381		Ok(format!("passed:tcp:{addr}"))
382	});
383
384	tcp_log_addrs
385		.chain(unix_log_addr.into_iter().map(Ok))
386		.chain(passed_tcp_log_addrs)
387		.collect()
388}