llldap/src/ldap/handler.rs

126 lines
3.8 KiB
Rust
Raw Normal View History

use ldap3_proto::LdapMsg;
2026-09-01 21:18:19 +02:00
use ldap3_proto::proto::LdapOp;
2026-09-01 19:06:33 +02:00
use crate::db::{Database, DatabaseInterface};
2026-09-03 20:31:28 +02:00
use crate::error::GlobalError;
2026-09-03 15:21:26 +02:00
use crate::ldap::{
LdapClientState, LdapStream, LdapStreamError, op_bind, op_ext, search_by_mail_filter,
};
2026-09-03 20:31:28 +02:00
use crate::listener::Listener;
pub async fn ldap_listen<D: DatabaseInterface>(listener: Listener, db: Database<D>) {
// If the connection is None, it's because the client aborted early
// so there's nothing to do about it.
loop {
match listener.accept_ldap().await {
Ok(Some(stream)) => {
let db = db.clone();
tokio::spawn(ldap_handler(stream, db));
}
Ok(None) => {
panic!("LDAP listener closed");
}
Err(e) => {
panic!("Failed to listen on LDAP listener");
}
}
}
}
2026-09-01 21:18:19 +02:00
#[tracing::instrument(name = "ldap", skip(stream, db), fields(session = %stream.session))]
pub async fn ldap_handler<D: DatabaseInterface>(mut stream: LdapStream, mut db: Database<D>) {
tracing::info! {
2026-09-01 21:18:19 +02:00
remote_addr = ?stream.remote_addr,
"New client connection"
};
2026-09-01 21:18:19 +02:00
let mut state = LdapClientState::new();
loop {
2026-09-01 21:18:19 +02:00
match stream.next().await {
Ok(msg) => match ldap_handler_inner(&mut stream, msg, &mut state, &mut db).await {
Ok(should_keep_alive) => {
if !should_keep_alive {
tracing::debug!("Finished connection");
return;
}
}
Err(e) => {
tracing::debug!(
reason = ?e,
"Failed to respond"
);
return;
}
2026-09-01 21:18:19 +02:00
},
Err(e) => {
tracing::debug!(
reason = ?e,
"Closing connection"
);
return;
}
}
}
}
2026-09-01 21:18:19 +02:00
/// Return true to keep the connection going, false to close it.
#[tracing::instrument(name = "ldap-handler", skip(client_state, db, stream))]
pub async fn ldap_handler_inner<D: DatabaseInterface>(
stream: &mut LdapStream,
msg: LdapMsg,
client_state: &mut LdapClientState,
db: &mut Database<D>,
) -> Result<bool, LdapStreamError> {
tracing::debug!(msg = ?msg, "Received LDAP message");
2026-09-01 21:18:19 +02:00
match msg {
// Disconnect
LdapMsg {
msgid: _,
op: LdapOp::UnbindRequest,
ctrl: _,
} => {
client_state.unbind();
// TODO: keep the connection open?
Ok(true)
}
LdapMsg {
msgid,
op: LdapOp::ExtendedRequest(ler),
ctrl: _,
} => {
op_ext(stream, ler, msgid, client_state).await?;
Ok(true)
}
LdapMsg {
msgid,
op: LdapOp::BindRequest(lbr),
ctrl: _,
} => {
if let Some(bound_dn) = op_bind(stream, db, lbr, msgid).await? {
tracing::debug!("Successful bind");
client_state.bind(bound_dn);
Ok(true)
} else {
2026-09-03 15:21:26 +02:00
client_state.unbind();
tracing::debug!("Failed bind or anonymous bind");
// We keep the connection open in case it's an anonymous bind
Ok(true)
2026-09-01 21:18:19 +02:00
}
}
2026-09-03 15:21:26 +02:00
LdapMsg {
msgid,
op: LdapOp::SearchRequest(sr),
// TODO: ctrl for pagination
ctrl: _,
} => {
search_by_mail_filter(stream, db, sr, msgid).await?;
Ok(true)
}
2026-09-01 21:18:19 +02:00
// Unsupported message
_ => {
tracing::warn!("Unsupported client message, closing connection");
Ok(false)
}
}
}