tuwunel_router/serve/
unix.rs1#![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}