/// <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 while (true) { 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; } } } } finally { lock (_syncObject) { if (_isDisposed && (_numberOfThreadsProcessing == 1)) { ReleaseResources(); } _numberOfThreadsProcessing--; } } }
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--; } } }