File: system\runtime\remoting\identity.cs
Project: ndp\clr\src\bcl\mscorlib.csproj (mscorlib)
// ==++==
// 
//   Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// ==--==
namespace System.Runtime.Remoting {
    using System.Globalization;
    using System.Runtime.Remoting;
    using System.Runtime.Remoting.Contexts;
    using System.Runtime.Remoting.Messaging;
    using System.Runtime.Remoting.Activation;
    using System.Runtime.Remoting.Lifetime;
    using System.Security.Cryptography;
    using Microsoft.Win32;
    using System.Threading;
    using System;
    //  Identity is the base class for remoting identities. An instance of Identity (or a derived class)
    //  is associated with each instance of a remoted object. Likewise, an instance of Identity is
    //  associated with each instance of a remoting proxy.
    //
    using System.Collections;
    internal class Identity {
        // We use a Guid to create a URI from. Each time a new URI is needed we increment
        // the sequence number and append it to the statically inited Guid.
        // private static readonly Guid IDGuid = Guid.NewGuid();
 
        internal static String ProcessIDGuid
        {
            get
            {
                return SharedStatics.Remoting_Identity_IDGuid;
            }
        }
 
        // We need the original and the configured one because we have to compare
        //   both when looking at a uri since something might be marshalled before
        //   the id is set.
        private static String s_originalAppDomainGuid = Guid.NewGuid().ToString().Replace('-', '_');
        private static String s_configuredAppDomainGuid = null;
 
        internal static String AppDomainUniqueId
        {
            get
            {
                if (s_configuredAppDomainGuid != null)
                    return s_configuredAppDomainGuid;
                else
                    return s_originalAppDomainGuid;
            } // get
 
        } // AppDomainGuid
 
        private static String s_originalAppDomainGuidString = "/" + s_originalAppDomainGuid.ToLower(CultureInfo.InvariantCulture) + "/";
        private static String s_configuredAppDomainGuidString = null;
 
        private static String s_IDGuidString = "/" + s_originalAppDomainGuid.ToLower(CultureInfo.InvariantCulture) + "/";
 
        // Used to get random numbers 
        private static RNGCryptoServiceProvider s_rng = new RNGCryptoServiceProvider();
 
        internal static String IDGuidString
        {
            get { return s_IDGuidString; }
        }
 
 
        internal static String RemoveAppNameOrAppGuidIfNecessary(String uri)
        {
            // uri is assumed to be in lower-case at this point
 
            // If the uri starts with either, "/<appname>/" or "/<appdomainguid>/" we
            //   should strip that off.
 
            // We only need to look further if the uri starts with a "/".
            if ((uri == null) || (uri.Length <= 1) || (uri[0] != '/'))
                return uri;
 
            // compare to process guid (guid string already has slash at beginnning and end)
            String guidStr;
            if (s_configuredAppDomainGuidString != null)
            {
                guidStr = s_configuredAppDomainGuidString;
                if (uri.Length > guidStr.Length)
                {
                    if (StringStartsWith(uri, guidStr))
                    {
                        // remove "/<appdomainguid>/"
                        return uri.Substring(guidStr.Length);
                    }
                }
            }
 
            // always need to check original guid as well in case the object with this
            //   uri was marshalled before we changed the app domain id
            guidStr = s_originalAppDomainGuidString;
            if (uri.Length > guidStr.Length)
            {
                if (StringStartsWith(uri, guidStr))
                {
                    // remove "/<appdomainguid>/"
                    return uri.Substring(guidStr.Length);
                }
            }
 
            // compare to application name (application name will never have slashes)
            String appName = RemotingConfiguration.ApplicationName;
            if (appName != null)
            {
                // add +2 to appName length for surrounding slashes
                if (uri.Length > (appName.Length + 2))
                {
                    if (String.Compare(uri, 1, appName, 0, appName.Length, true, CultureInfo.InvariantCulture) == 0)
                    {
                        // now, make sure there is a slash after "/<appname>" in uri
                        if (uri[appName.Length + 1] == '/')
                        {
                            // remove "/<appname>/"
                            return uri.Substring(appName.Length + 2);
                        }
                    }
                }
            }
 
            // it didn't start with "/<appname>/" or "/<processguid>/", so just remove the
            //   first slash and return.
            uri = uri.Substring(1);
            return uri;
        } // RemoveAppNameOrAppGuidIfNecessary
 
 
        private static bool StringStartsWith(String s1, String prefix)
        {
            // String.StartsWith uses String.Compare instead of String.CompareOrdinal,
            //   so we provide our own implementation of StartsWith.
 
            if (s1.Length < prefix.Length)
                return false;
 
            return (String.CompareOrdinal(s1, 0, prefix, 0, prefix.Length) == 0);
        } // StringStartsWith
 
