예제 #1
0
        /// <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;
        }
예제 #3
0
            /// <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);
                    }
                }
            }
예제 #4
0
            /// <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;
        }