feat: Implement LdapStream, begin event loop
This commit is contained in:
parent
d30df04064
commit
1ef02c58dd
8 changed files with 152 additions and 56 deletions
37
src/ldap/handler.rs
Normal file
37
src/ldap/handler.rs
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
use ldap3_proto::LdapMsg;
|
||||
|
||||
use crate::ldap::{LdapStream, LdapStreamError};
|
||||
|
||||
#[tracing::instrument(name = "ldap", skip(s), fields(session = %s.session))]
|
||||
pub async fn ldap_handler(mut s: LdapStream) {
|
||||
tracing::info! {
|
||||
remote_addr = ?s.remote_addr,
|
||||
"New client connection"
|
||||
};
|
||||
|
||||
loop {
|
||||
match s.next().await {
|
||||
Ok(msg) => {
|
||||
if let Err(e) = ldap_handler_inner(msg).await {
|
||||
tracing::debug!(
|
||||
reason = ?e,
|
||||
"Failed to respond"
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
tracing::debug!(
|
||||
reason = ?e,
|
||||
"Closing connection"
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn ldap_handler_inner(msg: LdapMsg) -> Result<(), LdapStreamError> {
|
||||
tracing::debug!(msg = ?msg, "Received LDAP message");
|
||||
Ok(())
|
||||
}
|
||||
4
src/ldap/mod.rs
Normal file
4
src/ldap/mod.rs
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
mod handler;
|
||||
pub use handler::ldap_handler;
|
||||
mod stream;
|
||||
pub use stream::{LdapStream, LdapStreamError};
|
||||
69
src/ldap/stream.rs
Normal file
69
src/ldap/stream.rs
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
use futures_util::StreamExt;
|
||||
use ldap3_proto::{LdapCodec, LdapMsg};
|
||||
use tokio::time::{Duration, timeout};
|
||||
use tokio_util::codec::Framed;
|
||||
use uuid::Uuid;
|
||||
|
||||
use std::fmt;
|
||||
|
||||
use crate::stream::{AbstractSocketAddr, AbstractStreamKind};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum LdapStreamError {
|
||||
ClientClosed,
|
||||
Timeout(Duration),
|
||||
LdapError(std::io::Error),
|
||||
}
|
||||
|
||||
impl fmt::Display for LdapStreamError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::ClientClosed => write!(f, "Client closed the connection"),
|
||||
Self::Timeout(d) => write!(f, "Client timeout after {}ms", d.as_millis()),
|
||||
Self::LdapError(e) => write!(f, "Failed to parse LDAP message: {e:?}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for LdapStreamError {}
|
||||
|
||||
pub struct LdapStream {
|
||||
pub remote_addr: AbstractSocketAddr,
|
||||
pub inner: Framed<AbstractStreamKind, LdapCodec>,
|
||||
/// An arbitrary ID to correlate logs with a specific TCP session
|
||||
pub session: Uuid,
|
||||
pub timeout: Duration,
|
||||
}
|
||||
|
||||
impl LdapStream {
|
||||
pub fn new(
|
||||
remote_addr: AbstractSocketAddr,
|
||||
kind: AbstractStreamKind,
|
||||
timeout: tokio::time::Duration,
|
||||
) -> Self {
|
||||
Self {
|
||||
remote_addr,
|
||||
inner: Framed::new(kind, LdapCodec::new(None, None)),
|
||||
session: Uuid::new_v4(),
|
||||
timeout,
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn next(&mut self) -> Result<LdapMsg, LdapStreamError> {
|
||||
// Check for timeout
|
||||
let Ok(msg) = timeout(self.timeout, self.inner.next()).await else {
|
||||
return Err(LdapStreamError::Timeout(self.timeout));
|
||||
};
|
||||
|
||||
// Check for closed client connection
|
||||
let Some(msg) = msg else {
|
||||
return Err(LdapStreamError::ClientClosed);
|
||||
};
|
||||
|
||||
// So far the message may or may not be a valid LDAP message,
|
||||
// but using the codec some other conditions (eg. OOM) may trigger
|
||||
// an error. We abort whether the message failed because it was malformed,
|
||||
// because of transient IO error, or any other error condition.
|
||||
msg.map_err(LdapStreamError::LdapError)
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue