Exemplo n.º 1
0
        private void WriteCurrentFragmentAndReset()
        {
            // log trace of the fragment
            PSEtwLog.LogAnalyticVerbose(
                PSEventId.SentRemotingFragment, PSOpcode.Send, PSTask.None,
                PSKeyword.Transport | PSKeyword.UseAlwaysAnalytic,
                (Int64)(_currentFragment.ObjectId),
                (Int64)(_currentFragment.FragmentId),
                _currentFragment.IsStartFragment ? 1 : 0,
                _currentFragment.IsEndFragment ? 1 : 0,
                (UInt32)(_currentFragment.BlobLength),
                new PSETWBinaryBlob(_currentFragment.Blob, 0, _currentFragment.BlobLength));

            // finally write into memory stream
            byte[] data             = _currentFragment.GetBytes();
            int    amountLeft       = data.Length;
            int    offSetToReadFrom = 0;

            // user asked us to notify immediately..so no need
            // to write into memory stream..instead give the
            // data directly to user and let him figure out what to do.
            // This will save write + read + dispose!!
            if (!_notifyOnWriteFragmentImmediately)
            {
                lock (_syncObject)
                {
                    // technically this should throw an exception..but remoting callstack
                    // is optimized ie., we are not locking in every layer (in powershell)
                    // to save on performance..as a result there may be cases where
                    // upper layer is trying to add stuff and stream is disposed while
                    // adding stuff.
                    if (_isDisposed)
                    {
                        return;
                    }

                    if (_writeStream == null)
                    {
                        _writeStream = new MemoryStream(_fragmentSize);
                        s_trace.WriteLine("Created write stream: {0}", _writeStream.GetHashCode());
                        _writeOffset = 0;
                    }

                    while (amountLeft > 0)
                    {
                        int dataLeftInWriteStream = _writeStream.Capacity - _writeOffset;
                        if (dataLeftInWriteStream == 0)
                        {
                            // enqueue the current write stream and create a new one.
                            EnqueueWriteStream();
                            dataLeftInWriteStream = _writeStream.Capacity - _writeOffset;
                        }

                        int amountToWriteIntoStream = (amountLeft > dataLeftInWriteStream) ? dataLeftInWriteStream : amountLeft;
                        amountLeft = amountLeft - amountToWriteIntoStream;
                        // write data
                        _writeStream.Position = _writeOffset;
                        _writeStream.Write(data, offSetToReadFrom, amountToWriteIntoStream);
                        offSetToReadFrom += amountToWriteIntoStream;
                        _writeOffset     += amountToWriteIntoStream;
                        _length          += amountToWriteIntoStream;
                    }
                }
            }

            // call the callback since we have data available
            if (_onDataAvailableCallback != null)
            {
                _onDataAvailableCallback(data, _currentFragment.IsEndFragment);
            }

            // prepare a new fragment
            _currentFragment.FragmentId      = ++_fragmentId;
            _currentFragment.IsStartFragment = false;
            _currentFragment.IsEndFragment   = false;
            _currentFragment.BlobLength      = 0;
            _currentFragment.Blob            = new byte[_fragmentSize];
        }
