Add Windows named-pipe local control

This commit is contained in:
Eric Wendland 2026-07-18 17:05:38 +02:00
commit 3c84713472
9 changed files with 338 additions and 72 deletions

View file

@ -2,6 +2,7 @@ pub mod backup;
mod daemon;
pub mod doctor;
mod local_control;
mod local_transport;
mod peer_client;
mod peer_control;
mod resource_contracts;
@ -68,6 +69,7 @@ use geth_types::{
use iroh::protocol::ProtocolHandler;
use iroh_docs::api::protocol::{AddrInfoOptions, ShareMode};
pub use local_control::handle_request_async;
use local_transport::LocalStream;
use peer_client::{request_overlay_wire, request_peer_control, request_pipe_wire};
use peer_control::{
IrohAlpnRoute, PeerControlCaller, authenticate_peer_control_caller, classify_iroh_alpn,
@ -85,7 +87,9 @@ use sync::{
KvDocsState, load_live_sync_cursor, store_live_sync_cursor, sync_now, sync_status_local,
};
use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader};
use tokio::net::{TcpListener, TcpStream, UnixListener, UnixStream};
use tokio::net::{TcpListener, TcpStream};
#[cfg(unix)]
use tokio::net::{UnixListener, UnixStream};
use tokio::sync::{mpsc, oneshot};
use tun_rs::{DeviceBuilder, Layer};
use wire::{
@ -429,7 +433,7 @@ pub async fn send_control(
paths: &GethPaths,
request: ControlRequest,
) -> Result<ControlResponse, NodeError> {
let mut stream = UnixStream::connect(paths.socket_path()).await?;
let mut stream = local_transport::connect(paths).await?;
stream
.write_all(geth_control::encode_request(&request)?.as_bytes())
.await?;
@ -445,7 +449,7 @@ pub async fn stream_ssh_proxy(
peer_node: String,
bearer_secret: Option<String>,
) -> Result<(), NodeError> {
let mut stream = UnixStream::connect(paths.socket_path()).await?;
let mut stream = local_transport::connect(paths).await?;
stream
.write_all(
geth_control::encode_request(&ControlRequest::SshProxyStream {
@ -461,7 +465,7 @@ pub async fn stream_ssh_proxy(
match geth_control::decode_response(&line)? {
ControlResponse::SshProxyConnected { allowed: true, .. } => {
let stream = reader.into_inner();
let (mut local_read, mut local_write) = stream.into_split();
let (mut local_read, mut local_write) = tokio::io::split(stream);
let mut stdin = tokio::io::stdin();
let mut stdout = tokio::io::stdout();
let upload = async {
@ -526,7 +530,7 @@ async fn stream_pipe_tcp(
bearer_secret: Option<String>,
client: TcpStream,
) -> Result<(), NodeError> {
let mut stream = UnixStream::connect(paths.socket_path()).await?;
let mut stream = local_transport::connect(paths).await?;
stream
.write_all(
geth_control::encode_request(&ControlRequest::PipeTcpStream {
@ -543,7 +547,7 @@ async fn stream_pipe_tcp(
match geth_control::decode_response(&line)? {
ControlResponse::PipeRemoteConnected { allowed: true, .. } => {
let daemon_stream = reader.into_inner();
let (mut daemon_read, mut daemon_write) = daemon_stream.into_split();
let (mut daemon_read, mut daemon_write) = tokio::io::split(daemon_stream);
let (mut client_read, mut client_write) = client.into_split();
let upload = async {
tokio::io::copy(&mut client_read, &mut daemon_write).await?;
@ -568,6 +572,7 @@ async fn stream_pipe_tcp(
}
}
#[cfg(unix)]
pub async fn run_unix_forward(
paths: &GethPaths,
listen_path: PathBuf,
@ -605,6 +610,20 @@ pub async fn run_unix_forward(
}
}
#[cfg(not(unix))]
pub async fn run_unix_forward(
_paths: &GethPaths,
_listen_path: PathBuf,
_peer_node: String,
_target_path: PathBuf,
_bearer_secret: Option<String>,
) -> Result<(), NodeError> {
Err(NodeError::UnsupportedPlatform(
geth_pipe::unix_forward_unsupported_message().to_owned(),
))
}
#[cfg(unix)]
async fn stream_pipe_unix(
paths: &GethPaths,
peer_node: String,
@ -612,7 +631,7 @@ async fn stream_pipe_unix(
bearer_secret: Option<String>,
client: UnixStream,
) -> Result<(), NodeError> {
let mut stream = UnixStream::connect(paths.socket_path()).await?;
let mut stream = local_transport::connect(paths).await?;
stream
.write_all(
geth_control::encode_request(&ControlRequest::PipeUnixStream {
@ -629,7 +648,7 @@ async fn stream_pipe_unix(
match geth_control::decode_response(&line)? {
ControlResponse::PipeRemoteConnected { allowed: true, .. } => {
let daemon_stream = reader.into_inner();
let (mut daemon_read, mut daemon_write) = daemon_stream.into_split();
let (mut daemon_read, mut daemon_write) = tokio::io::split(daemon_stream);
let (mut client_read, mut client_write) = client.into_split();
let upload = async {
tokio::io::copy(&mut client_read, &mut daemon_write).await?;
@ -654,7 +673,7 @@ async fn stream_pipe_unix(
}
}
async fn handle_stream(node: LocalNode, stream: UnixStream) -> Result<(), NodeError> {
async fn handle_stream(node: LocalNode, stream: LocalStream) -> Result<(), NodeError> {
let mut reader = BufReader::new(stream);
let mut line = String::new();
reader.read_line(&mut line).await?;
@ -737,6 +756,7 @@ async fn handle_stream(node: LocalNode, stream: UnixStream) -> Result<(), NodeEr
}
return result;
}
#[cfg(unix)]
if let ControlRequest::PipeUnixStream {
node: peer_node,
target_path,
@ -2661,7 +2681,7 @@ async fn handle_local_pipe_tcp_stream(
peer_node: &str,
target_addr: String,
bearer_secret: Option<String>,
local_stream: UnixStream,
local_stream: LocalStream,
) -> Result<(), NodeError> {
geth_pipe::validate_tcp_forward_target_addr(&target_addr)?;
let store = Store::open(&node.paths.metadata_db())?;
@ -2753,7 +2773,7 @@ async fn handle_local_pipe_tcp_stream(
let ControlResponse::PipeRemoteConnected { allowed: true, .. } = local_response else {
return Ok(());
};
let (mut local_read, mut local_write) = local_stream.into_split();
let (mut local_read, mut local_write) = tokio::io::split(local_stream);
let upload = async {
tokio::io::copy(&mut local_read, &mut remote_send)
.await
@ -2773,12 +2793,13 @@ async fn handle_local_pipe_tcp_stream(
Ok(())
}
#[cfg(unix)]
async fn handle_local_pipe_unix_stream(
node: LocalNode,
peer_node: &str,
target_path: PathBuf,
bearer_secret: Option<String>,
local_stream: UnixStream,
local_stream: LocalStream,
) -> Result<(), NodeError> {
let target_path = geth_pipe::validate_unix_forward_path(&target_path)?;
let target_display = target_path.display().to_string();
@ -2871,7 +2892,7 @@ async fn handle_local_pipe_unix_stream(
let ControlResponse::PipeRemoteConnected { allowed: true, .. } = local_response else {
return Ok(());
};
let (mut local_read, mut local_write) = local_stream.into_split();
let (mut local_read, mut local_write) = tokio::io::split(local_stream);
let upload = async {
tokio::io::copy(&mut local_read, &mut remote_send)
.await
@ -2988,7 +3009,7 @@ async fn handle_local_ssh_proxy_stream(
node: LocalNode,
peer_node: &str,
bearer_secret: Option<String>,
local_stream: UnixStream,
local_stream: LocalStream,
) -> Result<(), NodeError> {
let store = Store::open(&node.paths.metadata_db())?;
let stored = store
@ -3076,7 +3097,7 @@ async fn handle_local_ssh_proxy_stream(
let ControlResponse::SshProxyConnected { allowed: true, .. } = local_response else {
return Ok(());
};
let (mut local_read, mut local_write) = local_stream.into_split();
let (mut local_read, mut local_write) = tokio::io::split(local_stream);
let upload = async {
tokio::io::copy(&mut local_read, &mut remote_send)
.await
@ -5519,6 +5540,7 @@ async fn handle_pipe_tcp_wire_connection(
Ok(())
}
#[cfg(unix)]
async fn handle_pipe_unix_wire_connection(
node: LocalNode,
remote_endpoint_id: &str,
@ -5620,6 +5642,24 @@ async fn handle_pipe_unix_wire_connection(
Ok(())
}
#[cfg(not(unix))]
async fn handle_pipe_unix_wire_connection(
_node: LocalNode,
_remote_endpoint_id: &str,
_request: PipeUnixConnectWire,
mut send: iroh::endpoint::SendStream,
_recv: iroh::endpoint::RecvStream,
) -> Result<(), NodeError> {
let response = PipeWireResponse::Error {
message: geth_pipe::unix_forward_unsupported_message().to_owned(),
};
send.write_all(geth_control::encode_pipe_wire_response(&response)?.as_bytes())
.await
.map_err(|error| NodeError::IrohPeer(error.to_string()))?;
send.finish()
.map_err(|error| NodeError::IrohPeer(error.to_string()))
}
fn peer_gossip_endpoint_id(
node: &LocalNode,
peer_node: &str,
@ -12926,7 +12966,7 @@ mod tests {
socket.write_all(b"pong").await.expect("write tcp echo");
socket.shutdown().await.expect("shutdown tcp echo");
});
let (forward_client, forward_daemon) = UnixStream::pair().expect("unix stream pair");
let (forward_client, forward_daemon) = tokio::io::duplex(64 * 1024);
let right_node_id = right_card.node_id.to_string();
let left_for_forward = left.clone();
let tcp_target_for_forward = tcp_target.clone();
@ -12936,7 +12976,7 @@ mod tests {
&right_node_id,
tcp_target_for_forward,
None,
forward_daemon,
Box::new(forward_daemon),
)
.await
});
@ -12958,7 +12998,7 @@ mod tests {
other => panic!("unexpected pipe TCP stream response: {other:?}"),
}
let stream = forward_reader.into_inner();
let (mut forward_read, mut forward_write) = stream.into_split();
let (mut forward_read, mut forward_write) = tokio::io::split(stream);
forward_write
.write_all(b"ping")
.await
@ -13015,7 +13055,7 @@ mod tests {
&right_node_id,
unix_target_for_forward,
None,
unix_forward_daemon,
Box::new(unix_forward_daemon),
)
.await
});