Skip to main content

tuwunel_router/serve/
unix.rs

1#![cfg(unix)]
2
3use std::{
4	fs,
5	net::{IpAddr, Ipv4Addr, SocketAddr},
6	os::unix::{self, fs::PermissionsExt, net::UnixListener},
7	path::Path,
8};
9
10use axum::{Extension, Router, extract::ConnectInfo};
11use axum_server::Handle;
12use futures::{FutureExt, future::BoxFuture};
13use tuwunel_core::{Result, warn};
14
15#[tracing::instrument(skip_all, level = "debug")]
16pub(super) async fn serve<'a>(
17	router: &Router,
18	handle: &Handle<unix::net::SocketAddr>,
19	listeners: impl Iterator<Item = UnixListener>,
20	path: Option<&Path>,
21	socket_perms: u32,
22) -> Result<Vec<BoxFuture<'a, Result<(), std::io::Error>>>> {
23	let router = router
24		.clone()
25		.layer(Extension(ConnectInfo(SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), 0))))
26		.into_make_service();
27
28	let mut acceptors = listeners
29		.map(|listener| {
30			Ok(axum_server::from_unix(listener)?
31				.handle(handle.clone())
32				.serve(router.clone())
33				.boxed())
34		})
35		.collect::<Result<Vec<_>>>()?;
36
37	if let Some(path) = path {
38		if path.exists() {
39			warn!("Removing existing UNIX socket {path:?} (unclean shutdown?)...");
40			fs::remove_file(path)?;
41		}
42
43		let unix_listener = UnixListener::bind(path)?;
44		unix_listener.set_nonblocking(true)?;
45
46		let perms = fs::Permissions::from_mode(socket_perms);
47		fs::set_permissions(path, perms)?;
48
49		let bound_acceptor = axum_server::from_unix(unix_listener)?
50			.handle(handle.clone())
51			.serve(router)
52			.inspect({
53				let path = path.to_owned();
54				|_| {
55					if let Err(err) = fs::remove_file(path) {
56						warn!("Failed to remove UNIX socket: {err}");
57					}
58				}
59			})
60			.boxed();
61
62		acceptors.push(bound_acceptor);
63	}
64
65	Ok(acceptors)
66}