File: System\ServiceModel\Transactions\WsatConfiguration.cs
Project: ndp\cdf\src\WCF\ServiceModel\System.ServiceModel.csproj (System.ServiceModel)
//------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
//------------------------------------------------------------
 
namespace System.ServiceModel.Transactions
{
    using System;
    using System.Diagnostics.CodeAnalysis;
    using System.Runtime;
    using System.ServiceModel.Channels;
    using System.ServiceModel;
    using System.IO;
    using System.Net;
    using System.Security;
    using System.ServiceModel.ComIntegration;
    using System.ServiceModel.Security;
    using System.Transactions;
 
    using Microsoft.Transactions.Wsat.Messaging;
    using Microsoft.Transactions.Wsat.Protocol;
    using Microsoft.Transactions.Wsat.Recovery;
 
    class TransactionManagerConfigurationException : TransactionException
    {
        public TransactionManagerConfigurationException(string error, Exception e)
            :
            base(error, e)
        {
        }
 
        public TransactionManagerConfigurationException(string error)
            :
            base(error)
        {
        }
    }
 
    class WsatConfiguration
    {
        static readonly string DisabledRegistrationPath;
 
        const string WsatKey = @"Software\Microsoft\WSAT\3.0";
        const string OleTxUpgradeEnabledValue = "OleTxUpgradeEnabled";
        const bool OleTxUpgradeEnabledDefault = true;
 
        bool oleTxUpgradeEnabled;
 
        EndpointAddress localActivationService10;
        EndpointAddress localActivationService11;
 
        EndpointAddress remoteActivationService10;
        EndpointAddress remoteActivationService11;
 
        Uri registrationServiceAddress10;
        Uri registrationServiceAddress11;
 
        bool protocolService10Enabled = false;
        bool protocolService11Enabled = false;
        bool inboundEnabled;
 
        bool issuedTokensEnabled;
        TimeSpan maxTimeout;
 
        [SuppressMessage(FxCop.Category.Security, FxCop.Rule.AptcaMethodsShouldOnlyCallAptcaMethods, Justification = "The calls to BindingStrings are safe.")]
        static WsatConfiguration()
        {
            DisabledRegistrationPath = string.Concat(BindingStrings.AddressPrefix, "/", BindingStrings.RegistrationCoordinatorSuffix(ProtocolVersion.Version10), BindingStrings.DisabledSuffix);
        }
 
        [SuppressMessage(FxCop.Category.Security, FxCop.Rule.AptcaMethodsShouldOnlyCallAptcaMethods, Justification = "The calls to ProtocolInformationReader.IsV10Enabled and IsV11Enabled are safe.")]
        public WsatConfiguration()
        {
            // Get whereabouts
            WhereaboutsReader whereabouts = GetWhereabouts();
 
            ProtocolInformationReader protocol = whereabouts.ProtocolInformation;
            if (protocol != null)
            {
                this.protocolService10Enabled = protocol.IsV10Enabled;
                this.protocolService11Enabled = protocol.IsV11Enabled;
            }
 
            Initialize(whereabouts);
 
            // Read local registry flag
            this.oleTxUpgradeEnabled = ReadFlag(WsatKey, OleTxUpgradeEnabledValue, OleTxUpgradeEnabledDefault);
        }
 
