use crate::{
channel,
path::Path,
protocol::resolver::{
FromRead, FromWrite, Publisher, PublisherId, Resolved, ToRead, ToWrite,
},
utils,
};
use anyhow::Result;
use cross_krb5::{ClientCtx, InitiateFlags, Step};
use futures::channel::oneshot;
use fxhash::FxHashMap;
use netidx_core::pack::BoundedBytes;
use poolshark::global::{GPooled, Pool};
use std::{fmt::Debug, str::FromStr, sync::LazyLock, time::Duration};
use tokio::{net::TcpStream, task, time};
pub(super) const HELLO_TO: Duration = Duration::from_secs(15);
pub(super) static PUBLISHERPOOL: LazyLock<Pool<FxHashMap<PublisherId, Publisher>>> =
LazyLock::new(|| Pool::new(1000, 100));
pub(super) static RAWTOREADPOOL: LazyLock<Pool<Vec<ToRead>>> =
LazyLock::new(|| Pool::new(100, 10_000));
pub(super) static RAWFROMREADPOOL: LazyLock<Pool<Vec<FromRead>>> =
LazyLock::new(|| Pool::new(100, 10_000));
pub(super) static TOREADPOOL: LazyLock<Pool<Vec<(usize, ToRead)>>> =
LazyLock::new(|| Pool::new(100, 10_000));
pub(super) static FROMREADPOOL: LazyLock<Pool<Vec<(usize, FromRead)>>> =
LazyLock::new(|| Pool::new(100, 10_000));
pub(super) static RAWTOWRITEPOOL: LazyLock<Pool<Vec<ToWrite>>> =
LazyLock::new(|| Pool::new(100, 10_000));
pub(super) static RAWFROMWRITEPOOL: LazyLock<Pool<Vec<FromWrite>>> =
LazyLock::new(|| Pool::new(100, 10_000));
pub(super) static TOWRITEPOOL: LazyLock<Pool<Vec<(usize, ToWrite)>>> =
LazyLock::new(|| Pool::new(100, 10_000));
pub(super) static FROMWRITEPOOL: LazyLock<Pool<Vec<(usize, FromWrite)>>> =
LazyLock::new(|| Pool::new(100, 10_000));
pub(super) static RESOLVEDPOOL: LazyLock<Pool<Vec<Resolved>>> =
LazyLock::new(|| Pool::new(100, 10_000));
pub(super) static LISTPOOL: LazyLock<Pool<Vec<GPooled<Vec<Path>>>>> =
LazyLock::new(|| Pool::new(100, 10_000));
pub(super) static PATHPOOL: LazyLock<Pool<Vec<Path>>> =
LazyLock::new(|| Pool::new(100, 100));
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum DesiredAuth {
Anonymous,
Krb5 { upn: Option<String>, spn: Option<String> },
Local,
Tls { identity: Option<String> },
}
impl FromStr for DesiredAuth {
type Err = anyhow::Error;
fn from_str(s: &str) -> std::result::Result<DesiredAuth, Self::Err> {
match s {
"anonymous" => Ok(DesiredAuth::Anonymous),
"local" => Ok(DesiredAuth::Local),
"krb5" => Ok(DesiredAuth::Krb5 { upn: None, spn: None }),
"tls" => Ok(DesiredAuth::Tls { identity: None }),
_ => bail!("expected, anonymous, local, krb5, or tls"),
}
}
}
pub(super) type Response<F> =
(GPooled<FxHashMap<PublisherId, Publisher>>, GPooled<Vec<(usize, F)>>);
pub(super) type ResponseChan<F> = oneshot::Receiver<Response<F>>;
pub(crate) async fn krb5_authentication(
principal: Option<&str>,
target_principal: &str,
con: &mut TcpStream,
) -> Result<ClientCtx> {
async fn send(con: &mut TcpStream, token: &[u8]) -> Result<()> {
let token = BoundedBytes::<L>(utils::bytes(&*token));
Ok(time::timeout(HELLO_TO, channel::write_raw(con, &token)).await??)
}
const L: usize = 1 * 1024 * 1024;
let (mut ctx, token) = task::spawn_blocking({
let principal = principal.map(String::from);
let target_principal = String::from(target_principal);
move || {
ClientCtx::new(
InitiateFlags::empty(),
principal.as_ref().map(|s| s.as_str()),
&target_principal,
None,
)
}
})
.await??;
send(con, &*token).await?;
loop {
let token: BoundedBytes<L> =
time::timeout(HELLO_TO, channel::read_raw::<_, _, 1024>(con)).await??;
match task::spawn_blocking(move || ctx.step(&*token)).await?? {
Step::Continue((nctx, token)) => {
ctx = nctx;
send(con, &*token).await?
}
Step::Finished((ctx, token)) => {
if let Some(token) = token {
send(con, &*token).await?
}
break Ok(ctx);
}
}
}
}