Team Ai
Datasetpublic

MegaBites-AI/Windows-powershell

sourceHugging Facemitupdated 6mo agoView on Hugging Face
0likes372downloads
RemoteSessionNamedPipe.cs1318 linesDownload Raw Back to common
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4using System.Diagnostics.CodeAnalysis;5using System.Globalization;6using System.IO;7using System.IO.Pipes;8using System.Management.Automation.Internal;9using System.Management.Automation.Remoting.Server;10using System.Management.Automation.Tracing;11using System.Runtime.InteropServices;12using System.Security.AccessControl;13using System.Security.Principal;14using System.Threading;15 16using Microsoft.Win32.SafeHandles;17 18using Dbg = System.Diagnostics.Debug;19 20namespace System.Management.Automation.Remoting21{22    /// <summary>23    /// Shared named pipe utilities.24    /// </summary>25    internal static class NamedPipeUtils26    {27        #region Strings28 29        internal const string NamedPipeNamePrefix = "PSHost.";30#if UNIX31        internal const string DefaultAppDomainName = "None";32        // This `CoreFxPipe` prefix is defined by CoreFx33        internal const string NamedPipeNamePrefixSearch = "CoreFxPipe_PSHost*";34#else35        internal const string DefaultAppDomainName = "DefaultAppDomain";36        internal const string NamedPipeNamePrefixSearch = "PSHost*";37#endif38        // On non-Windows, .NET named pipes are limited to up to 104 characters39        internal const int MaxNamedPipeNameSize = 104;40 41        #endregion42 43        #region Static Methods44 45        /// <summary>46        /// Create a pipe name based on process information.47        /// E.g., "PSHost.ProcessStartTime.ProcessId.DefaultAppDomain.ProcessName"48        /// </summary>49        /// <param name="procId">Process Id.</param>50        /// <returns>Pipe name.</returns>51        internal static string CreateProcessPipeName(52            int procId)53        {54            return CreateProcessPipeName(55                System.Diagnostics.Process.GetProcessById(procId));56        }57 58        /// <summary>59        /// Create a pipe name based on process information.60        /// E.g., "PSHost.ProcessStartTime.ProcessId.DefaultAppDomain.ProcessName"61        /// </summary>62        /// <param name="proc">Process object.</param>63        /// <returns>Pipe name.</returns>64        internal static string CreateProcessPipeName(65            System.Diagnostics.Process proc)66        {67            return CreateProcessPipeName(proc, DefaultAppDomainName);68        }69 70        /// <summary>71        /// Create a pipe name based on process Id and appdomain name information.72        /// E.g., "PSHost.ProcessStartTime.ProcessId.DefaultAppDomain.ProcessName"73        /// </summary>74        /// <param name="procId">Process Id.</param>75        /// <param name="appDomainName">Name of process app domain to connect to.</param>76        /// <returns>Pipe name.</returns>77        internal static string CreateProcessPipeName(78            int procId,79            string appDomainName)80        {81            return CreateProcessPipeName(System.Diagnostics.Process.GetProcessById(procId), appDomainName);82        }83 84        /// <summary>85        /// Create a pipe name based on process and appdomain name information.86        /// E.g., "PSHost.ProcessStartTime.ProcessId.DefaultAppDomain.ProcessName"87        /// </summary>88        /// <param name="proc">Process object.</param>89        /// <param name="appDomainName">Name of process app domain to connect to.</param>90        /// <returns>Pipe name.</returns>91        internal static string CreateProcessPipeName(92            System.Diagnostics.Process proc,93            string appDomainName)94        {95            if (proc == null)96            {97                throw new PSArgumentNullException(nameof(proc));98            }99 100            if (string.IsNullOrEmpty(appDomainName))101            {102                appDomainName = DefaultAppDomainName;103            }104 105            System.Text.StringBuilder pipeNameBuilder = new System.Text.StringBuilder(MaxNamedPipeNameSize);106            pipeNameBuilder.Append(NamedPipeNamePrefix)107                // The starttime is there to prevent another process easily guessing the pipe name108                // and squatting on it.109                // There is a limit of 104 characters in total including the temp path to the named pipe file110                // on non-Windows systems, so we'll convert the starttime to hex and just take the first 8 characters.111#if UNIX112                .Append(proc.StartTime.ToFileTime().ToString("X8").AsSpan(1, 8))113#else114                .Append(proc.StartTime.ToFileTime().ToString(CultureInfo.InvariantCulture))115#endif116                .Append('.')117                .Append(proc.Id.ToString(CultureInfo.InvariantCulture))118                .Append('.')119                .Append(CleanAppDomainNameForPipeName(appDomainName))120                .Append('.')121                .Append(proc.ProcessName);122#if UNIX123            int charsToTrim = pipeNameBuilder.Length - MaxNamedPipeNameSize;124            if (charsToTrim > 0)125            {126                // TODO: In the case the pipe name is truncated, the user cannot connect to it using the cmdlet127                // unless we add a `-Force` type switch as it attempts to validate the current process name128                // matches the process name in the pipe name129                pipeNameBuilder.Remove(MaxNamedPipeNameSize + 1, charsToTrim);130            }131#endif132 133            return pipeNameBuilder.ToString();134        }135 136        private static string CleanAppDomainNameForPipeName(string appDomainName)137        {138            // Pipe names cannot contain the ':' character.  Remove unwanted characters.139            return appDomainName.Replace(":", string.Empty).Replace(" ", string.Empty);140        }141 142        /// <summary>143        /// Returns the current process AppDomain name.144        /// </summary>145        /// <returns>AppDomain Name string.</returns>146        internal static string GetCurrentAppDomainName()147        {148#if CORECLR // There is only one AppDomain per application in CoreCLR, which would be the default149            return DefaultAppDomainName;150#else       // Use the AppDomain in which current powershell is running151            return AppDomain.CurrentDomain.IsDefaultAppDomain() ? DefaultAppDomainName : AppDomain.CurrentDomain.FriendlyName;152#endif153        }154 155        #endregion156    }157 158    /// <summary>159    /// Native API for Named Pipes.160    /// </summary>161    internal static class NamedPipeNative162    {163        #region Pipe constants164 165        // Pipe open modes166        internal const uint PIPE_ACCESS_DUPLEX = 0x00000003;167        internal const uint PIPE_ACCESS_OUTBOUND = 0x00000002;168        internal const uint PIPE_ACCESS_INBOUND = 0x00000001;169 170        // Pipe modes171        internal const uint PIPE_TYPE_BYTE = 0x00000000;172        internal const uint PIPE_TYPE_MESSAGE = 0x00000004;173        internal const uint FILE_FLAG_OVERLAPPED = 0x40000000;174        internal const uint FILE_FLAG_FIRST_PIPE_INSTANCE = 0x00080000;175        internal const uint PIPE_WAIT = 0x00000000;176        internal const uint PIPE_NOWAIT = 0x00000001;177        internal const uint PIPE_READMODE_BYTE = 0x00000000;178        internal const uint PIPE_READMODE_MESSAGE = 0x00000002;179        internal const uint PIPE_ACCEPT_REMOTE_CLIENTS = 0x00000000;180        internal const uint PIPE_REJECT_REMOTE_CLIENTS = 0x00000008;181 182        // Pipe errors183        internal const uint ERROR_FILE_NOT_FOUND = 2;184        internal const uint ERROR_BROKEN_PIPE = 109;185        internal const uint ERROR_PIPE_BUSY = 231;186        internal const uint ERROR_NO_DATA = 232;187        internal const uint ERROR_MORE_DATA = 234;188        internal const uint ERROR_PIPE_CONNECTED = 535;189        internal const uint ERROR_IO_INCOMPLETE = 996;190        internal const uint ERROR_IO_PENDING = 997;191 192        #endregion193 194        #region Data structures195 196        [StructLayout(LayoutKind.Sequential)]197        internal class SECURITY_ATTRIBUTES198        {199            /// <summary>200            /// The size, in bytes, of this structure. Set this value to the size of the SECURITY_ATTRIBUTES structure.201            /// </summary>202            public int NLength;203 204            /// <summary>205            /// A pointer to a security descriptor for the object that controls the sharing of it.206            /// </summary>207            public IntPtr LPSecurityDescriptor = IntPtr.Zero;208 209            /// <summary>210            /// A Boolean value that specifies whether the returned handle is inherited when a new process is created.211            /// </summary>212            public bool InheritHandle;213 214            /// <summary>215            /// Initializes a new instance of the SECURITY_ATTRIBUTES class.216            /// </summary>217            public SECURITY_ATTRIBUTES()218            {219                this.NLength = 12;220            }221        }222 223        #endregion224 225        #region Pipe methods226 227        [DllImport(PinvokeDllNames.CreateNamedPipeDllName, SetLastError = true, CharSet = CharSet.Unicode)]228        internal static extern SafePipeHandle CreateNamedPipe(229           string lpName,230           uint dwOpenMode,231           uint dwPipeMode,232           uint nMaxInstances,233           uint nOutBufferSize,234           uint nInBufferSize,235           uint nDefaultTimeOut,236           SECURITY_ATTRIBUTES securityAttributes);237 238        internal static SECURITY_ATTRIBUTES GetSecurityAttributes(GCHandle securityDescriptorPinnedHandle, bool inheritHandle = false)239        {240            SECURITY_ATTRIBUTES securityAttributes = new NamedPipeNative.SECURITY_ATTRIBUTES();241            securityAttributes.InheritHandle = inheritHandle;242            securityAttributes.NLength = (int)Marshal.SizeOf(securityAttributes);243            securityAttributes.LPSecurityDescriptor = securityDescriptorPinnedHandle.AddrOfPinnedObject();244            return securityAttributes;245        }246 247        #endregion248    }249 250    /// <summary>251    /// Event arguments for listener thread end event.252    /// </summary>253    internal sealed class ListenerEndedEventArgs : EventArgs254    {255        #region Properties256 257        /// <summary>258        /// Exception reason for listener end event.  Can be null259        /// which indicates listener thread end is not due to an error.260        /// </summary>261        public Exception Reason { get; }262 263        /// <summary>264        /// True if listener should be restarted after ending.265        /// </summary>266        public bool RestartListener { get; }267 268        #endregion269 270        #region Constructors271 272        private ListenerEndedEventArgs() { }273 274        /// <summary>275        /// Constructor.276        /// </summary>277        /// <param name="reason">Listener end reason.</param>278        /// <param name="restartListener">Restart listener.</param>279        public ListenerEndedEventArgs(280            Exception reason,281            bool restartListener)282        {283            Reason = reason;284            RestartListener = restartListener;285        }286 287        #endregion288    }289 290    /// <summary>291    /// Light wrapper class for BCL NamedPipeServerStream class, that292    /// creates the named pipe server with process named pipe name,293    /// having correct access restrictions, and provides a listener294    /// thread loop.295    /// </summary>296    public sealed class RemoteSessionNamedPipeServer : IDisposable297    {298        #region Members299 300        private readonly object _syncObject;301        private readonly PowerShellTraceSource _tracer = PowerShellTraceSourceFactory.GetTraceSource();302 303        private const string _threadName = "IPC Listener Thread";304        private const int _namedPipeBufferSizeForRemoting = 32768;305        private const int _maxPipePathLengthLinux = 108;306        private const int _maxPipePathLengthMacOS = 104;307 308        // Singleton server.309        private static readonly object s_syncObject;310        internal static RemoteSessionNamedPipeServer IPCNamedPipeServer;311        internal static bool IPCNamedPipeServerEnabled;312 313        // Optional custom server.314        private static RemoteSessionNamedPipeServer _customNamedPipeServer;315 316        // Access mask constant taken from PipeSecurity access rights and is equivalent to317        // PipeAccessRights.FullControl.318        // See: https://msdn.microsoft.com/library/vstudio/bb348408(v=vs.100).aspx319        //320        private const int _pipeAccessMaskFullControl = 0x1f019f;321 322        #endregion323 324        #region Properties325 326        /// <summary>327        /// Returns the Named Pipe stream object.328        /// </summary>329        internal NamedPipeServerStream Stream { get; }330 331        /// <summary>332        /// Returns the Named Pipe name.333        /// </summary>334        internal string PipeName { get; }335 336        /// <summary>337        /// Returns true if listener is currently running.338        /// </summary>339        internal bool IsListenerRunning { get; private set; }340 341        /// <summary>342        /// Name of session configuration.343        /// </summary>344        internal string ConfigurationName { get; set; }345 346        /// <summary>347        /// Accessor for the named pipe reader.348        /// </summary>349        internal StreamReader TextReader { get; private set; }350 351        /// <summary>352        /// Accessor for the named pipe writer.353        /// </summary>354        internal StreamWriter TextWriter { get; private set; }355 356        /// <summary>357        /// Returns true if object is currently disposed.358        /// </summary>359        internal bool IsDisposed { get; private set; }360 361        /// <summary>362        /// Buffer size for PSRP fragmentor.363        /// </summary>364        internal static int NamedPipeBufferSizeForRemoting365        {366            get { return _namedPipeBufferSizeForRemoting; }367        }368 369        #endregion370 371        #region Events372 373        /// <summary>374        /// Event raised when the named pipe server listening thread375        /// ends.376        /// </summary>377        internal event EventHandler<ListenerEndedEventArgs> ListenerEnded;378 379        #endregion380 381        #region Constructors382 383        /// <summary>384        /// Creates a RemoteSessionNamedPipeServer with the current process and AppDomain information.385        /// </summary>386        /// <returns>RemoteSessionNamedPipeServer.</returns>387        internal static RemoteSessionNamedPipeServer CreateRemoteSessionNamedPipeServer()388        {389            string appDomainName = NamedPipeUtils.GetCurrentAppDomainName();390 391            return new RemoteSessionNamedPipeServer(NamedPipeUtils.CreateProcessPipeName(392                System.Diagnostics.Process.GetCurrentProcess(), appDomainName));393        }394 395        /// <summary>396        /// Constructor.  Creates named pipe server with provided pipe name.397        /// </summary>398        /// <param name="pipeName">Named Pipe name.</param>399        internal RemoteSessionNamedPipeServer(400            string pipeName)401        {402            if (pipeName == null)403            {404                throw new PSArgumentNullException(nameof(pipeName));405            }406 407            _syncObject = new object();408            PipeName = pipeName;409 410            Stream = CreateNamedPipe(411                serverName: ".",412                namespaceName: "pipe",413                coreName: pipeName,414                securityDesc: GetServerPipeSecurity());415        }416 417        /// <summary>418        /// Helper method to create a PowerShell transport named pipe via native API, along419        /// with a returned .Net NamedPipeServerStream object wrapping the named pipe.420        /// </summary>421        /// <param name="serverName">Named pipe server name.</param>422        /// <param name="namespaceName">Named pipe namespace name.</param>423        /// <param name="coreName">Named pipe core name.</param>424        /// <param name="securityDesc"></param>425        /// <returns>NamedPipeServerStream.</returns>426        private static NamedPipeServerStream CreateNamedPipe(427            string serverName,428            string namespaceName,429            string coreName,430            CommonSecurityDescriptor securityDesc)431        {432            if (serverName == null) { throw new PSArgumentNullException(nameof(serverName)); }433 434            if (namespaceName == null) { throw new PSArgumentNullException(nameof(namespaceName)); }435 436            if (coreName == null) { throw new PSArgumentNullException(nameof(coreName)); }437 438#if !UNIX439            string fullPipeName = @"\\" + serverName + @"\" + namespaceName + @"\" + coreName;440 441            // Create optional security attributes based on provided PipeSecurity.442            NamedPipeNative.SECURITY_ATTRIBUTES securityAttributes = null;443            GCHandle? securityDescHandle = null;444            if (securityDesc != null)445            {446                byte[] securityDescBuffer = new byte[securityDesc.BinaryLength];447                securityDesc.GetBinaryForm(securityDescBuffer, 0);448                securityDescHandle = GCHandle.Alloc(securityDescBuffer, GCHandleType.Pinned);449                securityAttributes = NamedPipeNative.GetSecurityAttributes(securityDescHandle.Value);450            }451 452            // Create named pipe.453            SafePipeHandle pipeHandle = NamedPipeNative.CreateNamedPipe(454                fullPipeName,455                NamedPipeNative.PIPE_ACCESS_DUPLEX | NamedPipeNative.FILE_FLAG_FIRST_PIPE_INSTANCE | NamedPipeNative.FILE_FLAG_OVERLAPPED,456                NamedPipeNative.PIPE_TYPE_MESSAGE | NamedPipeNative.PIPE_READMODE_MESSAGE | NamedPipeNative.PIPE_REJECT_REMOTE_CLIENTS,457                1,458                _namedPipeBufferSizeForRemoting,459                _namedPipeBufferSizeForRemoting,460                0,461                securityAttributes);462 463            int lastError = Marshal.GetLastWin32Error();464            securityDescHandle?.Free();465 466            if (pipeHandle.IsInvalid)467            {468                throw new PSInvalidOperationException(469                    StringUtil.Format(RemotingErrorIdStrings.CannotCreateNamedPipe, lastError));470            }471 472            // Create the .Net NamedPipeServerStream wrapper.473            try474            {475                return new NamedPipeServerStream(476                    PipeDirection.InOut,477                    true,                       // IsAsync478                    false,                      // IsConnected479                    pipeHandle);480            }481            catch (Exception)482            {483                pipeHandle.Dispose();484                throw;485            }486#else487            return new NamedPipeServerStream(488                pipeName: coreName,489                direction: PipeDirection.InOut,490                maxNumberOfServerInstances: 1,491                transmissionMode: PipeTransmissionMode.Byte,492                options: PipeOptions.Asynchronous | PipeOptions.CurrentUserOnly,493                inBufferSize: _namedPipeBufferSizeForRemoting,494                outBufferSize: _namedPipeBufferSizeForRemoting);495#endif496        }497 498        static RemoteSessionNamedPipeServer()499        {500            s_syncObject = new object();501 502            // Unless opt-out, all PowerShell instances will start with the named-pipe listener created and running.503            IPCNamedPipeServerEnabled = !Utils.GetEnvironmentVariableAsBool(name: "POWERSHELL_DIAGNOSTICS_OPTOUT", defaultValue: false);504 505            if (IPCNamedPipeServerEnabled)506            {507                CreateIPCNamedPipeServerSingleton();508                CreateProcessExitHandler();509            }510        }511 512        #endregion513 514        #region IDisposable515 516        /// <summary>517        /// Dispose.518        /// </summary>519        public void Dispose()520        {521            lock (_syncObject)522            {523                if (IsDisposed) { return; }524 525                IsDisposed = true;526            }527 528            if (TextReader != null)529            {530                try { TextReader.Dispose(); }531                catch (ObjectDisposedException) { }532 533                TextReader = null;534            }535 536            if (TextWriter != null)537            {538                try { TextWriter.Dispose(); }539                catch (ObjectDisposedException) { }540 541                TextWriter = null;542            }543 544            if (Stream != null)545            {546                try { Stream.Dispose(); }547                catch (ObjectDisposedException) { }548            }549        }550 551        #endregion552 553        #region Public Methods554 555        /// <summary>556        /// Creates the custom named pipe server with the given pipename.557        /// </summary>558        /// <param name="pipeName">The name of the pipe to create.</param>559        public static void CreateCustomNamedPipeServer(string pipeName)560        {561            lock (s_syncObject)562            {563                if (_customNamedPipeServer != null && !_customNamedPipeServer.IsDisposed)564                {565                    if (pipeName == _customNamedPipeServer.PipeName)566                    {567                        // we shouldn't recreate the server object if we're using the same pipeName568                        return;569                    }570 571                    // Dispose of the current pipe server so we can create a new one with the new pipeName572                    _customNamedPipeServer.Dispose();573                }574 575                if (!Platform.IsWindows)576                {577                    int maxNameLength = (Platform.IsLinux ? _maxPipePathLengthLinux : _maxPipePathLengthMacOS) - Path.GetTempPath().Length;578                    if (pipeName.Length > maxNameLength)579                    {580                        throw new InvalidOperationException(581                            string.Format(582                                RemotingErrorIdStrings.CustomPipeNameTooLong,583                                maxNameLength,584                                pipeName,585                                pipeName.Length));586                    }587                }588 589                try590                {591                    try592                    {593                        _customNamedPipeServer = new RemoteSessionNamedPipeServer(pipeName);594                    }595                    catch (IOException)596                    {597                        // Expected when named pipe server for this process already exists.598                        // This can happen if process has multiple AppDomains hosting PowerShell (SMA.dll).599                        return;600                    }601 602                    // Listener ended callback, used to create listening new pipe server.603                    _customNamedPipeServer.ListenerEnded += OnCustomNamedPipeServerEnded;604 605                    // Start the pipe server listening thread, and provide client connection callback.606                    _customNamedPipeServer.StartListening(ClientConnectionCallback);607                }608                catch (Exception)609                {610                    _customNamedPipeServer = null;611                }612            }613        }614 615        #endregion616 617        #region Private Methods618 619        /// <summary>620        /// Starts named pipe server listening thread.  When a client connects this thread621        /// makes a callback to implement the client communication.  When the thread ends622        /// this object is disposed and a new RemoteSessionNamedPipeServer must be created623        /// and a new listening thread started to handle subsequent client connections.624        /// </summary>625        /// <param name="clientConnectCallback">Connection callback.</param>626        internal void StartListening(627            Action<RemoteSessionNamedPipeServer> clientConnectCallback)628        {629            if (clientConnectCallback == null)630            {631                throw new PSArgumentNullException(nameof(clientConnectCallback));632            }633 634            lock (_syncObject)635            {636                if (IsListenerRunning)637                {638                    throw new InvalidOperationException(RemotingErrorIdStrings.NamedPipeAlreadyListening);639                }640 641                IsListenerRunning = true;642 643                // Create listener thread.644                Thread listenerThread = new Thread(ProcessListeningThread);645                listenerThread.Name = _threadName;646                listenerThread.IsBackground = true;647                listenerThread.Start(clientConnectCallback);648            }649        }650 651        internal static CommonSecurityDescriptor GetServerPipeSecurity()652        {653#if UNIX654            return null;655#else656            // Built-in Admin SID657            SecurityIdentifier adminSID = new SecurityIdentifier(WellKnownSidType.BuiltinAdministratorsSid, null);658            DiscretionaryAcl dacl = new DiscretionaryAcl(false, false, 1);659            dacl.AddAccess(660                AccessControlType.Allow,661                adminSID,662                _pipeAccessMaskFullControl,663                InheritanceFlags.None,664                PropagationFlags.None);665 666            CommonSecurityDescriptor securityDesc = new CommonSecurityDescriptor(667                false, false,668                ControlFlags.DiscretionaryAclPresent | ControlFlags.OwnerDefaulted | ControlFlags.GroupDefaulted,669                null, null, null, dacl);670 671            // Conditionally add User SID672            bool isAdminElevated = new WindowsPrincipal(WindowsIdentity.GetCurrent()).IsInRole(WindowsBuiltInRole.Administrator);673            if (!isAdminElevated)674            {675                securityDesc.DiscretionaryAcl.AddAccess(676                    AccessControlType.Allow,677                    WindowsIdentity.GetCurrent().User,678                    _pipeAccessMaskFullControl,679                    InheritanceFlags.None,680                    PropagationFlags.None);681            }682 683            return securityDesc;684#endif685        }686 687        /// <summary>688        /// Wait for client connection.689        /// </summary>690        private void WaitForConnection()691        {692            Stream.WaitForConnection();693        }694 695        /// <summary>696        /// Process listening thread.697        /// </summary>698        /// <param name="state">Client callback delegate.</param>699        [SuppressMessage("Microsoft.Reliability", "CA2001:AvoidCallingProblematicMethods", MessageId = "System.Runtime.InteropServices.SafeHandle.DangerousGetHandle")]700        private void ProcessListeningThread(object state)701        {702            string processId = Environment.ProcessId.ToString(CultureInfo.InvariantCulture);703            string appDomainName = NamedPipeUtils.GetCurrentAppDomainName();704 705            // Logging.706            _tracer.WriteMessage("RemoteSessionNamedPipeServer", "StartListening", Guid.Empty,707                "Listener thread started on Process {0} in AppDomainName {1}.", processId, appDomainName);708            PSEtwLog.LogOperationalInformation(709                PSEventId.NamedPipeIPC_ServerListenerStarted, PSOpcode.Open, PSTask.NamedPipe,710                PSKeyword.UseAlwaysOperational,711                processId, appDomainName);712 713            Exception ex = null;714            string userName = string.Empty;715            bool restartListenerThread = true;716 717            // Wait for connection.718            try719            {720                // Begin listening for a client connect.721                this.WaitForConnection();722 723                try724                {725#if UNIX726                    userName = System.Environment.UserName;727#else728                    userName = WindowsIdentity.GetCurrent().Name;729#endif730                }731                catch (System.Security.SecurityException) { }732 733                // Logging.734                _tracer.WriteMessage("RemoteSessionNamedPipeServer", "StartListening", Guid.Empty,735                    "Client connection started on Process {0} in AppDomainName {1} for User {2}.", processId, appDomainName, userName);736                PSEtwLog.LogOperationalInformation(737                    PSEventId.NamedPipeIPC_ServerConnect, PSOpcode.Connect, PSTask.NamedPipe,738                    PSKeyword.UseAlwaysOperational,739                    processId, appDomainName, userName);740 741                // Create reader/writer streams.742                TextReader = new StreamReader(Stream);743                TextWriter = new StreamWriter(Stream);744                TextWriter.AutoFlush = true;745            }746            catch (Exception e)747            {748                ex = e;749            }750 751            if (ex != null)752            {753                // Error during connection handling.  Don't try to restart listening thread.754                string errorMessage = !string.IsNullOrEmpty(ex.Message) ? ex.Message : string.Empty;755                _tracer.WriteMessage("RemoteSessionNamedPipeServer", "StartListening", Guid.Empty,756                    "Unexpected error in listener thread on process {0} in AppDomainName {1}.  Error Message: {2}", processId, appDomainName, errorMessage);757                PSEtwLog.LogOperationalError(PSEventId.NamedPipeIPC_ServerListenerError, PSOpcode.Exception, PSTask.NamedPipe,758                    PSKeyword.UseAlwaysOperational,759                    processId, appDomainName, errorMessage);760 761                Dispose();762                return;763            }764 765            // Start server session on new connection.766            ex = null;767            try768            {769                Action<RemoteSessionNamedPipeServer> clientConnectCallback = state as Action<RemoteSessionNamedPipeServer>;770                Dbg.Assert(clientConnectCallback != null, "Client callback should never be null.");771 772                // Handle a new client connect by making the callback.773                // The callback must handle all exceptions except774                // for a named pipe disposed or disconnected exception775                // which propagates up to the thread listener loop.776                clientConnectCallback(this);777            }778            catch (IOException)779            {780                // Expected connection terminated.781            }782            catch (ObjectDisposedException)783            {784                // Expected from PS transport close/dispose.785            }786            catch (Exception e)787            {788                ex = e;789                restartListenerThread = false;790            }791 792            // Logging.793            _tracer.WriteMessage("RemoteSessionNamedPipeServer", "StartListening", Guid.Empty,794                "Client connection ended on process {0} in AppDomainName {1} for User {2}.", processId, appDomainName, userName);795            PSEtwLog.LogOperationalInformation(796                PSEventId.NamedPipeIPC_ServerDisconnect, PSOpcode.Close, PSTask.NamedPipe,797                PSKeyword.UseAlwaysOperational,798                processId, appDomainName, userName);799 800            if (ex == null)801            {802                // Normal listener exit.803                _tracer.WriteMessage("RemoteSessionNamedPipeServer", "StartListening", Guid.Empty,804                    "Listener thread ended on process {0} in AppDomainName {1}.", processId, appDomainName);805                PSEtwLog.LogOperationalInformation(PSEventId.NamedPipeIPC_ServerListenerEnded, PSOpcode.Close, PSTask.NamedPipe,806                    PSKeyword.UseAlwaysOperational,807                    processId, appDomainName);808            }809            else810            {811                // Unexpected error.812                string errorMessage = !string.IsNullOrEmpty(ex.Message) ? ex.Message : string.Empty;813                _tracer.WriteMessage("RemoteSessionNamedPipeServer", "StartListening", Guid.Empty,814                    "Unexpected error in listener thread on process {0} in AppDomainName {1}.  Error Message: {2}", processId, appDomainName, errorMessage);815                PSEtwLog.LogOperationalError(PSEventId.NamedPipeIPC_ServerListenerError, PSOpcode.Exception, PSTask.NamedPipe,816                    PSKeyword.UseAlwaysOperational,817                    processId, appDomainName, errorMessage);818            }819 820            lock (_syncObject)821            {822                IsListenerRunning = false;823            }824 825            // Ensure this named pipe server object is disposed.826            Dispose();827 828            ListenerEnded.SafeInvoke(829                this,830                new ListenerEndedEventArgs(ex, restartListenerThread));831        }832 833        #endregion834 835        #region Static Methods836 837        /// <summary>838        /// Ensures the namedpipe singleton server is running and waits for a client connection.839        /// This is a blocking call that returns after the client connection ends.840        /// This method supports PowerShell running in "NamedPipeServerMode", which is used for841        /// PowerShell Direct Windows Server Container connection and management.842        /// </summary>843        /// <param name="configurationName">Name of the configuration to use.</param>844        internal static void RunServerMode(string configurationName)845        {846            IPCNamedPipeServerEnabled = true;847            CreateIPCNamedPipeServerSingleton();848 849            if (IPCNamedPipeServer == null)850            {851                throw new RuntimeException(RemotingErrorIdStrings.NamedPipeServerCannotStart);852            }853 854            IPCNamedPipeServer.ConfigurationName = configurationName;855 856            ManualResetEventSlim clientConnectionEnded = new ManualResetEventSlim(false);857            IPCNamedPipeServer.ListenerEnded -= OnIPCNamedPipeServerEnded;858            IPCNamedPipeServer.ListenerEnded += (sender, e) => clientConnectionEnded.Set();859 860            // Wait for server to service a single client connection.861            clientConnectionEnded.Wait();862            clientConnectionEnded.Dispose();863            IPCNamedPipeServerEnabled = false;864        }865 866        /// <summary>867        /// Creates the process named pipe server object singleton and868        /// starts the client listening thread.869        /// </summary>870        internal static void CreateIPCNamedPipeServerSingleton()871        {872            lock (s_syncObject)873            {874                if (!IPCNamedPipeServerEnabled) { return; }875 876                if (IPCNamedPipeServer == null || IPCNamedPipeServer.IsDisposed)877                {878                    try879                    {880                        try881                        {882                            IPCNamedPipeServer = CreateRemoteSessionNamedPipeServer();883                        }884                        catch (IOException)885                        {886                            // Expected when named pipe server for this process already exists.887                            // This can happen if process has multiple AppDomains hosting PowerShell (SMA.dll).888                            return;889                        }890 891                        // Listener ended callback, used to create listening new pipe server.892                        IPCNamedPipeServer.ListenerEnded += OnIPCNamedPipeServerEnded;893 894                        // Start the pipe server listening thread, and provide client connection callback.895                        IPCNamedPipeServer.StartListening(ClientConnectionCallback);896                    }897                    catch (Exception)898                    {899                        IPCNamedPipeServer = null;900                    }901                }902            }903        }904 905        private static void CreateProcessExitHandler()906        {907            AppDomain.CurrentDomain.ProcessExit += (sender, args) =>908            {909                IPCNamedPipeServerEnabled = false;910                RemoteSessionNamedPipeServer namedPipeServer = IPCNamedPipeServer;911                if (namedPipeServer != null)912                {913                    try914                    {915                        // Terminate the IPC thread.916                        namedPipeServer.Dispose();917                    }918                    catch (ObjectDisposedException)919                    {920                        // Ignore if object already disposed.921                    }922                    catch (Exception)923                    {924                        // Don't throw an exception on the app domain unload event thread.925                    }926                }927            };928        }929 930        private static void OnIPCNamedPipeServerEnded(object sender, ListenerEndedEventArgs args)931        {932            if (args.RestartListener)933            {934                CreateIPCNamedPipeServerSingleton();935            }936        }937 938        private static void OnCustomNamedPipeServerEnded(object sender, ListenerEndedEventArgs args)939        {940            if (args.RestartListener && sender is RemoteSessionNamedPipeServer server)941            {942                CreateCustomNamedPipeServer(server.PipeName);943            }944        }945 946        private static void ClientConnectionCallback(RemoteSessionNamedPipeServer pipeServer)947        {948            // Create server mediator object and begin remote session with client.949            NamedPipeProcessMediator.Run(950                string.Empty,951                pipeServer);952        }953 954        #endregion955    }956 957    /// <summary>958    /// Base class for RemoteSessionNamedPipeClient and ContainerSessionNamedPipeClient.959    /// </summary>960    internal class NamedPipeClientBase : IDisposable961    {962        #region Members963 964        private NamedPipeClientStream _clientPipeStream;965        private readonly PowerShellTraceSource _tracer = PowerShellTraceSourceFactory.GetTraceSource();966 967        #endregion968 969        #region Properties970 971        /// <summary>972        /// Accessor for the named pipe reader.973        /// </summary>974        public StreamReader TextReader { get; private set; }975 976        /// <summary>977        /// Accessor for the named pipe writer.978        /// </summary>979        public StreamWriter TextWriter { get; private set; }980 981        /// <summary>982        /// Name of pipe.983        /// </summary>984        public string PipeName985        {986            get;987            internal set;988        }989 990        #endregion991 992        #region IDisposable993 994        /// <summary>995        /// Dispose object.996        /// </summary>997        public void Dispose()998        {999            Dispose(true);1000            GC.SuppressFinalize(this);1001        }1002 1003        private void Dispose(bool disposing)1004        {1005            if (!disposing)1006            {1007                return;1008            }1009 1010            if (TextReader != null)1011            {1012                try { TextReader.Dispose(); }1013                catch (ObjectDisposedException) { }1014 1015                TextReader = null;1016            }1017 1018            if (TextWriter != null)1019            {1020                try { TextWriter.Dispose(); }1021                catch (ObjectDisposedException) { }1022 1023                TextWriter = null;1024            }1025 1026            if (_clientPipeStream != null)1027            {1028                try { _clientPipeStream.Dispose(); }1029                catch (ObjectDisposedException) { }1030            }1031        }1032 1033        #endregion1034 1035        #region Methods1036 1037        /// <summary>1038        /// Connect to named pipe server.  This is a blocking call until a1039        /// connection occurs or the timeout time has elapsed.1040        /// </summary>1041        /// <param name="timeout">Connection attempt timeout in milliseconds.</param>1042        public void Connect(1043            int timeout)1044        {1045            // Uses Native API to connect to pipe and return NamedPipeClientStream object.1046            _clientPipeStream = DoConnect(timeout);1047 1048            // Create reader/writer streams.1049            TextReader = new StreamReader(_clientPipeStream);1050            TextWriter = new StreamWriter(_clientPipeStream);1051            TextWriter.AutoFlush = true;1052 1053            _tracer.WriteMessage("NamedPipeClientBase", "Connect", Guid.Empty,1054                "Connection started on pipe: {0}", PipeName);1055        }1056 1057        /// <summary>1058        /// Closes the named pipe.1059        /// </summary>1060        public void Close() => _clientPipeStream?.Dispose();1061 1062        /// <summary>1063        /// Abort connection attempt.1064        /// </summary>1065        public virtual void AbortConnect()1066        { }1067 1068        /// <summary>1069        /// Begin connection attempt.1070        /// </summary>1071        protected virtual NamedPipeClientStream DoConnect(int timeout)1072        {1073            return null;1074        }1075 1076        #endregion1077    }1078 1079    /// <summary>1080    /// Light wrapper class for BCL NamedPipeClientStream class, that1081    /// creates the named pipe name and initiates connection to1082    /// target named pipe server.1083    /// </summary>1084    internal sealed class RemoteSessionNamedPipeClient : NamedPipeClientBase1085    {1086        #region Members1087 1088        private volatile bool _connecting;1089 1090        #endregion1091 1092        #region Constructors1093 1094        private RemoteSessionNamedPipeClient()1095        { }1096 1097        /// <summary>1098        /// Constructor.  Creates Named Pipe based on process object.1099        /// </summary>1100        /// <param name="process">Target process object for pipe.</param>1101        /// <param name="appDomainName">AppDomain name or null for default AppDomain.</param>1102        public RemoteSessionNamedPipeClient(System.Diagnostics.Process process, string appDomainName)1103            : this(NamedPipeUtils.CreateProcessPipeName(process, appDomainName))1104        { }1105 1106        /// <summary>1107        /// Constructor. Creates Named Pipe based on process Id.1108        /// </summary>1109        /// <param name="procId">Target process Id for pipe.</param>1110        /// <param name="appDomainName">AppDomain name or null for default AppDomain.</param>1111        public RemoteSessionNamedPipeClient(int procId, string appDomainName)1112            : this(NamedPipeUtils.CreateProcessPipeName(procId, appDomainName))1113        { }1114 1115        /// <summary>1116        /// Constructor. Creates Named Pipe based on name argument.1117        /// </summary>1118        /// <param name="pipeName">Named Pipe name.</param>1119        internal RemoteSessionNamedPipeClient(1120           string pipeName)1121        {1122            if (pipeName == null)1123            {1124                throw new PSArgumentNullException(nameof(pipeName));1125            }1126 1127            PipeName = pipeName;1128 1129            // Defer creating the .Net NamedPipeClientStream object until we connect.1130            // _clientPipeStream == null.1131        }1132 1133        /// <summary>1134        /// Constructor.1135        /// </summary>1136        /// <param name="serverName"></param>1137        /// <param name="namespaceName"></param>1138        /// <param name="coreName"></param>1139        internal RemoteSessionNamedPipeClient(1140            string serverName,1141            string namespaceName,1142            string coreName)1143        {1144            if (serverName == null) { throw new PSArgumentNullException(nameof(serverName)); }1145 1146            if (namespaceName == null) { throw new PSArgumentNullException(nameof(namespaceName)); }1147 1148            if (coreName == null) { throw new PSArgumentNullException(nameof(coreName)); }1149 1150            PipeName = @"\\" + serverName + @"\" + namespaceName + @"\" + coreName;1151 1152            // Defer creating the .Net NamedPipeClientStream object until we connect.1153            // _clientPipeStream == null.1154        }1155 1156        #endregion1157 1158        #region Public Methods1159 1160        /// <summary>1161        /// Abort connection attempt.1162        /// </summary>1163        public override void AbortConnect()1164        {1165            _connecting = false;1166        }1167 1168        #endregion1169 1170        #region Protected Methods1171 1172        /// <summary>1173        /// Begin connection attempt.1174        /// </summary>1175        protected override NamedPipeClientStream DoConnect(int timeout)1176        {1177            // Repeatedly attempt connection to pipe until timeout expires.1178            int startTime = Environment.TickCount;1179            int elapsedTime = 0;1180            _connecting = true;1181 1182            NamedPipeClientStream namedPipeClientStream = new NamedPipeClientStream(1183                serverName: ".",1184                pipeName: PipeName,1185                direction: PipeDirection.InOut,1186                options: PipeOptions.Asynchronous);1187 1188            namedPipeClientStream.ConnectAsync(timeout);1189 1190            do1191            {1192                if (!namedPipeClientStream.IsConnected)1193                {1194                    Thread.Sleep(100);1195                    elapsedTime = unchecked(Environment.TickCount - startTime);1196                    continue;1197                }1198 1199                _connecting = false;1200                return namedPipeClientStream;

Showing the first 1,200 of 1318 lines. Download the file for the rest.