Exemplo n.º 2
0
        /// <summary>
        /// Process data coming from the transport. This method analyses the data
        /// and if an object can be created, it creates one and calls the
        /// <paramref name="callback"/> with the deserialized object. This method
        /// does not assume all fragments to be available. So if not enough fragments are
        /// available it will simply return..
        /// </summary>
        /// <param name="data">
        /// Data to process.
        /// </param>
        /// <param name="callback">
        /// Callback to call once a complete deserialized object is available.
        /// </param>
        /// <returns>
        /// Defragmented Object if any, otherwise null.
        /// </returns>
        /// <exception cref="PSRemotingTransportException">
        /// 1. Fragment Ids not in sequence
        /// 2. Object Ids does not match
        /// 3. The current deserialized object size of the received data exceeded
        /// allowed maximum object size. The current deserialized object size is {0}.
        /// Allowed maximum object size is {1}.
        /// </exception>
        /// <remarks>
        /// Might throw other exceptions as the deserialized object is handled here.
        /// </remarks>
        internal void ProcessRawData(byte[] data, OnDataAvailableCallback callback)
        {
            Dbg.Assert(data != null, "Cannot process null data");
            Dbg.Assert(callback != null, "Callback cannot be null");

            lock (_syncObject)
            {
                if (_isDisposed)
                {
                    return;
                }

                _numberOfThreadsProcessing++;
                if (_numberOfThreadsProcessing > _maxNumberOfThreadsToAllowForProcessing)
                {
                    Dbg.Assert(false, "Multiple threads are not allowed in ProcessRawData.");
                }
            }

            try
            {
                _pendingDataStream.Write(data, 0, data.Length);

                // this do loop will process one deserialized object.
                // using a loop allows to process multiple objects within
                // the same packet
                do
                {
                    if (_pendingDataStream.Length <= FragmentedRemoteObject.HeaderLength)
                    {
                        // there is not enough data to be processed.
                        s_baseTracer.WriteLine("Not enough data to process. Data is less than header length. Data length is {0}. Header Length {1}.",
                                               _pendingDataStream.Length, FragmentedRemoteObject.HeaderLength);
                        return;
                    }

                    byte[] dataRead = _pendingDataStream.ToArray();

                    // there is enough data to process here. get the fragment header
                    long objectId = FragmentedRemoteObject.GetObjectId(dataRead, 0);
                    if (objectId <= 0)
                    {
                        throw new PSRemotingTransportException(RemotingErrorIdStrings.ObjectIdCannotBeLessThanZero);
                    }

                    long fragmentId = FragmentedRemoteObject.GetFragmentId(dataRead, 0);
                    bool sFlag      = FragmentedRemoteObject.GetIsStartFragment(dataRead, 0);
                    bool eFlag      = FragmentedRemoteObject.GetIsEndFragment(dataRead, 0);
                    int  blobLength = FragmentedRemoteObject.GetBlobLength(dataRead, 0);

                    if ((s_baseTracer.Options & PSTraceSourceOptions.WriteLine) != PSTraceSourceOptions.None)
                    {
                        s_baseTracer.WriteLine("Object Id: {0}", objectId);
                        s_baseTracer.WriteLine("Fragment Id: {0}", fragmentId);
                        s_baseTracer.WriteLine("Start Flag: {0}", sFlag);
                        s_baseTracer.WriteLine("End Flag: {0}", eFlag);
                        s_baseTracer.WriteLine("Blob Length: {0}", blobLength);
                    }

                    int totalLengthOfFragment = 0;

                    try
                    {
                        totalLengthOfFragment = checked (FragmentedRemoteObject.HeaderLength + blobLength);
                    }
                    catch (System.OverflowException)
                    {
                        s_baseTracer.WriteLine("Fragment too big.");
                        ResetReceiveData();
                        PSRemotingTransportException e = new PSRemotingTransportException(RemotingErrorIdStrings.ObjectIsTooBig);
                        throw e;
                    }

                    if (_pendingDataStream.Length < totalLengthOfFragment)
                    {
                        s_baseTracer.WriteLine("Not enough data to process packet. Data is less than expected blob length. Data length {0}. Expected Length {1}.",
                                               _pendingDataStream.Length, totalLengthOfFragment);
                        return;
                    }

                    // ensure object size limit is not reached
                    if (_maxReceivedObjectSize.HasValue)
                    {
                        _totalReceivedObjectSizeSoFar = unchecked (_totalReceivedObjectSizeSoFar + totalLengthOfFragment);
                        if ((_totalReceivedObjectSizeSoFar < 0) || (_totalReceivedObjectSizeSoFar > _maxReceivedObjectSize.Value))
                        {
                            s_baseTracer.WriteLine("ObjectSize > MaxReceivedObjectSize. ObjectSize is {0}. MaxReceivedObjectSize is {1}",
                                                   _totalReceivedObjectSizeSoFar, _maxReceivedObjectSize);
                            PSRemotingTransportException e = null;

                            if (_isCreateByClientTM)
                            {
                                e = new PSRemotingTransportException(PSRemotingErrorId.ReceivedObjectSizeExceededMaximumClient,
                                                                     RemotingErrorIdStrings.ReceivedObjectSizeExceededMaximumClient,
                                                                     _totalReceivedObjectSizeSoFar, _maxReceivedObjectSize);
                            }
                            else
                            {
                                e = new PSRemotingTransportException(PSRemotingErrorId.ReceivedObjectSizeExceededMaximumServer,
                                                                     RemotingErrorIdStrings.ReceivedObjectSizeExceededMaximumServer,
                                                                     _totalReceivedObjectSizeSoFar, _maxReceivedObjectSize);
                            }

                            ResetReceiveData();
                            throw e;
                        }
                    }

                    // appears like stream doesn't have individual position marker for read and write
                    // since we are going to read from now...
                    _pendingDataStream.Seek(0, SeekOrigin.Begin);

                    // we have enough data to process..so read the data from the stream and process.
                    byte[] oneFragment = new byte[totalLengthOfFragment];
                    // this will change position back to totalLengthOfFragment
                    int dataCount = _pendingDataStream.Read(oneFragment, 0, totalLengthOfFragment);
                    Dbg.Assert(dataCount == totalLengthOfFragment, "Unable to read enough data from the stream. Read failed");

                    PSEtwLog.LogAnalyticVerbose(
                        PSEventId.ReceivedRemotingFragment, PSOpcode.Receive, PSTask.None,
                        PSKeyword.Transport | PSKeyword.UseAlwaysAnalytic,
                        (Int64)objectId,
                        (Int64)fragmentId,
                        sFlag ? 1 : 0,
                        eFlag ? 1 : 0,
                        (UInt32)blobLength,
                        new PSETWBinaryBlob(oneFragment, FragmentedRemoteObject.HeaderLength, blobLength));

                    byte[] extraData = null;
                    if (totalLengthOfFragment < _pendingDataStream.Length)
                    {
                        // there is more data in the stream than fragment size..so save that data
                        extraData = new byte[_pendingDataStream.Length - totalLengthOfFragment];
                        _pendingDataStream.Read(extraData, 0, (int)(_pendingDataStream.Length - totalLengthOfFragment));
                    }

                    // reset incoming stream.
                    _pendingDataStream.Dispose();
                    _pendingDataStream = new MemoryStream();
                    if (extraData != null)
                    {
                        _pendingDataStream.Write(extraData, 0, extraData.Length);
                    }

                    if (sFlag)
                    {
                        _canIgnoreOffSyncFragments = false; //reset this upon receiving a start fragment of a fresh object
                        _currentObjectId           = objectId;
                        // Memory streams created with an unsigned byte array provide a non-resizable stream view
                        // of the data, and can only be written to. When using a byte array, you can neither append
                        // to nor shrink the stream, although you might be able to modify the existing contents
                        // depending on the parameters passed into the constructor. Empty memory streams are
                        // resizable, and can be written to and read from.
                        _dataToProcessStream = new MemoryStream();
                    }
                    else
                    {
                        // check if the data belongs to the same object as the start fragment
                        if (objectId != _currentObjectId)
                        {
                            s_baseTracer.WriteLine("ObjectId != CurrentObjectId");
                            //TODO - drop an ETW event
                            ResetReceiveData();
                            if (!_canIgnoreOffSyncFragments)
                            {
                                PSRemotingTransportException e = new PSRemotingTransportException(RemotingErrorIdStrings.ObjectIdsNotMatching);
                                throw e;
                            }
                            else
                            {
                                s_baseTracer.WriteLine("Ignoring ObjectId != CurrentObjectId");
                                continue;
                            }
                        }

                        if (fragmentId != (_currentFrgId + 1))
                        {
                            s_baseTracer.WriteLine("Fragment Id is not in sequence.");
                            //TODO - drop an ETW event
                            ResetReceiveData();
                            if (!_canIgnoreOffSyncFragments)
                            {
                                PSRemotingTransportException e = new PSRemotingTransportException(RemotingErrorIdStrings.FragmentIdsNotInSequence);
                                throw e;
                            }
                            else
                            {
                                s_baseTracer.WriteLine("Ignoring Fragment Id is not in sequence.");
                                continue;
                            }
                        }
                    }

                    // make fragment id from this packet as the current fragment id
                    _currentFrgId = fragmentId;
                    // store the blob in a separate stream
                    _dataToProcessStream.Write(oneFragment, FragmentedRemoteObject.HeaderLength, blobLength);

                    if (eFlag)
                    {
                        try
                        {
                            // appears like stream doesn't individual position marker for read and write
                            // since we are going to read from now..i am resetting position to 0.
                            _dataToProcessStream.Seek(0, SeekOrigin.Begin);
                            RemoteDataObject <PSObject> remoteObject = RemoteDataObject <PSObject> .CreateFrom(_dataToProcessStream, _defragmentor);

                            s_baseTracer.WriteLine("Runspace Id: {0}", remoteObject.RunspacePoolId);
                            s_baseTracer.WriteLine("PowerShell Id: {0}", remoteObject.PowerShellId);
                            // notify the caller that a deserialized object is available.
                            callback(remoteObject);
                        }
                        finally
                        {
                            // Reset the receive data buffers and start the process again.
                            ResetReceiveData();
                        }

                        if (_isDisposed)
                        {
                            break;
                        }
                    }
                } while (true);
            }
            finally
            {
                lock (_syncObject)
                {
                    if (_isDisposed && (_numberOfThreadsProcessing == 1))
                    {
                        ReleaseResources();
                    }

                    _numberOfThreadsProcessing--;
                }
            }
        }
