Team Ai
Datasetpublic

MegaBites-AI/Windows-powershell

sourceHugging Facemitupdated 6mo agoView on Hugging Face
0likes372downloads
serverremotesession.cs1096 linesDownload Raw Back to server
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4using System.IO;5using System.Management.Automation.Internal;6using System.Management.Automation.Remoting.Server;7using System.Management.Automation.Runspaces;8using System.Threading;9 10using Dbg = System.Management.Automation.Diagnostics;11 12namespace System.Management.Automation.Remoting13{14    /// <summary>15    /// By design, on the server side, each remote connection is represented by16    /// a ServerRemoteSession object, which contains one instance of this class.17    ///18    /// This class holds 4 pieces of information.19    /// 1. Client capability: This is the capability received during the negotiation process.20    /// 2. Server capability: This comes from default parameters.21    /// 3. Client configuration: This holds the remote session related configuration parameters that22    ///    the client sent to the server. This parameters can be changed and resent after the connection23    ///    is established.24    /// 4. Server configuration: this holds the server sider configuration parameters.25    ///26    /// All these together define the connection level parameters.27    /// </summary>28    internal class ServerRemoteSessionContext29    {30        #region Constructors31 32        /// <summary>33        /// The constructor instantiates a server capability object and a server configuration34        /// using default values.35        /// </summary>36        internal ServerRemoteSessionContext()37        {38            ServerCapability = RemoteSessionCapability.CreateServerCapability();39        }40 41        #endregion Constructors42 43        /// <summary>44        /// This property represents the capability that the server receives from the client.45        /// </summary>46        internal RemoteSessionCapability ClientCapability { get; set; }47 48        /// <summary>49        /// This property is the server capability generated on the server side.50        /// </summary>51        internal RemoteSessionCapability ServerCapability { get; set; }52 53        /// <summary>54        /// True if negotiation from client is succeeded...in which case ClientCapability55        /// is the capability that server agreed with.56        /// </summary>57        internal bool IsNegotiationSucceeded { get; set; }58    }59 60    /// <summary>61    /// This class is designed to be the server side controller of a remote connection.62    ///63    /// It contains a static entry point that the PowerShell server process will get into64    /// the server mode. At this entry point, a runspace configuration is passed in. This runspace65    /// configuration is used to instantiate a server side runspace.66    ///67    /// This class controls a remote connection by using a Session data structure handler, which68    /// in turn contains a Finite State Machine, and a transport mechanism.69    /// </summary>70    internal class ServerRemoteSession : RemoteSession71    {72        [TraceSource("ServerRemoteSession", "ServerRemoteSession")]73        private static readonly PSTraceSource s_trace = PSTraceSource.GetTracer("ServerRemoteSession", "ServerRemoteSession");74 75        private readonly PSSenderInfo _senderInfo;76        private readonly string _configProviderId;77        private readonly string _initParameters;78        private string _initScriptForOutOfProcRS;79        private PSSessionConfiguration _sessionConfigProvider;80 81        // used to apply quotas on command and session transportmanagers.82        private int? _maxRecvdObjectSize;83        private int? _maxRecvdDataSizeCommand;84 85        private ServerRunspacePoolDriver _runspacePoolDriver;86        private readonly PSRemotingCryptoHelperServer _cryptoHelper;87 88        // Specifies an optional endpoint configuration for out-of-proc session use.89        // Creates a pushed remote runspace session created with this configuration name.90        private string _configurationName;91 92        // Specifies an optional .pssc configuration file path for out-of-proc session use.93        // The .pssc file is used to configure the runspace for the endpoint session.94        private string _configurationFile;95 96        // Specifies an initial location of the powershell session.97        private string _initialLocation;98 99        #region Events100        /// <summary>101        /// Raised when session is closed.102        /// </summary>103        internal EventHandler<RemoteSessionStateMachineEventArgs> Closed;104        #endregion105 106        #region Constructors107 108        /// <summary>109        /// This constructor instantiates a ServerRemoteSession object and110        /// a ServerRemoteSessionDataStructureHandler object.111        /// </summary>112        /// <param name="senderInfo">113        /// Details about the user creating this session.114        /// </param>115        /// <param name="configurationProviderId">116        /// The resource URI for which this session is being created117        /// </param>118        /// <param name="initializationParameters">119        /// Initialization Parameters xml passed by WSMan API. This data is read from the config120        /// xml.121        /// </param>122        /// <param name="transportManager">123        /// The transport manager this session should use to send/receive data124        /// </param>125        /// <returns></returns>126        internal ServerRemoteSession(PSSenderInfo senderInfo,127            string configurationProviderId,128            string initializationParameters,129            AbstractServerSessionTransportManager transportManager)130        {131            Dbg.Assert(transportManager != null, "transportManager cannot be null.");132 133            // let input,output and error from native commands redirect as we have134            // to send (or receive) them back to client for action.135            NativeCommandProcessor.IsServerSide = true;136            _senderInfo = senderInfo;137            _configProviderId = configurationProviderId;138            _initParameters = initializationParameters;139            _cryptoHelper = (PSRemotingCryptoHelperServer)transportManager.CryptoHelper;140            _cryptoHelper.Session = this;141 142            Context = new ServerRemoteSessionContext();143            SessionDataStructureHandler = new ServerRemoteSessionDSHandlerImpl(this, transportManager);144            BaseSessionDataStructureHandler = SessionDataStructureHandler;145            SessionDataStructureHandler.CreateRunspacePoolReceived += HandleCreateRunspacePool;146            SessionDataStructureHandler.NegotiationReceived += HandleNegotiationReceived;147            SessionDataStructureHandler.SessionClosing += HandleSessionDSHandlerClosing;148            SessionDataStructureHandler.PublicKeyReceived += HandlePublicKeyReceived;149            transportManager.Closing += HandleResourceClosing;150 151            // update the quotas from sessionState..start with default size..and152            // when Custom Session Configuration is loaded (during runspace creation) update this.153            transportManager.ReceivedDataCollection.MaximumReceivedObjectSize =154                BaseTransportManager.MaximumReceivedObjectSize;155 156            // session transport manager can receive unlimited data..however each object is limited157            // by maxRecvdObjectSize. this is to allow clients to use a session for an unlimited time..158            // also the messages that can be sent to a session are limited and very controlled.159            // However a command transport manager can be restricted to receive only a fixed amount of data160            // controlled by maxRecvdDataSizeCommand..This is because commands can accept any number of input161            // objects162            transportManager.ReceivedDataCollection.MaximumReceivedDataSize = null;163        }164 165        #endregion Constructors166 167        #region Creation Factory168 169        /// <summary>170        /// Creates a server remote session for the supplied <paramref name="configurationProviderId"/>171        /// and <paramref name="transportManager"/>.172        /// </summary>173        /// <param name="senderInfo"></param>174        /// <param name="configurationProviderId"></param>175        /// <param name="initializationParameters">176        /// Initialization Parameters xml passed by WSMan API. This data is read from the config177        /// xml.178        /// </param>179        /// <param name="transportManager"></param>180        /// <param name="initialCommand">Optional initial command used for OutOfProc sessions.</param>181        /// <param name="configurationName">Optional configuration endpoint name for OutOfProc sessions.</param>182        /// <param name="configurationFile">Optional configuration file (.pssc) path for OutOfProc sessions.</param>183        /// <param name="initialLocation">Optional configuration initial location of the powershell session.</param>184        /// <returns></returns>185        /// <exception cref="InvalidOperationException">186        /// InitialSessionState provider with <paramref name="configurationProviderId"/> does187        /// not exist on the remote server.188        /// </exception>189        /*190                  <InitializationParameters>191                    <Param Name="PSVersion" Value="2.0" />192                    <Param Name="ApplicationBase" Value="<folder path>" />193                    ...194                  </InitializationParameters>195        */196        internal static ServerRemoteSession CreateServerRemoteSession(197            PSSenderInfo senderInfo,198            string configurationProviderId,199            string initializationParameters,200            AbstractServerSessionTransportManager transportManager,201            string initialCommand,202            string configurationName,203            string configurationFile,204            string initialLocation)205        {206            Dbg.Assert(207                (senderInfo != null) && (senderInfo.UserInfo != null),208                "senderInfo and userInfo cannot be null.");209 210            s_trace.WriteLine("Finding InitialSessionState provider for id : {0}", configurationProviderId);211 212            if (string.IsNullOrEmpty(configurationProviderId))213            {214                throw PSTraceSource.NewInvalidOperationException("RemotingErrorIdStrings.NonExistentInitialSessionStateProvider", configurationProviderId);215            }216 217            const string shellPrefix = System.Management.Automation.Remoting.Client.WSManNativeApi.ResourceURIPrefix;218            int index = configurationProviderId.IndexOf(shellPrefix, StringComparison.OrdinalIgnoreCase);219            senderInfo.ConfigurationName = (index == 0) ? configurationProviderId.Substring(shellPrefix.Length) : string.Empty;220            ServerRemoteSession result = new ServerRemoteSession(221                senderInfo,222                configurationProviderId,223                initializationParameters,224                transportManager)225            {226                _initScriptForOutOfProcRS = initialCommand,227                _configurationName = configurationName,228                _configurationFile = configurationFile,229                _initialLocation = initialLocation230            };231 232            // start state machine.233            RemoteSessionStateMachineEventArgs startEventArg = new RemoteSessionStateMachineEventArgs(RemoteSessionEvent.CreateSession);234            result.SessionDataStructureHandler.StateMachine.RaiseEvent(startEventArg);235 236            return result;237        }238 239        #endregion240 241        #region Overrides242 243        /// <summary>244        /// This indicates the remote session object is Client, Server or Listener.245        /// </summary>246        internal override RemotingDestination MySelf247        {248            get249            {250                return RemotingDestination.Server;251            }252        }253 254        /// <summary>255        /// This is the data dispatcher for the whole remote connection.256        /// This dispatcher is registered with the server side input queue's InputDataReady event.257        /// When the input queue has received data from client, it calls the InputDataReady listeners.258        ///259        /// This dispatcher distinguishes the negotiation packet as a special case. For all other data,260        /// it dispatches the data through Finite State Machines DoMessageReceived handler by raising the event261        /// MessageReceived. The FSM's DoMessageReceived handler further dispatches to the receiving262        /// components: such as runspace or pipeline which have their own data dispatching methods.263        /// </summary>264        /// <param name="sender">265        /// This parameter is not used by the method, in this implementation.266        /// </param>267        /// <param name="dataEventArg">268        /// This parameter contains the remote data received from client.269        /// </param>270        /// <exception cref="ArgumentNullException">271        /// If the parameter <paramref name="dataEventArg"/> is null.272        /// </exception>273        /// <exception cref="ArgumentException">274        /// If the parameter <paramref name="dataEventArg"/> does not contain remote data.275        /// </exception>276        /// <exception cref="PSRemotingDataStructureException">277        /// If the destination of the data is not for server.278        /// </exception>279        internal void DispatchInputQueueData(object sender, RemoteDataEventArgs dataEventArg)280        {281            if (dataEventArg == null)282            {283                throw PSTraceSource.NewArgumentNullException(nameof(dataEventArg));284            }285 286            RemoteDataObject<PSObject> rcvdData = dataEventArg.ReceivedData;287 288            if (rcvdData == null)289            {290                throw PSTraceSource.NewArgumentException(nameof(dataEventArg));291            }292 293            RemotingDestination destination = rcvdData.Destination;294 295            if ((destination & MySelf) != MySelf)296            {297                // this packet is not target for me.298                throw new PSRemotingDataStructureException(RemotingErrorIdStrings.RemotingDestinationNotForMe, MySelf, destination);299            }300 301            RemotingTargetInterface targetInterface = rcvdData.TargetInterface;302            RemotingDataType dataType = rcvdData.DataType;303 304            RemoteSessionStateMachineEventArgs messageReceivedArg = null;305 306            switch (targetInterface)307            {308                case RemotingTargetInterface.Session:309                    {310                        switch (dataType)311                        {312                            // TODO: Directly calling an event handler in StateMachine bypassing the StateMachine's313                            // loop of ProcessEvents()..This is needed as connection is already established and the314                            // following message does not change state. An ideal solution would be to process315                            // non-session messages in this class rather than by StateMachine.316                            case RemotingDataType.CreateRunspacePool:317                                messageReceivedArg = new RemoteSessionStateMachineEventArgs(RemoteSessionEvent.MessageReceived);318                                if (SessionDataStructureHandler.StateMachine.CanByPassRaiseEvent(messageReceivedArg))319                                {320                                    messageReceivedArg.RemoteData = rcvdData;321                                    SessionDataStructureHandler.StateMachine.DoMessageReceived(this, messageReceivedArg);322                                }323                                else324                                {325                                    SessionDataStructureHandler.StateMachine.RaiseEvent(messageReceivedArg);326                                }327 328                                break;329 330                            case RemotingDataType.CloseSession:331                                SessionDataStructureHandler.RaiseDataReceivedEvent(dataEventArg);332                                break;333 334                            case RemotingDataType.SessionCapability:335                                SessionDataStructureHandler.RaiseDataReceivedEvent(dataEventArg);336                                break;337 338                            case RemotingDataType.PublicKey:339                                SessionDataStructureHandler.RaiseDataReceivedEvent(dataEventArg);340                                break;341 342                            default:343                                Dbg.Assert(false, "Should never reach here");344                                break;345                        }346                    }347 348                    break;349 350                // TODO: Directly calling an event handler in StateMachine bypassing the StateMachine's351                // loop of ProcessEvents()..This is needed as connection is already established and the352                // following message does not change state. An ideal solution would be to process353                // non-session messages in this class rather than by StateMachine.354                case RemotingTargetInterface.RunspacePool:355                case RemotingTargetInterface.PowerShell:356                    // GETBACK357                    messageReceivedArg = new RemoteSessionStateMachineEventArgs(RemoteSessionEvent.MessageReceived);358                    if (SessionDataStructureHandler.StateMachine.CanByPassRaiseEvent(messageReceivedArg))359                    {360                        messageReceivedArg.RemoteData = rcvdData;361                        SessionDataStructureHandler.StateMachine.DoMessageReceived(this, messageReceivedArg);362                    }363                    else364                    {365                        SessionDataStructureHandler.StateMachine.RaiseEvent(messageReceivedArg);366                    }367 368                    break;369            }370        }371 372        /// <summary>373        /// Have received a public key from the other side374        /// Import or take other action based on the state.375        /// </summary>376        /// <param name="sender">Sender of this event, unused.</param>377        /// <param name="eventArgs">event arguments which contains the378        /// remote public key</param>379        private void HandlePublicKeyReceived(object sender, RemoteDataEventArgs<string> eventArgs)380        {381            if (SessionDataStructureHandler.StateMachine.State == RemoteSessionState.Established ||382                SessionDataStructureHandler.StateMachine.State == RemoteSessionState.EstablishedAndKeyRequested || // this is only for legacy clients383                SessionDataStructureHandler.StateMachine.State == RemoteSessionState.EstablishedAndKeyExchanged)384            {385                string remotePublicKey = eventArgs.Data;386 387                bool ret = _cryptoHelper.ImportRemotePublicKey(remotePublicKey);388 389                RemoteSessionStateMachineEventArgs args = null;390                if (!ret)391                {392                    // importing remote public key failed393                    // set state to closed394                    args = new RemoteSessionStateMachineEventArgs(RemoteSessionEvent.KeyReceiveFailed);395 396                    SessionDataStructureHandler.StateMachine.RaiseEvent(args);397                }398 399                args = new RemoteSessionStateMachineEventArgs(RemoteSessionEvent.KeyReceived);400                SessionDataStructureHandler.StateMachine.RaiseEvent(args);401            }402        }403 404        /// <summary>405        /// Start the key exchange process.406        /// </summary>407        internal override void StartKeyExchange()408        {409            if (SessionDataStructureHandler.StateMachine.State == RemoteSessionState.Established)410            {411                // send using data structure handler412                SessionDataStructureHandler.SendRequestForPublicKey();413 414                RemoteSessionStateMachineEventArgs eventArgs =415                    new RemoteSessionStateMachineEventArgs(RemoteSessionEvent.KeyRequested);416                SessionDataStructureHandler.StateMachine.RaiseEvent(eventArgs);417            }418        }419 420        /// <summary>421        /// Complete the Key exchange process.422        /// </summary>423        internal override void CompleteKeyExchange()424        {425            _cryptoHelper.CompleteKeyExchange();426        }427 428        /// <summary>429        /// Send an encrypted session key to the client.430        /// </summary>431        internal void SendEncryptedSessionKey()432        {433            Dbg.Assert(SessionDataStructureHandler.StateMachine.State == RemoteSessionState.EstablishedAndKeyReceived,434                "Sever must be in EstablishedAndKeyReceived state before you can attempt to send encrypted session key");435 436            string encryptedSessionKey = null;437            bool ret = _cryptoHelper.ExportEncryptedSessionKey(out encryptedSessionKey);438 439            RemoteSessionStateMachineEventArgs args = null;440            if (!ret)441            {442                args =443                    new RemoteSessionStateMachineEventArgs(RemoteSessionEvent.KeySendFailed);444                SessionDataStructureHandler.StateMachine.RaiseEvent(args);445            }446 447            SessionDataStructureHandler.SendEncryptedSessionKey(encryptedSessionKey);448 449            // complete the key exchange process450            CompleteKeyExchange();451 452            args = new RemoteSessionStateMachineEventArgs(RemoteSessionEvent.KeySent);453            SessionDataStructureHandler.StateMachine.RaiseEvent(args);454        }455 456        #endregion Overrides457 458        #region Properties459 460        /// <summary>461        /// This property returns the ServerRemoteSessionContext object created inside462        /// this object's constructor.463        /// </summary>464        internal ServerRemoteSessionContext Context { get; }465 466        /// <summary>467        /// This property returns the ServerRemoteSessionDataStructureHandler object created inside468        /// this object's constructor.469        /// </summary>470        internal ServerRemoteSessionDataStructureHandler SessionDataStructureHandler { get; }471 472        #endregion473 474        #region Private/Internal Methods475 476        /// <summary>477        /// Let the session clear its resources.478        /// </summary>479        /// <param name="reasonForClose"></param>480        internal void Close(RemoteSessionStateMachineEventArgs reasonForClose)481        {482            Closed.SafeInvoke(this, reasonForClose);483            if (_runspacePoolDriver != null)484                _runspacePoolDriver.Closed -= HandleResourceClosing;485        }486 487        /// <summary>488        /// ExecutesConnect. expects client capability and connect_runspacepool PSRP489        /// messages in connectData.490        /// If negotiation is successful and max and min runspaces in connect_runspacepool491        /// match the associated runspace pool parameters, it builds up server capability492        /// and runspace_initinfo in connectResponseData.493        /// This is a version of Connect that executes the whole connect algorithm in one single494        /// hop.495        /// This algorithm is being executed synchronously without associating with state machine.496        /// </summary>497        /// <param name="connectData"></param>498        /// <param name="connectResponseData"></param>499        /// The operation is being outside the statemachine because of multiple reasons associated with design simplicity500        /// - Support automatic disconnect and let wsman server stack take care of connection state501        /// - The response data should not travel in transports output stream but as part of connect response502        /// - We want this operation to be synchronous503        internal void ExecuteConnect(byte[] connectData, out byte[] connectResponseData)504        {505            connectResponseData = null;506            Fragmentor fragmentor = new Fragmentor(int.MaxValue, null);507            Fragmentor defragmentor = fragmentor;508 509            int totalDataLen = connectData.Length;510 511            if (totalDataLen < FragmentedRemoteObject.HeaderLength)512            {513                // raise exception514                throw new PSRemotingDataStructureException(RemotingErrorIdStrings.ServerConnectFailedOnInputValidation);515            }516 517            // TODO: Follow up on comment from Krishna regarding having the serialization/deserialization separate for this518            // operation. This could be integrated as helper functions in fragmentor/serializer components519            long fragmentId = FragmentedRemoteObject.GetFragmentId(connectData, 0);520            bool sFlag = FragmentedRemoteObject.GetIsStartFragment(connectData, 0);521            bool eFlag = FragmentedRemoteObject.GetIsEndFragment(connectData, 0);522            int blobLength = FragmentedRemoteObject.GetBlobLength(connectData, 0);523 524            if (blobLength > totalDataLen - FragmentedRemoteObject.HeaderLength)525            {526                // raise exception527                throw new PSRemotingDataStructureException(RemotingErrorIdStrings.ServerConnectFailedOnInputValidation);528            }529 530            if (!sFlag || !eFlag)531            {532                throw new PSRemotingDataStructureException(RemotingErrorIdStrings.ServerConnectFailedOnInputValidation);533            }534 535            // if session is not in expected state536            RemoteSessionState currentState = SessionDataStructureHandler.StateMachine.State;537            if (currentState != RemoteSessionState.Established &&538                currentState != RemoteSessionState.EstablishedAndKeyExchanged)539            {540                throw new PSRemotingDataStructureException(RemotingErrorIdStrings.ServerConnectFailedOnServerStateValidation);541            }542 543            // process first message544            MemoryStream serializedStream = new MemoryStream();545            serializedStream.Write(connectData, FragmentedRemoteObject.HeaderLength, blobLength);546 547            serializedStream.Seek(0, SeekOrigin.Begin);548            RemoteDataObject<PSObject> capabilityObject = RemoteDataObject<PSObject>.CreateFrom(serializedStream, defragmentor);549 550            if (capabilityObject == null)551            {552                throw new PSRemotingDataStructureException(RemotingErrorIdStrings.ServerConnectFailedOnInputValidation);553            }554 555            if ((capabilityObject.Destination != RemotingDestination.Server) || (capabilityObject.DataType != RemotingDataType.SessionCapability))556            {557                throw new PSRemotingDataStructureException(RemotingErrorIdStrings.ServerConnectFailedOnInputValidation);558            }559 560            // process second message561            int secondFragmentLength = totalDataLen - FragmentedRemoteObject.HeaderLength - blobLength;562            if (secondFragmentLength < FragmentedRemoteObject.HeaderLength)563            {564                throw new PSRemotingDataStructureException(RemotingErrorIdStrings.ServerConnectFailedOnInputValidation);565            }566 567            byte[] secondFragment = new byte[secondFragmentLength];568            Array.Copy(connectData, FragmentedRemoteObject.HeaderLength + blobLength, secondFragment, 0, secondFragmentLength);569 570            fragmentId = FragmentedRemoteObject.GetFragmentId(secondFragment, 0);571            sFlag = FragmentedRemoteObject.GetIsStartFragment(secondFragment, 0);572            eFlag = FragmentedRemoteObject.GetIsEndFragment(secondFragment, 0);573            blobLength = FragmentedRemoteObject.GetBlobLength(secondFragment, 0);574 575            if (blobLength != secondFragmentLength - FragmentedRemoteObject.HeaderLength)576            {577                throw new PSRemotingDataStructureException(RemotingErrorIdStrings.ServerConnectFailedOnInputValidation);578            }579 580            if (!sFlag || !eFlag)581            {582                throw new PSRemotingDataStructureException(RemotingErrorIdStrings.ServerConnectFailedOnInputValidation);583            }584 585            // process second message586            serializedStream = new MemoryStream();587            serializedStream.Write(secondFragment, FragmentedRemoteObject.HeaderLength, blobLength);588 589            serializedStream.Seek(0, SeekOrigin.Begin);590            RemoteDataObject<PSObject> connectRunspacePoolObject = RemoteDataObject<PSObject>.CreateFrom(serializedStream, defragmentor);591 592            if (connectRunspacePoolObject == null)593            {594                throw new PSRemotingDataStructureException(RemotingErrorIdStrings.ServerConnectFailedOnServerStateValidation);595            }596 597            if ((connectRunspacePoolObject.Destination != RemotingDestination.Server) || (connectRunspacePoolObject.DataType != RemotingDataType.ConnectRunspacePool))598            {599                throw new PSRemotingDataStructureException(RemotingErrorIdStrings.ServerConnectFailedOnInputValidation);600            }601 602            // We have the two objects required for validating the connect operation603            RemoteSessionCapability clientCapability;604            try605            {606                clientCapability = RemotingDecoder.GetSessionCapability(capabilityObject.Data);607            }608            catch (PSRemotingDataStructureException)609            {610                // this will happen if expected properties are not611                // received for session capability612                throw new PSRemotingDataStructureException(RemotingErrorIdStrings.ServerConnectFailedOnInputValidation);613            }614 615            try616            {617                RunServerNegotiationAlgorithm(clientCapability, true);618            }619            catch (PSRemotingDataStructureException)620            {621                throw;622            }623 624            // validate client connect_runspacepool request625            int clientRequestedMinRunspaces = -1;626            int clientRequestedMaxRunspaces = -1;627            bool clientRequestedRunspaceCount = false;628            if (connectRunspacePoolObject.Data.Properties[RemoteDataNameStrings.MinRunspaces] != null && connectRunspacePoolObject.Data.Properties[RemoteDataNameStrings.MaxRunspaces] != null)629            {630                try631                {632                    clientRequestedMinRunspaces = RemotingDecoder.GetMinRunspaces(connectRunspacePoolObject.Data);633                    clientRequestedMaxRunspaces = RemotingDecoder.GetMaxRunspaces(connectRunspacePoolObject.Data);634                    clientRequestedRunspaceCount = true;635                }636                catch (PSRemotingDataStructureException)637                {638                    throw new PSRemotingDataStructureException(RemotingErrorIdStrings.ServerConnectFailedOnInputValidation);639                }640            }641 642            // these should be positive and max should be greater than min643            if (clientRequestedRunspaceCount &&644                (clientRequestedMinRunspaces == -1 || clientRequestedMaxRunspaces == -1 || clientRequestedMinRunspaces > clientRequestedMaxRunspaces))645            {646                throw new PSRemotingDataStructureException(RemotingErrorIdStrings.ServerConnectFailedOnInputValidation);647            }648 649            if (_runspacePoolDriver == null)650            {651                throw new PSRemotingDataStructureException(RemotingErrorIdStrings.ServerConnectFailedOnServerStateValidation);652            }653 654            // currently only one runspace pool per session is allowed. make sure this ID in connect message is the same655            if (connectRunspacePoolObject.RunspacePoolId != _runspacePoolDriver.InstanceId)656            {657                throw new PSRemotingDataStructureException(RemotingErrorIdStrings.ServerConnectFailedOnInputValidation);658            }659 660            // we currently dont support adjusting runspace count on a connect operation.661            // there is a potential conflict here where in the runspace pool driver is still yet to process a queued662            // setMax or setMinrunspaces request.663            // TODO: resolve this.. probably by letting the runspace pool consume all messages before we execute this.664            if (clientRequestedRunspaceCount665                && (_runspacePoolDriver.RunspacePool.GetMaxRunspaces() != clientRequestedMaxRunspaces)666                && (_runspacePoolDriver.RunspacePool.GetMinRunspaces() != clientRequestedMinRunspaces))667            {668                throw new PSRemotingDataStructureException(RemotingErrorIdStrings.ServerConnectFailedOnMismatchedRunspacePoolProperties);669            }670 671            // all client messages are validated672            // now build up the server capabilities and connect response messages to be piggybacked on connect response673            RemoteDataObject capability = RemotingEncoder.GenerateServerSessionCapability(Context.ServerCapability, _runspacePoolDriver.InstanceId);674            RemoteDataObject runspacepoolInitData = RemotingEncoder.GenerateRunspacePoolInitData(_runspacePoolDriver.InstanceId,675                                                                                               _runspacePoolDriver.RunspacePool.GetMaxRunspaces(),676                                                                                               _runspacePoolDriver.RunspacePool.GetMinRunspaces());677 678            // having this stream operating separately will result in out of sync fragment Ids. but this is still OK679            // as this is executed only when connecting from a new client that does not have any previous fragments context.680            // no problem even if fragment Ids in this response and the sessiontransport stream clash (interfere) and its guaranteed681            // that the fragments in connect response are always complete (enclose a complete object).682            SerializedDataStream stream = new SerializedDataStream(4 * 1024); //Each message with fragment headers cannot cross 4k683            stream.Enter();684            capability.Serialize(stream, fragmentor);685            stream.Exit();686            stream.Enter();687            runspacepoolInitData.Serialize(stream, fragmentor);688            stream.Exit();689            byte[] outbuffer = stream.Read();690            Dbg.Assert(outbuffer != null, "connect response data should be serialized");691            stream.Dispose();692            // we are done693            connectResponseData = outbuffer;694 695            // enqueue a connect event in state machine to let session do any other post-connect operation696            // Do this outside of the synchronous connect operation, as otherwise connect can easily get deadlocked697            ThreadPool.QueueUserWorkItem(new WaitCallback(698                (object state) =>699                {700                    RemoteSessionStateMachineEventArgs startEventArg = new RemoteSessionStateMachineEventArgs(RemoteSessionEvent.ConnectSession);701                    SessionDataStructureHandler.StateMachine.RaiseEvent(startEventArg);702                }));703 704            _runspacePoolDriver.DataStructureHandler.ProcessConnect();705            return;706        }707 708        // pass on application private data when session is connected from new client709        internal void HandlePostConnect() => _runspacePoolDriver?.SendApplicationPrivateDataToClient();710 711        /// <summary>712        /// </summary>713        /// <param name="sender"></param>714        /// <param name="createRunspaceEventArg"></param>715        /// <exception cref="InvalidOperationException">716        /// 1. InitialSessionState cannot be null.717        /// 2. Non existent InitialSessionState provider for the shellID718        /// </exception>719        private void HandleCreateRunspacePool(object sender, RemoteDataEventArgs createRunspaceEventArg)720        {721            if (createRunspaceEventArg == null)722            {723                throw PSTraceSource.NewArgumentNullException(nameof(createRunspaceEventArg));724            }725 726            RemoteDataObject<PSObject> rcvdData = createRunspaceEventArg.ReceivedData;727            Dbg.Assert(rcvdData != null, "rcvdData must be non-null");728 729            _senderInfo.ApplicationArguments = RemotingDecoder.GetApplicationArguments(rcvdData.Data);730 731            // Get Initial Session State from custom session config suppliers732            // like Exchange.733            ConfigurationDataFromXML configurationData =734                PSSessionConfiguration.LoadEndPointConfiguration(_configProviderId, _initParameters);735            // used by Out-Of-Proc (IPC) runspace.736            configurationData.InitializationScriptForOutOfProcessRunspace = _initScriptForOutOfProcRS;737            // start with data from configuration XML and then override with data738            // from EndPointConfiguration type.739            _maxRecvdObjectSize = configurationData.MaxReceivedObjectSizeMB;740            _maxRecvdDataSizeCommand = configurationData.MaxReceivedCommandSizeMB;741 742            if (string.IsNullOrEmpty(configurationData.ConfigFilePath))743            {744                _sessionConfigProvider = configurationData.CreateEndPointConfigurationInstance();745            }746            else747            {748                System.Security.Principal.WindowsPrincipal windowsPrincipal = new System.Security.Principal.WindowsPrincipal(_senderInfo.UserInfo.WindowsIdentity);749                Func<string, bool> validator = (role) => windowsPrincipal.IsInRole(role);750                _sessionConfigProvider = new DISCPowerShellConfiguration(configurationData.ConfigFilePath, validator);751            }752 753            // exchange of ApplicationArguments and ApplicationPrivateData is be done as early as possible754            // (i.e. to let the _sessionConfigProvider bail out if it can't comply with client's versioning request)755            PSPrimitiveDictionary applicationPrivateData = _sessionConfigProvider.GetApplicationPrivateData(_senderInfo);756 757            InitialSessionState rsSessionStateToUse = null;758 759            if (configurationData.SessionConfigurationData != null)760            {761                // Use the provided WinRM endpoint runspace configuration information.762                try763                {764                    rsSessionStateToUse =765                        _sessionConfigProvider.GetInitialSessionState(configurationData.SessionConfigurationData, _senderInfo, _configProviderId);766                }767                catch (NotImplementedException)768                {769                    rsSessionStateToUse = _sessionConfigProvider.GetInitialSessionState(_senderInfo);770                }771            }772            else if (!string.IsNullOrEmpty(_configurationFile))773            {774                // Use the optional _configurationFile parameter to create the endpoint runspace configuration.775                // This parameter is only used by Out-Of-Proc transports (not WinRM transports).776                var discConfiguration = new Remoting.DISCPowerShellConfiguration(777                    configFile: _configurationFile,778                    roleVerifier: null, 779                    validateFile: true);780                rsSessionStateToUse = discConfiguration.GetInitialSessionState(_senderInfo);781            }782            else783            {784                // Create a runspace configuration based on the provided PSSessionConfiguration provider.785                // This can be either a 'default' configuration, or third party configuration PSSessionConfiguration provider object.786                // So far, only Exchange provides a custom PSSessionConfiguration provider implementation.787                rsSessionStateToUse = _sessionConfigProvider.GetInitialSessionState(_senderInfo);788            }789 790            if (rsSessionStateToUse == null)791            {792                throw PSTraceSource.NewInvalidOperationException(RemotingErrorIdStrings.InitialSessionStateNull, _configProviderId);793            }794 795            rsSessionStateToUse.ThrowOnRunspaceOpenError = true;796 797            // this might throw if the sender info is already present798            rsSessionStateToUse.Variables.Add(799                new SessionStateVariableEntry(RemoteDataNameStrings.SenderInfoPreferenceVariable,800                _senderInfo,801                Remoting.PSRemotingErrorInvariants.FormatResourceString(802                    RemotingErrorIdStrings.PSSenderInfoDescription),803                ScopedItemOptions.ReadOnly));804 805            // Get client PS version from PSSenderInfo.806            Version psClientVersion = null;807            if (_senderInfo.ApplicationArguments != null && _senderInfo.ApplicationArguments.ContainsKey("PSversionTable"))808            {809                var value = PSObject.Base(_senderInfo.ApplicationArguments["PSversionTable"]) as PSPrimitiveDictionary;810                if (value != null && value.ContainsKey("PSVersion"))811                {812                    psClientVersion = PSObject.Base(value["PSVersion"]) as Version;813                }814            }815 816            if (!string.IsNullOrEmpty(configurationData.EndPointConfigurationTypeName))817            {818                // user specified a type to load for configuration..use the values from this type.819                _maxRecvdObjectSize = _sessionConfigProvider.GetMaximumReceivedObjectSize(_senderInfo);820                _maxRecvdDataSizeCommand = _sessionConfigProvider.GetMaximumReceivedDataSizePerCommand(_senderInfo);821            }822 823            SessionDataStructureHandler.TransportManager.ReceivedDataCollection.MaximumReceivedObjectSize = _maxRecvdObjectSize;824            // MaximumReceivedDataSize is not set for session transport manager...see the constructor825            // for more info.826 827            Guid clientRunspacePoolId = rcvdData.RunspacePoolId;828            int minRunspaces = RemotingDecoder.GetMinRunspaces(rcvdData.Data);829            int maxRunspaces = RemotingDecoder.GetMaxRunspaces(rcvdData.Data);830            PSThreadOptions threadOptions = RemotingDecoder.GetThreadOptions(rcvdData.Data);831            ApartmentState apartmentState = RemotingDecoder.GetApartmentState(rcvdData.Data);832            HostInfo hostInfo = RemotingDecoder.GetHostInfo(rcvdData.Data);833 834            if (_runspacePoolDriver != null)835            {836                throw new PSRemotingDataStructureException(RemotingErrorIdStrings.RunspaceAlreadyExists,837                    _runspacePoolDriver.InstanceId);838            }839 840#if !UNIX841            bool isAdministrator = _senderInfo.UserInfo.IsInRole(System.Security.Principal.WindowsBuiltInRole.Administrator);842#else843            bool isAdministrator = false;844#endif845 846            ServerRunspacePoolDriver tmpDriver = new ServerRunspacePoolDriver(847                clientRunspacePoolId,848                minRunspaces,849                maxRunspaces,850                threadOptions,851                apartmentState,852                hostInfo,853                rsSessionStateToUse,854                applicationPrivateData,855                configurationData,856                this.SessionDataStructureHandler.TransportManager,857                isAdministrator,858                Context.ServerCapability,859                psClientVersion,860                _configurationName,861                _initialLocation);862 863            // attach the necessary event handlers and start the driver.864            Interlocked.Exchange(ref _runspacePoolDriver, tmpDriver);865            _runspacePoolDriver.Closed += HandleResourceClosing;866            _runspacePoolDriver.Start();867        }868 869        /// <summary>870        /// This handler method runs the negotiation algorithm. It decides if the negotiation is successful,871        /// or fails.872        /// </summary>873        /// <param name="sender"></param>874        /// <param name="negotiationEventArg">875        /// This parameter contains the client negotiation capability packet.876        /// </param>877        /// <exception cref="ArgumentNullException">878        /// If the parameter <paramref name="negotiationEventArg"/> is null.879        /// </exception>880        private void HandleNegotiationReceived(object sender, RemoteSessionNegotiationEventArgs negotiationEventArg)881        {882            if (negotiationEventArg == null)883            {884                throw PSTraceSource.NewArgumentNullException(nameof(negotiationEventArg));885            }886 887            try888            {889                Context.ClientCapability = negotiationEventArg.RemoteSessionCapability;890                // This will throw if there is an error running the algorithm891                RunServerNegotiationAlgorithm(negotiationEventArg.RemoteSessionCapability, false);892 893                // Send server's capability to client.894                RemoteSessionStateMachineEventArgs sendingNegotiationArg = new RemoteSessionStateMachineEventArgs(RemoteSessionEvent.NegotiationSending);895                SessionDataStructureHandler.StateMachine.RaiseEvent(sendingNegotiationArg);896 897                // if negotiation succeeded change the state to neg. completed.898                RemoteSessionStateMachineEventArgs negotiationCompletedArg =899                    new RemoteSessionStateMachineEventArgs(RemoteSessionEvent.NegotiationCompleted);900                SessionDataStructureHandler.StateMachine.RaiseEvent(negotiationCompletedArg);901            }902            catch (PSRemotingDataStructureException dse)903            {904                // Before setting to negotiation failed..send servers capability...that905                // way client can communicate differently if it wants to.906                RemoteSessionStateMachineEventArgs sendingNegotiationArg =907                    new RemoteSessionStateMachineEventArgs(RemoteSessionEvent.NegotiationSending);908                SessionDataStructureHandler.StateMachine.RaiseEvent(sendingNegotiationArg);909 910                RemoteSessionStateMachineEventArgs negotiationFailedArg =911                    new RemoteSessionStateMachineEventArgs(RemoteSessionEvent.NegotiationFailed,912                        dse);913                SessionDataStructureHandler.StateMachine.RaiseEvent(negotiationFailedArg);914            }915        }916 917        /// <summary>918        /// Handle session closing event to close runspace pool drivers this session is hosting.919        /// </summary>920        /// <param name="sender"></param>921        /// <param name="eventArgs"></param>922        private void HandleSessionDSHandlerClosing(object sender, EventArgs eventArgs)923        {924            _runspacePoolDriver?.Close();925 926            // dispose the session configuration object..this will let them927            // clean their resources.928            if (_sessionConfigProvider != null)929            {930                _sessionConfigProvider.Dispose();931                _sessionConfigProvider = null;932            }933        }934 935        /// <summary>936        /// This handles closing of any resource used by this session.937        /// Resources used are RunspacePoolDriver, TransportManager.938        /// </summary>939        /// <param name="sender"></param>940        /// <param name="args"></param>941        private void HandleResourceClosing(object sender, EventArgs args)942        {943            RemoteSessionStateMachineEventArgs closeSessionArgs = new RemoteSessionStateMachineEventArgs(RemoteSessionEvent.Close);944            closeSessionArgs.RemoteData = null;945            SessionDataStructureHandler.StateMachine.RaiseEvent(closeSessionArgs);946        }947 948        /// <summary>949        /// This is the server side remote session capability negotiation algorithm.950        /// </summary>951        /// <param name="clientCapability">952        /// This is the client capability that the server received from client.953        /// </param>954        /// <param name="onConnect">955        /// If the negotiation is on a connect (and not create)956        /// </param>957        /// <returns>958        /// The method returns true if the capability negotiation is successful.959        /// Otherwise, it returns false.960        /// </returns>961        /// <exception cref="PSRemotingDataStructureException">962        /// 1. PowerShell server does not support the PSVersion {1} negotiated by the client.963        ///    Make sure the client is compatible with the build {2} of PowerShell.964        /// 2. PowerShell server does not support the SerializationVersion {1} negotiated by the client.965        ///    Make sure the client is compatible with the build {2} of PowerShell.966        /// </exception>967        private bool RunServerNegotiationAlgorithm(RemoteSessionCapability clientCapability, bool onConnect)968        {969            Dbg.Assert(clientCapability != null, "Client capability cache must be non-null");970 971            Version clientProtocolVersion = clientCapability.ProtocolVersion;972            Version serverProtocolVersion = Context.ServerCapability.ProtocolVersion;973 974            if (onConnect)975            {976                // PS v7.6 server can support reconstruct/reconnect for all 2.x protocol versions that support reconstruct/reconnect (v2.2+).977                // Major protocol version differences (2.x -> 3.x) are not supported. A reconstruct can only be initiated by a client that understands disconnect (v2.2+).978                if (clientProtocolVersion == RemotingConstants.ProtocolVersion_2_2 ||979                    clientProtocolVersion == RemotingConstants.ProtocolVersion_2_3)980                {981                    // Report the server as the same version to the client.982                    // Client protocol: v2.2, v2.3983                    serverProtocolVersion = clientProtocolVersion;984                    Context.ServerCapability.ProtocolVersion = serverProtocolVersion;985                }986                else if (!(clientProtocolVersion.Major == serverProtocolVersion.Major &&987                           clientProtocolVersion.Minor >= serverProtocolVersion.Minor))988                {989                    // Throw for protocol versions 2.x that don't support disconnect/reconnect.990                    // Client protocol: < 2.2991                    PSRemotingDataStructureException reasonOfFailure =992                        new PSRemotingDataStructureException(RemotingErrorIdStrings.ServerConnectFailedOnNegotiation,993                            RemoteDataNameStrings.PS_STARTUP_PROTOCOL_VERSION_NAME,994                            clientProtocolVersion,995                            PSVersionInfo.GitCommitId,996                            RemotingConstants.ProtocolVersion);997                    throw reasonOfFailure;998                }999 1000                // All other minor versions are supported and the server returns its full capability.1001                // Client protocol: v2.4, v2.5 ...1002            }1003            else1004            {1005                if (clientProtocolVersion == RemotingConstants.ProtocolVersion_2_0 ||1006                    clientProtocolVersion == RemotingConstants.ProtocolVersion_2_1 ||1007                    clientProtocolVersion == RemotingConstants.ProtocolVersion_2_2 ||1008                    clientProtocolVersion == RemotingConstants.ProtocolVersion_2_3)1009                {1010                    // We support the those client versions and report the server as the same version to the client.1011                    serverProtocolVersion = clientProtocolVersion;1012                    Context.ServerCapability.ProtocolVersion = serverProtocolVersion;1013                }1014                else if (!(clientProtocolVersion.Major == serverProtocolVersion.Major &&1015                           clientProtocolVersion.Minor >= serverProtocolVersion.Minor))1016                {1017                    PSRemotingDataStructureException reasonOfFailure =1018                        new PSRemotingDataStructureException(RemotingErrorIdStrings.ServerNegotiationFailed,1019                            RemoteDataNameStrings.PS_STARTUP_PROTOCOL_VERSION_NAME,1020                            clientProtocolVersion,1021                            PSVersionInfo.GitCommitId,1022                            RemotingConstants.ProtocolVersion);1023                    throw reasonOfFailure;1024                }1025            }1026 1027            // PSVersion Check1028            Version clientPSVersion = clientCapability.PSVersion;1029            Version serverPSVersion = Context.ServerCapability.PSVersion;1030            if (!((clientPSVersion.Major == serverPSVersion.Major) &&1031                  (clientPSVersion.Minor >= serverPSVersion.Minor)))1032            {1033                PSRemotingDataStructureException reasonOfFailure =1034                    new PSRemotingDataStructureException(RemotingErrorIdStrings.ServerNegotiationFailed,1035                        RemoteDataNameStrings.PSVersion,1036                        clientPSVersion,1037                        PSVersionInfo.GitCommitId,1038                        RemotingConstants.ProtocolVersion);1039                throw reasonOfFailure;1040            }1041 1042            // SerializationVersion check1043            Version clientSerVersion = clientCapability.SerializationVersion;1044            Version serverSerVersion = Context.ServerCapability.SerializationVersion;1045            if (!((clientSerVersion.Major == serverSerVersion.Major) &&1046                  (clientSerVersion.Minor >= serverSerVersion.Minor)))1047            {1048                PSRemotingDataStructureException reasonOfFailure =1049                    new PSRemotingDataStructureException(RemotingErrorIdStrings.ServerNegotiationFailed,1050                        RemoteDataNameStrings.SerializationVersion,1051                        clientSerVersion,1052                        PSVersionInfo.GitCommitId,1053                        RemotingConstants.ProtocolVersion);1054                throw reasonOfFailure;1055            }1056 1057            return true;1058        }1059 1060        /// <summary>1061        /// </summary>1062        /// <param name="clientRunspacePoolId"></param>1063        /// <returns></returns>1064        internal ServerRunspacePoolDriver GetRunspacePoolDriver(Guid clientRunspacePoolId)1065        {1066            if (_runspacePoolDriver == null)1067            {1068                return null;1069            }1070 1071            if (_runspacePoolDriver.InstanceId == clientRunspacePoolId)1072            {1073                return _runspacePoolDriver;1074            }1075 1076            return null;1077        }1078 1079        /// <summary>1080        /// Used by Command Session to apply quotas on the command transport manager.1081        /// This method is here because ServerRemoteSession knows about InitialSessionState.1082        /// </summary>1083        /// <param name="cmdTransportManager">1084        /// Command TransportManager to apply the quota on.1085        /// </param>1086        internal void ApplyQuotaOnCommandTransportManager(AbstractServerTransportManager cmdTransportManager)1087        {1088            Dbg.Assert(cmdTransportManager != null, "cmdTransportManager cannot be null");1089            cmdTransportManager.ReceivedDataCollection.MaximumReceivedDataSize = _maxRecvdDataSizeCommand;1090            cmdTransportManager.ReceivedDataCollection.MaximumReceivedObjectSize = _maxRecvdObjectSize;1091        }1092 1093        #endregion1094    }1095}1096