        // DISCONNECTED_FULL denotes that the object is disconnected
        // from both local & remote (x-appdomain & higher) clients
 
        // DISCONNECTED_REM denotes that the object is disconnected
        // from remote (x-appdomain & higher) clients ... however
        // x-context proxies continue to work as expected.
 
        protected const int IDFLG_DISCONNECTED_FULL= 0x00000001;
        protected const int IDFLG_DISCONNECTED_REM = 0x00000002;
        protected const int IDFLG_IN_IDTABLE       = 0x00000004;
 
        protected const int IDFLG_CONTEXT_BOUND    = 0x00000010;
        protected const int IDFLG_WELLKNOWN        = 0x00000100;
        protected const int IDFLG_SERVER_SINGLECALL= 0x00000200;
        protected const int IDFLG_SERVER_SINGLETON = 0x00000400;
 
        internal int _flags;
 
        internal Object _tpOrObject;
        protected String _ObjURI;
        protected String _URL;
 
        // These have to be "Object" to use Interlocked operations
        internal Object _objRef;
        internal Object _channelSink;
 
        // Remoting proxy has this field too, we use the latter only for
        // ContextBoundObject identities.
        internal Object _envoyChain;
 
        // This manages the dynamically registered sinks for the proxy.
        internal DynamicPropertyHolder _dph;
 
        // Lease for object
        internal Lease _lease;
 
        // Set  when Identity is initializing and not yet ready for use.
        private volatile bool _initializing;
 
        internal static String ProcessGuid {get {return ProcessIDGuid;}}
 
        private static int GetNextSeqNum()
        {
            return SharedStatics.Remoting_Identity_GetNextSeqNum();
        }
 
        private static Byte[] GetRandomBytes()
        {
            // PERF? In a situation where objects need URIs at a very fast
            // rate, we will end up creating too many of these tiny byte-arrays
            // causing pressure on GC!
            // One option would be to have a buff in the managed thread class
            // and use that to get a chunk of random bytes consuming 
            // 18 bytes at a time. 
            // This would avoid the need to have a lock across threads.
            Byte[] randomBytes = new byte[18];
            s_rng.GetBytes(randomBytes);
            return randomBytes;
        }
 
 
        // Constructs a new identity using the given the URI. This is used for
        // creating client side identities.
        //
        //
        internal Identity(String objURI, String URL)
        {
            BCLDebug.Assert(objURI!=null,"objURI should not be null here");
            if (URL != null)
            {
                _flags |= IDFLG_WELLKNOWN;
                _URL = URL;
            }
            SetOrCreateURI(objURI, true /*calling from ID ctor*/);
        }
 
        // Constructs a new identity. This is used for creating server side
        // identities. The URI for server side identities is lazily generated
        // during the first call to Marshal because if we associate a URI with the
        // object at the time of creation then you cannot call Marshal with a
        // URI of your own choice.
        //
        //
        internal Identity(bool bContextBound)
        {
            if(bContextBound)
                _flags |= IDFLG_CONTEXT_BOUND;
        }
 
        internal bool IsContextBound {
            get  {
                return (_flags&IDFLG_CONTEXT_BOUND) == IDFLG_CONTEXT_BOUND;
            }
        }
 
        internal bool IsInitializing {
            get  {
                return (_initializing);
            }
            set {
                _initializing = value;
            }
        }
 
        internal bool IsWellKnown()
        {
            return (_flags&IDFLG_WELLKNOWN) == IDFLG_WELLKNOWN;
        }
 
        internal void SetInIDTable()
        {
            while(true) {
                int currentFlags = _flags;
                int newFlags = _flags | IDFLG_IN_IDTABLE;
                if(currentFlags == Interlocked.CompareExchange(ref _flags, newFlags, currentFlags))
                    break;
            }
        }
 
