Skip to content

Commit f15d795

Browse files
committed
Report exec policy refusals honestly
1 parent 09a289e commit f15d795

7 files changed

Lines changed: 306 additions & 71 deletions

File tree

src/daemon.rs

Lines changed: 59 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1414,6 +1414,7 @@ impl DaemonState {
14141414
tokio::spawn(run_raw_dial_socket(
14151415
listener,
14161416
self.endpoint_rx(),
1417+
peer.to_string(),
14171418
peer_addr.clone(),
14181419
alpn,
14191420
listener_cancel.clone(),
@@ -5078,6 +5079,7 @@ async fn run_dial_tcp_listener(
50785079
async fn run_raw_dial_socket(
50795080
listener: UnixListener,
50805081
endpoint_rx: watch::Receiver<CurrentEndpoint>,
5082+
peer: String,
50815083
peer_addr: EndpointAddr,
50825084
alpn: Vec<u8>,
50835085
listener_cancel: CancellationToken,
@@ -5109,6 +5111,7 @@ async fn run_raw_dial_socket(
51095111
}
51105112
};
51115113
let endpoint = endpoint_rx.borrow().clone();
5114+
let peer = peer.clone();
51125115
let peer_addr = peer_addr.clone();
51135116
let alpn = alpn.clone();
51145117
let cancel = daemon_cancel.clone();
@@ -5123,6 +5126,7 @@ async fn run_raw_dial_socket(
51235126
match handle_raw_dial_socket_connection(
51245127
local,
51255128
endpoint,
5129+
peer,
51265130
peer_addr,
51275131
alpn,
51285132
peer_connections,
@@ -5143,23 +5147,74 @@ async fn run_raw_dial_socket(
51435147
}
51445148

51455149
async fn handle_raw_dial_socket_connection(
5146-
local: UnixStream,
5150+
mut local: UnixStream,
51475151
endpoint: CurrentEndpoint,
5152+
peer: String,
51485153
peer_addr: EndpointAddr,
51495154
alpn: Vec<u8>,
51505155
peer_connections: Arc<mux::PeerConnections>,
51515156
) -> Result<()> {
51525157
let protocol = std::str::from_utf8(&alpn).context("protocol is not UTF-8")?;
5153-
let stream = peer_connections
5158+
let stream = match peer_connections
51545159
.open_stream(
51555160
&endpoint.endpoint,
51565161
endpoint.generation,
51575162
&peer_addr,
51585163
protocol,
51595164
mux::StreamActivity::Application,
51605165
)
5161-
.await?;
5162-
pipe_unix_iroh(local, stream.send, stream.recv).await?;
5166+
.await
5167+
{
5168+
Ok(stream) => stream,
5169+
Err(error) => {
5170+
if alpn == exec::EXEC_ALPN {
5171+
let (message, exit_code) = if mux::is_permanent_stream_denial(&error) {
5172+
(
5173+
format!("refused service \"exec\": {error}"),
5174+
exec::EXIT_EXEC_DISABLED,
5175+
)
5176+
} else {
5177+
(format!("failed to start service \"exec\": {error}"), 1)
5178+
};
5179+
// Preserve the dial error for diagnostics and backoff. The
5180+
// frame is for the local CLI, which otherwise sees only EOF.
5181+
let _ = exec::serve_exec_failure(&mut local, &message, exit_code).await;
5182+
}
5183+
let context = format!("dial to peer {peer:?} failed: {error}");
5184+
return Err(error).context(context);
5185+
}
5186+
};
5187+
if alpn == exec::EXEC_ALPN {
5188+
pipe_exec_unix_iroh(local, stream.send, stream.recv).await?;
5189+
} else {
5190+
pipe_unix_iroh(local, stream.send, stream.recv).await?;
5191+
}
5192+
Ok(())
5193+
}
5194+
5195+
/// Keep receiving exec frames after the peer stops its receive direction.
5196+
///
5197+
/// A policy refusal sends Error and Exit, then closes without reading the
5198+
/// command frame. That stopped send direction must not cancel the useful reply.
5199+
async fn pipe_exec_unix_iroh(
5200+
local: UnixStream,
5201+
mut send: SendStream,
5202+
mut recv: RecvStream,
5203+
) -> Result<()> {
5204+
let (mut local_read, mut local_write) = local.into_split();
5205+
let to_remote = async {
5206+
tokio::io::copy(&mut local_read, &mut send).await?;
5207+
send.finish()?;
5208+
Ok::<(), anyhow::Error>(())
5209+
};
5210+
let to_local = async {
5211+
tokio::io::copy(&mut recv, &mut local_write).await?;
5212+
let _ = local_write.shutdown().await;
5213+
Ok::<(), anyhow::Error>(())
5214+
};
5215+
let (to_remote, to_local) = tokio::join!(to_remote, to_local);
5216+
to_local?;
5217+
to_remote?;
51635218
Ok(())
51645219
}
51655220

0 commit comments

Comments
 (0)