Exemplo n.º 3
0
        internal void ProcessRawData(byte[] data, OnDataAvailableCallback callback)
        {
            lock (this.syncObject)
            {
                if (this.isDisposed)
                {
                    return;
                }
                this.numberOfThreadsProcessing++;
                int maxNumberOfThreadsToAllowForProcessing = this.maxNumberOfThreadsToAllowForProcessing;
                int numberOfThreadsProcessing = this.numberOfThreadsProcessing;
            }
            try
            {
                this.pendingDataStream.Write(data, 0, data.Length);
Label_005A:
                if (this.pendingDataStream.Length <= 0x15L)
                {
                    baseTracer.WriteLine(string.Format(CultureInfo.InvariantCulture, "Not enough data to process. Data is less than header length. Data length is {0}. Header Length {1}.", new object[] { this.pendingDataStream.Length, 0x15 }), new object[0]);
                }
                else
                {
                    byte[] fragmentBytes = this.pendingDataStream.GetBuffer();
                    long   objectId      = FragmentedRemoteObject.GetObjectId(fragmentBytes, 0);
                    if (objectId <= 0L)
                    {
                        throw new PSRemotingTransportException(RemotingErrorIdStrings.ObjectIdCannotBeLessThanZero);
                    }
                    long fragmentId      = FragmentedRemoteObject.GetFragmentId(fragmentBytes, 0);
                    bool isStartFragment = FragmentedRemoteObject.GetIsStartFragment(fragmentBytes, 0);
                    bool isEndFragment   = FragmentedRemoteObject.GetIsEndFragment(fragmentBytes, 0);
                    int  blobLength      = FragmentedRemoteObject.GetBlobLength(fragmentBytes, 0);
                    baseTracer.WriteLine(string.Format(CultureInfo.InvariantCulture, "Object Id: {0}", new object[] { objectId }), new object[0]);
                    baseTracer.WriteLine(string.Format(CultureInfo.InvariantCulture, "Fragment Id: {0}", new object[] { fragmentId }), new object[0]);
                    baseTracer.WriteLine(string.Format(CultureInfo.InvariantCulture, "Start Flag: {0}", new object[] { isStartFragment }), new object[0]);
                    baseTracer.WriteLine(string.Format(CultureInfo.InvariantCulture, "End Flag: {0}", new object[] { isEndFragment }), new object[0]);
                    baseTracer.WriteLine(string.Format(CultureInfo.InvariantCulture, "Blob Length: {0}", new object[] { blobLength }), new object[0]);
                    int count = 0;
                    try
                    {
                        count = 0x15 + blobLength;
                    }
                    catch (OverflowException)
                    {
                        baseTracer.WriteLine("Fragement too big.", new object[0]);
                        this.ResetRecieveData();
                        PSRemotingTransportException exception = new PSRemotingTransportException(RemotingErrorIdStrings.ObjectIsTooBig);
                        throw exception;
                    }
                    if (this.pendingDataStream.Length < count)
                    {
                        baseTracer.WriteLine(string.Format(CultureInfo.InvariantCulture, "Not enough data to process packet. Data is less than expected blob length. Data length {0}. Expected Length {1}.", new object[] { this.pendingDataStream.Length, count }), new object[0]);
                    }
                    else
                    {
                        if (this.maxReceivedObjectSize.HasValue)
                        {
                            this.totalReceivedObjectSizeSoFar += count;
                            if ((this.totalReceivedObjectSizeSoFar < 0) || (this.totalReceivedObjectSizeSoFar > this.maxReceivedObjectSize.Value))
                            {
                                baseTracer.WriteLine("ObjectSize > MaxReceivedObjectSize. ObjectSize is {0}. MaxReceivedObjectSize is {1}", new object[] { this.totalReceivedObjectSizeSoFar, this.maxReceivedObjectSize });
                                PSRemotingTransportException exception2 = null;
                                if (this.isCreateByClientTM)
                                {
                                    exception2 = new PSRemotingTransportException(PSRemotingErrorId.ReceivedObjectSizeExceededMaximumClient, RemotingErrorIdStrings.ReceivedObjectSizeExceededMaximumClient, new object[] { this.totalReceivedObjectSizeSoFar, this.maxReceivedObjectSize });
                                }
                                else
                                {
                                    exception2 = new PSRemotingTransportException(PSRemotingErrorId.ReceivedObjectSizeExceededMaximumServer, RemotingErrorIdStrings.ReceivedObjectSizeExceededMaximumServer, new object[] { this.totalReceivedObjectSizeSoFar, this.maxReceivedObjectSize });
                                }
                                this.ResetRecieveData();
                                throw exception2;
                            }
                        }
                        this.pendingDataStream.Seek(0L, SeekOrigin.Begin);
                        byte[] buffer = new byte[count];
                        this.pendingDataStream.Read(buffer, 0, count);
                        PSEtwLog.LogAnalyticVerbose(PSEventId.ReceivedRemotingFragment, PSOpcode.Receive, PSTask.None, PSKeyword.Transport | PSKeyword.UseAlwaysAnalytic, objectId, fragmentId, isStartFragment ? 1 : 0, isEndFragment ? 1 : 0, (int)blobLength, new PSETWBinaryBlob(buffer, 0x15, blobLength));
                        byte[] buffer3 = null;
                        if (count < this.pendingDataStream.Length)
                        {
                            buffer3 = new byte[this.pendingDataStream.Length - count];
                            this.pendingDataStream.Read(buffer3, 0, ((int)this.pendingDataStream.Length) - count);
                        }
                        this.pendingDataStream.Close();
                        this.pendingDataStream = new MemoryStream();
                        if (buffer3 != null)
                        {
                            this.pendingDataStream.Write(buffer3, 0, buffer3.Length);
                        }
                        if (isStartFragment)
                        {
                            this.canIgnoreOffSyncFragments = false;
                            this.currentObjectId           = objectId;
                            this.dataToProcessStream       = new MemoryStream();
                        }
                        else
                        {
                            if (objectId != this.currentObjectId)
                            {
                                baseTracer.WriteLine("ObjectId != CurrentObjectId", new object[0]);
                                this.ResetRecieveData();
                                if (!this.canIgnoreOffSyncFragments)
                                {
                                    PSRemotingTransportException exception3 = new PSRemotingTransportException(RemotingErrorIdStrings.ObjectIdsNotMatching);
                                    throw exception3;
                                }
                                baseTracer.WriteLine("Ignoring ObjectId != CurrentObjectId", new object[0]);
                                goto Label_005A;
                            }
                            if (fragmentId != (this.currentFrgId + 1L))
                            {
                                baseTracer.WriteLine("Fragment Id is not in sequence.", new object[0]);
                                this.ResetRecieveData();
                                if (!this.canIgnoreOffSyncFragments)
                                {
                                    PSRemotingTransportException exception4 = new PSRemotingTransportException(RemotingErrorIdStrings.FragmetIdsNotInSequence);
                                    throw exception4;
                                }
                                baseTracer.WriteLine("Ignoring Fragment Id is not in sequence.", new object[0]);
                                goto Label_005A;
                            }
                        }
                        this.currentFrgId = fragmentId;
                        this.dataToProcessStream.Write(buffer, 0x15, blobLength);
                        if (!isEndFragment)
                        {
                            goto Label_005A;
                        }
                        try
                        {
                            this.dataToProcessStream.Seek(0L, SeekOrigin.Begin);
                            RemoteDataObject <PSObject> obj2 = RemoteDataObject <PSObject> .CreateFrom(this.dataToProcessStream, this.defragmentor);

                            baseTracer.WriteLine(string.Format(CultureInfo.InvariantCulture, "Runspace Id: {0}", new object[] { obj2.RunspacePoolId }), new object[0]);
                            baseTracer.WriteLine(string.Format(CultureInfo.InvariantCulture, "PowerShell Id: {0}", new object[] { obj2.PowerShellId }), new object[0]);
                            callback(obj2);
                        }
                        finally
                        {
                            this.ResetRecieveData();
                        }
                        if (!this.isDisposed && this.pendingDataStream.Length > 0x15L)
                        {
                            goto Label_005A;
                        }
                    }
                }
            }
            finally
            {
                lock (this.syncObject)
                {
                    if (this.isDisposed && (this.numberOfThreadsProcessing == 1))
                    {
                        this.ReleaseResources();
                    }
                    this.numberOfThreadsProcessing--;
                }
            }
        }