use std::error::Error;
use std::fmt;
use std::io;
use std::result::Result as StdResult;
use crate::controls::Control;
use crate::exop::Exop;
use crate::protocol::{LdapOp, MaybeControls, ResultSender};
use crate::search::parse_refs;
use crate::search::ResultEntry;
use crate::RequestId;
use lber::common::TagClass;
use lber::parse::parse_uint;
use lber::structures::Tag;
use lber::universal::Types;
use lber::IResult;
use thiserror::Error;
use tokio::sync::{mpsc, oneshot};
use tokio::time;
pub type Result<T> = std::result::Result<T, LdapError>;
#[derive(Debug, Error)]
pub enum LdapError {
#[error("empty Unix domain socket path")]
EmptyUnixPath,
#[error("the port must be empty in the ldapi scheme")]
PortInUnixPath,
#[error("I/O error: {source}")]
Io {
#[from]
source: io::Error,
},
#[error("op send error: {source}")]
OpSend {
#[from]
source: mpsc::error::SendError<(RequestId, LdapOp, Tag, MaybeControls, ResultSender)>,
},
#[error("result recv error: {source}")]
ResultRecv {
#[from]
source: oneshot::error::RecvError,
},
#[error("id scrub send error: {source}")]
IdScrubSend {
#[from]
source: mpsc::error::SendError<RequestId>,
},
#[error("timeout: {elapsed}")]
Timeout {
#[from]
elapsed: time::error::Elapsed,
},
#[error("filter parse error")]
FilterParsing,
#[error("premature end of search stream")]
EndOfStream,
#[error("url parse error: {source}")]
UrlParsing {
#[from]
source: url::ParseError,
},
#[error("unknown LDAP URL scheme: {0}")]
UnknownScheme(String),
#[cfg(feature = "tls-native")]
#[error("native TLS error: {source}")]
NativeTLS {
#[from]
source: native_tls::Error,
},
#[cfg(feature = "tls-rustls")]
#[error("rustls error: {source}")]
Rustls {
#[from]
source: rustls::TLSError,
},
#[cfg(feature = "tls-rustls")]
#[error("rustls DNS error: {source}")]
DNSName {
#[from]
source: tokio_rustls::webpki::InvalidDNSNameError,
},
#[error("LDAP operation result: {result}")]
LdapResult {
#[from]
result: LdapResult,
},
#[error("empty value set for Add")]
AddNoValues,
#[error("adapter init error: {0}")]
AdapterInit(String),
}
impl From<LdapError> for io::Error {
fn from(le: LdapError) -> io::Error {
match le {
LdapError::Io { source, .. } => source,
_ => io::Error::new(io::ErrorKind::Other, format!("{}", le)),
}
}
}
#[derive(Clone, Debug)]
pub struct LdapResult {
pub rc: u32,
pub matched: String,
pub text: String,
pub refs: Vec<String>,
pub ctrls: Vec<Control>,
}
#[doc(hidden)]
impl From<Tag> for LdapResult {
fn from(t: Tag) -> LdapResult {
<LdapResultExt as From<Tag>>::from(t).0
}
}
impl Error for LdapResult {}
impl fmt::Display for LdapResult {
fn fmt(&self, f: &mut fmt::Formatter) -> StdResult<(), fmt::Error> {
fn description(this: &LdapResult) -> &'static str {
match this.rc {
0 => "success",
1 => "operationsError",
2 => "protocolError",
3 => "timeLimitExceeded",
4 => "sizeLimitExceeded",
5 => "compareFalse",
6 => "compareTrue",
7 => "authMethodNotSupported",
8 => "strongerAuthRequired",
10 => "referral",
11 => "adminLimitExceeded",
12 => "unavailableCriticalExtension",
13 => "confidentialityRequired",
14 => "saslBindInProgress",
16 => "noSuchAttribute",
17 => "undefinedAttributeType",
18 => "inappropriateMatching",
19 => "constraintViolation",
20 => "attributeOrValueExists",
21 => "invalidAttributeSyntax",
32 => "noSuchObject",
33 => "aliasProblem",
34 => "invalidDNSyntax",
36 => "aliasDereferencingProblem",
48 => "inappropriateAuthentication",
49 => "invalidCredentials",
50 => "insufficientAccessRights",
51 => "busy",
52 => "unavailable",
53 => "unwillingToPerform",
54 => "loopDetect",
64 => "namingViolation",
65 => "objectClassViolation",
66 => "notAllowedOnNonLeaf",
67 => "notAllowedOnRDN",
68 => "entryAlreadyExists",
69 => "objectClassModsProhibited",
71 => "affectsMultipleDSAs",
80 => "other",
88 => "abandoned",
122 => "assertionFailed",
_ => "unknown",
}
}
write!(
f,
"rc={} ({}), dn: \"{}\", text: \"{}\"",
self.rc,
description(self),
self.matched,
self.text
)
}
}
impl LdapResult {
pub fn success(self) -> Result<Self> {
if self.rc == 0 {
Ok(self)
} else {
Err(LdapError::from(self))
}
}
pub fn non_error(self) -> Result<Self> {
if self.rc == 0 || self.rc == 10 {
Ok(self)
} else {
Err(LdapError::from(self))
}
}
}
#[derive(Clone, Debug)]
pub(crate) struct LdapResultExt(pub LdapResult, pub Exop);
impl From<Tag> for LdapResultExt {
fn from(t: Tag) -> LdapResultExt {
let t = match t {
Tag::StructureTag(t) => t,
Tag::Null(_) => {
return LdapResultExt(
LdapResult {
rc: 0,
matched: String::from(""),
text: String::from(""),
refs: vec![],
ctrls: vec![],
},
Exop {
name: None,
val: None,
},
)
}
_ => unimplemented!(),
};
let mut tags = t.expect_constructed().expect("result sequence").into_iter();
let rc = match parse_uint(
tags.next()
.expect("element")
.match_class(TagClass::Universal)
.and_then(|t| t.match_id(Types::Enumerated as u64))
.and_then(|t| t.expect_primitive())
.expect("result code")
.as_slice(),
) {
IResult::Done(_, rc) => rc as u32,
_ => panic!("failed to parse result code"),
};
let matched = String::from_utf8(
tags.next()
.expect("element")
.expect_primitive()
.expect("octet string"),
)
.expect("matched dn");
let text = String::from_utf8(
tags.next()
.expect("element")
.expect_primitive()
.expect("octet string"),
)
.expect("diagnostic message");
let mut refs = Vec::new();
let mut exop_name = None;
let mut exop_val = None;
loop {
match tags.next() {
None => break,
Some(comp) => match comp.id {
3 => {
refs.extend(parse_refs(comp));
}
10 => {
exop_name = Some(
String::from_utf8(comp.expect_primitive().expect("octet string"))
.expect("exop name"),
);
}
11 => {
exop_val = Some(comp.expect_primitive().expect("octet string"));
}
_ => (),
},
}
}
LdapResultExt(
LdapResult {
rc,
matched,
text,
refs,
ctrls: vec![],
},
Exop {
name: exop_name,
val: exop_val,
},
)
}
}
#[derive(Clone, Debug)]
pub struct SearchResult(pub Vec<ResultEntry>, pub LdapResult);
impl SearchResult {
pub fn success(self) -> Result<(Vec<ResultEntry>, LdapResult)> {
if self.1.rc == 0 {
Ok((self.0, self.1))
} else {
Err(LdapError::from(self.1))
}
}
pub fn non_error(self) -> Result<(Vec<ResultEntry>, LdapResult)> {
if self.1.rc == 0 || self.1.rc == 10 {
Ok((self.0, self.1))
} else {
Err(LdapError::from(self.1))
}
}
}
#[derive(Clone, Debug)]
pub struct CompareResult(pub LdapResult);
impl CompareResult {
pub fn equal(self) -> Result<bool> {
match self.0.rc {
5 => Ok(false),
6 => Ok(true),
_ => Err(LdapError::from(self.0)),
}
}
pub fn non_error(self) -> Result<LdapResult> {
if self.0.rc == 5 || self.0.rc == 6 || self.0.rc == 10 {
Ok(self.0)
} else {
Err(LdapError::from(self.0))
}
}
}
#[derive(Clone, Debug)]
pub struct ExopResult(pub Exop, pub LdapResult);
impl ExopResult {
pub fn success(self) -> Result<(Exop, LdapResult)> {
if self.1.rc == 0 {
Ok((self.0, self.1))
} else {
Err(LdapError::from(self.1))
}
}
pub fn non_error(self) -> Result<(Exop, LdapResult)> {
if self.1.rc == 0 || self.1.rc == 10 {
Ok((self.0, self.1))
} else {
Err(LdapError::from(self.1))
}
}
}