108 lines
3.4 KiB
Rust
108 lines
3.4 KiB
Rust
//! Compound Request integration tests
|
|
//! Tests SMB2 compound request processing (CREATE + READ, CREATE + WRITE + CLOSE, etc.)
|
|
|
|
use smb_server::wire::header::{Command, SMB2_FLAGS_RELATED_OPERATIONS};
|
|
use smb_server::ntstatus;
|
|
|
|
/// Test compound request header flags
|
|
#[test]
|
|
fn test_compound_header_flags() {
|
|
// First request: no RELATED flag
|
|
let first_flags: u32 = 0;
|
|
assert_eq!(first_flags & SMB2_FLAGS_RELATED_OPERATIONS, 0);
|
|
|
|
// Second request: RELATED flag set
|
|
let second_flags = SMB2_FLAGS_RELATED_OPERATIONS;
|
|
assert_ne!(second_flags & SMB2_FLAGS_RELATED_OPERATIONS, 0);
|
|
|
|
// Verify RELATED flag value (MS-SMB2 §2.2.1.2)
|
|
assert_eq!(SMB2_FLAGS_RELATED_OPERATIONS, 0x0000_0004);
|
|
}
|
|
|
|
/// Test compound request NextCommand offset
|
|
#[test]
|
|
fn test_next_command_offset() {
|
|
// NextCommand = 0 indicates last request in chain
|
|
let last_next_command: u32 = 0;
|
|
assert_eq!(last_next_command, 0);
|
|
|
|
// NextCommand > 0 indicates next request offset
|
|
let next_command: u32 = 128;
|
|
assert!(next_command > 64); // Must be > SMB2_HEADER_LEN (64 bytes)
|
|
}
|
|
|
|
/// Test compound request chain validation
|
|
#[test]
|
|
fn test_compound_chain_validation() {
|
|
// Valid chain: CREATE -> READ -> CLOSE
|
|
let commands = vec![
|
|
Command::Create,
|
|
Command::Read,
|
|
Command::Close,
|
|
];
|
|
assert_eq!(commands.len(), 3);
|
|
assert_eq!(commands[0], Command::Create);
|
|
assert_eq!(commands[1], Command::Read);
|
|
assert_eq!(commands[2], Command::Close);
|
|
|
|
// Invalid chain: READ -> CREATE (CREATE must be first for file operations)
|
|
let invalid_commands = vec![
|
|
Command::Read,
|
|
Command::Create,
|
|
];
|
|
// This should be rejected by server (READ needs file_id from CREATE)
|
|
assert_ne!(invalid_commands[0], Command::Create);
|
|
}
|
|
|
|
/// Test compound request file_id inheritance
|
|
#[test]
|
|
fn test_file_id_inheritance() {
|
|
// FileId from CREATE response (16 bytes)
|
|
let file_id: [u8; 16] = [
|
|
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
|
|
0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10,
|
|
];
|
|
|
|
// Verify file_id format (persistent + volatile parts)
|
|
assert_eq!(file_id.len(), 16);
|
|
|
|
// Split into persistent (first 8 bytes) and volatile (last 8 bytes)
|
|
let persistent: [u8; 8] = file_id[0..8].try_into().unwrap();
|
|
let volatile: [u8; 8] = file_id[8..16].try_into().unwrap();
|
|
assert_ne!(persistent, volatile);
|
|
}
|
|
|
|
/// Test compound response stitching
|
|
#[test]
|
|
fn test_response_stitching() {
|
|
// Simulate two responses
|
|
let response1_len = 128;
|
|
let response2_len = 96;
|
|
|
|
// Calculate aligned lengths (8-byte alignment)
|
|
let aligned1 = (response1_len + 7) & !7;
|
|
let aligned2 = response2_len; // Last response, no padding
|
|
|
|
assert_eq!(aligned1, 128); // Already aligned
|
|
assert_eq!(aligned2, 96);
|
|
|
|
// Total compound response length
|
|
let total = aligned1 + aligned2;
|
|
assert_eq!(total, 224);
|
|
}
|
|
|
|
/// Test compound request error handling
|
|
#[test]
|
|
fn test_compound_error_handling() {
|
|
// MS-SMB2 §3.3.5.2.7: If any request fails, subsequent requests should not be processed
|
|
// However, all responses should still be returned (with error status)
|
|
|
|
// Simulate error in second request (READ after CREATE failed)
|
|
let error_status: u32 = ntstatus::STATUS_OBJECT_NAME_NOT_FOUND;
|
|
|
|
// Verify error status
|
|
assert_ne!(error_status, ntstatus::STATUS_SUCCESS);
|
|
|
|
// Error response should still be included in compound chain
|
|
// (with STATUS_PENDING for subsequent requests)
|
|
} |