use zeroize::Zeroizing;
use super::Broker;
impl Broker {
pub(crate) fn value_in(&self, bytes: &[u8]) -> Option<&str> {
self.held
.iter()
.find(|h| h.value_in(bytes))
.map(|h| h.secret.id.as_str())
}
fn keep(&self) -> usize {
self.held
.iter()
.map(super::Held::longest_value)
.max()
.unwrap_or(0)
.saturating_sub(1)
}
}
pub(crate) struct Holdback<'a> {
broker: &'a Broker,
pending: Zeroizing<Vec<u8>>,
}
impl<'a> Holdback<'a> {
pub(crate) fn new(broker: &'a Broker) -> Self {
Self {
broker,
pending: Zeroizing::new(Vec::new()),
}
}
pub(crate) fn push(&mut self, data: &[u8]) -> Result<Zeroizing<Vec<u8>>, &'a str> {
self.pending.extend_from_slice(data);
if let Some(id) = self.broker.value_in(&self.pending) {
return Err(id);
}
let release = self.pending.len().saturating_sub(self.broker.keep());
let rest = Zeroizing::new(self.pending[release..].to_vec());
self.pending.truncate(release);
Ok(std::mem::replace(&mut self.pending, rest))
}
pub(crate) fn finish(self) -> Zeroizing<Vec<u8>> {
self.pending
}
}
pub(crate) fn value_came_back(host: &str, id: &str) -> String {
format!(
"the answer from {host} holds the value of secret `{id}`; it was stopped before the value \
reached the client"
)
}
pub(crate) struct Echo<'a, F: FnMut(&str) + Send> {
hold: Option<Holdback<'a>>,
leaked: F,
}
impl<'a, F: FnMut(&str) + Send> Echo<'a, F> {
pub(crate) fn new(broker: &'a Broker, leaked: F) -> Self {
Self {
hold: Some(Holdback::new(broker)),
leaked,
}
}
}
impl<F: FnMut(&str) + Send> crate::tunnel::Answer for Echo<'_, F> {
fn pass(&mut self, chunk: &[u8]) -> Option<Zeroizing<Vec<u8>>> {
match self.hold.as_mut()?.push(chunk) {
Ok(release) => Some(release),
Err(id) => {
self.hold = None;
(self.leaked)(id);
None
}
}
}
fn end(&mut self) -> Zeroizing<Vec<u8>> {
self.hold.take().map(Holdback::finish).unwrap_or_default()
}
}