The Labs \ Source Viewer \ SSCLI \ System.Runtime.Remoting.Messaging \ StackBuilderSink

  1. // ==++==
  2. //
  3. //
  4. // Copyright (c) 2006 Microsoft Corporation. All rights reserved.
  5. //
  6. // The use and distribution terms for this software are contained in the file
  7. // named license.txt, which can be found in the root of this distribution.
  8. // By using this software in any fashion, you are agreeing to be bound by the
  9. // terms of this license.
  10. //
  11. // You must not remove this notice, or any other, from this software.
  12. //
  13. //
  14. // ==--==
  15. /*============================================================
  16. **
  17. ** File:    StackBuilderSink.cs
  18. **
  19. **
  20. ** Purpose: Terminating sink which will build a stack and
  21. **          make a method call on an object
  22. **
  23. **
  24. ===========================================================*/
  25. namespace System.Runtime.Remoting.Messaging
  26. {
  27.     using System.Runtime.InteropServices;
  28.     using System.Runtime.Remoting;
  29.     using System.Runtime.Remoting.Channels;
  30.     using System.Runtime.Remoting.Metadata;
  31.     using System.Reflection;
  32.     using System.Runtime.CompilerServices;
  33.     using System.Security.Principal;
  34. /* Assembly Access */   
  35.     using System;
  36.     using System.Globalization;
  37.    
  38.     [Serializable()]
  39.     internal class StackBuilderSink : IMessageSink
  40.     {
  41.        
  42.         //+============================================================
  43.         //
  44.         // Method: Constructor, public
  45.         //
  46.         // Synopsis: Store server object
  47.         //
  48.         // History: 06-May-1999 Created
  49.         //-============================================================
  50.         public StackBuilderSink(MarshalByRefObject server)
  51.         {
  52.             _server = server;
  53.         }
  54.         public StackBuilderSink(object server)
  55.         {
  56.             _server = server;
  57.             if (_server == null) {
  58.                 _bStatic = true;
  59.             }
  60.         }
  61.        
  62.         public virtual IMessage SyncProcessMessage(IMessage msg)
  63.         {
  64.             return SyncProcessMessage(msg, 0, false);
  65.         }
  66.        
  67.         internal virtual IMessage SyncProcessMessage(IMessage msg, int methodPtr, bool fExecuteInContext)
  68.         {
  69.             // Validate message here
  70.             IMessage errMsg = InternalSink.ValidateMessage(msg);
  71.             if (errMsg != null) {
  72.                 return errMsg;
  73.             }
  74.            
  75.             IMethodCallMessage mcMsg = msg as IMethodCallMessage;
  76.            
  77.             IMessage retMessage;
  78.             LogicalCallContext oldCallCtx = null;
  79.            
  80.             LogicalCallContext lcc = CallContext.GetLogicalCallContext();
  81.             object xADCall = lcc.GetData(CrossAppDomainSink.LCC_DATA_KEY);
  82.            
  83.             bool isCallContextSet = false;
  84.             try {
  85.                 object server = _server;
  86.                
  87.                 BCLDebug.Assert((server != null) == (!_bStatic), "Invalid state in stackbuilder sink?");
  88.                
  89.                 // validate the method base if necessary
  90.                 VerifyIsOkToCallMethod(server, mcMsg);
  91.                
  92.                 // install call context onto the thread, holding onto
  93.                 // the one that is currently on the thread
  94.                
  95.                 LogicalCallContext messageCallContext = null;
  96.                 if (mcMsg != null) {
  97.                     messageCallContext = mcMsg.LogicalCallContext;
  98.                 }
  99.                 else {
  100.                     messageCallContext = (LogicalCallContext)msg.Properties["__CallContext"];
  101.                 }
  102.                
  103.                 oldCallCtx = CallContext.SetLogicalCallContext(messageCallContext);
  104.                 isCallContextSet = true;
  105.                
  106.                 messageCallContext.PropagateIncomingHeadersToCallContext(msg);
  107.                
  108.                 PreserveThreadPrincipalIfNecessary(messageCallContext, oldCallCtx);
  109.                
  110.                
  111.                 // NOTE: target for dispatch will be NULL when the StackBuilderSink
  112.                 // is used for async delegates on static methods.
  113.                
  114.                 // *** NOTE ***
  115.                 // Although we always pass _server to these calls in the EE,
  116.                 // when we execute using Message::Dispatch we are using TP as
  117.                 // the this-ptr ... (what the call site thinks is the this-ptr)
  118.                 // when we execute using StackBuilderSink::PrivatePM we use
  119.                 // _server as the this-ptr (which could be different if there
  120.                 // is interception for strictly MBR types in the same AD).
  121.                 // ************
  122.                 if (IsOKToStackBlt(mcMsg, server) && ((Message)mcMsg).Dispatch(server, fExecuteInContext)) {
  123.                     //retMessage = StackBasedReturnMessage.GetObjectFromPool((Message)mcMsg);
  124.                     retMessage = new StackBasedReturnMessage();
  125.                     ((StackBasedReturnMessage)retMessage).InitFields((Message)mcMsg);
  126.                    
  127.                     // call context could be different then the one from before the call.
  128.                     LogicalCallContext latestCallContext = CallContext.GetLogicalCallContext();
  129.                     // retrieve outgoing response headers
  130.                     latestCallContext.PropagateOutgoingHeadersToMessage(retMessage);
  131.                    
  132.                     // Install call context back into Message (from the thread)
  133.                     ((StackBasedReturnMessage)retMessage).SetLogicalCallContext(latestCallContext);
  134.                 }
  135.                 else {
  136.                     MethodBase mb = GetMethodBase(mcMsg);
  137.                     object[] outArgs = null;
  138.                     object ret = null;
  139.                    
  140.                     RemotingMethodCachedData methodCache = InternalRemotingServices.GetReflectionCachedData(mb);
  141.                    
  142.                     Message.DebugOut("StackBuilderSink::Calling PrivateProcessMessage\n");
  143.                    
  144.                     object[] args = Message.CoerceArgs(mcMsg, methodCache.Parameters);
  145.                    
  146.                     ret = PrivateProcessMessage(mb.MethodHandle, args, server, methodPtr, fExecuteInContext, out outArgs);
  147.                     CopyNonByrefOutArgsFromOriginalArgs(methodCache, args, ref outArgs);
  148.                    
  149.                    
  150.                     // call context could be different then the one from before the call.
  151.                     LogicalCallContext latestCallContext = CallContext.GetLogicalCallContext();
  152.                    
  153.                     if (xADCall != null && ((bool)xADCall) == true && latestCallContext != null) {
  154.                         // Special case Principal since if might not be serializable before returning
  155.                         // ReturnMessage
  156.                         latestCallContext.RemovePrincipalIfNotSerializable();
  157.                     }
  158.                    
  159.                     retMessage = new ReturnMessage(ret, outArgs, (outArgs == null ? 0 : outArgs.Length), latestCallContext, mcMsg);
  160.                    
  161.                     // retrieve outgoing response headers
  162.                     latestCallContext.PropagateOutgoingHeadersToMessage(retMessage);
  163.                     // restore the call context on the thread
  164.                     CallContext.SetLogicalCallContext(oldCallCtx);
  165.                 }
  166.                
  167.                
  168.             }
  169.             catch (Exception e) {
  170.                 Message.DebugOut("StackBuilderSink::The server object probably threw an exception " + e.Message + e.StackTrace + "\n");
  171.                 retMessage = new ReturnMessage(e, mcMsg);
  172.                 ((ReturnMessage)retMessage).SetLogicalCallContext(mcMsg.LogicalCallContext);
  173.                
  174.                 if (isCallContextSet)
  175.                     CallContext.SetLogicalCallContext(oldCallCtx);
  176.             }
  177.            
  178.            
  179.             return retMessage;
  180.         }
  181.        
  182.        
  183.         public virtual IMessageCtrl AsyncProcessMessage(IMessage msg, IMessageSink replySink)
  184.         {
  185.             IMethodCallMessage mcMsg = (IMethodCallMessage)msg;
  186.            
  187.             IMessageCtrl retCtrl = null;
  188.             IMessage retMessage = null;
  189.             LogicalCallContext oldCallCtx = null;
  190.             bool isCallContextSet = false;
  191.             try {
  192.                 try {
  193.                     LogicalCallContext callCtx = (LogicalCallContext)mcMsg.Properties[Message.CallContextKey];
  194.                    
  195.                     object server = _server;
  196.                    
  197.                     // validate the method base if necessary
  198.                     VerifyIsOkToCallMethod(server, mcMsg);
  199.                    
  200.                     // install call context onto the thread, holding onto
  201.                     // the one that is currently on the thread
  202.                    
  203.                     oldCallCtx = CallContext.SetLogicalCallContext(callCtx);
  204.                     isCallContextSet = true;
  205.                     // retrieve incoming headers
  206.                     callCtx.PropagateIncomingHeadersToCallContext(msg);
  207.                    
  208.                     PreserveThreadPrincipalIfNecessary(callCtx, oldCallCtx);
  209.                    
  210.                     // see if this is a server message that was dispatched asynchronously
  211.                     ServerChannelSinkStack sinkStack = msg.Properties["__SinkStack"] as ServerChannelSinkStack;
  212.                     if (sinkStack != null)
  213.                         sinkStack.ServerObject = server;
  214.                    
  215.                     BCLDebug.Assert((server != null) == (!_bStatic), "Invalid state in stackbuilder sink?");
  216.                    
  217.                     MethodBase mb = GetMethodBase(mcMsg);
  218.                     object[] outArgs = null;
  219.                     object ret = null;
  220.                     RemotingMethodCachedData methodCache = InternalRemotingServices.GetReflectionCachedData(mb);
  221.                     object[] args = Message.CoerceArgs(mcMsg, methodCache.Parameters);
  222.                    
  223.                     ret = PrivateProcessMessage(mb.MethodHandle, args, server, 0, false, out outArgs);
  224.                     CopyNonByrefOutArgsFromOriginalArgs(methodCache, args, ref outArgs);
  225.                    
  226.                     if (replySink != null) {
  227.                         // call context could be different then the one from before the call.
  228.                         LogicalCallContext latestCallContext = CallContext.GetLogicalCallContext();
  229.                        
  230.                         if (latestCallContext != null) {
  231.                             // Special case Principal since if might not be serializable before returning
  232.                             // ReturnMessage
  233.                             latestCallContext.RemovePrincipalIfNotSerializable();
  234.                         }
  235.                        
  236.                         retMessage = new ReturnMessage(ret, outArgs, (outArgs == null ? 0 : outArgs.Length), latestCallContext, mcMsg);
  237.                        
  238.                         // retrieve outgoing response headers
  239.                         latestCallContext.PropagateOutgoingHeadersToMessage(retMessage);
  240.                        
  241.                     }
  242.                 }
  243.                 catch (Exception e) {
  244.                     if (replySink != null) {
  245.                         retMessage = new ReturnMessage(e, mcMsg);
  246.                         ((ReturnMessage)retMessage).SetLogicalCallContext((LogicalCallContext)mcMsg.Properties[Message.CallContextKey]);
  247.                        
  248.                     }
  249.                 }
  250.                 finally {
  251.                     // Call the reply sink without catching the exceptions
  252.                     // in it. In v2.0 any exceptions in the callback for example
  253.                     // would probably bring down the process
  254.                     replySink.SyncProcessMessage(retMessage);
  255.                 }
  256.             }
  257.             finally {
  258.                 // restore the call context on the thread
  259.                 if (isCallContextSet)
  260.                     CallContext.SetLogicalCallContext(oldCallCtx);
  261.             }
  262.            
  263.             return retCtrl;
  264.         }
  265.         // AsyncProcessMessage
  266.         public IMessageSink NextSink {
  267. // there is no nextSink for the StackBuilderSink
  268.             get { return null; }
  269.         }
  270.        
  271.         // This check if the call-site on the TP is in our own AD
  272.         // It also handles the special case where the TP is on
  273.         // a well-known object and we cannot do stack-blting
  274.         internal bool IsOKToStackBlt(IMethodMessage mcMsg, object server)
  275.         {
  276.             bool bOK = false;
  277.             Message msg = mcMsg as Message;
  278.             if (null != msg) {
  279.                 IInternalMessage iiMsg = (IInternalMessage)msg;
  280.                
  281.                 // If there is a frame in the message we can always
  282.                 // Blt it (provided it is not a proxy to a well-known
  283.                 // object in our own appDomain)!
  284.                 // The GetThisPtr == server test allows for people to wrap
  285.                 // our proxy with their own interception .. in that case
  286.                 // we should not blt the stack.
  287.                 if (msg.GetFramePtr() != IntPtr.Zero && msg.GetThisPtr() == server && (iiMsg.IdentityObject == null || (iiMsg.IdentityObject != null && iiMsg.IdentityObject == iiMsg.ServerIdentityObject))) {
  288.                     bOK = true;
  289.                 }
  290.             }
  291.            
  292.             return bOK;
  293.         }
  294.        
  295.         private static MethodBase GetMethodBase(IMethodMessage msg)
  296.         {
  297.             MethodBase mb = msg.MethodBase;
  298.             if (null == mb) {
  299.                 BCLDebug.Trace("REMOTE", "Method missing w/name ", msg.MethodName);
  300.                 throw new RemotingException(String.Format(CultureInfo.CurrentCulture, Environment.GetResourceString("Remoting_Message_MethodMissing"), msg.MethodName, msg.TypeName));
  301.             }
  302.            
  303.             return mb;
  304.         }
  305.        
  306.         private static void VerifyIsOkToCallMethod(object server, IMethodMessage msg)
  307.         {
  308.             bool bTypeChecked = false;
  309.             MarshalByRefObject mbr = server as MarshalByRefObject;
  310.             if (mbr != null) {
  311.                 bool fServer;
  312.                 Identity id = MarshalByRefObject.GetIdentity(mbr, out fServer);
  313.                 if (id != null) {
  314.                     ServerIdentity srvId = id as ServerIdentity;
  315.                     if ((srvId != null) && srvId.MarshaledAsSpecificType) {
  316.                         Type srvType = srvId.ServerType;
  317.                         if (srvType != null) {
  318.                             MethodBase mb = GetMethodBase(msg);
  319.                             Type declaringType = mb.DeclaringType;
  320.                            
  321.                             // make sure that srvType is not more restrictive than method base
  322.                             // (i.e. someone marshaled with a specific type or interface exposed)
  323.                             if ((declaringType != srvType) && !declaringType.IsAssignableFrom(srvType)) {
  324.                                 throw new RemotingException(String.Format(CultureInfo.CurrentCulture, Environment.GetResourceString("Remoting_InvalidCallingType"), mb.DeclaringType.FullName, srvType.FullName));
  325.                                
  326.                             }
  327.                             // Set flag so we don't repeat this work below.
  328.                             if (declaringType.IsInterface) {
  329.                                 VerifyNotIRemoteDispatch(declaringType);
  330.                             }
  331.                             bTypeChecked = true;
  332.                         }
  333.                     }
  334.                 }
  335.                
  336.                 // We must always verify that the type corresponding to
  337.                 // the method being invoked is compatible with the real server
  338.                 // type.
  339.                 if (!bTypeChecked) {
  340.                     MethodBase mb = GetMethodBase(msg);
  341.                     Type reflectedType = mb.ReflectedType;
  342.                     if (!reflectedType.IsInterface) {
  343.                         if (!reflectedType.IsInstanceOfType(mbr)) {
  344.                             throw new RemotingException(String.Format(CultureInfo.CurrentCulture, Environment.GetResourceString("Remoting_InvalidCallingType"), reflectedType.FullName, mbr.GetType().FullName));
  345.                         }
  346.                     }
  347.                     // This code prohibits calls made to System.EnterpriseServices.IRemoteDispatch
  348.                     // so that remote call cannot bypass lowFilterLevel logic in the serializers.
  349.                     // This special casing should be removed in the future
  350.                     else {
  351.                         VerifyNotIRemoteDispatch(reflectedType);
  352.                     }
  353.                 }
  354.             }
  355.            
  356.         }
  357.         // VerifyIsOkToCallMethod
  358.         // This code prohibits calls made to System.EnterpriseServices.IRemoteDispatch
  359.         // so that remote call cannot bypass lowFilterLevel logic in the serializers.
  360.         // This special casing should be removed in the future
  361.         // Check whether we are calling IRemoteDispatch
  362.         private static void VerifyNotIRemoteDispatch(Type reflectedType)
  363.         {
  364.             if (reflectedType.FullName.Equals(sIRemoteDispatch) && reflectedType.Module.Assembly.nGetSimpleName().Equals(sIRemoteDispatchAssembly)) {
  365.                 throw new RemotingException(Environment.GetResourceString("Remoting_CantInvokeIRemoteDispatch"));
  366.             }
  367.         }
  368.        
  369.         // copies references of non-byref [In, Out] args from the input args to
  370.         // the output args array.
  371.         internal void CopyNonByrefOutArgsFromOriginalArgs(RemotingMethodCachedData methodCache, object[] args, ref object[] marshalResponseArgs)
  372.         {
  373.             int[] map = methodCache.NonRefOutArgMap;
  374.             if (map.Length > 0) {
  375.                 if (marshalResponseArgs == null)
  376.                     marshalResponseArgs = new object[methodCache.Parameters.Length];
  377.                
  378.                 foreach (int pos in map) {
  379.                     marshalResponseArgs[pos] = args[pos];
  380.                 }
  381.             }
  382.         }
  383.        
  384.        
  385.         // For the incoming call, we sometimes need to preserve the thread principal from
  386.         // the executing thread, instead of blindly bashing it with the one from the message.
  387.         // For instance, in cross process calls, the principal will always be null
  388.         // in the incoming message. However, when we are hosted in ASP.Net, ASP.Net will handle
  389.         // authentication and set up the thread principal. We should dispatch the call
  390.         // using the identity that it set up.
  391.         static internal void PreserveThreadPrincipalIfNecessary(LogicalCallContext messageCallContext, LogicalCallContext threadCallContext)
  392.         {
  393.             BCLDebug.Assert(messageCallContext != null, "message should always have a call context");
  394.            
  395.             if (threadCallContext != null) {
  396.                 if (messageCallContext.Principal == null) {
  397.                     IPrincipal currentPrincipal = threadCallContext.Principal;
  398.                     if (currentPrincipal != null) {
  399.                         messageCallContext.Principal = currentPrincipal;
  400.                     }
  401.                 }
  402.             }
  403.         }
  404.         // PreserveThreadPrincipalIfNecessary
  405.        
  406.         internal object ServerObject {
  407.             get { return _server; }
  408.         }
  409.        
  410.        
  411.         //+============================================================
  412.         //
  413.         // Method: PrivateProcessMessage, public
  414.         //
  415.         // Synopsis: does the actual work of building the stack,
  416.         // finding the correct code address and calling it.
  417.         //
  418.         // History: 06-May-1999 Created
  419.         //-============================================================
  420.         [MethodImplAttribute(MethodImplOptions.InternalCall)]
  421.         private extern object _PrivateProcessMessage(IntPtr md, object[] args, object server, int methodPtr, bool fExecuteInContext, out object[] outArgs);
  422.         public object PrivateProcessMessage(RuntimeMethodHandle md, object[] args, object server, int methodPtr, bool fExecuteInContext, out object[] outArgs)
  423.         {
  424.             return _PrivateProcessMessage(md.Value, args, server, methodPtr, fExecuteInContext, out outArgs);
  425.         }
  426.        
  427.         private object _server;
  428.         // target object
  429.         private static string sIRemoteDispatch = "System.EnterpriseServices.IRemoteDispatch";
  430.         private static string sIRemoteDispatchAssembly = "System.EnterpriseServices";
  431.        
  432.         bool _bStatic;
  433.     }
  434. }

Developer Fusion