| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255 | // Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.#if !NO_REMOTINGusing System.Reactive.Disposables;using System.Runtime.Remoting;using System.Runtime.Remoting.Lifetime;using System.Security;using System.Threading;//// DESIGN: The MarshalByRefObject (MBRO) implementations for RemotableObserver and RemotableSubscription act as//         self-sponsoring objects controlling their lease times in order to tie those to the lifetime of the//         underlying observable sequence (ended by OnError or OnCompleted) or the user-controlled subscription//         lifetime. If we were to implement InitializeLifetimeService to return null, we'd end up with leases//         that are infinite, so we need a more fine-grained lease scheme. The default configuration would time//         out after 5 minutes, causing clients to fail while they're still observing the sequence. To solve//         this, those MBROs also implement ISponsor with a Renewal method that continues to renew the lease//         upon every call. When the sequence comes to an end or the subscription is disposed, the sponsor gets//         unregistered, allowing the objects to be reclaimed eventually by the Remoting infrastructure.//// SECURITY: Registration and unregistration of sponsors is protected by SecurityCritical annotations. The//           implementation of ISponsor is known (i.e. no foreign implementation can be passed in) at the call//           sites of the Register and Unregister methods. The call to Register happens in the SecurityCritical//           InitializeLifetimeService method and is called by trusted Remoting infrastructure. The Renewal//           method is also marked as SecurityCritical and called by Remoting. The Unregister method is wrapped//           in a ***SecurityTreatAsSafe*** private method which only gets called by the observer's OnError and//           OnCompleted notifications, or the subscription's Dispose method. In the former case, the sequence//           indicates it has reached the end, and hence resources can be reclaimed. Clients will no longer be//           connected to the source due to auto-detach behavior enforced in the SerializableObservable client-//           side implementation. In the latter case of disposing the subscription, the client is in control//           and will cause the underlying remote subscription to be disposed as well, allowing resources to be//           reclaimed. Rogue messages on either the data or the subscription channel can cause a DoS of the//           client-server communication but this is subject to the security of the Remoting channels used. In//           no case an untrusted party can cause _extension_ of the lease time.////// Notice this assembly is marked as APTCA in official builds, causing methods to be treated as transparent,// thus requiring the ***SecurityTreatAsSafe*** annotation on the security boundaries described above. When not// applied, the following exception would occur at runtime:////    System.MethodAccessException:////    Attempt by security transparent method 'System.Reactive.Linq.QueryLanguage+RemotableObservable`1+//    RemotableSubscription<T>.Unregister()' to access security critical method 'System.Runtime.Remoting.Lifetime.//    ILease.Unregister(System.Runtime.Remoting.Lifetime.ISponsor)' failed.////    Assembly 'System.Reactive.Linq, Version=2.0.ymmdd.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35'//    is marked with the AllowPartiallyTrustedCallersAttribute, and uses the level 2 security transparency model.//    Level 2 transparency causes all methods in AllowPartiallyTrustedCallers assemblies to become security//    transparent by default, which may be the cause of this exception.////// The two CodeAnalysis suppressions below are explained by the Justification property (scroll to the right)://[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2136:TransparencyAnnotationsShouldNotConflictFxCopRule", Scope = "member", Target = "System.Reactive.Linq.QueryLanguage+RemotableObserver`1.#Unregister()", Justification = "This error only occurs while running FxCop on local builds that don't have NO_CODECOVERAGE set, causing the assembly not to be marked with APTCA (see AssemblyInfo.cs). When APTCA is enabled in official builds, this SecurityTreatAsSafe annotation is required.")][assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2136:TransparencyAnnotationsShouldNotConflictFxCopRule", Scope = "member", Target = "System.Reactive.Linq.QueryLanguage+RemotableObservable`1+RemotableSubscription.#Unregister()", Justification = "This error only occurs while running FxCop on local builds that don't have NO_CODECOVERAGE set, causing the assembly not to be marked with APTCA (see AssemblyInfo.cs). When APTCA is enabled in official builds, this SecurityTreatAsSafe annotation is required.")]namespace System.Reactive.Linq{    public static partial class RemotingObservable    {        #region Remotable        private static IObservable<TSource> Remotable_<TSource>(IObservable<TSource> source)        {            return new SerializableObservable<TSource>(new RemotableObservable<TSource>(source, null));        }        private static IObservable<TSource> Remotable_<TSource>(IObservable<TSource> source, ILease lease)        {            return new SerializableObservable<TSource>(new RemotableObservable<TSource>(source, lease));        }        [Serializable]        class SerializableObservable<T> : IObservable<T>        {            readonly RemotableObservable<T> remotableObservable;            public SerializableObservable(RemotableObservable<T> remotableObservable)            {                this.remotableObservable = remotableObservable;            }            public IDisposable Subscribe(IObserver<T> observer)            {                var d = new SingleAssignmentDisposable();                var o = Observer.Create<T>(                    observer.OnNext,                    ex =>                    {                        //                        // Make call to the remote subscription, causing lease renewal to be stopped.                        //                        using (d)                        {                            observer.OnError(ex);                        }                    },                    () =>                    {                        //                        // Make call to the remote subscription, causing lease renewal to be stopped.                        //                        using (d)                        {                            observer.OnCompleted();                        }                    }                );                //                // [OK] Use of unsafe Subscribe: non-pretentious transparent wrapping through remoting; exception coming from the remote object is not re-routed.                //                d.Disposable = remotableObservable.Subscribe/*Unsafe*/(new RemotableObserver<T>(o));                return d;            }        }        class RemotableObserver<T> : MarshalByRefObject, IObserver<T>, ISponsor        {            readonly IObserver<T> underlyingObserver;            public RemotableObserver(IObserver<T> underlyingObserver)            {                this.underlyingObserver = underlyingObserver;            }            public void OnNext(T value)            {                underlyingObserver.OnNext(value);            }            public void OnError(Exception exception)            {                try                {                    underlyingObserver.OnError(exception);                }                finally                {                    Unregister();                }            }            public void OnCompleted()            {                try                {                    underlyingObserver.OnCompleted();                }                finally                {                    Unregister();                }            }            [SecuritySafeCritical] // See remarks at the top of the file.            private void Unregister()            {                var lease = (ILease)RemotingServices.GetLifetimeService(this);                if (lease != null)                    lease.Unregister(this);            }            [SecurityCritical]            public override object InitializeLifetimeService()            {                var lease = (ILease)base.InitializeLifetimeService();                lease.Register(this);                return lease;            }            [SecurityCritical]            TimeSpan ISponsor.Renewal(ILease lease)            {                return lease.InitialLeaseTime;            }        }        [Serializable]        sealed class RemotableObservable<T> : MarshalByRefObject, IObservable<T>        {            readonly IObservable<T> underlyingObservable;            readonly ILease lease;            public RemotableObservable(IObservable<T> underlyingObservable, ILease lease)            {                this.underlyingObservable = underlyingObservable;                this.lease = lease;            }            public IDisposable Subscribe(IObserver<T> observer)            {                //                // [OK] Use of unsafe Subscribe: non-pretentious transparent wrapping through remoting; throwing across remoting boundaries is fine.                //                return new RemotableSubscription(underlyingObservable.Subscribe/*Unsafe*/(observer));            }            [SecurityCritical]            public override object InitializeLifetimeService()            {                return lease;            }            sealed class RemotableSubscription : MarshalByRefObject, IDisposable, ISponsor            {                private IDisposable underlyingSubscription;                public RemotableSubscription(IDisposable underlyingSubscription)                {                    this.underlyingSubscription = underlyingSubscription;                }                public void Dispose()                {                    //                    // Avoiding double-dispose and dropping the reference upon disposal.                    //                    using (Interlocked.Exchange(ref underlyingSubscription, Disposable.Empty))                    {                        Unregister();                    }                }                [SecuritySafeCritical] // See remarks at the top of the file.                private void Unregister()                {                    var lease = (ILease)RemotingServices.GetLifetimeService(this);                    if (lease != null)                        lease.Unregister(this);                }                [SecurityCritical]                public override object InitializeLifetimeService()                {                    var lease = (ILease)base.InitializeLifetimeService();                    lease.Register(this);                    return lease;                }                [SecurityCritical]                TimeSpan ISponsor.Renewal(ILease lease)                {                    return lease.InitialLeaseTime;                }            }        }        #endregion    }}#endif
 |