        void Initialize(WhereaboutsReader whereabouts)
        {
            // MB 47153: don't throw system exception if whereabouts data is broken
            try
            {
                InitializeForUnmarshal(whereabouts);
                InitializeForMarshal(whereabouts);
            }
            catch (UriFormatException e)
            {
                // UriBuilder.Uri can throw this if the URI is ultimately invalid 
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                    new TransactionManagerConfigurationException(SR.GetString(SR.WsatUriCreationFailed), e));
            }
            catch (ArgumentOutOfRangeException e)
            {
                // UriBuilder constructor can throw this if port < 0
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                    new TransactionManagerConfigurationException(SR.GetString(SR.WsatUriCreationFailed), e));
            }
        }
 
        public bool OleTxUpgradeEnabled
        {
            get { return this.oleTxUpgradeEnabled; }
        }
 
        public TimeSpan MaxTimeout
        {
            get { return this.maxTimeout; }
        }
 
        public bool IssuedTokensEnabled
        {
            get { return this.issuedTokensEnabled; }
        }
 
        public bool InboundEnabled
        {
            get { return this.inboundEnabled; }
        }
 
        public bool IsProtocolServiceEnabled(ProtocolVersion protocolVersion)
        {
            switch (protocolVersion)
            {
                case ProtocolVersion.Version10:
                    return this.protocolService10Enabled;
 
                case ProtocolVersion.Version11:
                    return this.protocolService11Enabled;
 
                default:
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                        new ArgumentException(SR.GetString(SR.InvalidWsatProtocolVersion)));
            }
        }
 
        public EndpointAddress LocalActivationService(ProtocolVersion protocolVersion)
        {
            switch (protocolVersion)
            {
                case ProtocolVersion.Version10:
                    return this.localActivationService10;
 
                case ProtocolVersion.Version11:
                    return this.localActivationService11;
 
                default:
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                        new ArgumentException(SR.GetString(SR.InvalidWsatProtocolVersion)));
            }
        }
 
 
        public EndpointAddress RemoteActivationService(ProtocolVersion protocolVersion)
        {
            switch (protocolVersion)
            {
                case ProtocolVersion.Version10:
                    return this.remoteActivationService10;
 
                case ProtocolVersion.Version11:
                    return this.remoteActivationService11;
 
                default:
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                        new ArgumentException(SR.GetString(SR.InvalidWsatProtocolVersion)));
            }
        }
 
        public EndpointAddress CreateRegistrationService(AddressHeader refParam, ProtocolVersion protocolVersion)
        {
            switch (protocolVersion)
            {
                case ProtocolVersion.Version10:
                    return new EndpointAddress(this.registrationServiceAddress10, refParam);
 
                case ProtocolVersion.Version11:
                    return new EndpointAddress(this.registrationServiceAddress11, refParam);
 
                default:
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                        new ArgumentException(SR.GetString(SR.InvalidWsatProtocolVersion)));
            }
        }
 
        public bool IsLocalRegistrationService(EndpointAddress endpoint, ProtocolVersion protocolVersion)
        {
            if (endpoint.Uri == null)
                return false;
 
            switch (protocolVersion)
            {
                case ProtocolVersion.Version10:
                    return endpoint.Uri == this.registrationServiceAddress10;
                case ProtocolVersion.Version11:
                    return endpoint.Uri == this.registrationServiceAddress11;
                default:
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                        new ArgumentException(SR.GetString(SR.InvalidWsatProtocolVersion)));
            }
        }
 
        public bool IsDisabledRegistrationService(EndpointAddress endpoint)
        {
            return endpoint.Uri.AbsolutePath == DisabledRegistrationPath;
        }
 
        //
        // Internals
        //
 
        WhereaboutsReader GetWhereabouts()
        {
            try
            {
                return new WhereaboutsReader(TransactionInterop.GetWhereabouts());
            }
            catch (SerializationException e)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                    new TransactionManagerConfigurationException(SR.GetString(SR.WhereaboutsReadFailed), e));
            }
            // If GetWhereabouts throws TransactionException, let it propagate
        }
 
        [SuppressMessage(FxCop.Category.Security, FxCop.Rule.AptcaMethodsShouldOnlyCallAptcaMethods, Justification = "The calls to the ProtocolInformationReader properties and to BindingStrings.RegistrationCoordinatorSuffix(..) are safe.")]
        void InitializeForUnmarshal(WhereaboutsReader whereabouts)
        {
            ProtocolInformationReader protocol = whereabouts.ProtocolInformation;
            if (protocol != null && protocol.NetworkInboundAccess)
            {
                this.inboundEnabled = true;
 
                bool isTmLocal = string.Compare(Environment.MachineName,
                                                protocol.NodeName,
                                                StringComparison.OrdinalIgnoreCase) == 0;
 
                string spnIdentity;
 
                string activationCoordinatorSuffix10 =
                    BindingStrings.ActivationCoordinatorSuffix(ProtocolVersion.Version10);
 
                string activationCoordinatorSuffix11 =
                    BindingStrings.ActivationCoordinatorSuffix(ProtocolVersion.Version11);
 
                if (protocol.IsClustered ||
                   (protocol.NetworkClientAccess && !isTmLocal))
                {
                    if (protocol.IsClustered)
                    {
                        // We cannot reliably perform mutual authentication against a clustered resource
                        // See MB 43523 for more details on this
 
                        spnIdentity = null;
                    }
                    else
                    {
                        spnIdentity = "host/" + protocol.HostName;
                    }
 
                    if (protocol.IsV10Enabled)
                    {
                        this.remoteActivationService10 = CreateActivationEndpointAddress(protocol,
                                                                                         activationCoordinatorSuffix10,
                                                                                         spnIdentity,
                                                                                         true);
                    }
 
                    if (protocol.IsV11Enabled)
                    {
                        this.remoteActivationService11 = CreateActivationEndpointAddress(protocol,
                                                                                         activationCoordinatorSuffix11,
                                                                                         spnIdentity,
                                                                                         true);
                    }
                }
 
                if (isTmLocal)
                {
                    spnIdentity = "host/" + protocol.NodeName;
 
                    // The net.pipe Activation endpoint uses the host name as a discriminant
                    // for cluster scenarios with more than one service on a node.
                    if (protocol.IsV10Enabled)
                    {
                        this.localActivationService10 = CreateActivationEndpointAddress(protocol,
                                                                                        activationCoordinatorSuffix10,
                                                                                        spnIdentity,
                                                                                        false);
                    }
 
                    if (protocol.IsV11Enabled)
                    {
                        this.localActivationService11 = CreateActivationEndpointAddress(protocol,
                                                                                        activationCoordinatorSuffix11,
                                                                                        spnIdentity,
                                                                                        false);
                    }
                }
            }
        }
 
        [SuppressMessage(FxCop.Category.Security, FxCop.Rule.AptcaMethodsShouldOnlyCallAptcaMethods, Justification = "The calls to the ProtocolInformationReader properties (HostName, HttpsPort, BasePath) are safe.")]
        EndpointAddress CreateActivationEndpointAddress(ProtocolInformationReader protocol,
                                              string suffix,
                                              string spnIdentity,
                                              bool isRemote)
        {
            string uriScheme;
            string host;
            int port;
            string path;
 
            if (isRemote)
            {
                uriScheme = Uri.UriSchemeHttps;
                host = protocol.HostName;
                port = protocol.HttpsPort;
                path = protocol.BasePath + "/" + suffix + BindingStrings.RemoteProxySuffix;
            }
            else
            {
                uriScheme = Uri.UriSchemeNetPipe;
                host = "localhost";
                port = -1;
                path = protocol.HostName + "/" + protocol.BasePath + "/" + suffix;
            }
 
            UriBuilder builder = new UriBuilder(uriScheme, host, port, path);
 
            if (spnIdentity != null)
            {
                EndpointIdentity identity = EndpointIdentity.CreateSpnIdentity(spnIdentity);
                return new EndpointAddress(builder.Uri, identity);
            }
            else
            {
                return new EndpointAddress(builder.Uri);
            }
        }
 
        [SuppressMessage(FxCop.Category.Security, FxCop.Rule.AptcaMethodsShouldOnlyCallAptcaMethods, Justification = "The calls to the ProtocolInformationReader properties and to BindingStrings.RegistrationCoordinatorSuffix(..) are safe.")]
        void InitializeForMarshal(WhereaboutsReader whereabouts)
        {
            ProtocolInformationReader protocol = whereabouts.ProtocolInformation;
            if (protocol != null && protocol.NetworkOutboundAccess)
            {
                // We can marshal outgoing transactions using a valid address
                if (protocol.IsV10Enabled)
                {
                    UriBuilder builder10 = new UriBuilder(Uri.UriSchemeHttps,
                                                          protocol.HostName,
                                                          protocol.HttpsPort,
                                                          protocol.BasePath + "/" +
                                                          BindingStrings.RegistrationCoordinatorSuffix(ProtocolVersion.Version10));
                    this.registrationServiceAddress10 = builder10.Uri;
                }
 
                // when we have a WSAT1.1 coordinator 
                if (protocol.IsV11Enabled)
                {
                    UriBuilder builder11 = new UriBuilder(Uri.UriSchemeHttps,
                                                          protocol.HostName,
                                                          protocol.HttpsPort,
                                                          protocol.BasePath + "/" +
                                                          BindingStrings.RegistrationCoordinatorSuffix(ProtocolVersion.Version11));
 
                    this.registrationServiceAddress11 = builder11.Uri;
                }
 
                this.issuedTokensEnabled = protocol.IssuedTokensEnabled;
                this.maxTimeout = protocol.MaxTimeout;
            }
            else
            {
                // Generate an address that will not work
                // We do this in order to generate coordination contexts that can be propagated 
                // between processes on the same node even if WS-AT is disabled
                UriBuilder builder = new UriBuilder(Uri.UriSchemeHttps,
                                                    whereabouts.HostName,
                                                    443,
                                                    DisabledRegistrationPath);
 
                this.registrationServiceAddress10 = builder.Uri;
                this.registrationServiceAddress11 = builder.Uri;
                this.issuedTokensEnabled = false;
                this.maxTimeout = TimeSpan.FromMinutes(5);
            }
        }
 
        static object ReadValue(string key, string value)
        {
            try
            {
                using (RegistryHandle regKey = RegistryHandle.GetNativeHKLMSubkey(key, false))
                {
                    if (regKey == null)
                        return null;
 
                    return regKey.GetValue(value);
                }
            }
            catch (SecurityException e)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                    new TransactionManagerConfigurationException(SR.GetString(SR.WsatRegistryValueReadError, value), e));
            }
            catch (IOException e)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                    new TransactionManagerConfigurationException(SR.GetString(SR.WsatRegistryValueReadError, value), e));
            }
        }
 
        static int ReadInt(string key, string value, int defaultValue)
        {
            object regValue = ReadValue(key, value);
            if (regValue == null || !(regValue is Int32))
                return defaultValue;
 
            return (int)regValue;
        }
 
        static bool ReadFlag(string key, string value, bool defaultValue)
        {
            return (int)ReadInt(key, value, defaultValue ? 1 : 0) != 0;
        }
    }
}