        [System.Security.SecurityCritical]  // auto-generated
        internal void ResetInIDTable(bool bResetURI)
        {
            BCLDebug.Assert(IdentityHolder.TableLock.IsWriterLockHeld, "IDTable should be write-locked");
            while(true) {
                int currentFlags = _flags;
                int newFlags = _flags & (~IDFLG_IN_IDTABLE);
                if(currentFlags == Interlocked.CompareExchange(ref _flags, newFlags, currentFlags))
                    break;
            }
            // bResetURI is true for the external API call to Disconnect, it is
            // false otherwise. Thus when a user Disconnects an object 
            // its URI will get reset but if lifetime service times it out 
            // it will not clear out the URIs
            if (bResetURI)
            {
                ((ObjRef)_objRef).URI = null;
                _ObjURI = null;
            }
        }
 
        internal bool IsInIDTable()
        {
            return((_flags & IDFLG_IN_IDTABLE) == IDFLG_IN_IDTABLE);
        }
 
        internal void SetFullyConnected()
        {
            BCLDebug.Assert(
                this is ServerIdentity,
                "should be setting these flags for srvIDs only!");
            BCLDebug.Assert(
                (_ObjURI != null),
                "Object must be assigned a URI to be fully connected!");
 
            while(true) {
                int currentFlags = _flags;
                int newFlags = _flags & (~(IDFLG_DISCONNECTED_FULL | IDFLG_DISCONNECTED_REM));
                if(currentFlags == Interlocked.CompareExchange(ref _flags, newFlags, currentFlags))
                    break;
            }
        }
 
        internal bool IsFullyDisconnected()
        {
            BCLDebug.Assert(
                this is ServerIdentity,
                "should be setting these flags for srvIDs only!");
            return (_flags&IDFLG_DISCONNECTED_FULL) == IDFLG_DISCONNECTED_FULL;
        }
 
        internal bool IsRemoteDisconnected()
        {
            BCLDebug.Assert(
                this is ServerIdentity,
                "should be setting these flags for srvIDs only!");
            return (_flags&IDFLG_DISCONNECTED_REM) == IDFLG_DISCONNECTED_REM;
        }
 
        internal bool IsDisconnected()
        {
            BCLDebug.Assert(
                this is ServerIdentity,
                "should be setting these flags for srvIDs only!");
            return (IsFullyDisconnected() || IsRemoteDisconnected());
        }
 
        // Get the URI
        internal String URI
        {
            get
            {
                if(IsWellKnown())
                {
                    return _URL;
                }
                else
                {
                    return _ObjURI;
                }
            }
        }
 
        internal String ObjURI
        {
            get { return _ObjURI; }
        }
 
        internal MarshalByRefObject TPOrObject
        {
            get
            {
                return (MarshalByRefObject) _tpOrObject;
            }
        }
 
       //   Set the transparentProxy field protecting against ----s. The returned transparent
       //   proxy could be different than the one the caller is attempting to set.
       //
        internal Object  RaceSetTransparentProxy(Object tpObj)
        {
            if (_tpOrObject == null)
                Interlocked.CompareExchange(ref _tpOrObject, tpObj, null);
            return _tpOrObject;
        }
 
        // Get the ObjRef.
        internal ObjRef ObjectRef
        {
            [System.Security.SecurityCritical]  // auto-generated
            get
            {
                return (ObjRef) _objRef;
            }
        }
 
       //   Set the objRef field protecting against ----s. The returned objRef
       //   could be different than the one the caller is attempting to set.
       //
        [System.Security.SecurityCritical]  // auto-generated
        internal ObjRef  RaceSetObjRef(ObjRef objRefGiven)
        {
            if (_objRef == null)
            {
                Interlocked.CompareExchange(ref _objRef, objRefGiven, null);
            }
            return (ObjRef) _objRef;
        }
 
 
        // Get the ChannelSink.
        internal IMessageSink ChannelSink
        {
            get { return (IMessageSink) _channelSink;}
        }
 
       //   Set the channelSink field protecting against ----s. The returned
       //   channelSink proxy could be different than the one the caller is
       //   attempting to set.
       //
        internal IMessageSink  RaceSetChannelSink(IMessageSink channelSink)
        {
            if (_channelSink == null)
            {
                Interlocked.CompareExchange(
                                        ref _channelSink,
                                        channelSink,
                                        null);
            }
            return (IMessageSink) _channelSink;
        }
 
