use std::net::SocketAddr;
use tokio::sync::oneshot;
use crate::core::{IntroId, Timestamp};
use crate::error::{AcceptError, AuthError, IntroError};
use crate::identity::{Identity, PublicKeyOf};
use super::connection::Connection;
use super::shared::{Command, Shell};
#[must_use = "dropping a staged accept object silently rejects the inbound \
connection; call read_identity(), or drop it deliberately"]
pub struct Intro<I: Identity> {
shell: Shell<I>,
id: IntroId,
source: SocketAddr,
sender_index: u32,
consumed: bool,
}
impl<I: Identity> Intro<I> {
pub(crate) fn new(shell: Shell<I>, id: IntroId, source: SocketAddr, sender_index: u32) -> Self {
Self {
shell,
id,
source,
sender_index,
consumed: false,
}
}
pub fn source(&self) -> SocketAddr {
self.shell
.state
.borrow()
.endpoint
.intro_source(self.id)
.unwrap_or(self.source)
}
pub fn sender_index(&self) -> u32 {
self.shell
.state
.borrow()
.endpoint
.intro_sender_index(self.id)
.unwrap_or(self.sender_index)
}
pub async fn read_identity(mut self) -> Result<Claimed<I>, IntroError> {
let shell = self.shell.clone();
let id = self.id;
let result = round_trip(&shell, |reply| Command::ReadIdentity(id, reply)).await;
self.consumed = true;
match result {
Some(Ok(claimed)) => Ok(Claimed {
shell,
id,
claimed_static: claimed,
consumed: false,
}),
Some(Err(error)) => {
shell.send(Command::Reject(id));
Err(error)
}
None => Err(IntroError::EndpointDropped),
}
}
}
impl<I: Identity> Drop for Intro<I> {
fn drop(&mut self) {
if !self.consumed {
self.shell.send(Command::Reject(self.id));
}
}
}
impl<I: Identity> std::fmt::Debug for Intro<I> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Intro")
.field("source", &self.source())
.field("sender_index", &self.sender_index())
.finish_non_exhaustive()
}
}
#[must_use = "dropping a staged accept object silently rejects the inbound \
connection; call authenticate(), or drop it deliberately"]
pub struct Claimed<I: Identity> {
shell: Shell<I>,
id: IntroId,
claimed_static: PublicKeyOf<I>,
consumed: bool,
}
impl<I: Identity> Claimed<I> {
pub fn claimed_static(&self) -> &PublicKeyOf<I> {
&self.claimed_static
}
pub async fn authenticate(self) -> Result<Proven<I>, AuthError>
where
I::Suite: crate::packet::Handshake<Psk = ()>,
{
self.authenticate_with(()).await
}
pub async fn authenticate_with(
mut self,
psk: crate::identity::PskOf<I>,
) -> Result<Proven<I>, AuthError> {
let shell = self.shell.clone();
let id = self.id;
let result = round_trip(&shell, move |reply| Command::Authenticate(id, psk, reply)).await;
self.consumed = true;
match result {
Some(Ok((peer_static, timestamp))) => Ok(Proven {
shell,
id,
peer_static,
timestamp,
consumed: false,
}),
Some(Err(error)) => {
shell.send(Command::Reject(id));
Err(error)
}
None => Err(AuthError::EndpointDropped),
}
}
}
impl<I: Identity> Drop for Claimed<I> {
fn drop(&mut self) {
if !self.consumed {
self.shell.send(Command::Reject(self.id));
}
}
}
impl<I: Identity> std::fmt::Debug for Claimed<I> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Claimed").finish_non_exhaustive()
}
}
#[must_use = "dropping a staged accept object silently rejects the inbound \
connection; call accept(), or drop it deliberately"]
pub struct Proven<I: Identity> {
shell: Shell<I>,
id: IntroId,
peer_static: PublicKeyOf<I>,
timestamp: Timestamp,
consumed: bool,
}
impl<I: Identity> Proven<I> {
pub fn peer_static(&self) -> &PublicKeyOf<I> {
&self.peer_static
}
pub fn timestamp(&self) -> Timestamp {
self.timestamp
}
pub async fn accept(mut self) -> Result<Connection<I::Suite>, AcceptError> {
let shell = self.shell.clone();
let id = self.id;
let peer_static = self.peer_static.clone();
let result = round_trip(&shell, move |reply| {
Command::AcceptChain(id, peer_static, reply)
})
.await;
self.consumed = true;
match result {
Some(result) => result,
None => Err(AcceptError::EndpointDropped),
}
}
}
impl<I: Identity> Drop for Proven<I> {
fn drop(&mut self) {
if !self.consumed {
self.shell.send(Command::Reject(self.id));
}
}
}
impl<I: Identity> std::fmt::Debug for Proven<I> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Proven")
.field("timestamp", &self.timestamp)
.finish_non_exhaustive()
}
}
async fn round_trip<I: Identity, T>(
shell: &Shell<I>,
command: impl FnOnce(oneshot::Sender<T>) -> Command<I>,
) -> Option<T> {
if shell.driver_stopped() {
return None;
}
let (tx, rx) = oneshot::channel();
shell.send(command(tx));
rx.await.ok()
}