use std::sync::Mutex;
use rustler::{Encoder, ResourceArc};
use crate::{Device, TOKIO_RUNTIME, atoms};
pub(crate) struct LoopbackHandleResource {
inner: Mutex<Option<tailscale::LoopbackHandle>>,
}
#[rustler::resource_impl]
impl rustler::Resource for LoopbackHandleResource {}
#[derive(rustler::NifStruct)]
#[module = "Tailscale.WaitingFile"]
struct WaitingFile {
name: String,
size: u64,
}
impl From<tailscale::WaitingFile> for WaitingFile {
fn from(value: tailscale::WaitingFile) -> Self {
Self {
name: value.name,
size: value.size,
}
}
}
#[derive(rustler::NifStruct)]
#[module = "Tailscale.TkaStatus"]
struct TkaStatus {
head: String,
disabled: bool,
}
#[rustler::nif(schedule = "DirtyIo")]
fn fetch_id_token(env: rustler::Env<'_>, dev: ResourceArc<Device>, audience: &str) -> impl Encoder {
let dev = dev.inner.clone();
let audience = audience.to_owned();
match TOKIO_RUNTIME.block_on(async move { dev.fetch_id_token(&audience).await }) {
Ok(token) => (atoms::ok(), token).encode(env),
Err(e) => (atoms::error(), e.to_string()).encode(env),
}
}
#[rustler::nif]
fn metrics(dev: ResourceArc<Device>) -> String {
dev.inner.metrics()
}
#[rustler::nif(schedule = "DirtyIo")]
fn self_key_expiry_unix(env: rustler::Env<'_>, dev: ResourceArc<Device>) -> impl Encoder {
let dev = dev.inner.clone();
match TOKIO_RUNTIME.block_on(async move { dev.self_key_expiry_unix().await }) {
Ok(expiry) => (atoms::ok(), expiry).encode(env),
Err(e) => (atoms::error(), e.to_string()).encode(env),
}
}
#[rustler::nif(schedule = "DirtyIo")]
fn self_key_expired(env: rustler::Env<'_>, dev: ResourceArc<Device>) -> impl Encoder {
let dev = dev.inner.clone();
match TOKIO_RUNTIME.block_on(async move { dev.self_key_expired().await }) {
Ok(expired) => (atoms::ok(), expired).encode(env),
Err(e) => (atoms::error(), e.to_string()).encode(env),
}
}
#[rustler::nif(schedule = "DirtyIo")]
fn taildrop_waiting_files(env: rustler::Env<'_>, dev: ResourceArc<Device>) -> impl Encoder {
match dev.inner.taildrop_waiting_files() {
Ok(files) => (
atoms::ok(),
files.into_iter().map(WaitingFile::from).collect::<Vec<_>>(),
)
.encode(env),
Err(e) => (atoms::error(), e.to_string()).encode(env),
}
}
#[rustler::nif(schedule = "DirtyIo")]
fn taildrop_delete_file(
env: rustler::Env<'_>,
dev: ResourceArc<Device>,
name: &str,
) -> impl Encoder {
match dev.inner.taildrop_delete_file(name) {
Ok(()) => (atoms::ok(), atoms::ok()).encode(env),
Err(e) => (atoms::error(), e.to_string()).encode(env),
}
}
#[rustler::nif(schedule = "DirtyIo")]
fn taildrop_save_file(
env: rustler::Env<'_>,
dev: ResourceArc<Device>,
name: &str,
dst_path: &str,
) -> impl Encoder {
let result = (|| {
let (mut src, _size) = dev
.inner
.taildrop_open_file(name)
.map_err(|e| e.to_string())?;
let mut dst = std::fs::File::create(dst_path).map_err(|e| e.to_string())?;
std::io::copy(&mut src, &mut dst).map_err(|e| e.to_string())
})();
match result {
Ok(n) => (atoms::ok(), n).encode(env),
Err(e) => (atoms::error(), e).encode(env),
}
}
#[rustler::nif(schedule = "DirtyIo")]
fn taildrop_send_file(
env: rustler::Env<'_>,
dev: ResourceArc<Device>,
peer_name: &str,
file_name: &str,
src_path: &str,
) -> impl Encoder {
let dev = dev.inner.clone();
let peer_name = peer_name.to_owned();
let file_name = file_name.to_owned();
let src_path = src_path.to_owned();
let result = TOKIO_RUNTIME.block_on(async move {
let peer = dev
.peer_by_name(&peer_name)
.await
.map_err(|e| e.to_string())?
.ok_or_else(|| "no such peer".to_string())?;
let file = tokio::fs::File::open(&src_path)
.await
.map_err(|e| e.to_string())?;
let len = file.metadata().await.map_err(|e| e.to_string())?.len();
dev.send_file(&peer, &file_name, len, file)
.await
.map_err(|e| e.to_string())
});
match result {
Ok(()) => (atoms::ok(), atoms::ok()).encode(env),
Err(e) => (atoms::error(), e).encode(env),
}
}
#[rustler::nif(schedule = "DirtyIo")]
fn capture_pcap(env: rustler::Env<'_>, dev: ResourceArc<Device>, dst_path: &str) -> impl Encoder {
let dev = dev.inner.clone();
let dst_path = dst_path.to_owned();
let result = TOKIO_RUNTIME.block_on(async move {
let file = std::fs::File::create(&dst_path).map_err(|e| e.to_string())?;
dev.capture_pcap(file).await.map_err(|e| e.to_string())
});
match result {
Ok(()) => (atoms::ok(), atoms::ok()).encode(env),
Err(e) => (atoms::error(), e).encode(env),
}
}
#[rustler::nif(schedule = "DirtyIo")]
fn stop_capture(env: rustler::Env<'_>, dev: ResourceArc<Device>) -> impl Encoder {
let dev = dev.inner.clone();
match TOKIO_RUNTIME.block_on(async move { dev.stop_capture().await }) {
Ok(()) => (atoms::ok(), atoms::ok()).encode(env),
Err(e) => (atoms::error(), e.to_string()).encode(env),
}
}
#[rustler::nif(schedule = "DirtyIo")]
fn loopback(env: rustler::Env<'_>, dev: ResourceArc<Device>) -> impl Encoder {
let dev = dev.inner.clone();
match TOKIO_RUNTIME.block_on(async move { dev.loopback().await }) {
Ok((addr, cred, handle)) => {
let handle = ResourceArc::new(LoopbackHandleResource {
inner: Mutex::new(Some(handle)),
});
(
atoms::ok(),
(crate::sockaddr_to_erl(env, addr), cred, handle),
)
.encode(env)
}
Err(e) => (atoms::error(), e.to_string()).encode(env),
}
}
#[rustler::nif]
fn loopback_stop(
env: rustler::Env<'_>,
handle: ResourceArc<LoopbackHandleResource>,
) -> impl Encoder {
if let Ok(mut guard) = handle.inner.lock() {
guard.take();
}
atoms::ok().encode(env)
}
#[rustler::nif(schedule = "DirtyIo")]
fn tka_status(env: rustler::Env<'_>, dev: ResourceArc<Device>) -> impl Encoder {
let dev = dev.inner.clone();
match TOKIO_RUNTIME.block_on(async move { dev.tka_status().await }) {
Ok(None) => (atoms::ok(), Option::<()>::None).encode(env),
Ok(Some(status)) => (
atoms::ok(),
TkaStatus {
head: status.head,
disabled: status.disabled,
},
)
.encode(env),
Err(e) => (atoms::error(), e.to_string()).encode(env),
}
}