e79aa3c0ed
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
440 lines
18 KiB
C#
440 lines
18 KiB
C#
//------------------------------------------------------------
|
|
// 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;
|
|
}
|
|
}
|
|
}
|