use std::net::{IpAddr, SocketAddr};
use std::time::Duration;
use tokio_util::sync::CancellationToken;
use auths_pairing_daemon::{HostAllowlist, PairingDaemonBuilder, PairingDaemonHandle};
use auths_sdk::pairing::{CreateSessionRequest, SubmitConfirmationRequest, SubmitResponseRequest};
pub fn detect_lan_ip() -> std::io::Result<IpAddr> {
let network = auths_pairing_daemon::IfAddrsNetworkInterfaces;
auths_pairing_daemon::NetworkInterfaces::detect_lan_ip(&network)
.map_err(|e| std::io::Error::new(std::io::ErrorKind::AddrNotAvailable, e.to_string()))
}
pub struct LanPairingServer {
addr: SocketAddr,
cancel: CancellationToken,
handle: PairingDaemonHandle,
_task: tokio::task::JoinHandle<()>,
pairing_token_b64: String,
}
impl LanPairingServer {
pub async fn start(session: CreateSessionRequest, bind_ip: IpAddr) -> anyhow::Result<Self> {
let daemon = PairingDaemonBuilder::new().build(session)?;
let pairing_token_b64 = daemon.token().to_string();
let cancel = CancellationToken::new();
let listener = tokio::net::TcpListener::bind(SocketAddr::new(bind_ip, 0))
.await
.map_err(|e| {
anyhow::anyhow!(
"Could not bind to {} — check that your device is on the correct \
network, or use relay-based pairing. ({})",
bind_ip,
e
)
})?;
let addr = listener.local_addr()?;
let allowlist = HostAllowlist::for_bound_addr(addr, None);
let (router, handle) = daemon.into_parts(allowlist);
let cancel_clone = cancel.clone();
let task = tokio::spawn(async move {
let server = axum::serve(
listener,
router.into_make_service_with_connect_info::<SocketAddr>(),
);
tokio::select! {
_ = server => {}
_ = cancel_clone.cancelled() => {}
}
});
Ok(Self {
addr,
cancel,
handle,
_task: task,
pairing_token_b64,
})
}
pub fn addr(&self) -> SocketAddr {
self.addr
}
pub fn pairing_token(&self) -> &str {
&self.pairing_token_b64
}
pub fn advertise(
&self,
port: u16,
short_code: &str,
controller_did: &str,
) -> Option<
Result<Box<dyn auths_pairing_daemon::AdvertiseHandle>, auths_pairing_daemon::DaemonError>,
> {
self.handle.advertise(port, short_code, controller_did)
}
pub async fn wait_for_response(
&mut self,
timeout: Duration,
) -> Result<SubmitResponseRequest, auths_sdk::error::PairingError> {
self.handle
.wait_for_response(timeout)
.await
.map_err(|e| match e {
auths_pairing_daemon::DaemonError::Pairing(pe) => pe,
other => auths_sdk::error::PairingError::LocalServerError(other.to_string()),
})
}
pub async fn wait_for_confirmation(
&self,
timeout: Duration,
) -> Option<SubmitConfirmationRequest> {
self.handle.wait_for_confirmation(timeout).await
}
pub fn shutdown(self) {
self.cancel.cancel();
}
}