use bytes::{Buf, BufMut, BytesMut};
use std::io::{self, Cursor};
use tokio_util::codec::{Decoder, Encoder};
use tracing::debug;
use super::protocol::*;
const LDAP_BIND_REQUEST: u8 = 0x60;
const LDAP_BIND_RESPONSE: u8 = 0x61;
const LDAP_UNBIND_REQUEST: u8 = 0x42;
const LDAP_SEARCH_REQUEST: u8 = 0x63;
const LDAP_SEARCH_RESULT_ENTRY: u8 = 0x64;
const LDAP_SEARCH_RESULT_DONE: u8 = 0x65;
const LDAP_COMPARE_REQUEST: u8 = 0x6e;
const LDAP_COMPARE_RESPONSE: u8 = 0x6f;
const LDAP_ABANDON_REQUEST: u8 = 0x50;
const LDAP_EXTENDED_REQUEST: u8 = 0x77;
const LDAP_EXTENDED_RESPONSE: u8 = 0x78;
pub struct SimpleLdapCodec;
impl SimpleLdapCodec {
fn read_length(buf: &mut Cursor<&[u8]>) -> io::Result<usize> {
if buf.remaining() < 1 {
return Err(io::Error::new(
io::ErrorKind::UnexpectedEof,
"Not enough data for length",
));
}
let first_byte = buf.get_u8();
if first_byte & 0x80 == 0 {
Ok(first_byte as usize)
} else {
let num_octets = (first_byte & 0x7f) as usize;
if num_octets > 4 || buf.remaining() < num_octets {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"Invalid length encoding",
));
}
let mut length = 0usize;
for _ in 0..num_octets {
length = (length << 8) | (buf.get_u8() as usize);
}
Ok(length)
}
}
fn write_length(buf: &mut BytesMut, length: usize) {
if length < 128 {
buf.put_u8(length as u8);
} else if length < 256 {
buf.put_u8(0x81);
buf.put_u8(length as u8);
} else if length < 65536 {
buf.put_u8(0x82);
buf.put_u16(length as u16);
} else {
buf.put_u8(0x83);
buf.put_u8((length >> 16) as u8);
buf.put_u8((length >> 8) as u8);
buf.put_u8(length as u8);
}
}
fn read_string(buf: &mut Cursor<&[u8]>) -> io::Result<String> {
if buf.remaining() < 1 || buf.get_u8() != 0x04 {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"Expected OCTET STRING",
));
}
let length = Self::read_length(buf)?;
if buf.remaining() < length {
return Err(io::Error::new(
io::ErrorKind::UnexpectedEof,
"Not enough data for string",
));
}
if buf.remaining() < length {
return Err(io::Error::new(
io::ErrorKind::UnexpectedEof,
format!(
"Not enough bytes: need {} but only {} available",
length,
buf.remaining()
),
));
}
let mut bytes = vec![0u8; length];
buf.copy_to_slice(&mut bytes);
String::from_utf8(bytes)
.map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "Invalid UTF-8"))
}
fn write_string(buf: &mut BytesMut, s: &str) {
buf.put_u8(0x04); Self::write_length(buf, s.len());
buf.put_slice(s.as_bytes());
}
fn read_integer(buf: &mut Cursor<&[u8]>) -> io::Result<u32> {
if buf.remaining() < 1 || buf.get_u8() != 0x02 {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"Expected INTEGER",
));
}
let length = Self::read_length(buf)?;
if length > 4 || buf.remaining() < length {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"Invalid integer",
));
}
let mut value = 0u32;
for _ in 0..length {
value = (value << 8) | (buf.get_u8() as u32);
}
Ok(value)
}
fn write_integer(buf: &mut BytesMut, value: u32) {
buf.put_u8(0x02); if value < 128 {
buf.put_u8(1);
buf.put_u8(value as u8);
} else if value < 32768 {
buf.put_u8(2);
buf.put_u16(value as u16);
} else {
buf.put_u8(4);
buf.put_u32(value);
}
}
fn read_filter(cursor: &mut Cursor<&[u8]>) -> io::Result<String> {
if cursor.remaining() < 1 {
return Err(io::Error::new(
io::ErrorKind::UnexpectedEof,
"No filter data",
));
}
let tag = cursor.get_u8();
let length = Self::read_length(cursor)?;
match tag {
0xA0 => {
let _end_pos = cursor.position() + length as u64;
let mut filters = Vec::new();
while cursor.position() < _end_pos {
filters.push(Self::read_filter(cursor)?);
}
Ok(format!("(&{})", filters.join("")))
}
0xA1 => {
let _end_pos = cursor.position() + length as u64;
let mut filters = Vec::new();
while cursor.position() < _end_pos {
filters.push(Self::read_filter(cursor)?);
}
Ok(format!("(|{})", filters.join("")))
}
0xA2 => {
let filter = Self::read_filter(cursor)?;
Ok(format!("(!{})", filter))
}
0xA3 => {
let _end_pos = cursor.position() + length as u64;
let attr = Self::read_string(cursor)?;
let value = Self::read_string(cursor)?;
Ok(format!("({}={})", attr, value))
}
0xA4 => {
let _end_pos = cursor.position() + length as u64;
let attr = Self::read_string(cursor)?;
if cursor.position() < _end_pos
&& cursor.get_ref()[cursor.position() as usize] == 0x30
{
cursor.get_u8(); let _seq_len = Self::read_length(cursor)?;
let mut parts = Vec::new();
let mut has_initial = false;
let mut has_final = false;
while cursor.position() < _end_pos {
let sub_tag = cursor.get_u8();
let sub_len = Self::read_length(cursor)?;
if cursor.remaining() < sub_len {
return Err(io::Error::new(
io::ErrorKind::UnexpectedEof,
format!(
"Not enough bytes for substring: need {} but only {} available",
sub_len,
cursor.remaining()
),
));
}
let mut bytes = vec![0u8; sub_len];
cursor.copy_to_slice(&mut bytes);
let value = String::from_utf8(bytes).map_err(|_| {
io::Error::new(io::ErrorKind::InvalidData, "Invalid UTF-8")
})?;
match sub_tag {
0x80 => {
has_initial = true;
parts.insert(0, value);
}
0x81 => {
parts.push(format!("*{}", value));
}
0x82 => {
has_final = true;
parts.push(format!("*{}", value));
}
_ => {}
}
}
let mut filter = format!("({}=", attr);
if !has_initial {
filter.push('*');
}
filter.push_str(&parts.join(""));
if !has_final {
filter.push('*');
}
filter.push(')');
Ok(filter)
} else {
Ok(format!("({}=*)", attr))
}
}
0xA5 => {
let attr = Self::read_string(cursor)?;
let value = Self::read_string(cursor)?;
Ok(format!("({}>={})", attr, value))
}
0xA6 => {
let attr = Self::read_string(cursor)?;
let value = Self::read_string(cursor)?;
Ok(format!("({}<={})", attr, value))
}
0x87 => {
if cursor.remaining() < length {
return Err(io::Error::new(
io::ErrorKind::UnexpectedEof,
format!(
"Not enough bytes for present attr: need {} but only {} available",
length,
cursor.remaining()
),
));
}
let mut bytes = vec![0u8; length];
cursor.copy_to_slice(&mut bytes);
let attr = String::from_utf8(bytes)
.map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "Invalid UTF-8"))?;
Ok(format!("({}=*)", attr))
}
0xA8 => {
let _end_pos = cursor.position() + length as u64;
let attr = Self::read_string(cursor)?;
let value = Self::read_string(cursor)?;
Ok(format!("({}~={})", attr, value))
}
0xA9 => {
let end_pos = cursor.position() + length as u64;
let mut matching_rule = None;
let mut attr_type = None;
let mut match_value = String::new();
let mut dn_attributes = false;
while cursor.position() < end_pos {
if cursor.remaining() < 1 {
break;
}
let tag = cursor.get_u8();
let len = Self::read_length(cursor)?;
match tag {
0x81 => {
if cursor.remaining() < len {
return Err(io::Error::new(
io::ErrorKind::UnexpectedEof,
format!("Not enough bytes for matching rule: need {} but only {} available", len, cursor.remaining()),
));
}
let mut bytes = vec![0u8; len];
cursor.copy_to_slice(&mut bytes);
matching_rule = Some(String::from_utf8(bytes).map_err(|_| {
io::Error::new(io::ErrorKind::InvalidData, "Invalid UTF-8")
})?);
}
0x82 => {
if cursor.remaining() < len {
return Err(io::Error::new(
io::ErrorKind::UnexpectedEof,
format!("Not enough bytes for attr type: need {} but only {} available", len, cursor.remaining()),
));
}
let mut bytes = vec![0u8; len];
cursor.copy_to_slice(&mut bytes);
attr_type = Some(String::from_utf8(bytes).map_err(|_| {
io::Error::new(io::ErrorKind::InvalidData, "Invalid UTF-8")
})?);
}
0x83 => {
if cursor.remaining() < len {
return Err(io::Error::new(
io::ErrorKind::UnexpectedEof,
format!("Not enough bytes for match value: need {} but only {} available", len, cursor.remaining()),
));
}
let mut bytes = vec![0u8; len];
cursor.copy_to_slice(&mut bytes);
match_value = String::from_utf8(bytes).map_err(|_| {
io::Error::new(io::ErrorKind::InvalidData, "Invalid UTF-8")
})?;
}
0x84 => {
if len == 1 {
dn_attributes = cursor.get_u8() != 0x00;
}
}
_ => {
cursor.advance(len);
}
}
}
let mut filter = String::from("(");
if let Some(attr) = attr_type {
filter.push_str(&attr);
}
if dn_attributes {
filter.push_str(":dn");
}
if let Some(rule) = matching_rule {
filter.push(':');
filter.push_str(&rule);
}
filter.push_str(":=");
filter.push_str(&match_value);
filter.push(')');
Ok(filter)
}
_ => {
cursor.set_position(cursor.position() + length as u64);
Ok("(objectClass=*)".to_string()) }
}
}
}
impl Decoder for SimpleLdapCodec {
type Item = LdapMessage;
type Error = io::Error;
fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
if src.len() < 5 {
return Ok(None);
}
if src[0] != 0x30 {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"Expected SEQUENCE",
));
}
let (msg_length, header_len) = if src[1] & 0x80 == 0 {
(src[1] as usize, 2)
} else {
let num_octets = (src[1] & 0x7f) as usize;
if src.len() < 2 + num_octets {
return Ok(None);
}
let mut length = 0usize;
for i in 0..num_octets {
length = (length << 8) | (src[2 + i] as usize);
}
(length, 2 + num_octets)
};
let total_len = header_len + msg_length;
if src.len() < total_len {
return Ok(None); }
let mut cursor = Cursor::new(&src[..total_len]);
cursor.set_position(header_len as u64);
let message_id = Self::read_integer(&mut cursor)?;
let op_tag = cursor.get_u8();
debug!(
"Received LDAP message: id={}, op_tag=0x{:02x}",
message_id, op_tag
);
let protocol_op = match op_tag {
LDAP_BIND_REQUEST => {
let _length = Self::read_length(&mut cursor)?;
let version = Self::read_integer(&mut cursor)? as u8;
let dn = Self::read_string(&mut cursor)?;
let auth = if cursor.remaining() > 0 {
let auth_tag = cursor.get_u8();
if auth_tag == 0x80 {
let pass_len = Self::read_length(&mut cursor)?;
if cursor.remaining() < pass_len {
return Err(io::Error::new(
io::ErrorKind::UnexpectedEof,
format!(
"Not enough bytes for password: need {} but only {} available",
pass_len,
cursor.remaining()
),
));
}
let mut pass_bytes = vec![0u8; pass_len];
cursor.copy_to_slice(&mut pass_bytes);
let password = String::from_utf8(pass_bytes).map_err(|_| {
io::Error::new(io::ErrorKind::InvalidData, "Invalid UTF-8")
})?;
BindAuthentication::Simple(password)
} else {
BindAuthentication::Anonymous
}
} else {
BindAuthentication::Anonymous
};
LdapProtocolOp::BindRequest {
version,
dn,
authentication: auth,
}
}
LDAP_UNBIND_REQUEST => LdapProtocolOp::UnbindRequest,
LDAP_SEARCH_REQUEST => {
let _length = Self::read_length(&mut cursor)?;
let base_dn = Self::read_string(&mut cursor)?;
if cursor.remaining() < 1 || cursor.get_u8() != 0x0A {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"Expected ENUMERATED for scope",
));
}
let scope_len = Self::read_length(&mut cursor)?;
if scope_len != 1 || cursor.remaining() < 1 {
return Err(io::Error::new(io::ErrorKind::InvalidData, "Invalid scope"));
}
let scope_value = cursor.get_u8();
let scope = match scope_value {
0 => SearchScope::BaseObject,
1 => SearchScope::SingleLevel,
2 => SearchScope::WholeSubtree,
_ => SearchScope::WholeSubtree, };
if cursor.remaining() < 1 || cursor.get_u8() != 0x0A {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"Expected ENUMERATED for derefAliases",
));
}
let deref_len = Self::read_length(&mut cursor)?;
if deref_len != 1 || cursor.remaining() < 1 {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"Invalid derefAliases",
));
}
let _deref_value = cursor.get_u8();
let size_limit = Self::read_integer(&mut cursor)?;
let time_limit = Self::read_integer(&mut cursor)?;
if cursor.remaining() < 1 || cursor.get_u8() != 0x01 {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"Expected BOOLEAN for typesOnly",
));
}
let bool_len = Self::read_length(&mut cursor)?;
if bool_len != 1 || cursor.remaining() < 1 {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"Invalid boolean",
));
}
let types_only = cursor.get_u8() != 0x00;
let filter = Self::read_filter(&mut cursor)?;
let mut attributes = Vec::new();
if cursor.remaining() > 0 && cursor.get_ref()[cursor.position() as usize] == 0x30 {
cursor.get_u8(); let attrs_len = Self::read_length(&mut cursor)?;
let attrs_end = cursor.position() + attrs_len as u64;
while cursor.position() < attrs_end {
let attr = Self::read_string(&mut cursor)?;
attributes.push(attr);
}
}
LdapProtocolOp::SearchRequest {
base_dn,
scope,
deref_aliases: DerefAliases::NeverDerefAliases,
size_limit,
time_limit,
types_only,
filter,
attributes,
}
}
LDAP_COMPARE_REQUEST => {
let _length = Self::read_length(&mut cursor)?;
let dn = Self::read_string(&mut cursor)?;
if cursor.remaining() < 1 || cursor.get_u8() != 0x30 {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"Expected SEQUENCE for AttributeValueAssertion",
));
}
let ava_len = Self::read_length(&mut cursor)?;
let ava_end = cursor.position() + ava_len as u64;
let attribute = Self::read_string(&mut cursor)?;
let value = if cursor.position() < ava_end {
Self::read_string(&mut cursor)?
} else {
String::new()
};
LdapProtocolOp::CompareRequest {
dn,
attribute,
value,
}
}
LDAP_ABANDON_REQUEST => {
let length = Self::read_length(&mut cursor)?;
if length > 4 || cursor.remaining() < length {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"Invalid abandon message ID",
));
}
let mut abandon_message_id = 0u32;
for _ in 0..length {
abandon_message_id = (abandon_message_id << 8) | (cursor.get_u8() as u32);
}
LdapProtocolOp::AbandonRequest {
message_id: abandon_message_id,
}
}
LDAP_EXTENDED_REQUEST => {
let _length = Self::read_length(&mut cursor)?;
if cursor.remaining() < 1 || cursor.get_u8() != 0x80 {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"Expected context-specific [0] for requestName",
));
}
let name_len = Self::read_length(&mut cursor)?;
if cursor.remaining() < name_len {
return Err(io::Error::new(
io::ErrorKind::UnexpectedEof,
format!(
"Not enough bytes for attr name: need {} but only {} available",
name_len,
cursor.remaining()
),
));
}
let mut name_bytes = vec![0u8; name_len];
cursor.copy_to_slice(&mut name_bytes);
let name = String::from_utf8(name_bytes).map_err(|_| {
io::Error::new(io::ErrorKind::InvalidData, "Invalid UTF-8 in OID")
})?;
let value = if cursor.remaining() > 0
&& cursor.get_ref()[cursor.position() as usize] == 0x81
{
cursor.get_u8(); let value_len = Self::read_length(&mut cursor)?;
if cursor.remaining() < value_len {
return Err(io::Error::new(
io::ErrorKind::UnexpectedEof,
format!(
"Not enough bytes for attr value: need {} but only {} available",
value_len,
cursor.remaining()
),
));
}
let mut value_bytes = vec![0u8; value_len];
cursor.copy_to_slice(&mut value_bytes);
Some(value_bytes)
} else {
None
};
LdapProtocolOp::ExtendedRequest { name, value }
}
_ => {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
format!("Unsupported operation tag: 0x{:02x}", op_tag),
));
}
};
src.advance(total_len);
Ok(Some(LdapMessage {
message_id,
protocol_op,
}))
}
}
impl Encoder<LdapMessage> for SimpleLdapCodec {
type Error = io::Error;
fn encode(&mut self, item: LdapMessage, dst: &mut BytesMut) -> Result<(), Self::Error> {
dst.reserve(256);
let mut content = BytesMut::new();
Self::write_integer(&mut content, item.message_id);
match item.protocol_op {
LdapProtocolOp::BindResponse { ref result } => {
let mut bind_content = BytesMut::new();
Self::write_integer(&mut bind_content, result.result_code as u32);
Self::write_string(&mut bind_content, &result.matched_dn);
Self::write_string(&mut bind_content, &result.diagnostic_message);
content.put_u8(LDAP_BIND_RESPONSE);
Self::write_length(&mut content, bind_content.len());
content.put(bind_content);
}
LdapProtocolOp::SearchResultEntry {
ref dn,
ref attributes,
} => {
let mut entry_content = BytesMut::new();
Self::write_string(&mut entry_content, dn);
let mut attrs_content = BytesMut::new();
for (name, values) in attributes {
let mut attr_content = BytesMut::new();
Self::write_string(&mut attr_content, name);
let mut values_content = BytesMut::new();
for value in values {
Self::write_string(&mut values_content, value);
}
attr_content.put_u8(0x31); Self::write_length(&mut attr_content, values_content.len());
attr_content.put(values_content);
attrs_content.put_u8(0x30); Self::write_length(&mut attrs_content, attr_content.len());
attrs_content.put(attr_content);
}
entry_content.put_u8(0x30); Self::write_length(&mut entry_content, attrs_content.len());
entry_content.put(attrs_content);
content.put_u8(LDAP_SEARCH_RESULT_ENTRY);
Self::write_length(&mut content, entry_content.len());
content.put(entry_content);
}
LdapProtocolOp::SearchResultDone { ref result } => {
let mut done_content = BytesMut::new();
Self::write_integer(&mut done_content, result.result_code as u32);
Self::write_string(&mut done_content, &result.matched_dn);
Self::write_string(&mut done_content, &result.diagnostic_message);
content.put_u8(LDAP_SEARCH_RESULT_DONE);
Self::write_length(&mut content, done_content.len());
content.put(done_content);
}
LdapProtocolOp::CompareResponse { ref result } => {
let mut response_content = BytesMut::new();
Self::write_integer(&mut response_content, result.result_code as u32);
Self::write_string(&mut response_content, &result.matched_dn);
Self::write_string(&mut response_content, &result.diagnostic_message);
content.put_u8(LDAP_COMPARE_RESPONSE);
Self::write_length(&mut content, response_content.len());
content.put(response_content);
}
LdapProtocolOp::ExtendedResponse {
ref result,
ref name,
ref value,
} => {
let mut response_content = BytesMut::new();
Self::write_integer(&mut response_content, result.result_code as u32);
Self::write_string(&mut response_content, &result.matched_dn);
Self::write_string(&mut response_content, &result.diagnostic_message);
if let Some(oid) = name {
response_content.put_u8(0x8A); Self::write_length(&mut response_content, oid.len());
response_content.put_slice(oid.as_bytes());
}
if let Some(val) = value {
response_content.put_u8(0x8B); Self::write_length(&mut response_content, val.len());
response_content.put_slice(val);
}
content.put_u8(LDAP_EXTENDED_RESPONSE);
Self::write_length(&mut content, response_content.len());
content.put(response_content);
}
_ => {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"Unsupported operation for encoding",
));
}
}
dst.put_u8(0x30); Self::write_length(dst, content.len());
dst.put(content);
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::collections::HashMap;
#[test]
fn test_read_write_length_short_form() {
let mut buf = BytesMut::new();
SimpleLdapCodec::write_length(&mut buf, 42);
let mut cursor = Cursor::new(buf.as_ref());
let length = SimpleLdapCodec::read_length(&mut cursor).unwrap();
assert_eq!(length, 42);
}
#[test]
fn test_read_write_length_long_form_1_byte() {
let mut buf = BytesMut::new();
SimpleLdapCodec::write_length(&mut buf, 200);
let mut cursor = Cursor::new(buf.as_ref());
let length = SimpleLdapCodec::read_length(&mut cursor).unwrap();
assert_eq!(length, 200);
}
#[test]
fn test_read_write_length_long_form_2_bytes() {
let mut buf = BytesMut::new();
SimpleLdapCodec::write_length(&mut buf, 1000);
let mut cursor = Cursor::new(buf.as_ref());
let length = SimpleLdapCodec::read_length(&mut cursor).unwrap();
assert_eq!(length, 1000);
}
#[test]
fn test_read_write_length_long_form_3_bytes() {
let mut buf = BytesMut::new();
SimpleLdapCodec::write_length(&mut buf, 100000);
let mut cursor = Cursor::new(buf.as_ref());
let length = SimpleLdapCodec::read_length(&mut cursor).unwrap();
assert_eq!(length, 100000);
}
#[test]
fn test_read_length_insufficient_data() {
let buf = vec![];
let mut cursor = Cursor::new(buf.as_ref());
let result = SimpleLdapCodec::read_length(&mut cursor);
assert!(result.is_err());
assert_eq!(result.unwrap_err().kind(), io::ErrorKind::UnexpectedEof);
}
#[test]
fn test_read_length_invalid_long_form() {
let buf = vec![0x85]; let mut cursor = Cursor::new(buf.as_ref());
let result = SimpleLdapCodec::read_length(&mut cursor);
assert!(result.is_err());
assert_eq!(result.unwrap_err().kind(), io::ErrorKind::InvalidData);
}
#[test]
fn test_read_write_string() {
let mut buf = BytesMut::new();
SimpleLdapCodec::write_string(&mut buf, "hello world");
let mut cursor = Cursor::new(buf.as_ref());
let string = SimpleLdapCodec::read_string(&mut cursor).unwrap();
assert_eq!(string, "hello world");
}
#[test]
fn test_read_string_invalid_tag() {
let buf = vec![0x05]; let mut cursor = Cursor::new(buf.as_ref());
let result = SimpleLdapCodec::read_string(&mut cursor);
assert!(result.is_err());
assert_eq!(result.unwrap_err().kind(), io::ErrorKind::InvalidData);
}
#[test]
fn test_read_string_insufficient_data() {
let buf = vec![0x04, 0x10]; let mut cursor = Cursor::new(buf.as_ref());
let result = SimpleLdapCodec::read_string(&mut cursor);
assert!(result.is_err());
assert_eq!(result.unwrap_err().kind(), io::ErrorKind::UnexpectedEof);
}
#[test]
fn test_read_string_invalid_utf8() {
let buf = vec![0x04, 0x02, 0xFF, 0xFF]; let mut cursor = Cursor::new(buf.as_ref());
let result = SimpleLdapCodec::read_string(&mut cursor);
assert!(result.is_err());
assert_eq!(result.unwrap_err().kind(), io::ErrorKind::InvalidData);
}
#[test]
fn test_read_write_integer() {
let mut buf = BytesMut::new();
SimpleLdapCodec::write_integer(&mut buf, 42);
let mut cursor = Cursor::new(buf.as_ref());
let value = SimpleLdapCodec::read_integer(&mut cursor).unwrap();
assert_eq!(value, 42);
}
#[test]
fn test_read_integer_invalid_tag() {
let buf = vec![0x03]; let mut cursor = Cursor::new(buf.as_ref());
let result = SimpleLdapCodec::read_integer(&mut cursor);
assert!(result.is_err());
assert_eq!(result.unwrap_err().kind(), io::ErrorKind::InvalidData);
}
#[test]
fn test_decode_empty_buffer() {
let mut codec = SimpleLdapCodec;
let mut buf = BytesMut::new();
let result = codec.decode(&mut buf).unwrap();
assert!(result.is_none());
}
#[test]
fn test_decode_partial_message() {
let mut codec = SimpleLdapCodec;
let mut buf = BytesMut::from(&[0x30, 0x10][..]); let result = codec.decode(&mut buf).unwrap();
assert!(result.is_none());
}
#[test]
fn test_decode_invalid_sequence_tag() {
let mut codec = SimpleLdapCodec;
let mut buf = BytesMut::from(&[0x31, 0x02, 0x00, 0x00, 0x00][..]); let result = codec.decode(&mut buf);
assert!(result.is_err());
assert_eq!(result.unwrap_err().kind(), io::ErrorKind::InvalidData);
}
#[test]
fn test_encode_bind_request() {
let mut codec = SimpleLdapCodec;
let mut buf = BytesMut::new();
let msg = LdapMessage {
message_id: 1,
protocol_op: LdapProtocolOp::BindRequest {
version: 3,
dn: "".to_string(), authentication: BindAuthentication::Anonymous,
},
};
let result = codec.encode(msg, &mut buf);
assert!(result.is_err());
assert_eq!(result.unwrap_err().kind(), io::ErrorKind::InvalidData);
}
#[test]
fn test_encode_bind_response() {
let mut codec = SimpleLdapCodec;
let mut buf = BytesMut::new();
let msg = LdapMessage {
message_id: 1,
protocol_op: LdapProtocolOp::BindResponse {
result: LdapResult::success(),
},
};
let result = codec.encode(msg, &mut buf);
assert!(result.is_ok());
assert!(!buf.is_empty());
}
#[test]
fn test_encode_search_result_entry() {
let mut codec = SimpleLdapCodec;
let mut buf = BytesMut::new();
let mut attrs = HashMap::new();
attrs.insert("cn".to_string(), vec!["test".to_string()]);
let msg = LdapMessage {
message_id: 2,
protocol_op: LdapProtocolOp::SearchResultEntry {
dn: "cn=test,dc=example,dc=com".to_string(),
attributes: attrs,
},
};
let result = codec.encode(msg, &mut buf);
assert!(result.is_ok());
assert!(!buf.is_empty());
}
#[test]
fn test_encode_search_result_done() {
let mut codec = SimpleLdapCodec;
let mut buf = BytesMut::new();
let msg = LdapMessage {
message_id: 3,
protocol_op: LdapProtocolOp::SearchResultDone {
result: LdapResult::error(LdapResultCode::NoSuchObject, "Not found".to_string()),
},
};
let result = codec.encode(msg, &mut buf);
assert!(result.is_ok());
assert!(!buf.is_empty());
}
#[test]
fn test_encode_unsupported_operation() {
let mut codec = SimpleLdapCodec;
let mut buf = BytesMut::new();
let msg = LdapMessage {
message_id: 1,
protocol_op: LdapProtocolOp::UnbindRequest,
};
let result = codec.encode(msg, &mut buf);
assert!(result.is_err());
assert_eq!(result.unwrap_err().kind(), io::ErrorKind::InvalidData);
}
#[test]
fn test_roundtrip_bind_response() {
let mut codec = SimpleLdapCodec;
let original = LdapMessage {
message_id: 42,
protocol_op: LdapProtocolOp::BindResponse {
result: LdapResult::success(),
},
};
let mut buf = BytesMut::new();
codec.encode(original.clone(), &mut buf).unwrap();
assert!(!buf.is_empty());
assert_eq!(buf[0], 0x30); }
#[test]
fn test_write_integer_various_sizes() {
let mut buf = BytesMut::new();
SimpleLdapCodec::write_integer(&mut buf, 127);
assert_eq!(buf[0], 0x02); assert_eq!(buf[1], 0x01); assert_eq!(buf[2], 127);
let mut buf = BytesMut::new();
SimpleLdapCodec::write_integer(&mut buf, 300);
assert_eq!(buf[0], 0x02); assert_eq!(buf[1], 0x02); assert_eq!(buf[2], 0x01); assert_eq!(buf[3], 0x2C); }
#[test]
fn test_read_integer_multi_byte() {
let buf = vec![0x02, 0x02, 0x01, 0x2C]; let mut cursor = Cursor::new(buf.as_ref());
let value = SimpleLdapCodec::read_integer(&mut cursor).unwrap();
assert_eq!(value, 300);
}
#[test]
fn test_decode_with_debug_logging() {
let mut codec = SimpleLdapCodec;
let mut buf = BytesMut::new();
buf.put_u8(0x30); buf.put_u8(0x0C); buf.put_u8(0x02); buf.put_u8(0x01); buf.put_u8(0x01); buf.put_u8(0x60); buf.put_u8(0x07); buf.put_u8(0x02); buf.put_u8(0x01); buf.put_u8(0x03); buf.put_u8(0x04); buf.put_u8(0x00); buf.put_u8(0x80); buf.put_u8(0x00);
let result = codec.decode(&mut buf);
assert!(result.is_ok());
let msg = result.unwrap().unwrap();
assert_eq!(msg.message_id, 1);
}
#[test]
fn test_decode_compare_request() {
let mut codec = SimpleLdapCodec;
let mut buf = BytesMut::new();
let mut compare_content = BytesMut::new();
compare_content.put_u8(0x04); compare_content.put_u8(0x0e); compare_content.put_slice(b"cn=test,dc=com");
compare_content.put_u8(0x30); compare_content.put_u8(0x0a);
compare_content.put_u8(0x04); compare_content.put_u8(0x02); compare_content.put_slice(b"cn");
compare_content.put_u8(0x04); compare_content.put_u8(0x04); compare_content.put_slice(b"test");
let compare_len = compare_content.len();
let mut message_content = BytesMut::new();
message_content.put_u8(0x02); message_content.put_u8(0x01); message_content.put_u8(0x01);
message_content.put_u8(0x6e); message_content.put_u8(compare_len as u8); message_content.put(compare_content);
buf.put_u8(0x30); buf.put_u8(message_content.len() as u8);
buf.put(message_content);
let result = codec.decode(&mut buf);
if let Err(e) = &result {
panic!("Decode failed: {:?}", e);
}
assert!(result.is_ok());
let msg = result.unwrap().unwrap();
assert_eq!(msg.message_id, 1);
match msg.protocol_op {
LdapProtocolOp::CompareRequest {
dn,
attribute,
value,
} => {
assert_eq!(dn, "cn=test,dc=com");
assert_eq!(attribute, "cn");
assert_eq!(value, "test");
}
_ => panic!("Expected CompareRequest"),
}
}
#[test]
fn test_decode_abandon_request() {
let mut codec = SimpleLdapCodec;
let mut buf = BytesMut::new();
let mut message_content = BytesMut::new();
message_content.put_u8(0x02); message_content.put_u8(0x01); message_content.put_u8(0x01);
message_content.put_u8(0x50); message_content.put_u8(0x01); message_content.put_u8(0x05);
buf.put_u8(0x30); buf.put_u8(message_content.len() as u8);
buf.put(message_content);
let result = codec.decode(&mut buf);
assert!(result.is_ok());
let msg = result.unwrap().unwrap();
assert_eq!(msg.message_id, 1);
match msg.protocol_op {
LdapProtocolOp::AbandonRequest { message_id } => {
assert_eq!(message_id, 5);
}
_ => panic!("Expected AbandonRequest"),
}
}
#[test]
fn test_decode_extended_request() {
let mut codec = SimpleLdapCodec;
let mut buf = BytesMut::new();
let mut message_content = BytesMut::new();
message_content.put_u8(0x02); message_content.put_u8(0x01); message_content.put_u8(0x01);
let mut extended_content = BytesMut::new();
let oid = "1.3.6.1.4.1.1466.20037";
extended_content.put_u8(0x80); extended_content.put_u8(oid.len() as u8);
extended_content.put_slice(oid.as_bytes());
message_content.put_u8(0x77); message_content.put_u8(extended_content.len() as u8);
message_content.put(extended_content);
buf.put_u8(0x30); buf.put_u8(message_content.len() as u8);
buf.put(message_content);
let result = codec.decode(&mut buf);
assert!(result.is_ok());
let msg = result.unwrap().unwrap();
assert_eq!(msg.message_id, 1);
match msg.protocol_op {
LdapProtocolOp::ExtendedRequest { name, value } => {
assert_eq!(name, "1.3.6.1.4.1.1466.20037");
assert!(value.is_none());
}
_ => panic!("Expected ExtendedRequest"),
}
}
#[test]
fn test_decode_malformed_message() {
let mut codec = SimpleLdapCodec;
let mut buf = BytesMut::new();
buf.put_u8(0x31); buf.put_u8(0x05);
buf.put_slice(&[0x02, 0x01, 0x01, 0x60, 0x00]);
let result = codec.decode(&mut buf);
assert!(result.is_err());
assert_eq!(result.unwrap_err().kind(), io::ErrorKind::InvalidData);
}
#[test]
fn test_decode_truncated_messages() {
let mut codec = SimpleLdapCodec;
let mut buf = BytesMut::new();
buf.put_u8(0x30); buf.put_u8(0x03); buf.put_u8(0x02); buf.put_u8(0x02); buf.put_u8(0x01);
let result = codec.decode(&mut buf);
assert!(result.is_err());
}
#[test]
fn test_decode_invalid_length_encoding() {
let mut codec = SimpleLdapCodec;
let mut buf = BytesMut::new();
buf.put_u8(0x30); buf.put_u8(0xFF);
let result = codec.decode(&mut buf);
assert!(result.is_ok());
assert!(result.unwrap().is_none()); }
#[test]
fn test_decode_oversized_message() {
let mut codec = SimpleLdapCodec;
let mut buf = BytesMut::new();
buf.put_u8(0x30); buf.put_u8(0x84); buf.put_slice(&[0x00, 0x10, 0x00, 0x00]); buf.put_slice(&[0x02, 0x01, 0x01]);
let result = codec.decode(&mut buf);
assert!(result.is_ok()); assert!(result.unwrap().is_none());
}
#[test]
fn test_decode_invalid_operation_tag() {
let mut codec = SimpleLdapCodec;
let mut buf = BytesMut::new();
let mut message_content = BytesMut::new();
message_content.put_u8(0x02); message_content.put_u8(0x01); message_content.put_u8(0x01);
message_content.put_u8(0x99); message_content.put_u8(0x00);
buf.put_u8(0x30); buf.put_u8(message_content.len() as u8);
buf.put(message_content);
let result = codec.decode(&mut buf);
assert!(result.is_err());
assert!(result
.unwrap_err()
.to_string()
.contains("Unsupported operation tag"));
}
#[test]
fn test_decode_invalid_abandon_message_id() {
let mut codec = SimpleLdapCodec;
let mut buf = BytesMut::new();
let mut message_content = BytesMut::new();
message_content.put_u8(0x02); message_content.put_u8(0x01); message_content.put_u8(0x01);
message_content.put_u8(0x50); message_content.put_u8(0x05); message_content.put_slice(&[0x01, 0x02, 0x03, 0x04, 0x05]);
buf.put_u8(0x30); buf.put_u8(message_content.len() as u8);
buf.put(message_content);
let result = codec.decode(&mut buf);
assert!(result.is_err());
}
#[test]
fn test_encode_extended_response() {
let mut codec = SimpleLdapCodec;
let msg = LdapMessage {
message_id: 1,
protocol_op: LdapProtocolOp::ExtendedResponse {
result: LdapResult {
result_code: LdapResultCode::Unavailable,
matched_dn: String::new(),
diagnostic_message: "StartTLS is not supported".to_string(),
},
name: Some("1.3.6.1.4.1.1466.20037".to_string()),
value: None,
},
};
let mut buf = BytesMut::new();
let result = codec.encode(msg, &mut buf);
assert!(result.is_ok());
assert!(!buf.is_empty());
assert_eq!(buf[0], 0x30);
}
#[test]
fn test_encode_compare_response() {
let mut codec = SimpleLdapCodec;
let msg = LdapMessage {
message_id: 1,
protocol_op: LdapProtocolOp::CompareResponse {
result: LdapResult {
result_code: LdapResultCode::CompareTrue,
matched_dn: "cn=test,dc=com".to_string(),
diagnostic_message: String::new(),
},
},
};
let mut buf = BytesMut::new();
let result = codec.encode(msg, &mut buf);
assert!(result.is_ok());
assert!(!buf.is_empty());
assert_eq!(buf[0], 0x30);
let mut found_response_tag = false;
for i in 0..buf.len() {
if buf[i] == LDAP_COMPARE_RESPONSE {
found_response_tag = true;
break;
}
}
assert!(found_response_tag, "Compare response tag not found");
}
#[test]
fn test_decode_search_request_with_empty_attributes() {
let mut codec = SimpleLdapCodec;
let mut buf = BytesMut::new();
let mut search_content = BytesMut::new();
search_content.put_u8(0x04); search_content.put_u8(0x00);
search_content.put_u8(0x0A); search_content.put_u8(0x01);
search_content.put_u8(0x02);
search_content.put_u8(0x0A); search_content.put_u8(0x01);
search_content.put_u8(0x00);
search_content.put_u8(0x02); search_content.put_u8(0x01);
search_content.put_u8(0x00);
search_content.put_u8(0x02); search_content.put_u8(0x01);
search_content.put_u8(0x00);
search_content.put_u8(0x01); search_content.put_u8(0x01);
search_content.put_u8(0x00);
search_content.put_u8(0x87); search_content.put_u8(0x0B); search_content.put_slice(b"objectClass");
let search_len = search_content.len();
let mut message_content = BytesMut::new();
message_content.put_u8(0x02); message_content.put_u8(0x01);
message_content.put_u8(0x01);
message_content.put_u8(0x63); message_content.put_u8(search_len as u8);
message_content.put(search_content);
buf.put_u8(0x30); buf.put_u8(message_content.len() as u8);
buf.put(message_content);
let result = codec.decode(&mut buf);
assert!(result.is_ok());
let msg = result.unwrap().unwrap();
match msg.protocol_op {
LdapProtocolOp::SearchRequest { attributes, .. } => {
assert!(attributes.is_empty());
}
_ => panic!("Expected SearchRequest"),
}
}
#[test]
fn test_decode_search_request_with_special_filter() {
let mut codec = SimpleLdapCodec;
let mut buf = BytesMut::new();
let mut search_content = BytesMut::new();
let base_dn = b"dc=example,dc=com";
search_content.put_u8(0x04); search_content.put_u8(base_dn.len() as u8); search_content.put_slice(base_dn);
search_content.put_u8(0x0A); search_content.put_u8(0x01);
search_content.put_u8(0x01);
search_content.put_u8(0x0A); search_content.put_u8(0x01);
search_content.put_u8(0x00);
search_content.put_u8(0x02); search_content.put_u8(0x01);
search_content.put_u8(0x64);
search_content.put_u8(0x02); search_content.put_u8(0x01);
search_content.put_u8(0x1E);
search_content.put_u8(0x01); search_content.put_u8(0x01);
search_content.put_u8(0xFF);
let mut equality_content = BytesMut::new();
equality_content.put_u8(0x04); equality_content.put_u8(0x02); equality_content.put_slice(b"cn");
equality_content.put_u8(0x04); equality_content.put_u8(0x04); equality_content.put_slice(b"test");
let mut not_content = BytesMut::new();
not_content.put_u8(0xA3); not_content.put_u8(equality_content.len() as u8); not_content.put(equality_content);
search_content.put_u8(0xA2); search_content.put_u8(not_content.len() as u8);
search_content.put(not_content);
let mut attrs_content = BytesMut::new();
attrs_content.put_u8(0x04); attrs_content.put_u8(0x02); attrs_content.put_slice(b"cn");
search_content.put_u8(0x30); search_content.put_u8(attrs_content.len() as u8); search_content.put(attrs_content);
let search_len = search_content.len();
let mut message_content = BytesMut::new();
message_content.put_u8(0x02); message_content.put_u8(0x01);
message_content.put_u8(0x02);
message_content.put_u8(0x63); if search_len > 127 {
panic!(
"Search content too long for simple encoding: {}",
search_len
);
}
message_content.put_u8(search_len as u8);
message_content.put(search_content);
buf.put_u8(0x30); let msg_len = message_content.len();
if msg_len > 127 {
panic!("Message content too long for simple encoding: {}", msg_len);
}
buf.put_u8(msg_len as u8);
buf.put(message_content);
let result = codec.decode(&mut buf);
match &result {
Err(e) => panic!("Decode failed: {:?}", e),
Ok(None) => panic!("Decode returned None - need more data"),
Ok(Some(_)) => {} }
let msg = result.unwrap().unwrap();
match msg.protocol_op {
LdapProtocolOp::SearchRequest {
filter,
types_only,
size_limit,
time_limit,
..
} => {
assert!(filter.contains("(!"));
assert!(filter.contains("cn=test"));
assert!(types_only);
assert_eq!(size_limit, 100);
assert_eq!(time_limit, 30);
}
_ => panic!("Expected SearchRequest"),
}
}
#[test]
fn test_decode_extended_request_with_value() {
let mut codec = SimpleLdapCodec;
let mut buf = BytesMut::new();
let mut extended_content = BytesMut::new();
let oid = "1.2.3.4.5";
extended_content.put_u8(0x80); extended_content.put_u8(oid.len() as u8);
extended_content.put_slice(oid.as_bytes());
let value_data = b"test value data";
extended_content.put_u8(0x81); extended_content.put_u8(value_data.len() as u8);
extended_content.put_slice(value_data);
let mut message_content = BytesMut::new();
message_content.put_u8(0x02); message_content.put_u8(0x01);
message_content.put_u8(0x03);
message_content.put_u8(0x77); message_content.put_u8(extended_content.len() as u8);
message_content.put(extended_content);
buf.put_u8(0x30); buf.put_u8(message_content.len() as u8);
buf.put(message_content);
let result = codec.decode(&mut buf);
assert!(result.is_ok());
let msg = result.unwrap().unwrap();
match msg.protocol_op {
LdapProtocolOp::ExtendedRequest { name, value } => {
assert_eq!(name, "1.2.3.4.5");
assert!(value.is_some());
assert_eq!(value.unwrap(), b"test value data");
}
_ => panic!("Expected ExtendedRequest"),
}
}
#[test]
fn test_encode_extended_response_with_value() {
let mut codec = SimpleLdapCodec;
let msg = LdapMessage {
message_id: 42,
protocol_op: LdapProtocolOp::ExtendedResponse {
result: LdapResult::success(),
name: Some("1.2.3.4.5".to_string()),
value: Some(vec![0x01, 0x02, 0x03, 0x04]),
},
};
let mut buf = BytesMut::new();
let result = codec.encode(msg, &mut buf);
assert!(result.is_ok());
assert!(!buf.is_empty());
assert_eq!(buf[0], 0x30);
let mut has_name = false;
let mut has_value = false;
for i in 0..buf.len() - 1 {
if buf[i] == 0x8A {
has_name = true;
}
if buf[i] == 0x8B {
has_value = true;
}
}
assert!(has_name, "Response name not found");
assert!(has_value, "Response value not found");
}
}