        // Get/Set the Envoy Sink chain..
        internal IMessageSink EnvoyChain
        {
            get
            {
                return (IMessageSink)_envoyChain;
            }
        }
 
        // Get/Set Lease
        internal Lease Lease
        {
            get
            {
                return _lease;
            }
            set
            {
                _lease = value;
            }
        }
 
 
       //   Set the channelSink field protecting against ----s. The returned
       //   channelSink proxy could be different than the one the caller is
       //   attempting to set.
       //
        internal IMessageSink RaceSetEnvoyChain(
                    IMessageSink envoyChain)
        {
            if (_envoyChain == null)
            {
                Interlocked.CompareExchange(
                                ref _envoyChain,
                                envoyChain,
                                null);
            }
            return (IMessageSink) _envoyChain;
        }
 
        // A URI is lazily generated for the identity based on a GUID.
        // Well known objects supply their own URI
        internal void SetOrCreateURI(String uri)
        {
            SetOrCreateURI(uri, false);
        }
 
        internal void SetOrCreateURI(String uri, bool bIdCtor)
        {
            if(bIdCtor == false)
            {
                // This method is called either from the ID Constructor or
                // with a writeLock on the ID Table
                BCLDebug.Assert(IdentityHolder.TableLock.IsWriterLockHeld, "IDTable should be write-locked");
                if (null != _ObjURI) {
                    throw new RemotingException(
                        Environment.GetResourceString("Remoting_SetObjectUriForMarshal__UriExists"));
                }
            }
 
            if(null == uri)
            {
                // We insert the tick count, so that the uri is not 100% predictable.
                // (i.e. perhaps we should consider using a random number as well)
                String random = System.Convert.ToBase64String(GetRandomBytes());
                // Need to replace the '/' with '_' since '/' is not a valid uri char
                _ObjURI = (IDGuidString + random.Replace('/',  '_') + "_" + GetNextSeqNum().ToString(CultureInfo.InvariantCulture.NumberFormat) + ".rem").ToLower(CultureInfo.InvariantCulture);
            }
            else
            {
                if (this is ServerIdentity)
                    _ObjURI = IDGuidString + uri;
                else
                    _ObjURI = uri;
            }
        } // SetOrCreateURI
 
        // This is used by ThreadAffinity/Synchronization contexts
        // (Shares the seqNum space with URIs)
        internal static String GetNewLogicalCallID()
        {
            return IDGuidString + GetNextSeqNum();
        }
 
        [System.Security.SecurityCritical]  // auto-generated
        [System.Diagnostics.Conditional("_DEBUG")]
        internal virtual void AssertValid()
        {
            if (URI != null)
            {
                Identity resolvedIdentity = IdentityHolder.ResolveIdentity(URI);
                BCLDebug.Assert(
                    (resolvedIdentity == null) || (resolvedIdentity == this),
                    "Server ID mismatch with URI");
            }
        }
 
        [System.Security.SecurityCritical]  // auto-generated
        internal bool AddProxySideDynamicProperty(IDynamicProperty prop)
        {
            lock(this)
            {
                if (_dph == null)
                {
                    DynamicPropertyHolder dph = new DynamicPropertyHolder();
                    lock(this)
                    {
                        if (_dph == null)
                        {
                            _dph = dph;
                        }
                    }
                }
                return _dph.AddDynamicProperty(prop);
            }
        }
 
        [System.Security.SecurityCritical]  // auto-generated
        internal bool RemoveProxySideDynamicProperty(String name)
        {
            lock(this)
            {
                if (_dph == null)
                {
                    throw new RemotingException(
                        String.Format(
                            CultureInfo.CurrentCulture, Environment.GetResourceString("Remoting_Contexts_NoProperty"),
                            name));
                }
                return _dph.RemoveDynamicProperty(name);
            }
        }
 
        internal ArrayWithSize ProxySideDynamicSinks
        {
            [System.Security.SecurityCritical]  // auto-generated
            get
            {
                if (_dph == null)
                {
                    return null;
                }
                else
                {
                    return _dph.DynamicSinks;
                }
            }
        }
 
    #if _DEBUG
        public override String ToString()
        {
            return ("IDENTITY: " + " URI = " + _ObjURI);
        }
    #endif
    }
}