/
githubmirror
/
PowerShell
Обзор
Документация
Войти
/
githubmirror
/
PowerShell
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/System.Management.Automation/engine/remoting/common/WireDataFormat/RemoteHost.cs
750 строк
28 KB
Frode Flaten
Use null propagation operator in `remoting` code (#17790)
01 авг 2022, 19:21
Не верифицирован
01 авг 2022, 19:21
7c62490
Код
Авторство
О чём код?
// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Management.Automation.Host; using System.Reflection; using Dbg = System.Management.Automation.Diagnostics; using InternalHostUserInterface = System.Management.Automation.Internal.Host.InternalHostUserInterface; namespace System.Management.Automation.Remoting { /// <summary> /// Executes methods; can be encoded and decoded for transmission over the /// wire. /// </summary> internal class RemoteHostCall { /// <summary> /// Method name. /// </summary> internal string MethodName { get { return _methodInfo.Name; } } /// <summary> /// Method id. /// </summary> internal RemoteHostMethodId MethodId { get; } /// <summary> /// Parameters. /// </summary> internal object[] Parameters { get; } /// <summary> /// Method info. /// </summary> private readonly RemoteHostMethodInfo _methodInfo; /// <summary> /// Call id. /// </summary> private readonly long _callId; /// <summary> /// Call id. /// </summary> internal long CallId { get { return _callId; } } /// <summary> /// Computer name to be used in messages. /// </summary> private string _computerName; /// <summary> /// Constructor for RemoteHostCall. /// </summary> internal RemoteHostCall(long callId, RemoteHostMethodId methodId, object[] parameters) { Dbg.Assert(parameters != null, "Expected parameters != null"); _callId = callId; MethodId = methodId; Parameters = parameters; _methodInfo = RemoteHostMethodInfo.LookUp(methodId); } /// <summary> /// Encode parameters. /// </summary> private static PSObject EncodeParameters(object[] parameters) { // Encode the parameters and wrap the array into an ArrayList and then into a PSObject. ArrayList parameterList = new ArrayList(); for (int i = 0; i < parameters.Length; ++i) { object parameter = parameters[i] == null ? null : RemoteHostEncoder.EncodeObject(parameters[i]); parameterList.Add(parameter); } return new PSObject(parameterList); } /// <summary> /// Decode parameters. /// </summary> private static object[] DecodeParameters(PSObject parametersPSObject, Type[] parameterTypes) { // Extract the ArrayList and decode the parameters. ArrayList parameters = (ArrayList)parametersPSObject.BaseObject; List<object> decodedParameters = new List<object>(); Dbg.Assert(parameters.Count == parameterTypes.Length, "Expected parameters.Count == parameterTypes.Length"); for (int i = 0; i < parameters.Count; ++i) { object parameter = parameters[i] == null ? null : RemoteHostEncoder.DecodeObject(parameters[i], parameterTypes[i]); decodedParameters.Add(parameter); } return decodedParameters.ToArray(); } /// <summary> /// Encode. /// </summary> internal PSObject Encode() { // Add all host information as data. PSObject data = RemotingEncoder.CreateEmptyPSObject(); // Encode the parameters for transport. PSObject parametersPSObject = EncodeParameters(Parameters); // Embed everything into the main PSobject. data.Properties.Add(new PSNoteProperty(RemoteDataNameStrings.CallId, _callId)); data.Properties.Add(new PSNoteProperty(RemoteDataNameStrings.MethodId, MethodId)); data.Properties.Add(new PSNoteProperty(RemoteDataNameStrings.MethodParameters, parametersPSObject)); return data; } /// <summary> /// Decode. /// </summary> internal static RemoteHostCall Decode(PSObject data) { Dbg.Assert(data != null, "Expected data != null"); // Extract all the fields from data. long callId = RemotingDecoder.GetPropertyValue<long>(data, RemoteDataNameStrings.CallId); PSObject parametersPSObject = RemotingDecoder.GetPropertyValue<PSObject>(data, RemoteDataNameStrings.MethodParameters); RemoteHostMethodId methodId = RemotingDecoder.GetPropertyValue<RemoteHostMethodId>(data, RemoteDataNameStrings.MethodId); // Look up all the info related to the method. RemoteHostMethodInfo methodInfo = RemoteHostMethodInfo.LookUp(methodId); // Decode the parameters. object[] parameters = DecodeParameters(parametersPSObject, methodInfo.ParameterTypes); // Create and return the RemoteHostCall. return new RemoteHostCall(callId, methodId, parameters); } /// <summary> /// Is void method. /// </summary> internal bool IsVoidMethod { get { return _methodInfo.ReturnType == typeof(void); } } /// <summary> /// Execute void method. /// </summary> internal void ExecuteVoidMethod(PSHost clientHost) { // The clientHost can be null if the user creates a runspace object without providing // a host parameter. if (clientHost == null) { return; } RemoteRunspace remoteRunspaceToClose = null; if (this.IsSetShouldExitOrPopRunspace) { remoteRunspaceToClose = GetRemoteRunspaceToClose(clientHost); } try { object targetObject = this.SelectTargetObject(clientHost); MyMethodBase.Invoke(targetObject, Parameters); } finally { remoteRunspaceToClose?.Close(); } } /// <summary> /// Get remote runspace to close. /// </summary> private static RemoteRunspace GetRemoteRunspaceToClose(PSHost clientHost) { // Figure out if we need to close the remote runspace. Return null if we don't. // Are we a Start-PSSession enabled host? IHostSupportsInteractiveSession host = clientHost as IHostSupportsInteractiveSession; if (host == null || !host.IsRunspacePushed) { return null; } // Now inspect the runspace. RemoteRunspace remoteRunspace = host.Runspace as RemoteRunspace; if (remoteRunspace == null || !remoteRunspace.ShouldCloseOnPop) { return null; } // At this point it is clear we have to close the remote runspace, so return it. return remoteRunspace; } /// <summary> /// My method base. /// </summary> private MethodBase MyMethodBase { get { return (MethodBase)_methodInfo.InterfaceType.GetMethod(_methodInfo.Name, _methodInfo.ParameterTypes); } } /// <summary> /// Execute non void method. /// </summary> internal RemoteHostResponse ExecuteNonVoidMethod(PSHost clientHost) { // The clientHost can be null if the user creates a runspace object without providing // a host parameter. if (clientHost == null) { throw RemoteHostExceptions.NewNullClientHostException(); } object targetObject = this.SelectTargetObject(clientHost); RemoteHostResponse remoteHostResponse = this.ExecuteNonVoidMethodOnObject(targetObject); return remoteHostResponse; } /// <summary> /// Execute non void method on object. /// </summary> private RemoteHostResponse ExecuteNonVoidMethodOnObject(object instance) { // Create variables to store result of execution. Exception exception = null; object returnValue = null; // Invoke the method and store its return values. try { if (MethodId == RemoteHostMethodId.GetBufferContents) { throw new PSRemotingDataStructureException(RemotingErrorIdStrings.RemoteHostGetBufferContents, _computerName.ToUpper()); } returnValue = MyMethodBase.Invoke(instance, Parameters); } catch (Exception e) { // Catch-all OK, 3rd party callout. exception = e.InnerException; } // Create a RemoteHostResponse object to store the return value and exceptions. return new RemoteHostResponse(_callId, MethodId, returnValue, exception); } /// <summary> /// Get the object that this method should be invoked on. /// </summary> private object SelectTargetObject(PSHost host) { if (host == null || host.UI == null) { return null; } if (_methodInfo.InterfaceType == typeof(PSHost)) { return host; } if (_methodInfo.InterfaceType == typeof(IHostSupportsInteractiveSession)) { return host; } if (_methodInfo.InterfaceType == typeof(PSHostUserInterface)) { return host.UI; } if (_methodInfo.InterfaceType == typeof(IHostUISupportsMultipleChoiceSelection)) { return host.UI; } if (_methodInfo.InterfaceType == typeof(PSHostRawUserInterface)) { return host.UI.RawUI; } throw RemoteHostExceptions.NewUnknownTargetClassException(_methodInfo.InterfaceType.ToString()); } /// <summary> /// Is set should exit. /// </summary> internal bool IsSetShouldExit { get { return MethodId == RemoteHostMethodId.SetShouldExit; } } /// <summary> /// Is set should exit or pop runspace. /// </summary> internal bool IsSetShouldExitOrPopRunspace { get { return MethodId == RemoteHostMethodId.SetShouldExit || MethodId == RemoteHostMethodId.PopRunspace; } } /// <summary> /// This message performs various security checks on the /// remote host call message. If there is a need to modify /// the message or discard it for security reasons then /// such modifications will be made here. /// </summary> /// <param name="computerName">computer name to use in /// warning messages</param> /// <returns>A collection of remote host calls which will /// have to be executed before this host call can be /// executed.</returns> internal Collection<RemoteHostCall> PerformSecurityChecksOnHostMessage(string computerName) { Dbg.Assert(!string.IsNullOrEmpty(computerName), "Computer Name must be passed for use in warning messages"); _computerName = computerName; Collection<RemoteHostCall> prerequisiteCalls = new Collection<RemoteHostCall>(); // check if the incoming message is a PromptForCredential message // if so, do the following: // (a) prepend "PowerShell Credential Request" in the title // (b) prepend "Message from Server XXXXX" to the text message if (MethodId == RemoteHostMethodId.PromptForCredential1 || MethodId == RemoteHostMethodId.PromptForCredential2) { // modify the caption which is _parameters[0] string modifiedCaption = ModifyCaption((string)Parameters[0]); // modify the message which is _parameters[1] string modifiedMessage = ModifyMessage((string)Parameters[1], computerName); Parameters[0] = modifiedCaption; Parameters[1] = modifiedMessage; } // Check if the incoming message is a Prompt message // if so, then do the following: // (a) check if any of the field descriptions // correspond to PSCredential // (b) if field descriptions correspond to // PSCredential modify the caption and // message as in the previous case above else if (MethodId == RemoteHostMethodId.Prompt) { // check if any of the field descriptions is for type // PSCredential if (Parameters.Length == 3) { Collection<FieldDescription> fieldDescs = (Collection<FieldDescription>)Parameters[2]; bool havePSCredential = false; foreach (FieldDescription fieldDesc in fieldDescs) { fieldDesc.IsFromRemoteHost = true; Type fieldType = InternalHostUserInterface.GetFieldType(fieldDesc); if (fieldType != null) { if (fieldType == typeof(PSCredential)) { havePSCredential = true; fieldDesc.ModifiedByRemotingProtocol = true; } else if (fieldType == typeof(System.Security.SecureString)) { prerequisiteCalls.Add(ConstructWarningMessageForSecureString( computerName, RemotingErrorIdStrings.RemoteHostPromptSecureStringPrompt)); } } } if (havePSCredential) { // modify the caption which is parameter[0] string modifiedCaption = ModifyCaption((string)Parameters[0]); // modify the message which is parameter[1] string modifiedMessage = ModifyMessage((string)Parameters[1], computerName); Parameters[0] = modifiedCaption; Parameters[1] = modifiedMessage; } } } // Check if the incoming message is a readline as secure string // if so do the following: // (a) Specify a warning message that the server is // attempting to read something securely on the client else if (MethodId == RemoteHostMethodId.ReadLineAsSecureString) { prerequisiteCalls.Add(ConstructWarningMessageForSecureString( computerName, RemotingErrorIdStrings.RemoteHostReadLineAsSecureStringPrompt)); } // check if the incoming call is GetBufferContents // if so do the following: // (a) Specify a warning message that the server is // attempting to read the screen buffer contents // on screen and it has been blocked // (b) Modify the message so that call is not executed else if (MethodId == RemoteHostMethodId.GetBufferContents) { prerequisiteCalls.Add(ConstructWarningMessageForGetBufferContents(computerName)); } return prerequisiteCalls; } /// <summary> /// Provides the modified caption for the given caption /// Used in ensuring that remote prompt messages are /// tagged with "PowerShell Credential Request" /// </summary> /// <param name="caption">Caption to modify.</param> /// <returns>New modified caption.</returns> private static string ModifyCaption(string caption) { string pscaption = CredUI.PromptForCredential_DefaultCaption; if (!caption.Equals(pscaption, StringComparison.OrdinalIgnoreCase)) { string modifiedCaption = PSRemotingErrorInvariants.FormatResourceString( RemotingErrorIdStrings.RemoteHostPromptForCredentialModifiedCaption, caption); return modifiedCaption; } return caption; } /// <summary> /// Provides the modified message for the given one /// Used in ensuring that remote prompt messages /// contain a warning that they originate from a /// different computer. /// </summary> /// <param name="message">Original message to modify.</param> /// <param name="computerName">computername to include in the /// message</param> /// <returns>Message which contains a warning as well.</returns> private static string ModifyMessage(string message, string computerName) { string modifiedMessage = PSRemotingErrorInvariants.FormatResourceString( RemotingErrorIdStrings.RemoteHostPromptForCredentialModifiedMessage, computerName.ToUpper(), message); return modifiedMessage; } /// <summary> /// Creates a warning message which displays to the user a /// warning stating that the remote host computer is /// actually attempting to read a line as a secure string. /// </summary> /// <param name="computerName">computer name to include /// in warning</param> /// <param name="resourceString">Resource string to use.</param> /// <returns>A constructed remote host call message /// which will display the warning.</returns> private static RemoteHostCall ConstructWarningMessageForSecureString(string computerName, string resourceString) { string warning = PSRemotingErrorInvariants.FormatResourceString( resourceString, computerName.ToUpper()); return new RemoteHostCall(ServerDispatchTable.VoidCallId, RemoteHostMethodId.WriteWarningLine, new object[] { warning }); } /// <summary> /// Creates a warning message which displays to the user a /// warning stating that the remote host computer is /// attempting to read the host's buffer contents and that /// it was suppressed. /// </summary> /// <param name="computerName">computer name to include /// in warning</param> /// <returns>A constructed remote host call message /// which will display the warning.</returns> private static RemoteHostCall ConstructWarningMessageForGetBufferContents(string computerName) { string warning = PSRemotingErrorInvariants.FormatResourceString( RemotingErrorIdStrings.RemoteHostGetBufferContents, computerName.ToUpper()); return new RemoteHostCall(ServerDispatchTable.VoidCallId, RemoteHostMethodId.WriteWarningLine, new object[] { warning }); } } /// <summary> /// Encapsulates the method response semantics. Method responses are generated when /// RemoteHostCallPacket objects are executed. They can contain both the return values of /// the execution as well as exceptions that were thrown in the RemoteHostCallPacket /// execution. They can be encoded and decoded for transporting over the wire. A /// method response can be used to transport the result of an execution and then to /// simulate the execution on the other end. /// </summary> internal class RemoteHostResponse { /// <summary> /// Call id. /// </summary> private readonly long _callId; /// <summary> /// Call id. /// </summary> internal long CallId { get { return _callId; } } /// <summary> /// Method id. /// </summary> private readonly RemoteHostMethodId _methodId; /// <summary> /// Return value. /// </summary> private readonly object _returnValue; /// <summary> /// Exception. /// </summary> private readonly Exception _exception; /// <summary> /// Constructor for RemoteHostResponse. /// </summary> internal RemoteHostResponse(long callId, RemoteHostMethodId methodId, object returnValue, Exception exception) { _callId = callId; _methodId = methodId; _returnValue = returnValue; _exception = exception; } /// <summary> /// Simulate execution. /// </summary> internal object SimulateExecution() { if (_exception != null) { throw _exception; } return _returnValue; } /// <summary> /// Encode and add return value. /// </summary> private static void EncodeAndAddReturnValue(PSObject psObject, object returnValue) { // Do nothing if the return value is null. if (returnValue == null) { return; } // Otherwise add the property. RemoteHostEncoder.EncodeAndAddAsProperty(psObject, RemoteDataNameStrings.MethodReturnValue, returnValue); } /// <summary> /// Decode return value. /// </summary> private static object DecodeReturnValue(PSObject psObject, Type returnType) { object returnValue = RemoteHostEncoder.DecodePropertyValue(psObject, RemoteDataNameStrings.MethodReturnValue, returnType); return returnValue; } /// <summary> /// Encode and add exception. /// </summary> private static void EncodeAndAddException(PSObject psObject, Exception exception) { RemoteHostEncoder.EncodeAndAddAsProperty(psObject, RemoteDataNameStrings.MethodException, exception); } /// <summary> /// Decode exception. /// </summary> private static Exception DecodeException(PSObject psObject) { object result = RemoteHostEncoder.DecodePropertyValue(psObject, RemoteDataNameStrings.MethodException, typeof(Exception)); if (result == null) { return null; } if (result is Exception) { return (Exception)result; } throw RemoteHostExceptions.NewDecodingFailedException(); } /// <summary> /// Encode. /// </summary> internal PSObject Encode() { // Create a data object and put everything in that and return it. PSObject data = RemotingEncoder.CreateEmptyPSObject(); EncodeAndAddReturnValue(data, _returnValue); EncodeAndAddException(data, _exception); data.Properties.Add(new PSNoteProperty(RemoteDataNameStrings.CallId, _callId)); data.Properties.Add(new PSNoteProperty(RemoteDataNameStrings.MethodId, _methodId)); return data; } /// <summary> /// Decode. /// </summary> internal static RemoteHostResponse Decode(PSObject data) { Dbg.Assert(data != null, "Expected data != null"); // Extract all the fields from data. long callId = RemotingDecoder.GetPropertyValue<long>(data, RemoteDataNameStrings.CallId); RemoteHostMethodId methodId = RemotingDecoder.GetPropertyValue<RemoteHostMethodId>(data, RemoteDataNameStrings.MethodId); // Decode the return value and the exception. RemoteHostMethodInfo methodInfo = RemoteHostMethodInfo.LookUp(methodId); object returnValue = DecodeReturnValue(data, methodInfo.ReturnType); Exception exception = DecodeException(data); // Use these values to create a RemoteHostResponse and return it. return new RemoteHostResponse(callId, methodId, returnValue, exception); } } /// <summary> /// The RemoteHostExceptions class. /// </summary> internal static class RemoteHostExceptions { /// <summary> /// New remote runspace does not support push runspace exception. /// </summary> internal static Exception NewRemoteRunspaceDoesNotSupportPushRunspaceException() { string resourceString = PSRemotingErrorInvariants.FormatResourceString( RemotingErrorIdStrings.RemoteRunspaceDoesNotSupportPushRunspace); return new PSRemotingDataStructureException(resourceString); } /// <summary> /// New decoding failed exception. /// </summary> internal static Exception NewDecodingFailedException() { string resourceString = PSRemotingErrorInvariants.FormatResourceString( RemotingErrorIdStrings.RemoteHostDecodingFailed); return new PSRemotingDataStructureException(resourceString); } /// <summary> /// New not implemented exception. /// </summary> internal static Exception NewNotImplementedException(RemoteHostMethodId methodId) { RemoteHostMethodInfo methodInfo = RemoteHostMethodInfo.LookUp(methodId); string resourceString = PSRemotingErrorInvariants.FormatResourceString( RemotingErrorIdStrings.RemoteHostMethodNotImplemented, methodInfo.Name); return new PSRemotingDataStructureException(resourceString, new PSNotImplementedException()); } /// <summary> /// New remote host call failed exception. /// </summary> internal static Exception NewRemoteHostCallFailedException(RemoteHostMethodId methodId) { RemoteHostMethodInfo methodInfo = RemoteHostMethodInfo.LookUp(methodId); string resourceString = PSRemotingErrorInvariants.FormatResourceString( RemotingErrorIdStrings.RemoteHostCallFailed, methodInfo.Name); return new PSRemotingDataStructureException(resourceString); } /// <summary> /// New decoding error for error record exception. /// </summary> internal static Exception NewDecodingErrorForErrorRecordException() { return new PSRemotingDataStructureException(RemotingErrorIdStrings.DecodingErrorForErrorRecord); } /// <summary> /// New remote host data encoding not supported exception. /// </summary> internal static Exception NewRemoteHostDataEncodingNotSupportedException(Type type) { Dbg.Assert(type != null, "Expected type != null"); return new PSRemotingDataStructureException( RemotingErrorIdStrings.RemoteHostDataEncodingNotSupported, type.ToString()); } /// <summary> /// New remote host data decoding not supported exception. /// </summary> internal static Exception NewRemoteHostDataDecodingNotSupportedException(Type type) { Dbg.Assert(type != null, "Expected type != null"); return new PSRemotingDataStructureException( RemotingErrorIdStrings.RemoteHostDataDecodingNotSupported, type.ToString()); } /// <summary> /// New unknown target class exception. /// </summary> internal static Exception NewUnknownTargetClassException(string className) { Dbg.Assert(className != null, "Expected className != null"); return new PSRemotingDataStructureException(RemotingErrorIdStrings.UnknownTargetClass, className); } internal static Exception NewNullClientHostException() { return new PSRemotingDataStructureException(RemotingErrorIdStrings.RemoteHostNullClientHost); } } }