use std::path::Path;
use std::time::Duration;
use net::adapter::net::behavior::enrollment_storage::StorageError;
use net_sdk::enrollment::bundle::MeshContact;
use net_sdk::enrollment::device::{DeviceJoin, DeviceJoinError};
use crate::error::{connection_failure, generic, invalid_args, CliError};
const ATTACH_WAIT: Duration = Duration::from_secs(20);
pub(crate) struct JoinedGuard {
_join: DeviceJoin,
}
pub(crate) fn peer_override(
args: &crate::commands::aggregator::RemoteAttachArgs,
) -> Result<Option<MeshContact>, CliError> {
if args.psk_hex.is_some() {
return Err(invalid_args(
"--joined supplies the PSK from the join; drop --psk-hex",
));
}
if args.inspect_target {
return Err(invalid_args(
"--inspect-target is not available with --joined",
));
}
match (&args.node_addr, &args.node_pubkey, &args.remote_node_id) {
(None, None, None) => Ok(None),
(Some(addr), Some(pubkey), Some(node_id)) => {
let addr: std::net::SocketAddr = addr
.parse()
.map_err(|e| invalid_args(format!("--node-addr `{addr}`: {e}")))?;
let noise_pubkey = crate::parsers::hex_decode_32(pubkey)
.map_err(|e| invalid_args(format!("--node-pubkey: {e}")))?;
let node_id = crate::parsers::parse_u64_flexible(node_id)
.map_err(|e| invalid_args(format!("--node-id: {e}")))?;
Ok(Some(MeshContact {
addr: Some(addr),
noise_pubkey,
node_id,
relay: None,
}))
}
_ => Err(invalid_args(
"a peer named next to --joined needs --node-addr, --node-pubkey and --node-id",
)),
}
}
pub(crate) async fn attach(
state_root: &Path,
bind: &str,
peer: Option<MeshContact>,
) -> Result<(net_sdk::Mesh, &'static str, JoinedGuard), CliError> {
let join_dir = state_root.join(super::enrollment::JOIN_SUBDIR);
if !join_dir.exists() {
return Err(invalid_args(format!(
"{} has not joined a mesh (no join state)",
state_root.display()
)));
}
let jd = join_dir.clone();
let join = tokio::task::spawn_blocking(move || DeviceJoin::open(&jd))
.await
.map_err(|e| generic(format!("join state task failed: {e}")))?
.map_err(|e| match e {
DeviceJoinError::Storage(StorageError::Busy) => invalid_args(format!(
"the join at {} is in use (is `net-mesh up` running on it? stop it with \
`net-mesh down` first)",
state_root.display()
)),
other => generic(format!("join state {}: {other}", join_dir.display())),
})?;
if let Some(at) = join.left_at() {
return Err(invalid_args(format!(
"this device left the mesh at unix {at}; nothing to run as"
)));
}
let bundle = join.bundle().ok_or_else(|| {
invalid_args("the join is not complete (no delivered bundle); finish `net-mesh join` first")
})?;
let bind = crate::context::parse_bind_literal(bind)?;
let mut psk = *bundle.psk().expose_bytes();
let built = net_sdk::MeshBuilder::new(&bind.to_string(), &psk)
.map_err(|e| invalid_args(format!("mesh bind {bind}: {e}")));
crate::secret::zeroize_slice(&mut psk);
let mesh = built?
.identity(join.identity().clone())
.announce_noise_key(true)
.build()
.await
.map_err(|e| connection_failure(format!("mesh start on {bind}: {e}")))?;
mesh.start();
let contact = peer.unwrap_or_else(|| bundle.contact().clone());
let path = super::enrollment::attach_contact(mesh.node(), &contact, ATTACH_WAIT)
.await
.map_err(|e| connection_failure(format!("attach to the enrolled node failed: {e}")))?;
Ok((mesh, path, JoinedGuard { _join: join }))
}