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, err, 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
25 .clone()
26 .layer(Extension(ConnectInfo(SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 0))))
27 .into_make_service();
28
29 let mut acceptors = listeners
30 .map(|listener| {
31 Ok(axum_server::from_unix(listener)?
32 .handle(handle.clone())
33 .serve(router.clone())
34 .boxed())
35 })
36 .collect::<Result<Vec<_>>>()?;
37
38 if let Some(path) = path {
39 if path.exists() {
40 warn!("Removing existing UNIX socket {path:?} (unclean shutdown?)...");
41 fs::remove_file(path).map_err(|e| {
42 err!(Config(
43 "unix_socket_path",
44 "Failed to remove stale UNIX socket at {path:?}: {e}",
45 ))
46 })?;
47 }
48
49 let unix_listener = UnixListener::bind(path).map_err(|e| {
50 err!(Config("unix_socket_path", "Failed to bind UNIX socket at {path:?}: {e}",))
51 })?;
52
53 unix_listener.set_nonblocking(true)?;
54
55 let perms = fs::Permissions::from_mode(socket_perms);
56 fs::set_permissions(path, perms).map_err(|e| {
57 err!(Config(
58 "unix_socket_path",
59 "Failed to set permissions {socket_perms:o} on UNIX socket at {path:?}: {e}",
60 ))
61 })?;
62
63 let bound_acceptor = axum_server::from_unix(unix_listener)?
64 .handle(handle.clone())
65 .serve(router)
66 .inspect({
67 let path = path.to_owned();
68 |_| {
69 if let Err(err) = fs::remove_file(path) {
70 warn!("Failed to remove UNIX socket: {err}");
71 }
72 }
73 })
74 .boxed();
75
76 acceptors.push(bound_acceptor);
77 }
78
79 Ok(acceptors)
80}