/// <summary> /// Receive and validate SMB2 Read Response /// </summary> /// <param name="readResponseSize">Size of SMB2 read response</param> public void ValidateReadResponse(int credits, int readResponseSize) { // receive read response, maybe in several SMBDirect data transfer messages SmbdDataTransferMessage transferPackage = new SmbdDataTransferMessage(); byte[] readResponsePacket = new byte[readResponseSize]; int offset = 0; NtStatus status; while (offset < readResponseSize) { while (smbdAdapter.ClientConnection.ReceiveCredits > 0) { status = (NtStatus)smbdAdapter.SmbdReceivDataTransferMessage( smbdAdapter.TestConfig.Smb2ConnectionTimeout, out transferPackage ); BaseTestSite.Log.Add( LogEntryKind.Debug, "Received a SMBDirect Data Transfer message, current Connection.ReceiveCredits is {0}", smbdAdapter.ClientConnection.ReceiveCredits); #region Assert BaseTestSite.Assert.AreEqual <NtStatus>( NtStatus.STATUS_SUCCESS, status, "Received SMBDirect Data Transfer Message with status {0}", status); BaseTestSite.Assert.AreEqual <uint>( (uint)(readResponseSize - offset), transferPackage.DataLength + transferPackage.RemainingDataLength, "Received SMBDirect Data Transfer Message: DataLength {0}, RemainingDataLength {1}", transferPackage.DataLength, transferPackage.RemainingDataLength); BaseTestSite.Assert.AreEqual <uint>( 0, transferPackage.DataOffset % 8, "Received SMBDirect Data Transfer Message: DataOffset {0}, which should be 8-byte aligned", transferPackage.DataOffset); #endregion Array.Copy(transferPackage.Buffer, 0, readResponsePacket, offset, transferPackage.DataLength); offset += (int)transferPackage.DataLength; if (transferPackage.RemainingDataLength == 0) { break; } } BaseTestSite.Assert.IsTrue(transferPackage.CreditsGranted > 0, "SMBDirect Data Tranfer message with last credit should with CreditsGranted greater than 0"); #region send empty message to grant credit status = NtStatus.STATUS_SUCCESS; do { status = smbdAdapter.SmbdPostReceive(); }while (status == NtStatus.STATUS_SUCCESS && smbdAdapter.ClientConnection.ReceiveCredits < credits); // check receive queue before sending BaseTestSite.Assert.AreEqual <int>( 0, smbdAdapter.ReceiveEntryInQueue, "No message should be sent from server"); smbdAdapter.SmbdSendDataTransferMessage( smbdAdapter.ClientConnection.SendCreditTarget, (ushort)smbdAdapter.ClientConnection.ReceiveCredits, SmbdDataTransfer_Flags.NONE, 0, 0, 0, new byte[0]); BaseTestSite.Log.Add( LogEntryKind.Debug, "Grant {0} credits to peer, current Connection.ReceiveCredits is {0}", smbdAdapter.ClientConnection.ReceiveCredits); #endregion } #region Decode SMB2 READ response and validate object endpoint = new object(); int consumedLength; int expectedLength; StackPacket[] stackPackets = smbdAdapter.Decoder.Smb2DecodePacketCallback( endpoint, readResponsePacket, out consumedLength, out expectedLength); Smb2ReadResponsePacket package = (Smb2ReadResponsePacket)stackPackets[0]; READ_Response readResponse = package.PayLoad; BaseTestSite.Assert.AreEqual <NtStatus>( NtStatus.STATUS_SUCCESS, (NtStatus)package.Header.Status, "Status of SMB2 Read File is {0}", (NtStatus)package.Header.Status); #endregion }
private Smb2Packet DecodeSingleResponsePacket( byte[] messageBytes, bool ignoreCompoundFlag, ulong realSessionId, uint realTreeId, out int consumedLength, out int expectedLength ) { Packet_Header smb2Header; bool isLeaseBreakPacket = false; int offset = 0; smb2Header = TypeMarshal.ToStruct<Packet_Header>(messageBytes, ref offset); if (smb2Header.Command == Smb2Command.OPLOCK_BREAK) { ushort structureSize = TypeMarshal.ToStruct<ushort>(messageBytes, ref offset); if (structureSize == (ushort)OplockLeaseBreakStructureSize.LeaseBreakNotification || structureSize == (ushort)OplockLeaseBreakStructureSize.LeaseBreakResponse || structureSize == 9) // Add this condition temporally to handle LeaseBreakResponse is error response (i.e. structureSize == 9), but this will still hide the condition when OplockBreakResponse is error response { isLeaseBreakPacket = true; } } Smb2SinglePacket packet = null; ushort structSize = BitConverter.ToUInt16(messageBytes, Smb2Consts.Smb2HeaderLen); switch (smb2Header.Command) { case Smb2Command.CANCEL: packet = new Smb2CancelResponsePacket(); break; case Smb2Command.CHANGE_NOTIFY: packet = new Smb2ChangeNotifyResponsePacket(); break; case Smb2Command.CLOSE: packet = new Smb2CloseResponsePacket(); break; case Smb2Command.CREATE: packet = new Smb2CreateResponsePacket(); break; case Smb2Command.ECHO: packet = new Smb2EchoResponsePacket(); break; case Smb2Command.FLUSH: packet = new Smb2FlushResponsePacket(); break; case Smb2Command.IOCTL: packet = new Smb2IOCtlResponsePacket(); break; case Smb2Command.LOCK: packet = new Smb2LockResponsePacket(); break; case Smb2Command.LOGOFF: packet = new Smb2LogOffResponsePacket(); break; case Smb2Command.NEGOTIATE: packet = new Smb2NegotiateResponsePacket(); break; case Smb2Command.OPLOCK_BREAK: if (smb2Header.MessageId == ulong.MaxValue) { if (!isLeaseBreakPacket) { packet = new Smb2OpLockBreakNotificationPacket(); } else { packet = new Smb2LeaseBreakNotificationPacket(); } } else { if (!isLeaseBreakPacket) { packet = new Smb2OpLockBreakResponsePacket(); } else { packet = new Smb2LeaseBreakResponsePacket(); } } break; case Smb2Command.QUERY_DIRECTORY: packet = new Smb2QueryDirectoryResponePacket(); break; case Smb2Command.QUERY_INFO: packet = new Smb2QueryInfoResponsePacket(); break; case Smb2Command.READ: packet = new Smb2ReadResponsePacket(); break; case Smb2Command.SESSION_SETUP: packet = new Smb2SessionSetupResponsePacket(); break; case Smb2Command.SET_INFO: packet = new Smb2SetInfoResponsePacket(); break; case Smb2Command.TREE_CONNECT: packet = new Smb2TreeConnectResponsePacket(); break; case Smb2Command.TREE_DISCONNECT: packet = new Smb2TreeDisconnectResponsePacket(); break; case Smb2Command.WRITE: packet = new Smb2WriteResponsePacket(); break; default: throw new InvalidOperationException("Received an unknown packet! the type of the packet is " + smb2Header.Command.ToString()); } if (IsErrorPacket(smb2Header)) { var error = new Smb2ErrorResponsePacket(); error.FromBytes(messageBytes, out consumedLength, out expectedLength); packet.Header = error.Header; packet.Error = error; } else { packet.FromBytes(messageBytes, out consumedLength, out expectedLength); } //if ignoreCompoundFlag is false, means the process of decoding this packet //is not part of the process of decoding a compound packet. We will update //context here. if (!ignoreCompoundFlag) { // TODO } return packet; }
/// <summary> /// Run compression test against given input parameters. /// 1. Write testData to test file given by treeId and fileId, and compress message based on compressWriteRequest using compressionAlgorithmForTest. /// 2. Read out the data just written, and request compressing READ response message based on compressReadRequest. /// 3. Check whether the READ response based on readResponseShouldBeCompressed. /// 4. Check whether data read out is equal to test data. /// </summary> /// <param name="client">SMB2 functional client to use.</param> /// <param name="variant">Compression test variant.</param> /// <param name="treeId">TreeId to use.</param> /// <param name="fileId">FileId to use.</param> /// <param name="isLargeFile">Whether is large file.</param> public void Run(Smb2FunctionalClient client, CompressionTestVariant variant, uint treeId, FILEID fileId, bool isLargeFile = false) { if (compressionAlgorithmForTest != CompressionAlgorithm.NONE) { if (!client.Smb2Client.CompressionInfo.CompressionIds.Any(compressionAlgorithmSupported => compressionAlgorithmSupported == compressionAlgorithmForTest)) { // The specified compression algorithm is not supported by SUT. return; } } BaseTestSite.Log.Add( LogEntryKind.TestStep, "Test will trigger WRITE request with CompressWrite: {0} and preferred compression algorithm: {1}.", compressWriteRequest, compressionAlgorithmForTest ); // Specify the compression algorithm for write request. client.Smb2Client.CompressionInfo.PreferredCompressionAlgorithm = compressionAlgorithmForTest; if (compressWriteRequestBufferOnly) { client.Smb2Client.CompressionInfo.CompressBufferOnly = true; } if (isLargeFile) { // Write several times for existing testData for large data write and read tests // Base testData: // Length of LZ77 and LZ77 Huffman testData is 300 bytes. // Length of LZNT1 testData is 142 bytes. // Length of Pattern_V1 testData is 256 bytes. // Length of Compressible data testData is 2048 bytes. // We will change testData to 1 MB, and write 100 times to generate a 100 MB file int writeRequestCount = 100; ulong offset = 0; // According to real experience with copying large file to shared folder, the client will request 1 MB packet per request. // Change testData to 512 bytes * 2048 = 1 megabytes, so we can test large file if (compressionAlgorithmForTest == CompressionAlgorithm.LZ77Huffman) { // LZ77Huffman match length needs to be less than 65538, so we use 512*128=65536. var test512Bytes = Enumerable.Repeat(testData, 2).SelectMany(a => a).Take(512).ToArray(); testData = Enumerable.Repeat(test512Bytes, 128).SelectMany(b => b).ToArray(); } else if (variant == CompressionTestVariant.ChainedCompressibleWritePatternV1AtFront) { // For ChainedCompressibleWritePatternV1AtFront, we need to prepend some data which can be compressed with PatternV1. var test256Bytes = commonCompressibleData.Take(256).ToArray(); var newCommonCompressibleData = Enumerable.Repeat(test256Bytes, 4 * 1024 - 1).SelectMany(b => b).ToArray(); testData = GenerateByteArray(exampleTestData[CompressionAlgorithm.Pattern_V1], newCommonCompressibleData); } else if (variant == CompressionTestVariant.ChainedCompressibleRead || variant == CompressionTestVariant.ChainedCompressibleWritePatternV1AtEnd) { // For ChainedCompressibleRead or ChainedCompressibleWritePatternV1AtEnd, we need to append some data which can be compressed with PatternV1. var test256Bytes = commonCompressibleData.Take(256).ToArray(); var newCommonCompressibleData = Enumerable.Repeat(test256Bytes, 4 * 1024 - 1).SelectMany(b => b).ToArray(); testData = GenerateByteArray(newCommonCompressibleData, exampleTestData[CompressionAlgorithm.Pattern_V1]); } else if (variant == CompressionTestVariant.ChainedCompressibleWritePatternV1AtFrontAndEnd) { // For ChainedCompressibleWritePatternV1AtFrontAndEnd, we need to add start and end with PatternV1. var test256Bytes = commonCompressibleData.Take(256).ToArray(); var newCommonCompressibleData = Enumerable.Repeat(test256Bytes, 4 * 1024 - 2).SelectMany(b => b).ToArray(); testData = GenerateByteArray(exampleTestData[CompressionAlgorithm.Pattern_V1], newCommonCompressibleData, exampleTestData[CompressionAlgorithm.Pattern_V1]); } else { // For other cases, testData with a length of 1 MB will be used. var test512Bytes = Enumerable.Repeat(testData, 4).SelectMany(a => a).Take(512).ToArray(); testData = Enumerable.Repeat(test512Bytes, 2048).SelectMany(b => b).ToArray(); } int requestBytes = testData.Length; for (int time = 0; time < writeRequestCount; time++) { client.Write(treeId, fileId, testData, offset, compressWrite: compressWriteRequest); offset += (uint)requestBytes; } } else { client.Write(treeId, fileId, testData, compressWrite: compressWriteRequest); } if (compressWriteRequestBufferOnly) { client.Smb2Client.CompressionInfo.CompressBufferOnly = false; } byte[] readOutData = null; bool readResponseIsCompressed = false; bool readResponseIsChained = false; Smb2ReadResponsePacket readResponsePacket = null; Smb2CompressedPacket compressedPacket = null; Action <Smb2Packet> Smb2Client_PacketReceived = (Smb2Packet obj) => { if (obj is Smb2ReadResponsePacket) { readResponsePacket = obj as Smb2ReadResponsePacket; readResponseIsCompressed = readResponsePacket.Compressed; compressedPacket = readResponsePacket.CompressedPacket; if (compressedPacket is Smb2ChainedCompressedPacket) { readResponseIsChained = true; } } }; BaseTestSite.Log.Add( LogEntryKind.TestStep, "Test will trigger READ request with CompressRead: {0} and check whether READ response is compressed: {1}.", compressReadRequest, readResponseShouldBeCompressed ); client.Smb2Client.PacketReceived += Smb2Client_PacketReceived; if (isLargeFile) { // Read several times for exist testData for large data write and read tests int requestBytes = testData.Length; int readRequestCount = 100; ulong offset = 0; for (int time = 0; time < readRequestCount; time++) { client.Read(treeId, fileId, (uint)offset, (uint)requestBytes, out readOutData, compressRead: compressReadRequest); BaseTestSite.Assert.IsTrue(Enumerable.SequenceEqual(testData, readOutData), $"Request times:{time + 1}, packet offset: {offset}, byteSize:{requestBytes}, the read out content MUST be the same with that is written."); offset += (uint)requestBytes; } } else { client.Read(treeId, fileId, 0, (uint)testData.Length, out readOutData, compressRead: compressReadRequest); BaseTestSite.Assert.IsTrue(Enumerable.SequenceEqual(testData, readOutData), "The read out content MUST be the same with that is written."); } client.Smb2Client.PacketReceived -= Smb2Client_PacketReceived; if (compressReadRequest) { if (readResponseShouldBeCompressed) { BaseTestSite.Assert.IsTrue(readResponseIsCompressed && compressedPacket != null, "[MS-SMB2] section 3.3.5.12: When SMB2_READFLAG_REQUEST_COMPRESSED is specified in read request, the server MUST compress the message if compression will shrink the message size."); BaseTestSite.Log.Add(LogEntryKind.Debug, "Read response is compressed using {0}.", compressedPacket.Header.CompressionAlgorithm); if (readResponseShouldBeChained) { BaseTestSite.Assert.IsTrue(readResponseIsChained, "The read response should be chained."); } else { BaseTestSite.Assert.IsFalse(readResponseIsChained, "The read response should not be chained."); } } else { BaseTestSite.Assert.IsTrue(!readResponseIsCompressed && compressedPacket == null, "[MS-SMB2] section 3.3.5.12: When SMB2_READFLAG_REQUEST_COMPRESSED is specified in read request, the server MUST NOT compress the message if compression will not shrink the message size."); } } else { BaseTestSite.Log.Add(LogEntryKind.Debug, "SMB2_READFLAG_REQUEST_COMPRESSED is not specified in read request, and read response is compressed: {0}.", readResponseIsCompressed); if (readResponseIsCompressed) { BaseTestSite.Assert.IsTrue(compressedPacket != null, "Compressed packet is received."); BaseTestSite.Log.Add(LogEntryKind.Debug, "Read response is compressed using {0}.", compressedPacket.Header.CompressionAlgorithm); } } }
/// <summary> /// Run compression test against given input parameters. /// 1. Write testData to test file given by treeId and fileId, and compress message based on compressWriteRequest using compressionAlgorithmForTest. /// 2. Read out the data just written, and request compressing READ response message based on compressReadRequest. /// 3. Check whether the READ response based on readResponseShouldBeCompressed. /// 4. Check whether data read out is equal to test data. /// </summary> /// <param name="client">SMB2 functional client to use.</param> /// <param name="treeId">TreeId to use.</param> /// <param name="fileId">FileId to use.</param> public void Run(Smb2FunctionalClient client, uint treeId, FILEID fileId) { if (compressionAlgorithmForTest != CompressionAlgorithm.NONE) { if (!client.Smb2Client.CompressionInfo.CompressionIds.Any(compressionAlgorithmSupported => compressionAlgorithmSupported == compressionAlgorithmForTest)) { // The specified compression algorithm is not supported by SUT. return; } } BaseTestSite.Log.Add( LogEntryKind.TestStep, "Test will trigger WRITE request with CompressWrite: {0} and preferred compression algorithm: {1}.", compressWriteRequest, compressionAlgorithmForTest ); // Specify the compression algorithm for write request. client.Smb2Client.CompressionInfo.PreferredCompressionAlgorithm = compressionAlgorithmForTest; if (compressWriteRequestBufferOnly) { client.Smb2Client.CompressionInfo.CompressBufferOnly = true; } client.Write(treeId, fileId, testData, compressWrite: compressWriteRequest); if (compressWriteRequestBufferOnly) { client.Smb2Client.CompressionInfo.CompressBufferOnly = false; } byte[] readOutData = null; bool readResponseIsCompressed = false; bool readResponseIsChained = false; Smb2ReadResponsePacket readResponsePacket = null; Smb2CompressedPacket compressedPacket = null; Action <Smb2Packet> Smb2Client_PacketReceived = (Smb2Packet obj) => { if (obj is Smb2ReadResponsePacket) { readResponsePacket = obj as Smb2ReadResponsePacket; readResponseIsCompressed = readResponsePacket.Compressed; compressedPacket = readResponsePacket.CompressedPacket; if (compressedPacket is Smb2ChainedCompressedPacket) { readResponseIsChained = true; } } }; BaseTestSite.Log.Add( LogEntryKind.TestStep, "Test will trigger READ request with CompressRead: {0} and check whether READ response is compressed: {1}.", compressReadRequest, readResponseShouldBeCompressed ); client.Smb2Client.PacketReceived += Smb2Client_PacketReceived; client.Read(treeId, fileId, 0, (uint)testData.Length, out readOutData, compressRead: compressReadRequest); client.Smb2Client.PacketReceived -= Smb2Client_PacketReceived; if (compressReadRequest) { if (readResponseShouldBeCompressed) { BaseTestSite.Assert.IsTrue(readResponseIsCompressed && compressedPacket != null, "[MS-SMB2] section 3.3.5.12: When SMB2_READFLAG_REQUEST_COMPRESSED is specified in read request, the server MUST compress the message if compression will shrink the message size."); BaseTestSite.Log.Add(LogEntryKind.Debug, "Read response is compressed using {0}.", compressedPacket.Header.CompressionAlgorithm); if (readResponseShouldBeChained) { BaseTestSite.Assert.IsTrue(readResponseIsChained, "The read response should be chained."); } else { BaseTestSite.Assert.IsFalse(readResponseIsChained, "The read response should not be chained."); } } else { BaseTestSite.Assert.IsTrue(!readResponseIsCompressed && compressedPacket == null, "[MS-SMB2] section 3.3.5.12: When SMB2_READFLAG_REQUEST_COMPRESSED is specified in read request, the server MUST NOT compress the message if compression will not shrink the message size."); } } else { BaseTestSite.Log.Add(LogEntryKind.Debug, "SMB2_READFLAG_REQUEST_COMPRESSED is not specified in read request, and read response is compressed: {0}.", readResponseIsCompressed); if (readResponseIsCompressed) { BaseTestSite.Assert.IsTrue(compressedPacket != null, "Compressed packet is received."); BaseTestSite.Log.Add(LogEntryKind.Debug, "Read response is compressed using {0}.", compressedPacket.Header.CompressionAlgorithm); } } BaseTestSite.Assert.IsTrue(Enumerable.SequenceEqual(testData, readOutData), "The read out content MUST be the same with that is written."); }
public Smb2ReadResponsePacket CreateReadResponse( Smb2Endpoint endpoint, uint status, ulong messageId, byte[] buffer ) { if (buffer == null || buffer.Length == 0) { throw new ArgumentException("buffer should at least contains 1 byte.", "buffer"); } Smb2ReadResponsePacket packet = new Smb2ReadResponsePacket(); packet.Header.Status = status; SetHeader(packet, endpoint, messageId); packet.PayLoad.StructureSize = READ_Response_StructureSize_Values.V1; packet.PayLoad.Reserved = READ_Response_Reserved_Values.V1; packet.PayLoad.Reserved2 = READ_Response_Reserved2_Values.V1; packet.PayLoad.DataRemaining = DataRemaining_Values.V1; packet.PayLoad.DataOffset = Smb2Consts.DataOffsetInReadResponse; packet.PayLoad.DataLength = (uint)buffer.Length; packet.PayLoad.Buffer = buffer; packet.Sign(); return packet; }