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;
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",
));
}
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)?;
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 => {
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))
}
_ => {
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)?;
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,
}
}
_ => {
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);
}
_ => {
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_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");
}
}