internal static TransportErrorOccuredEventArgs ConstructTransportErrorEventArgs(IntPtr wsmanAPIHandle, WSManClientSessionTransportManager wsmanSessionTM, WSManNativeApi.WSManError errorStruct, TransportMethodEnum transportMethodReportingError, string resourceString, params object[] resourceArgs) { PSRemotingTransportException exception; if ((errorStruct.errorCode == -2144108135) && (wsmanSessionTM != null)) { string redirectLocation = WSManNativeApi.WSManGetSessionOptionAsString(wsmanSessionTM.SessionHandle, WSManNativeApi.WSManSessionOption.WSMAN_OPTION_REDIRECT_LOCATION); string str2 = ParseEscapeWSManErrorMessage(WSManNativeApi.WSManGetErrorMessage(wsmanAPIHandle, errorStruct.errorCode)).Trim(); exception = new PSRemotingTransportRedirectException(redirectLocation, PSRemotingErrorId.URIEndPointNotResolved, RemotingErrorIdStrings.URIEndPointNotResolved, new object[] { str2, redirectLocation }); } else if ((errorStruct.errorCode == -2144108485) && (wsmanSessionTM != null)) { string str3 = wsmanSessionTM.ConnectionInfo.ShellUri.Replace("http://schemas.microsoft.com/powershell/", string.Empty); string str4 = PSRemotingErrorInvariants.FormatResourceString(RemotingErrorIdStrings.InvalidConfigurationName, new object[] { str3, wsmanSessionTM.ConnectionInfo.ComputerName }); exception = new PSRemotingTransportException(PSRemotingErrorId.InvalidConfigurationName, RemotingErrorIdStrings.ConnectExCallBackError, new object[] { wsmanSessionTM.ConnectionInfo.ComputerName, str4 }) { TransportMessage = ParseEscapeWSManErrorMessage(WSManNativeApi.WSManGetErrorMessage(wsmanAPIHandle, errorStruct.errorCode)) }; } else { string str5 = PSRemotingErrorInvariants.FormatResourceString(resourceString, resourceArgs); exception = new PSRemotingTransportException(PSRemotingErrorId.TroubleShootingHelpTopic, RemotingErrorIdStrings.TroubleShootingHelpTopic, new object[] { str5 }) { TransportMessage = ParseEscapeWSManErrorMessage(WSManNativeApi.WSManGetErrorMessage(wsmanAPIHandle, errorStruct.errorCode)) }; } exception.ErrorCode = errorStruct.errorCode; return new TransportErrorOccuredEventArgs(exception, transportMethodReportingError); }
protected WSManPluginServerSession( WSManNativeApi.WSManPluginRequest creationRequestDetails, WSManPluginServerTransportManager transportMgr) { _syncObject = new Object(); this.creationRequestDetails = creationRequestDetails; this.transportMgr = transportMgr; transportMgr.PrepareCalled += new EventHandler<EventArgs>(this.HandlePrepareFromTransportManager); transportMgr.WSManTransportErrorOccured += new EventHandler<TransportErrorOccuredEventArgs>(this.HandleTransportError); }
/// <summary> /// unlock the shell / command specified so that the shell / command /// starts sending data to the client. /// </summary> /// <param name="pluginContext"></param> /// <param name="requestDetails"></param> /// <param name="flags"></param> /// <param name="shellContext"></param> /// <param name="commandContext"></param> /// <param name="streamSet"></param> internal void EnableShellOrCommandToSendDataToClient( IntPtr pluginContext, WSManNativeApi.WSManPluginRequest requestDetails, int flags, IntPtr shellContext, IntPtr commandContext, WSManNativeApi.WSManStreamIDSet_UnToMan streamSet) { if (!validateIncomingContexts(requestDetails, shellContext, "EnableShellOrCommandToSendDataToClient")) { return; } SetThreadProperties(requestDetails); PSEtwLog.LogAnalyticInformational(PSEventId.ServerClientReceiveRequest, PSOpcode.Open, PSTask.None, PSKeyword.ManagedPlugin | PSKeyword.UseAlwaysAnalytic, ((IntPtr)shellContext).ToString(), ((IntPtr)commandContext).ToString(), requestDetails.ToString()); WSManPluginShellSession mgdShellSession = GetFromActiveShellSessions(shellContext); if (null == mgdShellSession) { ReportOperationComplete( requestDetails, WSManPluginErrorCodes.InvalidShellContext, StringUtil.Format( RemotingErrorIdStrings.WSManPluginInvalidShellContext) ); return; } WSManPluginOperationShutdownContext ctxtToReport = new WSManPluginOperationShutdownContext(pluginContext, shellContext, IntPtr.Zero, true); if (null == ctxtToReport) { ReportOperationComplete(requestDetails, WSManPluginErrorCodes.OutOfMemory); return; } if (IntPtr.Zero == commandContext) { // the instruction is destined for shell (runspace) session. so let shell handle it if (mgdShellSession.EnableSessionToSendDataToClient(requestDetails, flags, streamSet, ctxtToReport)) { return; } } else { // the instruction is destined for command ctxtToReport.commandContext = commandContext; WSManPluginCommandSession mgdCmdSession = mgdShellSession.GetCommandSession(commandContext); if (null == mgdCmdSession) { ReportOperationComplete( requestDetails, WSManPluginErrorCodes.InvalidCommandContext, StringUtil.Format( RemotingErrorIdStrings.WSManPluginInvalidCommandContext)); return; } if (mgdCmdSession.EnableSessionToSendDataToClient(requestDetails, flags, streamSet, ctxtToReport)) { return; } } }
/// <summary> /// Connect /// </summary> /// <param name="requestDetails"></param> /// <param name="flags"></param> /// <param name="shellContext"></param> /// <param name="commandContext"></param> /// <param name="inboundConnectInformation"></param> internal void ConnectShellOrCommand( WSManNativeApi.WSManPluginRequest requestDetails, int flags, IntPtr shellContext, IntPtr commandContext, WSManNativeApi.WSManData_UnToMan inboundConnectInformation) { if (!validateIncomingContexts(requestDetails, shellContext, "ConnectShellOrCommand")) { return; } //TODO... What does this mean from a new client that has specified diff locale from original client? SetThreadProperties(requestDetails); //TODO.. Add new ETW events and log /*etwTracer.AnalyticChannel.WriteInformation(PSEventId.ServerReceivedData, PSOpcode.Open, PSTask.None, ((IntPtr)shellContext).ToString(), ((IntPtr)commandContext).ToString(), ((IntPtr)requestDetails).ToString());*/ WSManPluginShellSession mgdShellSession = GetFromActiveShellSessions(shellContext); if (null == mgdShellSession) { ReportOperationComplete( requestDetails, WSManPluginErrorCodes.InvalidShellContext, StringUtil.Format( RemotingErrorIdStrings.WSManPluginInvalidShellContext)); return; } if (IntPtr.Zero == commandContext) { mgdShellSession.ExecuteConnect(requestDetails, flags, inboundConnectInformation); return; } // this connect is on a command WSManPluginCommandSession mgdCmdSession = mgdShellSession.GetCommandSession(commandContext); if (null == mgdCmdSession) { ReportOperationComplete( requestDetails, WSManPluginErrorCodes.InvalidCommandContext, StringUtil.Format( RemotingErrorIdStrings.WSManPluginInvalidCommandContext)); return; } mgdCmdSession.ExecuteConnect(requestDetails, flags, inboundConnectInformation); }
/// <summary> /// Create a new command in the shell context. /// </summary> /// <param name="pluginContext"></param> /// <param name="requestDetails"></param> /// <param name="flags"></param> /// <param name="shellContext"></param> /// <param name="commandLine"></param> /// <param name="arguments"></param> internal void CreateCommand( IntPtr pluginContext, WSManNativeApi.WSManPluginRequest requestDetails, int flags, IntPtr shellContext, string commandLine, WSManNativeApi.WSManCommandArgSet arguments) { if (!validateIncomingContexts(requestDetails, shellContext, "WSManRunShellCommandEx")) { return; } SetThreadProperties(requestDetails); PSEtwLog.LogAnalyticInformational(PSEventId.ServerCreateCommandSession, PSOpcode.Connect, PSTask.None, PSKeyword.ManagedPlugin | PSKeyword.UseAlwaysAnalytic, ((IntPtr)shellContext).ToString(), requestDetails.ToString()); WSManPluginShellSession mgdShellSession = GetFromActiveShellSessions(shellContext); if (null == mgdShellSession) { ReportOperationComplete( requestDetails, WSManPluginErrorCodes.InvalidShellContext, StringUtil.Format( RemotingErrorIdStrings.WSManPluginInvalidShellContext)); return; } mgdShellSession.CreateCommand(pluginContext, requestDetails, flags, commandLine, arguments); }
/// <summary> /// Create a new shell in the plugin context. /// </summary> /// <param name="pluginContext"></param> /// <param name="requestDetails"></param> /// <param name="flags"></param> /// <param name="extraInfo"></param> /// <param name="startupInfo"></param> /// <param name="inboundShellInformation"></param> internal void CreateShell( IntPtr pluginContext, WSManNativeApi.WSManPluginRequest requestDetails, int flags, string extraInfo, WSManNativeApi.WSManShellStartupInfo_UnToMan startupInfo, WSManNativeApi.WSManData_UnToMan inboundShellInformation) { if (null == requestDetails) { // Nothing can be done because requestDetails are required to report operation complete PSEtwLog.LogAnalyticInformational(PSEventId.ReportOperationComplete, PSOpcode.Close, PSTask.None, PSKeyword.ManagedPlugin | PSKeyword.UseAlwaysAnalytic, "null", Convert.ToString(WSManPluginErrorCodes.NullInvalidInput, CultureInfo.InvariantCulture), StringUtil.Format( RemotingErrorIdStrings.WSManPluginNullInvalidInput, "requestDetails", "WSManPluginShell"), String.Empty); return; } if ((null == requestDetails.senderDetails) || (null == requestDetails.operationInfo)) { ReportOperationComplete( requestDetails, WSManPluginErrorCodes.NullInvalidInput, StringUtil.Format( RemotingErrorIdStrings.WSManPluginNullInvalidInput, "requestDetails", "WSManPluginShell")); return; } if (null == startupInfo) { ReportOperationComplete( requestDetails, WSManPluginErrorCodes.NullInvalidInput, StringUtil.Format( RemotingErrorIdStrings.WSManPluginNullInvalidInput, "startupInfo", "WSManPluginShell")); return; } if ((0 == startupInfo.inputStreamSet.streamIDsCount) || (0 == startupInfo.outputStreamSet.streamIDsCount)) { ReportOperationComplete( requestDetails, WSManPluginErrorCodes.NullInvalidStreamSets, StringUtil.Format( RemotingErrorIdStrings.WSManPluginNullInvalidStreamSet, WSManPluginConstants.SupportedInputStream, WSManPluginConstants.SupportedOutputStream)); return; } if (String.IsNullOrEmpty(extraInfo)) { ReportOperationComplete( requestDetails, WSManPluginErrorCodes.NullInvalidInput, StringUtil.Format( RemotingErrorIdStrings.WSManPluginNullInvalidInput, "extraInfo", "WSManPluginShell")); return; } WSManPluginInstance.SetThreadProperties(requestDetails); // check if protocolversion option is honored if (!EnsureOptionsComply(requestDetails)) { return; } int result = WSManPluginConstants.ExitCodeSuccess; WSManPluginShellSession mgdShellSession; WSManPluginOperationShutdownContext context; System.Byte[] convertedBase64 = null; try { PSSenderInfo senderInfo = GetPSSenderInfo(requestDetails.senderDetails); // inbound shell information is already verified by pwrshplugin.dll.. so no need // to verify here. WSManPluginServerTransportManager serverTransportMgr; if (Platform.IsWindows) { serverTransportMgr = new WSManPluginServerTransportManager(BaseTransportManager.DefaultFragmentSize, new PSRemotingCryptoHelperServer()); } else { serverTransportMgr = new WSManPluginServerTransportManager(BaseTransportManager.DefaultFragmentSize, null); } PSEtwLog.LogAnalyticInformational(PSEventId.ServerCreateRemoteSession, PSOpcode.Connect, PSTask.None, PSKeyword.ManagedPlugin | PSKeyword.UseAlwaysAnalytic, requestDetails.ToString(), senderInfo.UserInfo.Identity.Name, requestDetails.resourceUri); ServerRemoteSession remoteShellSession = ServerRemoteSession.CreateServerRemoteSession(senderInfo, requestDetails.resourceUri, extraInfo, serverTransportMgr); if (null == remoteShellSession) { WSManPluginInstance.ReportWSManOperationComplete( requestDetails, WSManPluginErrorCodes.SessionCreationFailed); return; } context = new WSManPluginOperationShutdownContext(pluginContext, requestDetails.unmanagedHandle, IntPtr.Zero, false); if (null == context) { ReportOperationComplete(requestDetails, WSManPluginErrorCodes.OutOfMemory); return; } // Create a shell session wrapper to track and service future interactions. mgdShellSession = new WSManPluginShellSession(requestDetails, serverTransportMgr, remoteShellSession, context); AddToActiveShellSessions(mgdShellSession); mgdShellSession.SessionClosed += new EventHandler<EventArgs>(HandleShellSessionClosed); if (null != inboundShellInformation) { if ((uint)WSManNativeApi.WSManDataType.WSMAN_DATA_TYPE_TEXT != inboundShellInformation.Type) { // only text data is supported ReportOperationComplete( requestDetails, WSManPluginErrorCodes.InvalidInputDatatype, StringUtil.Format( RemotingErrorIdStrings.WSManPluginInvalidInputDataType, "WSMAN_DATA_TYPE_TEXT")); DeleteFromActiveShellSessions(requestDetails.unmanagedHandle); return; } else { convertedBase64 = ServerOperationHelpers.ExtractEncodedXmlElement( inboundShellInformation.Text, WSManNativeApi.PS_CREATION_XML_TAG); } } // now report the shell context to WSMan. PSEtwLog.LogAnalyticInformational(PSEventId.ReportContext, PSOpcode.Connect, PSTask.None, PSKeyword.ManagedPlugin | PSKeyword.UseAlwaysAnalytic, requestDetails.ToString(), requestDetails.ToString()); result = wsmanPinvokeStatic.WSManPluginReportContext(requestDetails.unmanagedHandle, 0, requestDetails.unmanagedHandle); if (WSManPluginConstants.ExitCodeSuccess != result) { ReportOperationComplete( requestDetails, WSManPluginErrorCodes.ReportContextFailed, StringUtil.Format( RemotingErrorIdStrings.WSManPluginReportContextFailed)); DeleteFromActiveShellSessions(requestDetails.unmanagedHandle); return; } } catch (System.Exception e) { CommandProcessorBase.CheckForSevereException(e); PSEtwLog.LogOperationalError(PSEventId.TransportError, PSOpcode.Connect, PSTask.None, PSKeyword.UseAlwaysOperational, "00000000-0000-0000-0000-000000000000", "00000000-0000-0000-0000-000000000000", Convert.ToString(WSManPluginErrorCodes.ManagedException, CultureInfo.InvariantCulture), e.Message, e.StackTrace); DeleteFromActiveShellSessions(requestDetails.unmanagedHandle); ReportOperationComplete( requestDetails, WSManPluginErrorCodes.ManagedException, StringUtil.Format( RemotingErrorIdStrings.WSManPluginManagedException, e.Message)); return; } bool isRegisterWaitForSingleObjectSucceeded = true; //always synchronize calls to OperationComplete once notification handle is registered.. else duplicate OperationComplete calls are bound to happen lock (mgdShellSession.shellSyncObject) { mgdShellSession.registeredShutdownNotification = 1; // Wrap the provided handle so it can be passed to the registration function EventWaitHandle eventWaitHandle = new EventWaitHandle(false, EventResetMode.AutoReset); if (Platform.IsWindows) { SafeWaitHandle safeWaitHandle = new SafeWaitHandle(requestDetails.shutdownNotificationHandle, false); // Owned by WinRM ClrFacade.SetSafeWaitHandle(eventWaitHandle, safeWaitHandle); } else { //On non-windows platforms the shutdown notification is done through a callback instead of a windows event handle. //Register the callback and this will then signal the event. Note, the gch object is deleted in the shell shutdown //notification that will always come in to shut down the operation. GCHandle gch = GCHandle.Alloc(eventWaitHandle); IntPtr p = GCHandle.ToIntPtr(gch); wsmanPinvokeStatic.WSManPluginRegisterShutdownCallback( requestDetails.unmanagedHandle, WSManPluginManagedEntryWrapper.workerPtrs.UnmanagedStruct.wsManPluginShutdownCallbackNative, p); } mgdShellSession.registeredShutDownWaitHandle = ThreadPool.RegisterWaitForSingleObject( eventWaitHandle, new WaitOrTimerCallback(WSManPluginManagedEntryWrapper.PSPluginOperationShutdownCallback), context, -1, // INFINITE true); // TODO: Do I need to worry not being able to set missing WT_TRANSFER_IMPERSONATION? if (null == mgdShellSession.registeredShutDownWaitHandle) { isRegisterWaitForSingleObjectSucceeded = false; } } if (!isRegisterWaitForSingleObjectSucceeded) { mgdShellSession.registeredShutdownNotification = 0; WSManPluginInstance.ReportWSManOperationComplete( requestDetails, WSManPluginErrorCodes.ShutdownRegistrationFailed); DeleteFromActiveShellSessions(requestDetails.unmanagedHandle); return; } try { if (convertedBase64 != null) { mgdShellSession.SendOneItemToSessionHelper(convertedBase64, WSManPluginConstants.SupportedInputStream); } } catch (System.Exception e) { CommandProcessorBase.CheckForSevereException(e); PSEtwLog.LogOperationalError(PSEventId.TransportError, PSOpcode.Connect, PSTask.None, PSKeyword.UseAlwaysOperational, "00000000-0000-0000-0000-000000000000", "00000000-0000-0000-0000-000000000000", Convert.ToString(WSManPluginErrorCodes.ManagedException, CultureInfo.InvariantCulture), e.Message, e.StackTrace); if (Interlocked.Exchange(ref mgdShellSession.registeredShutdownNotification, 0) == 1) { // unregister callback.. wait for any ongoing callbacks to complete.. nothing much we could do if this fails bool ignore = mgdShellSession.registeredShutDownWaitHandle.Unregister(null); mgdShellSession.registeredShutDownWaitHandle = null; //this will called OperationComplete PerformCloseOperation(context); } return; } return; }
/// <summary> /// Alternate wrapper for WSManPluginOperationComplete. TODO: Needed? I could easily use the handle instead and get rid of this? It is only for easier refactoring... /// </summary> /// <param name="requestDetails"></param> /// <param name="errorCode"></param> /// <param name="errorMessage">Pre-formatted localized string</param> /// <returns></returns> internal static void ReportOperationComplete( WSManNativeApi.WSManPluginRequest requestDetails, WSManPluginErrorCodes errorCode, string errorMessage) { if (null != requestDetails) { ReportOperationComplete(requestDetails.unmanagedHandle, errorCode, errorMessage); } // else cannot report if requestDetails is null. }
/// <summary> /// extract message from exception (if any) and report operation complete with it to WSMan /// </summary> /// <param name="requestDetails"></param> /// <param name="reasonForClose"></param> internal static void ReportWSManOperationComplete( WSManNativeApi.WSManPluginRequest requestDetails, Exception reasonForClose) { Dbg.Assert(null != requestDetails, "requestDetails cannot be null in operation complete."); WSManPluginErrorCodes error = WSManPluginErrorCodes.NoError; String errorMessage = String.Empty; String stackTrace = String.Empty; if (null != reasonForClose) { error = WSManPluginErrorCodes.ManagedException; errorMessage = reasonForClose.Message; stackTrace = reasonForClose.StackTrace; } PSEtwLog.LogAnalyticInformational(PSEventId.ReportOperationComplete, PSOpcode.Close, PSTask.None, PSKeyword.ManagedPlugin | PSKeyword.UseAlwaysAnalytic, requestDetails.ToString(), Convert.ToString(error, CultureInfo.InvariantCulture), errorMessage, stackTrace); if (null != reasonForClose) { // report operation complete to wsman with the error message (if any). ReportOperationComplete( requestDetails, WSManPluginErrorCodes.ManagedException, StringUtil.Format( RemotingErrorIdStrings.WSManPluginManagedException, reasonForClose.Message)); } else { ReportOperationComplete( requestDetails.unmanagedHandle, WSManPluginErrorCodes.NoError); } }
internal WSManPluginCommandSession( WSManNativeApi.WSManPluginRequest creationRequestDetails, WSManPluginServerTransportManager transportMgr, ServerRemoteSession remoteSession) : base(creationRequestDetails, transportMgr) { _remoteSession = remoteSession; cmdSyncObject = new System.Object(); }
/// <summary> /// Main Routine for Connect on a Shell. /// Calls in server remotesessions ExecuteConnect to run the Connect algorithm /// This call is synchronous. i.e WSManOperationComplete will be called before the routine completes /// </summary> /// <param name="requestDetails"></param> /// <param name="flags"></param> /// <param name="inboundConnectInformation"></param> internal override void ExecuteConnect( WSManNativeApi.WSManPluginRequest requestDetails, // in int flags, // in WSManNativeApi.WSManData_UnToMan inboundConnectInformation) // in optional { if (null == inboundConnectInformation) { WSManPluginInstance.ReportOperationComplete( requestDetails, WSManPluginErrorCodes.NullInvalidInput, StringUtil.Format( RemotingErrorIdStrings.WSManPluginNullInvalidInput, "inboundConnectInformation", "WSManPluginShellConnect")); return; } //not registering shutdown event as this is a synchronous operation. IntPtr responseXml = IntPtr.Zero; try { System.Byte[] inputData; System.Byte[] outputData; // Retrieve the string (Base64 encoded) inputData = ServerOperationHelpers.ExtractEncodedXmlElement( inboundConnectInformation.Text, WSManNativeApi.PS_CONNECT_XML_TAG); //this will raise exceptions on failure try { _remoteSession.ExecuteConnect(inputData, out outputData); //construct Xml to send back string responseData = String.Format(System.Globalization.CultureInfo.InvariantCulture, "<{0} xmlns=\"{1}\">{2}</{0}>", WSManNativeApi.PS_CONNECTRESPONSE_XML_TAG, WSManNativeApi.PS_XML_NAMESPACE, Convert.ToBase64String(outputData)); //TODO: currently using OperationComplete to report back the responseXml. This will need to change to use WSManReportObject //that is currently internal. WSManPluginInstance.ReportOperationComplete(requestDetails, WSManPluginErrorCodes.NoError, responseData); } catch (PSRemotingDataStructureException ex) { WSManPluginInstance.ReportOperationComplete(requestDetails, WSManPluginErrorCodes.PluginConnectOperationFailed, ex.Message); } } catch (OutOfMemoryException) { WSManPluginInstance.ReportOperationComplete(requestDetails, WSManPluginErrorCodes.OutOfMemory); } finally { if (responseXml != IntPtr.Zero) { Marshal.FreeHGlobal(responseXml); } } return; }
// Create a new command in the shell context. internal void CreateCommand( IntPtr pluginContext, WSManNativeApi.WSManPluginRequest requestDetails, int flags, string commandLine, WSManNativeApi.WSManCommandArgSet arguments) { try { // inbound cmd information is already verified.. so no need to verify here. WSManPluginCommandTransportManager serverCmdTransportMgr = new WSManPluginCommandTransportManager(transportMgr); serverCmdTransportMgr.Initialize(); // Apply quota limits on the command transport manager _remoteSession.ApplyQuotaOnCommandTransportManager(serverCmdTransportMgr); WSManPluginCommandSession mgdCmdSession = new WSManPluginCommandSession(requestDetails, serverCmdTransportMgr, _remoteSession); AddToActiveCmdSessions(mgdCmdSession); mgdCmdSession.SessionClosed += new EventHandler<EventArgs>(this.HandleCommandSessionClosed); mgdCmdSession.shutDownContext = new WSManPluginOperationShutdownContext( pluginContext, creationRequestDetails.unmanagedHandle, mgdCmdSession.creationRequestDetails.unmanagedHandle, false); do { if (!mgdCmdSession.ProcessArguments(arguments)) { WSManPluginInstance.ReportOperationComplete( requestDetails, WSManPluginErrorCodes.InvalidArgSet, StringUtil.Format( RemotingErrorIdStrings.WSManPluginInvalidArgSet, "WSManPluginCommand")); break; } // Report plugin context to WSMan mgdCmdSession.ReportContext(); } while (false); } catch (System.Exception e) { CommandProcessorBase.CheckForSevereException(e); // if there is an exception creating remote session send the message to client. WSManPluginInstance.ReportOperationComplete( requestDetails, WSManPluginErrorCodes.ManagedException, StringUtil.Format( RemotingErrorIdStrings.WSManPluginManagedException, e.Message)); } }
internal WSManPluginShellSession( WSManNativeApi.WSManPluginRequest creationRequestDetails, WSManPluginServerTransportManager transportMgr, ServerRemoteSession remoteSession, WSManPluginOperationShutdownContext shutDownContext) : base(creationRequestDetails, transportMgr) { _remoteSession = remoteSession; _remoteSession.Closed += new EventHandler<RemoteSessionStateMachineEventArgs>(this.HandleServerRemoteSessionClosed); _activeCommandSessions = new Dictionary<IntPtr, WSManPluginCommandSession>(); this.shellSyncObject = new System.Object(); this.shutDownContext = shutDownContext; }
internal abstract void ExecuteConnect( WSManNativeApi.WSManPluginRequest requestDetails, // in int flags, // in WSManNativeApi.WSManData_UnToMan inboundConnectInformation); // in optional
internal bool EnableSessionToSendDataToClient( WSManNativeApi.WSManPluginRequest requestDetails, int flags, WSManNativeApi.WSManStreamIDSet_UnToMan streamSet, WSManPluginOperationShutdownContext ctxtToReport) { if (true == isClosed) { WSManPluginInstance.ReportWSManOperationComplete(requestDetails, lastErrorReported); return false; } if ((null == streamSet) || (1 != streamSet.streamIDsCount)) { // only "stdout" is the supported output stream. WSManPluginInstance.ReportOperationComplete( requestDetails, WSManPluginErrorCodes.InvalidOutputStream, StringUtil.Format( RemotingErrorIdStrings.WSManPluginInvalidOutputStream, WSManPluginConstants.SupportedOutputStream)); return false; } if (!String.Equals(streamSet.streamIDs[0], WSManPluginConstants.SupportedOutputStream, StringComparison.Ordinal)) { // only "stdout" is the supported output stream. WSManPluginInstance.ReportOperationComplete( requestDetails, WSManPluginErrorCodes.InvalidOutputStream, StringUtil.Format( RemotingErrorIdStrings.WSManPluginInvalidOutputStream, WSManPluginConstants.SupportedOutputStream)); return false; } return transportMgr.EnableTransportManagerSendDataToClient(requestDetails, ctxtToReport); }
internal void DoClose( bool isShuttingDown, Exception reasonForClose) { if (_isClosed) { return; } lock (_syncObject) { if (_isClosed) { return; } _isClosed = true; _lastErrorReported = reasonForClose; if (!_isRequestPending) { // release threads blocked on the sending data to client if any _waitHandle.Set(); } } // only one thread will reach here // let everyone know that we are about to close try { RaiseClosingEvent(); foreach (var cmdTransportKvp in _activeCmdTransportManagers) { cmdTransportKvp.Value.Close(reasonForClose); } _activeCmdTransportManagers.Clear(); if (_registeredShutDownWaitHandle != null) { // This will not wait for the callback to complete. _registeredShutDownWaitHandle.Unregister(null); _registeredShutDownWaitHandle = null; } // Delete the context only if isShuttingDown != true. isShuttingDown will // be true only when the method is called from RegisterWaitForSingleObject // handler..in which case the context will be freed from the callback. if (_shutDownContext != null) { _shutDownContext = null; } // This might happen when client did not send a receive request // but the server is closing if (_requestDetails != null) { // Notify that no more data is being sent on this transport. WSManNativeApi.WSManPluginReceiveResult( _requestDetails.unmanagedHandle, (int)WSManNativeApi.WSManFlagReceive.WSMAN_FLAG_RECEIVE_RESULT_NO_MORE_DATA, WSManPluginConstants.SupportedOutputStream, IntPtr.Zero, WSManNativeApi.WSMAN_COMMAND_STATE_DONE, 0); WSManPluginInstance.ReportWSManOperationComplete(_requestDetails, reasonForClose); // We should not use request details again after reporting operation complete // so releasing the resource. Remember not to free this memory as this memory // is allocated and owned by WSMan. _requestDetails = null; } } finally { // dispose resources _waitHandle.Dispose(); } }
internal bool ProcessArguments( WSManNativeApi.WSManCommandArgSet arguments) { if (1 != arguments.argsCount) { return false; } System.Byte[] convertedBase64 = Convert.FromBase64String(arguments.args[0]); transportMgr.ProcessRawData(convertedBase64, WSManPluginConstants.SupportedInputStream); return true; }
internal void SendOneItemToSession( WSManNativeApi.WSManPluginRequest requestDetails, int flags, string stream, WSManNativeApi.WSManData_UnToMan inboundData) { if ((!String.Equals(stream, WSManPluginConstants.SupportedInputStream, StringComparison.Ordinal)) && (!String.Equals(stream, WSManPluginConstants.SupportedPromptResponseStream, StringComparison.Ordinal))) { WSManPluginInstance.ReportOperationComplete( requestDetails, WSManPluginErrorCodes.InvalidInputStream, StringUtil.Format( RemotingErrorIdStrings.WSManPluginInvalidInputStream, WSManPluginConstants.SupportedInputStream)); return; } if (null == inboundData) { // no data is supplied..just ignore. WSManPluginInstance.ReportOperationComplete( requestDetails, WSManPluginErrorCodes.NoError); return; } if ((uint)WSManNativeApi.WSManDataType.WSMAN_DATA_TYPE_BINARY != inboundData.Type) { // only binary data is supported WSManPluginInstance.ReportOperationComplete( requestDetails, WSManPluginErrorCodes.InvalidInputDatatype, StringUtil.Format( RemotingErrorIdStrings.WSManPluginInvalidInputStream, "WSMAN_DATA_TYPE_BINARY")); return; } lock (_syncObject) { if (true == isClosed) { WSManPluginInstance.ReportWSManOperationComplete(requestDetails, lastErrorReported); return; } // store the send request details..because the operation complete // may happen from a different thread. sendRequestDetails = requestDetails; } SendOneItemToSessionHelper(inboundData.Data, stream); // report operation complete. ReportSendOperationComplete(); }
/// <summary> /// /// </summary> /// <param name="requestDetails"></param> internal void Stop( WSManNativeApi.WSManPluginRequest requestDetails) { // stop the command..command will be stoped if we raise ClosingEvent on // transport manager. transportMgr.PerformStop(); WSManPluginInstance.ReportWSManOperationComplete(requestDetails, null); }
/// <summary> /// Sets thread properties like UI Culture, Culture etc..This is needed as code is transitioning from /// unmanaged heap to managed heap...and thread properties are not set correctly during this /// transition. /// Currently WSMan provider supplies only UI Culture related data..so only UI Culture is set. /// </summary> /// <param name="requestDetails"></param> internal static void SetThreadProperties( WSManNativeApi.WSManPluginRequest requestDetails) { // requestDetails cannot not be null. Dbg.Assert(null != requestDetails, "requestDetails cannot be null"); //IntPtr nativeLocaleData = IntPtr.Zero; WSManNativeApi.WSManDataStruct outputStruct = new WSManNativeApi.WSManDataStruct(); int hResult = wsmanPinvokeStatic.WSManPluginGetOperationParameters( requestDetails.unmanagedHandle, WSManPluginConstants.WSManPluginParamsGetRequestedLocale, outputStruct); //ref nativeLocaleData); bool retreivingLocaleSucceeded = (0 == hResult); WSManNativeApi.WSManData_UnToMan localeData = WSManNativeApi.WSManData_UnToMan.UnMarshal(outputStruct); // nativeLocaleData //IntPtr nativeDataLocaleData = IntPtr.Zero; hResult = wsmanPinvokeStatic.WSManPluginGetOperationParameters( requestDetails.unmanagedHandle, WSManPluginConstants.WSManPluginParamsGetRequestedDataLocale, outputStruct); //ref nativeDataLocaleData); bool retreivingDataLocaleSucceeded = ((int)WSManPluginErrorCodes.NoError == hResult); WSManNativeApi.WSManData_UnToMan dataLocaleData = WSManNativeApi.WSManData_UnToMan.UnMarshal(outputStruct); // nativeDataLocaleData // Set the UI Culture try { if (retreivingLocaleSucceeded && ((uint)WSManNativeApi.WSManDataType.WSMAN_DATA_TYPE_TEXT == localeData.Type)) { CultureInfo uiCultureToUse = new CultureInfo(localeData.Text); ClrFacade.SetCurrentThreadUiCulture(uiCultureToUse); } } // ignore if there is any exception constructing the culture.. catch (ArgumentException) { } // Set the Culture try { if (retreivingDataLocaleSucceeded && ((uint)WSManNativeApi.WSManDataType.WSMAN_DATA_TYPE_TEXT == dataLocaleData.Type)) { CultureInfo cultureToUse = new CultureInfo(dataLocaleData.Text); ClrFacade.SetCurrentThreadCulture(cultureToUse); } } // ignore if there is any exception constructing the culture.. catch (ArgumentException) { } }
/// <summary> /// Main routine for connect on a command/pipeline.. Currently NO-OP /// will be enhanced later to support intelligent connect... like ending input streams on pipelines /// that are still waiting for input data /// </summary> /// <param name="requestDetails"></param> /// <param name="flags"></param> /// <param name="inboundConnectInformation"></param> internal override void ExecuteConnect( WSManNativeApi.WSManPluginRequest requestDetails, int flags, WSManNativeApi.WSManData_UnToMan inboundConnectInformation) { WSManPluginInstance.ReportOperationComplete( requestDetails, WSManPluginErrorCodes.NoError); return; }
/// <summary> /// Wrapper for WSManPluginOperationComplete. It performs validation prior to making the call. /// </summary> /// <param name="requestDetails"></param> /// <param name="errorCode"></param> internal static void ReportOperationComplete( WSManNativeApi.WSManPluginRequest requestDetails, WSManPluginErrorCodes errorCode) { if (null != requestDetails && IntPtr.Zero != requestDetails.unmanagedHandle) { wsmanPinvokeStatic.WSManPluginOperationComplete( requestDetails.unmanagedHandle, 0, (int)errorCode, null); } // else cannot report if requestDetails is null. }
/// <summary> /// Constructs a WSManTransportErrorOccuredEventArgs instance from the supplied data /// </summary> /// <param name="wsmanAPIHandle"> /// WSMan API handle to use to get error messages from WSMan error id(s) /// </param> /// <param name="wsmanSessionTM"> /// Session Transportmanager to use to get error messages (for redirect) /// </param> /// <param name="errorStruct"> /// Error structure supplied by callbacks from WSMan API /// </param> /// <param name="transportMethodReportingError"> /// The transport method call that reported this error. /// </param> /// <param name="resourceString"> /// resource string that holds the message. /// </param> /// <param name="resourceArgs"> /// Arguments to pass to the resource /// </param> /// <returns> /// An instance of WSManTransportErrorOccuredEventArgs /// </returns> internal static TransportErrorOccuredEventArgs ConstructTransportErrorEventArgs(IntPtr wsmanAPIHandle, WSManClientSessionTransportManager wsmanSessionTM, WSManNativeApi.WSManError errorStruct, TransportMethodEnum transportMethodReportingError, string resourceString, params object[] resourceArgs) { PSRemotingTransportException e; //For the first two special error conditions, it is remotely possible that the wsmanSessionTM is null when the failures are returned //as part of command TM operations (could be returned because of RC retries under the hood) //Not worth to handle these cases separately as there are very corner scenarios, but need to make sure wsmanSessionTM is not referenced // Destination server is reporting that URI redirect is required for this user. if ((errorStruct.errorCode == WSManNativeApi.ERROR_WSMAN_REDIRECT_REQUESTED) && (wsmanSessionTM != null)) { IntPtr wsmanSessionHandle = wsmanSessionTM.SessionHandle; // populate the transport message with the redirection uri..this will // allow caller to make a new connection. string redirectLocation = WSManNativeApi.WSManGetSessionOptionAsString(wsmanSessionHandle, WSManNativeApi.WSManSessionOption.WSMAN_OPTION_REDIRECT_LOCATION); string winrmMessage = ParseEscapeWSManErrorMessage( WSManNativeApi.WSManGetErrorMessage(wsmanAPIHandle, errorStruct.errorCode)).Trim(); e = new PSRemotingTransportRedirectException(redirectLocation, PSRemotingErrorId.URIEndPointNotResolved, RemotingErrorIdStrings.URIEndPointNotResolved, winrmMessage, redirectLocation); } else if ((errorStruct.errorCode == WSManNativeApi.ERROR_WSMAN_INVALID_RESOURCE_URI) && (wsmanSessionTM != null)) { string configurationName = wsmanSessionTM.ConnectionInfo.ShellUri.Replace(Remoting.Client.WSManNativeApi.ResourceURIPrefix, string.Empty); string errorMessage = PSRemotingErrorInvariants.FormatResourceString(RemotingErrorIdStrings.InvalidConfigurationName, configurationName, wsmanSessionTM.ConnectionInfo.ComputerName); e = new PSRemotingTransportException(PSRemotingErrorId.InvalidConfigurationName, RemotingErrorIdStrings.ConnectExCallBackError, wsmanSessionTM.ConnectionInfo.ComputerName, errorMessage); e.TransportMessage = ParseEscapeWSManErrorMessage( WSManNativeApi.WSManGetErrorMessage(wsmanAPIHandle, errorStruct.errorCode)); } else { // Construct specific error message and then append this message pointing to our own // help topic. PowerShell's about help topic "about_Remote_Troubleshooting" should // contain all the trouble shooting information. string wsManErrorMessage = PSRemotingErrorInvariants.FormatResourceString(resourceString, resourceArgs); e = new PSRemotingTransportException(PSRemotingErrorId.TroubleShootingHelpTopic, RemotingErrorIdStrings.TroubleShootingHelpTopic, wsManErrorMessage); e.TransportMessage = ParseEscapeWSManErrorMessage( WSManNativeApi.WSManGetErrorMessage(wsmanAPIHandle, errorStruct.errorCode)); } e.ErrorCode = errorStruct.errorCode; TransportErrorOccuredEventArgs eventargs = new TransportErrorOccuredEventArgs(e, transportMethodReportingError); return eventargs; }
/// <summary> /// Helper function to validate incoming values /// </summary> /// <param name="requestDetails"></param> /// <param name="shellContext"></param> /// <param name="inputFunctionName"></param> /// <returns></returns> private bool validateIncomingContexts( WSManNativeApi.WSManPluginRequest requestDetails, IntPtr shellContext, string inputFunctionName) { if (null == requestDetails) { // Nothing can be done because requestDetails are required to report operation complete PSEtwLog.LogAnalyticInformational(PSEventId.ReportOperationComplete, PSOpcode.Close, PSTask.None, PSKeyword.ManagedPlugin | PSKeyword.UseAlwaysAnalytic, "null", Convert.ToString(WSManPluginErrorCodes.NullInvalidInput, CultureInfo.InvariantCulture), StringUtil.Format( RemotingErrorIdStrings.WSManPluginNullInvalidInput, "requestDetails", inputFunctionName), String.Empty); return false; } if (IntPtr.Zero == shellContext) { ReportOperationComplete( requestDetails, WSManPluginErrorCodes.NullShellContext, StringUtil.Format( RemotingErrorIdStrings.WSManPluginNullShellContext, "ShellContext", inputFunctionName)); return false; } return true; }
/// <summary> /// Sets a DWORD value for a WSMan Session option. /// </summary> /// <param name="option"></param> /// <param name="dwordData"></param> /// <exception cref="PSInvalidOperationException"> /// Setting session option failed with a non-zero error code. /// </exception> internal void SetWSManSessionOption(WSManNativeApi.WSManSessionOption option, int dwordData) { int result = WSManNativeApi.WSManSetSessionOption(_wsManSessionHandle, option, new WSManNativeApi.WSManDataDWord(dwordData)); if (result != 0) { // Get the error message from WSMan string errorMessage = WSManNativeApi.WSManGetErrorMessage(WSManAPIData.WSManAPIHandle, result); PSInvalidOperationException exception = new PSInvalidOperationException(errorMessage); throw exception; } }
internal void StopCommand( WSManNativeApi.WSManPluginRequest requestDetails, IntPtr shellContext, IntPtr commandContext) { if (null == requestDetails) { // Nothing can be done because requestDetails are required to report operation complete PSEtwLog.LogAnalyticInformational(PSEventId.ReportOperationComplete, PSOpcode.Close, PSTask.None, PSKeyword.ManagedPlugin | PSKeyword.UseAlwaysAnalytic, "null", Convert.ToString(WSManPluginErrorCodes.NullInvalidInput, CultureInfo.InvariantCulture), StringUtil.Format( RemotingErrorIdStrings.WSManPluginNullInvalidInput, "requestDetails", "StopCommand"), String.Empty); return; } SetThreadProperties(requestDetails); PSEtwLog.LogAnalyticInformational(PSEventId.ServerStopCommand, PSOpcode.Disconnect, PSTask.None, PSKeyword.ManagedPlugin | PSKeyword.UseAlwaysAnalytic, ((IntPtr)shellContext).ToString(), ((IntPtr)commandContext).ToString(), requestDetails.ToString()); WSManPluginShellSession mgdShellSession = GetFromActiveShellSessions(shellContext); if (null == mgdShellSession) { ReportOperationComplete( requestDetails, WSManPluginErrorCodes.InvalidShellContext, StringUtil.Format( RemotingErrorIdStrings.WSManPluginInvalidShellContext)); return; } WSManPluginCommandSession mgdCommandSession = mgdShellSession.GetCommandSession(commandContext); if (null == mgdCommandSession) { ReportOperationComplete( requestDetails, WSManPluginErrorCodes.InvalidCommandContext, StringUtil.Format( RemotingErrorIdStrings.WSManPluginInvalidCommandContext)); return; } mgdCommandSession.Stop(requestDetails); }
/// <summary> /// Sets a string value for a WSMan Session option. /// </summary> /// <param name="option"></param> /// <param name="stringData"></param> /// <exception cref="PSInvalidOperationException"> /// Setting session option failed with a non-zero error code. /// </exception> internal void SetWSManSessionOption(WSManNativeApi.WSManSessionOption option, string stringData) { using (WSManNativeApi.WSManData_ManToUn data = new WSManNativeApi.WSManData_ManToUn(stringData)) { int result = WSManNativeApi.WSManSetSessionOption(_wsManSessionHandle, option, data); if (result != 0) { // Get the error message from WSMan string errorMessage = WSManNativeApi.WSManGetErrorMessage(WSManAPIData.WSManAPIHandle, result); PSInvalidOperationException exception = new PSInvalidOperationException(errorMessage); throw exception; } } }
/// <summary> /// Send data to the shell / command specified. /// </summary> /// <param name="requestDetails"></param> /// <param name="flags"></param> /// <param name="shellContext"></param> /// <param name="commandContext"></param> /// <param name="stream"></param> /// <param name="inboundData"></param> internal void SendOneItemToShellOrCommand( WSManNativeApi.WSManPluginRequest requestDetails, int flags, IntPtr shellContext, IntPtr commandContext, string stream, WSManNativeApi.WSManData_UnToMan inboundData) { if (!validateIncomingContexts(requestDetails, shellContext, "SendOneItemToShellOrCommand")) { return; } SetThreadProperties(requestDetails); PSEtwLog.LogAnalyticInformational(PSEventId.ServerReceivedData, PSOpcode.Open, PSTask.None, PSKeyword.ManagedPlugin | PSKeyword.UseAlwaysAnalytic, ((IntPtr)shellContext).ToString(), ((IntPtr)commandContext).ToString(), requestDetails.ToString()); WSManPluginShellSession mgdShellSession = GetFromActiveShellSessions(shellContext); if (null == mgdShellSession) { ReportOperationComplete( requestDetails, WSManPluginErrorCodes.InvalidShellContext, StringUtil.Format( RemotingErrorIdStrings.WSManPluginInvalidShellContext) ); return; } if (IntPtr.Zero == commandContext) { // the data is destined for shell (runspace) session. so let shell handle it mgdShellSession.SendOneItemToSession(requestDetails, flags, stream, inboundData); return; } // the data is destined for command. WSManPluginCommandSession mgdCmdSession = mgdShellSession.GetCommandSession(commandContext); if (null == mgdCmdSession) { ReportOperationComplete( requestDetails, WSManPluginErrorCodes.InvalidCommandContext, StringUtil.Format( RemotingErrorIdStrings.WSManPluginInvalidCommandContext)); return; } mgdCmdSession.SendOneItemToSession(requestDetails, flags, stream, inboundData); }
/// <summary> /// Was private. Made protected internal for easier testing /// </summary> /// <param name="requestDetails"></param> /// <returns></returns> protected internal bool EnsureOptionsComply( WSManNativeApi.WSManPluginRequest requestDetails) { WSManNativeApi.WSManOption[] options = requestDetails.operationInfo.optionSet.options; bool isProtocolVersionDeclared = false; for (int i = 0; i < options.Length; i++) // What about requestDetails.operationInfo.optionSet.optionsCount? It is a hold over from the C++ API. Safer is Length. { WSManNativeApi.WSManOption option = options[i]; if (String.Equals(option.name, WSManPluginConstants.PowerShellStartupProtocolVersionName, StringComparison.Ordinal)) { if (!EnsureProtocolVersionComplies(requestDetails, option.value)) { return false; } isProtocolVersionDeclared = true; } if (0 == String.Compare(option.name, 0, WSManPluginConstants.PowerShellOptionPrefix, 0, WSManPluginConstants.PowerShellOptionPrefix.Length, StringComparison.Ordinal)) { if (option.mustComply) { ReportOperationComplete( requestDetails, WSManPluginErrorCodes.OptionNotUnderstood, StringUtil.Format( RemotingErrorIdStrings.WSManPluginOptionNotUnderstood, option.name, System.Management.Automation.PSVersionInfo.BuildVersion, WSManPluginConstants.PowerShellStartupProtocolVersionValue)); return false; } } } if (!isProtocolVersionDeclared) { ReportOperationComplete( requestDetails, WSManPluginErrorCodes.ProtocolVersionNotFound, StringUtil.Format( RemotingErrorIdStrings.WSManPluginProtocolVersionNotFound, WSManPluginConstants.PowerShellStartupProtocolVersionName, System.Management.Automation.PSVersionInfo.BuildVersion, WSManPluginConstants.PowerShellStartupProtocolVersionValue)); return false; } return true; }
/// <summary> /// used to create PSPrincipal object from senderDetails struct. /// </summary> /// <param name="senderDetails"></param> /// <returns></returns> private PSSenderInfo GetPSSenderInfo( WSManNativeApi.WSManSenderDetails senderDetails) { // senderDetails will not be null. Dbg.Assert(null != senderDetails, "senderDetails cannot be null"); // Construct PSIdentity PSCertificateDetails psCertDetails = null; // Construct Certificate Details if (null != senderDetails.certificateDetails) { psCertDetails = new PSCertificateDetails( senderDetails.certificateDetails.subject, senderDetails.certificateDetails.issuerName, senderDetails.certificateDetails.issuerThumbprint); } // Construct PSPrincipal PSIdentity psIdentity = new PSIdentity(senderDetails.authenticationMechanism, true, senderDetails.senderName, psCertDetails); // For Virtual and RunAs accounts WSMan specifies the client token via an environment variable and // senderDetails.clientToken should not be used. IntPtr clientToken = GetRunAsClientToken(); clientToken = (clientToken != IntPtr.Zero) ? clientToken : senderDetails.clientToken; WindowsIdentity windowsIdentity = null; if (clientToken != IntPtr.Zero) { try { windowsIdentity = new WindowsIdentity(clientToken, senderDetails.authenticationMechanism); } // Suppress exceptions..So windowsIdentity = null in these cases catch (ArgumentException) { // userToken is 0. // -or- // userToken is duplicated and invalid for impersonation. } catch (System.Security.SecurityException) { // The caller does not have the correct permissions. // -or- // A Win32 error occurred. } } PSPrincipal userPrincipal = new PSPrincipal(psIdentity, windowsIdentity); PSSenderInfo result = new PSSenderInfo(userPrincipal, senderDetails.httpUrl); return result; }
/// <summary> /// Verifies that the protocol version is in the correct syntax and supported. /// </summary> /// <param name="requestDetails"></param> /// <param name="clientVersionString"></param> /// <returns></returns> protected internal bool EnsureProtocolVersionComplies( WSManNativeApi.WSManPluginRequest requestDetails, string clientVersionString) { if (String.Equals(clientVersionString, WSManPluginConstants.PowerShellStartupProtocolVersionValue, StringComparison.Ordinal)) { return true; } // Check if major versions are equal and server's minor version is smaller.. // if so client's version is supported by the server. The understanding is // that minor version changes do not break the protocol. System.Version clientVersion = Utils.StringToVersion(clientVersionString); System.Version serverVersion = Utils.StringToVersion(WSManPluginConstants.PowerShellStartupProtocolVersionValue); if ((null != clientVersion) && (null != serverVersion) && (clientVersion.Major == serverVersion.Major) && (clientVersion.Minor >= serverVersion.Minor)) { return true; } ReportOperationComplete( requestDetails, WSManPluginErrorCodes.ProtocolVersionNotMatch, StringUtil.Format( RemotingErrorIdStrings.WSManPluginProtocolVersionNotMatch, WSManPluginConstants.PowerShellStartupProtocolVersionValue, System.Management.Automation.PSVersionInfo.BuildVersion, clientVersionString)); return false; }
/// <summary> /// If flush is true, data will be sent immediately to the client. This is accomplished /// by using WSMAN_FLAG_RECEIVE_FLUSH flag provided by WSMan API. /// </summary> /// <param name="data"></param> /// <param name="flush"></param> /// <param name="reportAsPending"></param> /// <param name="reportAsDataBoundary"></param> protected override void SendDataToClient( byte[] data, bool flush, bool reportAsPending, bool reportAsDataBoundary) { if (true == _isClosed) { return; } // double-check locking mechanism is used here to avoid entering into lock // every time data is sent..entering/exiting from lock is costly. if (!_isRequestPending) { // Dont send data until we have received request from client. // The following blocks the calling thread. The thread is // unblocked once a request from client arrives. _waitHandle.WaitOne(); _isRequestPending = true; // at this point request must be pending..so dispose waitHandle _waitHandle.Dispose(); } int result = (int)WSManPluginErrorCodes.NoError; // at this point we have pending request from client. so it is safe // to send data to client using WSMan API. using (WSManNativeApi.WSManData_ManToUn dataToBeSent = new WSManNativeApi.WSManData_ManToUn(data)) { lock (_syncObject) { if (!_isClosed) { int flags = 0; if (flush) { flags |= (int)WSManNativeApi.WSManFlagReceive.WSMAN_FLAG_RECEIVE_FLUSH; } if (reportAsDataBoundary) { // currently assigning hardcoded value for this flag, this is a new change in wsman.h and needs to be replaced with the actual definition once // modified wsman.h is in public headers flags |= (int)WSManNativeApi.WSManFlagReceive.WSMAN_FLAG_RECEIVE_RESULT_DATA_BOUNDARY; } result = WSManNativeApi.WSManPluginReceiveResult( _requestDetails.unmanagedHandle, flags, WSManPluginConstants.SupportedOutputStream, dataToBeSent, reportAsPending ? WSManNativeApi.WSMAN_COMMAND_STATE_PENDING : null, 0); } } } if ((int)WSManPluginErrorCodes.NoError != result) { ReportError(result, "WSManPluginReceiveResult"); } }
/// <summary> /// report operation complete to WSMan and supply a reason (if any) /// </summary> /// <param name="requestDetails"></param> /// <param name="errorCode"></param> internal static void ReportWSManOperationComplete( WSManNativeApi.WSManPluginRequest requestDetails, WSManPluginErrorCodes errorCode) { Dbg.Assert(null != requestDetails, "requestDetails cannot be null in operation complete."); PSEtwLog.LogAnalyticInformational(PSEventId.ReportOperationComplete, PSOpcode.Close, PSTask.None, PSKeyword.ManagedPlugin | PSKeyword.UseAlwaysAnalytic, (requestDetails.unmanagedHandle).ToString(), Convert.ToString(errorCode, CultureInfo.InvariantCulture), String.Empty, String.Empty); ReportOperationComplete(requestDetails.unmanagedHandle, errorCode); }
/// <summary> /// Report session context to WSMan..this will let WSMan send ACK to /// client and client can send data. /// </summary> internal void ReportContext() { int result = 0; bool isRegisterWaitForSingleObjectFailed = false; lock (_syncObject) { if (true == isClosed) { return; } if (!isContextReported) { isContextReported = true; PSEtwLog.LogAnalyticInformational(PSEventId.ReportContext, PSOpcode.Connect, PSTask.None, PSKeyword.ManagedPlugin | PSKeyword.UseAlwaysAnalytic, creationRequestDetails.ToString(), creationRequestDetails.ToString()); //RACE TO BE FIXED - As soon as this API is called, WinRM service will send CommandResponse back and Signal is expected anytime // If Signal comes and executes before registering the notification handle, cleanup will be messed result = WSManNativeApi.WSManPluginReportContext(creationRequestDetails.unmanagedHandle, 0, creationRequestDetails.unmanagedHandle); if (Platform.IsWindows && (WSManPluginConstants.ExitCodeSuccess == result)) { registeredShutdownNotification = 1; // Wrap the provided handle so it can be passed to the registration function SafeWaitHandle safeWaitHandle = new SafeWaitHandle(creationRequestDetails.shutdownNotificationHandle, false); // Owned by WinRM EventWaitHandle eventWaitHandle = new EventWaitHandle(false, EventResetMode.AutoReset); ClrFacade.SetSafeWaitHandle(eventWaitHandle, safeWaitHandle); // Register shutdown notification handle this.registeredShutDownWaitHandle = ThreadPool.RegisterWaitForSingleObject( eventWaitHandle, new WaitOrTimerCallback(WSManPluginManagedEntryWrapper.PSPluginOperationShutdownCallback), shutDownContext, -1, // INFINITE true); // TODO: Do I need to worry not being able to set missing WT_TRANSFER_IMPERSONATION? if (null == this.registeredShutDownWaitHandle) { isRegisterWaitForSingleObjectFailed = true; registeredShutdownNotification = 0; } } } } if ((WSManPluginConstants.ExitCodeSuccess != result) || (isRegisterWaitForSingleObjectFailed)) { string errorMessage; if (isRegisterWaitForSingleObjectFailed) { errorMessage = StringUtil.Format(RemotingErrorIdStrings.WSManPluginShutdownRegistrationFailed); } else { errorMessage = StringUtil.Format(RemotingErrorIdStrings.WSManPluginReportContextFailed); } // Report error and close the session Exception mgdException = new InvalidOperationException(errorMessage); Close(mgdException); } }
internal static TransportErrorOccuredEventArgs ConstructTransportErrorEventArgs( IntPtr wsmanAPIHandle, IntPtr wsmanSessionHandle, WSManNativeApi.WSManError errorStruct, TransportMethodEnum transportMethodReportingError, PSRemotingErrorId errorResourceID, params object[] resourceArgs) { PSRemotingTransportException e; if (errorStruct.errorCode == -2144108135) { string sessionOptionAsString = WSManNativeApi.WSManGetSessionOptionAsString(wsmanSessionHandle, WSManNativeApi.WSManSessionOption.WSMAN_OPTION_REDIRECT_LOCATION); string str = WSManTransportManagerUtils.ParseEscapeWSManErrorMessage(WSManNativeApi.WSManGetErrorMessage(wsmanAPIHandle, errorStruct.errorCode)).Trim(); e = (PSRemotingTransportException) new PSRemotingTransportRedirectException(sessionOptionAsString, PSRemotingErrorId.URIEndPointNotResolved, new object[2] { (object)str, (object)sessionOptionAsString }); } else { e = new PSRemotingTransportException(PSRemotingErrorId.TroubleShootingHelpTopic, new object[1] { (object)PSRemotingErrorInvariants.FormatResourceString(errorResourceID, resourceArgs) }); e.TransportMessage = WSManTransportManagerUtils.ParseEscapeWSManErrorMessage(WSManNativeApi.WSManGetErrorMessage(wsmanAPIHandle, errorStruct.errorCode)); } e.ErrorCode = errorStruct.errorCode; return(new TransportErrorOccuredEventArgs(e, transportMethodReportingError)); }