You've already forked linux-packaging-mono
Rewrite with hard-coded offsets into the PE file format to discern if a binary is PE32 or PE32+, and then to determine if it contains a "CLR Data Directory" entry that looks valid. Tested with PE32 and PE32+ compiled Mono binaries, PE32 and PE32+ native binaries, and a random assortment of garbage files. Former-commit-id: 9e7ac86ec84f653a2f79b87183efd5b0ebda001b
4845 lines
202 KiB
C#
4845 lines
202 KiB
C#
//----------------------------------------------------------
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
//------------------------------------------------------------
|
|
|
|
namespace System.ServiceModel.Security
|
|
{
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.IdentityModel.Claims;
|
|
using System.IdentityModel.Selectors;
|
|
using System.IdentityModel.Tokens;
|
|
using System.Runtime;
|
|
using System.ServiceModel;
|
|
using System.ServiceModel.Channels;
|
|
using System.ServiceModel.Description;
|
|
using System.ServiceModel.Diagnostics;
|
|
using System.ServiceModel.Dispatcher;
|
|
using System.ServiceModel.Security.Tokens;
|
|
using System.Threading;
|
|
using System.Xml;
|
|
using System.Globalization;
|
|
using System.ServiceModel.Diagnostics.Application;
|
|
|
|
// Please use 'sdv //depot/devdiv/private/indigo_xws/ndp/indigo/src/ServiceModel/System/ServiceModel/Security/SecuritySessionListenerFactory.cs'
|
|
// to see version history before the file was renamed
|
|
// This class is named Settings since the only public APIs are for
|
|
// settings; however, this class also manages all functionality
|
|
// for session channels through internal APIs
|
|
sealed class SecuritySessionServerSettings : IListenerSecureConversationSessionSettings, ISecurityCommunicationObject
|
|
{
|
|
internal const string defaultKeyRenewalIntervalString = "15:00:00";
|
|
internal const string defaultKeyRolloverIntervalString = "00:05:00";
|
|
internal const string defaultInactivityTimeoutString = "00:02:00";
|
|
|
|
internal static readonly TimeSpan defaultKeyRenewalInterval = TimeSpan.Parse(defaultKeyRenewalIntervalString, CultureInfo.InvariantCulture);
|
|
internal static readonly TimeSpan defaultKeyRolloverInterval = TimeSpan.Parse(defaultKeyRolloverIntervalString, CultureInfo.InvariantCulture);
|
|
internal const bool defaultTolerateTransportFailures = true;
|
|
internal const int defaultMaximumPendingSessions = 128;
|
|
internal static readonly TimeSpan defaultInactivityTimeout = TimeSpan.Parse(defaultInactivityTimeoutString, CultureInfo.InvariantCulture);
|
|
|
|
int maximumPendingSessions;
|
|
Dictionary<UniqueId, IServerReliableChannelBinder> pendingSessions1;
|
|
Dictionary<UniqueId, IServerReliableChannelBinder> pendingSessions2;
|
|
IOThreadTimer inactivityTimer;
|
|
TimeSpan inactivityTimeout;
|
|
bool tolerateTransportFailures;
|
|
TimeSpan maximumKeyRenewalInterval;
|
|
TimeSpan keyRolloverInterval;
|
|
int maximumPendingKeysPerSession;
|
|
SecurityProtocolFactory sessionProtocolFactory;
|
|
ICommunicationObject channelAcceptor;
|
|
Dictionary<UniqueId, IServerSecuritySessionChannel> activeSessions;
|
|
ChannelListenerBase securityChannelListener;
|
|
ChannelBuilder channelBuilder;
|
|
SecurityStandardsManager standardsManager;
|
|
SecurityTokenParameters issuedTokenParameters;
|
|
SecurityTokenAuthenticator sessionTokenAuthenticator;
|
|
ISecurityContextSecurityTokenCache sessionTokenCache;
|
|
SecurityTokenResolver sessionTokenResolver;
|
|
WrapperSecurityCommunicationObject communicationObject;
|
|
volatile bool acceptNewWork;
|
|
MessageVersion messageVersion;
|
|
TimeSpan closeTimeout;
|
|
TimeSpan openTimeout;
|
|
TimeSpan sendTimeout;
|
|
Uri listenUri;
|
|
SecurityListenerSettingsLifetimeManager settingsLifetimeManager;
|
|
bool canRenewSession = true;
|
|
object thisLock = new object();
|
|
|
|
public SecuritySessionServerSettings()
|
|
{
|
|
activeSessions = new Dictionary<UniqueId, IServerSecuritySessionChannel>();
|
|
|
|
this.maximumKeyRenewalInterval = defaultKeyRenewalInterval;
|
|
this.maximumPendingKeysPerSession = 5;
|
|
this.keyRolloverInterval = defaultKeyRolloverInterval;
|
|
this.inactivityTimeout = defaultInactivityTimeout;
|
|
this.tolerateTransportFailures = defaultTolerateTransportFailures;
|
|
this.maximumPendingSessions = defaultMaximumPendingSessions;
|
|
this.communicationObject = new WrapperSecurityCommunicationObject(this);
|
|
}
|
|
|
|
internal ChannelBuilder ChannelBuilder
|
|
{
|
|
get
|
|
{
|
|
return this.channelBuilder;
|
|
}
|
|
set
|
|
{
|
|
this.communicationObject.ThrowIfDisposedOrImmutable();
|
|
this.channelBuilder = value;
|
|
}
|
|
}
|
|
|
|
internal SecurityListenerSettingsLifetimeManager SettingsLifetimeManager
|
|
{
|
|
get
|
|
{
|
|
return this.settingsLifetimeManager;
|
|
}
|
|
set
|
|
{
|
|
this.communicationObject.ThrowIfDisposedOrImmutable();
|
|
this.settingsLifetimeManager = value;
|
|
}
|
|
}
|
|
|
|
internal ChannelListenerBase SecurityChannelListener
|
|
{
|
|
get
|
|
{
|
|
return this.securityChannelListener;
|
|
}
|
|
set
|
|
{
|
|
this.communicationObject.ThrowIfDisposedOrImmutable();
|
|
this.securityChannelListener = value;
|
|
}
|
|
}
|
|
|
|
Uri Uri
|
|
{
|
|
get
|
|
{
|
|
this.communicationObject.ThrowIfNotOpened();
|
|
return this.listenUri;
|
|
}
|
|
}
|
|
|
|
object ThisLock
|
|
{
|
|
get
|
|
{
|
|
return this.thisLock;
|
|
}
|
|
}
|
|
|
|
public SecurityTokenAuthenticator SessionTokenAuthenticator
|
|
{
|
|
get
|
|
{
|
|
return this.sessionTokenAuthenticator;
|
|
}
|
|
}
|
|
|
|
public ISecurityContextSecurityTokenCache SessionTokenCache
|
|
{
|
|
get
|
|
{
|
|
return this.sessionTokenCache;
|
|
}
|
|
}
|
|
|
|
public SecurityTokenResolver SessionTokenResolver
|
|
{
|
|
get
|
|
{
|
|
return this.sessionTokenResolver;
|
|
}
|
|
}
|
|
|
|
public SecurityTokenParameters IssuedSecurityTokenParameters
|
|
{
|
|
get
|
|
{
|
|
return this.issuedTokenParameters;
|
|
}
|
|
set
|
|
{
|
|
this.communicationObject.ThrowIfDisposedOrImmutable();
|
|
this.issuedTokenParameters = value;
|
|
}
|
|
}
|
|
|
|
public SecurityStandardsManager SecurityStandardsManager
|
|
{
|
|
get
|
|
{
|
|
return this.standardsManager;
|
|
}
|
|
set
|
|
{
|
|
this.communicationObject.ThrowIfDisposedOrImmutable();
|
|
this.standardsManager = value;
|
|
}
|
|
}
|
|
|
|
public bool TolerateTransportFailures
|
|
{
|
|
get
|
|
{
|
|
return this.tolerateTransportFailures;
|
|
}
|
|
set
|
|
{
|
|
this.communicationObject.ThrowIfDisposedOrImmutable();
|
|
this.tolerateTransportFailures = value;
|
|
}
|
|
}
|
|
|
|
public bool CanRenewSession
|
|
{
|
|
get
|
|
{
|
|
return this.canRenewSession;
|
|
}
|
|
set
|
|
{
|
|
this.canRenewSession = value;
|
|
}
|
|
}
|
|
|
|
public int MaximumPendingSessions
|
|
{
|
|
get
|
|
{
|
|
return this.maximumPendingSessions;
|
|
}
|
|
set
|
|
{
|
|
if (value <= 0)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value"));
|
|
}
|
|
|
|
this.communicationObject.ThrowIfDisposedOrImmutable();
|
|
this.maximumPendingSessions = value;
|
|
}
|
|
}
|
|
|
|
public TimeSpan InactivityTimeout
|
|
{
|
|
get
|
|
{
|
|
return this.inactivityTimeout;
|
|
}
|
|
set
|
|
{
|
|
if (value <= TimeSpan.Zero)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value", SR.GetString(SR.TimeSpanMustbeGreaterThanTimeSpanZero)));
|
|
}
|
|
|
|
this.communicationObject.ThrowIfDisposedOrImmutable();
|
|
this.inactivityTimeout = value;
|
|
}
|
|
}
|
|
|
|
public TimeSpan MaximumKeyRenewalInterval
|
|
{
|
|
get
|
|
{
|
|
return this.maximumKeyRenewalInterval;
|
|
}
|
|
set
|
|
{
|
|
if (value <= TimeSpan.Zero)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value", SR.GetString(SR.TimeSpanMustbeGreaterThanTimeSpanZero)));
|
|
}
|
|
this.communicationObject.ThrowIfDisposedOrImmutable();
|
|
this.maximumKeyRenewalInterval = value;
|
|
}
|
|
}
|
|
|
|
public TimeSpan KeyRolloverInterval
|
|
{
|
|
get
|
|
{
|
|
return this.keyRolloverInterval;
|
|
}
|
|
set
|
|
{
|
|
if (value <= TimeSpan.Zero)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value", SR.GetString(SR.TimeSpanMustbeGreaterThanTimeSpanZero)));
|
|
}
|
|
this.communicationObject.ThrowIfDisposedOrImmutable();
|
|
this.keyRolloverInterval = value;
|
|
}
|
|
}
|
|
|
|
public int MaximumPendingKeysPerSession
|
|
{
|
|
get
|
|
{
|
|
return this.maximumPendingKeysPerSession;
|
|
}
|
|
set
|
|
{
|
|
if (value <= 0)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value", SR.GetString(SR.ValueMustBeGreaterThanZero)));
|
|
}
|
|
this.communicationObject.ThrowIfDisposedOrImmutable();
|
|
this.maximumPendingKeysPerSession = value;
|
|
}
|
|
}
|
|
|
|
public SecurityProtocolFactory SessionProtocolFactory
|
|
{
|
|
get
|
|
{
|
|
return this.sessionProtocolFactory;
|
|
}
|
|
set
|
|
{
|
|
this.communicationObject.ThrowIfDisposedOrImmutable();
|
|
this.sessionProtocolFactory = value;
|
|
}
|
|
}
|
|
|
|
public MessageVersion MessageVersion
|
|
{
|
|
get
|
|
{
|
|
return this.messageVersion;
|
|
}
|
|
}
|
|
|
|
public TimeSpan OpenTimeout
|
|
{
|
|
get
|
|
{
|
|
return this.openTimeout;
|
|
}
|
|
}
|
|
|
|
public TimeSpan CloseTimeout
|
|
{
|
|
get
|
|
{
|
|
return this.closeTimeout;
|
|
}
|
|
}
|
|
|
|
public TimeSpan SendTimeout
|
|
{
|
|
get
|
|
{
|
|
return this.sendTimeout;
|
|
}
|
|
}
|
|
|
|
|
|
// ISecurityCommunicationObject members
|
|
public TimeSpan DefaultOpenTimeout
|
|
{
|
|
get { return ServiceDefaults.OpenTimeout; }
|
|
}
|
|
|
|
public TimeSpan DefaultCloseTimeout
|
|
{
|
|
get { return ServiceDefaults.CloseTimeout; }
|
|
}
|
|
|
|
public IAsyncResult OnBeginClose(TimeSpan timeout, AsyncCallback callback, object state)
|
|
{
|
|
return new OperationWithTimeoutAsyncResult(new OperationWithTimeoutCallback(this.OnClose), timeout, callback, state);
|
|
}
|
|
|
|
public IAsyncResult OnBeginOpen(TimeSpan timeout, AsyncCallback callback, object state)
|
|
{
|
|
return new OperationWithTimeoutAsyncResult(new OperationWithTimeoutCallback(this.OnOpen), timeout, callback, state);
|
|
}
|
|
|
|
public void OnClosed()
|
|
{
|
|
}
|
|
|
|
public void OnClosing()
|
|
{
|
|
}
|
|
|
|
public void OnEndClose(IAsyncResult result)
|
|
{
|
|
OperationWithTimeoutAsyncResult.End(result);
|
|
}
|
|
|
|
public void OnEndOpen(IAsyncResult result)
|
|
{
|
|
OperationWithTimeoutAsyncResult.End(result);
|
|
}
|
|
|
|
public void OnFaulted()
|
|
{
|
|
}
|
|
|
|
public void OnOpened()
|
|
{
|
|
}
|
|
|
|
public void OnOpening()
|
|
{
|
|
}
|
|
|
|
public void OnAbort()
|
|
{
|
|
this.AbortPendingChannels();
|
|
this.OnAbortCore();
|
|
}
|
|
|
|
public void OnClose(TimeSpan timeout)
|
|
{
|
|
TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
|
|
this.ClosePendingChannels(timeoutHelper.RemainingTime());
|
|
this.OnCloseCore(timeoutHelper.RemainingTime());
|
|
}
|
|
|
|
internal void Close(TimeSpan timeout)
|
|
{
|
|
this.communicationObject.Close(timeout);
|
|
}
|
|
|
|
internal IAsyncResult BeginClose(TimeSpan timeout, AsyncCallback callback, object state)
|
|
{
|
|
return this.communicationObject.BeginClose(timeout, callback, state);
|
|
}
|
|
|
|
internal void EndClose(IAsyncResult result)
|
|
{
|
|
this.communicationObject.EndClose(result);
|
|
}
|
|
|
|
internal void Abort()
|
|
{
|
|
this.communicationObject.Abort();
|
|
}
|
|
|
|
internal void Open(TimeSpan timeout)
|
|
{
|
|
this.communicationObject.Open(timeout);
|
|
}
|
|
|
|
internal IAsyncResult BeginOpen(TimeSpan timeout, AsyncCallback callback, object state)
|
|
{
|
|
return this.communicationObject.BeginOpen(timeout, callback, state);
|
|
}
|
|
|
|
internal void EndOpen(IAsyncResult result)
|
|
{
|
|
this.communicationObject.EndOpen(result);
|
|
}
|
|
|
|
void OnCloseCore(TimeSpan timeout)
|
|
{
|
|
TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
|
|
if (this.inactivityTimer != null)
|
|
{
|
|
this.inactivityTimer.Cancel();
|
|
}
|
|
if (this.sessionProtocolFactory != null)
|
|
{
|
|
this.sessionProtocolFactory.Close(false, timeoutHelper.RemainingTime());
|
|
}
|
|
if (this.sessionTokenAuthenticator != null)
|
|
{
|
|
SecurityUtils.CloseTokenAuthenticatorIfRequired(this.sessionTokenAuthenticator, timeoutHelper.RemainingTime());
|
|
}
|
|
}
|
|
|
|
void OnAbortCore()
|
|
{
|
|
if (this.inactivityTimer != null)
|
|
{
|
|
this.inactivityTimer.Cancel();
|
|
}
|
|
if (this.sessionProtocolFactory != null)
|
|
{
|
|
this.sessionProtocolFactory.Close(true, TimeSpan.Zero);
|
|
}
|
|
if (this.sessionTokenAuthenticator != null)
|
|
{
|
|
SecurityUtils.AbortTokenAuthenticatorIfRequired(this.sessionTokenAuthenticator);
|
|
}
|
|
}
|
|
|
|
void SetupSessionTokenAuthenticator()
|
|
{
|
|
RecipientServiceModelSecurityTokenRequirement requirement = new RecipientServiceModelSecurityTokenRequirement();
|
|
this.issuedTokenParameters.InitializeSecurityTokenRequirement(requirement);
|
|
requirement.KeyUsage = SecurityKeyUsage.Signature;
|
|
requirement.ListenUri = this.listenUri;
|
|
requirement.SecurityBindingElement = this.sessionProtocolFactory.SecurityBindingElement;
|
|
requirement.SecurityAlgorithmSuite = this.sessionProtocolFactory.IncomingAlgorithmSuite;
|
|
requirement.SupportSecurityContextCancellation = true;
|
|
requirement.MessageSecurityVersion = sessionProtocolFactory.MessageSecurityVersion.SecurityTokenVersion;
|
|
requirement.AuditLogLocation = sessionProtocolFactory.AuditLogLocation;
|
|
requirement.SuppressAuditFailure = sessionProtocolFactory.SuppressAuditFailure;
|
|
requirement.MessageAuthenticationAuditLevel = sessionProtocolFactory.MessageAuthenticationAuditLevel;
|
|
requirement.Properties[ServiceModelSecurityTokenRequirement.MessageDirectionProperty] = MessageDirection.Input;
|
|
if (sessionProtocolFactory.EndpointFilterTable != null)
|
|
{
|
|
requirement.Properties[ServiceModelSecurityTokenRequirement.EndpointFilterTableProperty] = sessionProtocolFactory.EndpointFilterTable;
|
|
}
|
|
this.sessionTokenAuthenticator = this.sessionProtocolFactory.SecurityTokenManager.CreateSecurityTokenAuthenticator(requirement, out this.sessionTokenResolver);
|
|
if (!(this.sessionTokenAuthenticator is IIssuanceSecurityTokenAuthenticator))
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SecuritySessionRequiresIssuanceAuthenticator, typeof(IIssuanceSecurityTokenAuthenticator), this.sessionTokenAuthenticator.GetType())));
|
|
}
|
|
if (sessionTokenResolver == null || (!(sessionTokenResolver is ISecurityContextSecurityTokenCache)))
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SecuritySessionRequiresSecurityContextTokenCache, this.sessionTokenResolver.GetType(), typeof(ISecurityContextSecurityTokenCache))));
|
|
}
|
|
this.sessionTokenCache = (ISecurityContextSecurityTokenCache)this.sessionTokenResolver;
|
|
}
|
|
|
|
|
|
public void OnOpen(TimeSpan timeout)
|
|
{
|
|
if (this.sessionProtocolFactory == null)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SecuritySessionProtocolFactoryShouldBeSetBeforeThisOperation)));
|
|
}
|
|
if (this.standardsManager == null)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SecurityStandardsManagerNotSet, this.GetType())));
|
|
}
|
|
if (this.issuedTokenParameters == null)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.IssuedSecurityTokenParametersNotSet, this.GetType())));
|
|
}
|
|
if (this.maximumKeyRenewalInterval < this.keyRolloverInterval)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.KeyRolloverGreaterThanKeyRenewal)));
|
|
}
|
|
if (this.securityChannelListener == null)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SecurityChannelListenerNotSet, this.GetType())));
|
|
}
|
|
if (this.settingsLifetimeManager == null)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SecuritySettingsLifetimeManagerNotSet, this.GetType())));
|
|
}
|
|
|
|
this.messageVersion = this.channelBuilder.Binding.MessageVersion;
|
|
this.listenUri = this.securityChannelListener.Uri;
|
|
this.openTimeout = this.securityChannelListener.InternalOpenTimeout;
|
|
this.closeTimeout = this.securityChannelListener.InternalCloseTimeout;
|
|
this.sendTimeout = this.securityChannelListener.InternalSendTimeout;
|
|
TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
|
|
this.pendingSessions1 = new Dictionary<UniqueId, IServerReliableChannelBinder>();
|
|
this.pendingSessions2 = new Dictionary<UniqueId, IServerReliableChannelBinder>();
|
|
if (this.inactivityTimeout < TimeSpan.MaxValue)
|
|
{
|
|
this.inactivityTimer = new IOThreadTimer(new Action<object>(this.OnTimer), this, false);
|
|
this.inactivityTimer.Set(this.inactivityTimeout);
|
|
}
|
|
this.ConfigureSessionSecurityProtocolFactory();
|
|
this.sessionProtocolFactory.Open(false, timeoutHelper.RemainingTime());
|
|
SetupSessionTokenAuthenticator();
|
|
((IIssuanceSecurityTokenAuthenticator)this.sessionTokenAuthenticator).IssuedSecurityTokenHandler = this.OnTokenIssued;
|
|
((IIssuanceSecurityTokenAuthenticator)this.sessionTokenAuthenticator).RenewedSecurityTokenHandler = this.OnTokenRenewed;
|
|
this.acceptNewWork = true;
|
|
SecurityUtils.OpenTokenAuthenticatorIfRequired(this.sessionTokenAuthenticator, timeoutHelper.RemainingTime());
|
|
}
|
|
|
|
public void StopAcceptingNewWork()
|
|
{
|
|
this.acceptNewWork = false;
|
|
}
|
|
|
|
int GetPendingSessionCount()
|
|
{
|
|
return this.pendingSessions1.Count + this.pendingSessions2.Count + ((IInputQueueChannelAcceptor)this.channelAcceptor).PendingCount;
|
|
}
|
|
|
|
void AbortPendingChannels()
|
|
{
|
|
lock (ThisLock)
|
|
{
|
|
if (this.pendingSessions1 != null)
|
|
{
|
|
foreach (IServerReliableChannelBinder pendingChannelBinder in pendingSessions1.Values)
|
|
{
|
|
pendingChannelBinder.Abort();
|
|
}
|
|
}
|
|
if (this.pendingSessions2 != null)
|
|
{
|
|
foreach (IServerReliableChannelBinder pendingChannelBinder in pendingSessions2.Values)
|
|
{
|
|
pendingChannelBinder.Abort();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ClosePendingChannels(TimeSpan timeout)
|
|
{
|
|
TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
|
|
lock (ThisLock)
|
|
{
|
|
foreach (IServerReliableChannelBinder pendingChannelBinder in pendingSessions1.Values)
|
|
{
|
|
pendingChannelBinder.Close(timeoutHelper.RemainingTime());
|
|
}
|
|
foreach (IServerReliableChannelBinder pendingChannelBinder in pendingSessions2.Values)
|
|
{
|
|
pendingChannelBinder.Close(timeoutHelper.RemainingTime());
|
|
}
|
|
}
|
|
}
|
|
|
|
void ConfigureSessionSecurityProtocolFactory()
|
|
{
|
|
if (this.sessionProtocolFactory is SessionSymmetricMessageSecurityProtocolFactory)
|
|
{
|
|
AddressingVersion addressing = MessageVersion.Default.Addressing;
|
|
if (this.channelBuilder != null)
|
|
{
|
|
MessageEncodingBindingElement encoding = this.channelBuilder.Binding.Elements.Find<MessageEncodingBindingElement>();
|
|
if (encoding != null)
|
|
{
|
|
addressing = encoding.MessageVersion.Addressing;
|
|
}
|
|
}
|
|
|
|
if (addressing != AddressingVersion.WSAddressing10 && addressing != AddressingVersion.WSAddressingAugust2004)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
|
|
new ProtocolException(SR.GetString(SR.AddressingVersionNotSupported, addressing)));
|
|
}
|
|
|
|
SessionSymmetricMessageSecurityProtocolFactory messagePf = (SessionSymmetricMessageSecurityProtocolFactory)this.sessionProtocolFactory;
|
|
if (!messagePf.ApplyIntegrity || !messagePf.RequireIntegrity)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SecuritySessionRequiresMessageIntegrity)));
|
|
}
|
|
MessagePartSpecification bodyPart = new MessagePartSpecification(true);
|
|
messagePf.ProtectionRequirements.IncomingSignatureParts.AddParts(bodyPart, this.SecurityStandardsManager.SecureConversationDriver.CloseAction);
|
|
messagePf.ProtectionRequirements.IncomingSignatureParts.AddParts(bodyPart, this.SecurityStandardsManager.SecureConversationDriver.CloseResponseAction);
|
|
messagePf.ProtectionRequirements.OutgoingSignatureParts.AddParts(bodyPart, this.SecurityStandardsManager.SecureConversationDriver.CloseResponseAction);
|
|
messagePf.ProtectionRequirements.OutgoingSignatureParts.AddParts(bodyPart, this.SecurityStandardsManager.SecureConversationDriver.CloseAction);
|
|
messagePf.ProtectionRequirements.OutgoingSignatureParts.AddParts(bodyPart, addressing.FaultAction);
|
|
messagePf.ProtectionRequirements.OutgoingSignatureParts.AddParts(bodyPart, addressing.DefaultFaultAction);
|
|
messagePf.ProtectionRequirements.OutgoingSignatureParts.AddParts(bodyPart, DotNetSecurityStrings.SecuritySessionFaultAction);
|
|
if (messagePf.ApplyConfidentiality)
|
|
{
|
|
messagePf.ProtectionRequirements.OutgoingEncryptionParts.AddParts(MessagePartSpecification.NoParts, this.SecurityStandardsManager.SecureConversationDriver.CloseResponseAction);
|
|
messagePf.ProtectionRequirements.OutgoingEncryptionParts.AddParts(MessagePartSpecification.NoParts, this.SecurityStandardsManager.SecureConversationDriver.CloseAction);
|
|
messagePf.ProtectionRequirements.OutgoingEncryptionParts.AddParts(bodyPart, addressing.FaultAction);
|
|
messagePf.ProtectionRequirements.OutgoingEncryptionParts.AddParts(bodyPart, addressing.DefaultFaultAction);
|
|
messagePf.ProtectionRequirements.OutgoingEncryptionParts.AddParts(bodyPart, DotNetSecurityStrings.SecuritySessionFaultAction);
|
|
}
|
|
if (messagePf.RequireConfidentiality)
|
|
{
|
|
messagePf.ProtectionRequirements.IncomingEncryptionParts.AddParts(MessagePartSpecification.NoParts, this.SecurityStandardsManager.SecureConversationDriver.CloseAction);
|
|
messagePf.ProtectionRequirements.IncomingEncryptionParts.AddParts(MessagePartSpecification.NoParts, this.SecurityStandardsManager.SecureConversationDriver.CloseResponseAction);
|
|
}
|
|
messagePf.SecurityTokenParameters = this.IssuedSecurityTokenParameters;
|
|
}
|
|
else if (this.sessionProtocolFactory is SessionSymmetricTransportSecurityProtocolFactory)
|
|
{
|
|
SessionSymmetricTransportSecurityProtocolFactory transportPf = (SessionSymmetricTransportSecurityProtocolFactory)this.sessionProtocolFactory;
|
|
transportPf.AddTimestamp = true;
|
|
transportPf.SecurityTokenParameters = this.IssuedSecurityTokenParameters;
|
|
transportPf.SecurityTokenParameters.RequireDerivedKeys = false;
|
|
}
|
|
else
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException());
|
|
}
|
|
}
|
|
|
|
internal IChannelAcceptor<TChannel> CreateAcceptor<TChannel>()
|
|
where TChannel : class, IChannel
|
|
{
|
|
if (this.channelAcceptor != null)
|
|
{
|
|
Fx.Assert("SecuritySessionServerSettings.CreateAcceptor (this.channelAcceptor != null)");
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SSSSCreateAcceptor)));
|
|
}
|
|
object listenerSecurityState = this.sessionProtocolFactory.CreateListenerSecurityState();
|
|
if (typeof(TChannel) == typeof(IReplySessionChannel))
|
|
{
|
|
this.channelAcceptor = new SecuritySessionChannelAcceptor<IReplySessionChannel>(this.SecurityChannelListener, listenerSecurityState);
|
|
}
|
|
else if (typeof(TChannel) == typeof(IDuplexSessionChannel))
|
|
{
|
|
this.channelAcceptor = new SecuritySessionChannelAcceptor<IDuplexSessionChannel>(this.SecurityChannelListener, listenerSecurityState);
|
|
}
|
|
else
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException());
|
|
}
|
|
return (IChannelAcceptor<TChannel>)this.channelAcceptor;
|
|
}
|
|
|
|
internal IChannelListener CreateInnerChannelListener()
|
|
{
|
|
if (this.ChannelBuilder.CanBuildChannelListener<IDuplexSessionChannel>())
|
|
{
|
|
return this.ChannelBuilder.BuildChannelListener<IDuplexSessionChannel>(new MatchNoneMessageFilter(), int.MinValue);
|
|
}
|
|
else if (this.ChannelBuilder.CanBuildChannelListener<IDuplexChannel>())
|
|
{
|
|
return this.ChannelBuilder.BuildChannelListener<IDuplexChannel>(new MatchNoneMessageFilter(), int.MinValue);
|
|
}
|
|
else if (this.ChannelBuilder.CanBuildChannelListener<IReplyChannel>())
|
|
{
|
|
return this.ChannelBuilder.BuildChannelListener<IReplyChannel>(new MatchNoneMessageFilter(), int.MinValue);
|
|
}
|
|
else
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException());
|
|
}
|
|
}
|
|
|
|
void OnTokenRenewed(SecurityToken newToken, SecurityToken oldToken)
|
|
{
|
|
this.communicationObject.ThrowIfClosed();
|
|
if (!this.acceptNewWork)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new EndpointNotFoundException(SR.GetString(SR.SecurityListenerClosing)));
|
|
}
|
|
SecurityContextSecurityToken newSecurityContextToken = newToken as SecurityContextSecurityToken;
|
|
if (newSecurityContextToken == null)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.GetString(SR.SessionTokenIsNotSecurityContextToken, newToken.GetType(), typeof(SecurityContextSecurityToken))));
|
|
}
|
|
SecurityContextSecurityToken oldSecurityContextToken = oldToken as SecurityContextSecurityToken;
|
|
if (oldSecurityContextToken == null)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.GetString(SR.SessionTokenIsNotSecurityContextToken, oldToken.GetType(), typeof(SecurityContextSecurityToken))));
|
|
}
|
|
IServerSecuritySessionChannel sessionChannel = this.FindSessionChannel(newSecurityContextToken.ContextId);
|
|
if (sessionChannel == null)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new MessageSecurityException(SR.GetString(SR.CannotFindSecuritySession, newSecurityContextToken.ContextId)));
|
|
}
|
|
sessionChannel.RenewSessionToken(newSecurityContextToken, oldSecurityContextToken);
|
|
}
|
|
|
|
IServerReliableChannelBinder CreateChannelBinder(SecurityContextSecurityToken sessionToken, EndpointAddress remoteAddress)
|
|
{
|
|
IServerReliableChannelBinder result = null;
|
|
MessageFilter sctFilter = new SecuritySessionFilter(sessionToken.ContextId, this.sessionProtocolFactory.StandardsManager, (this.sessionProtocolFactory.SecurityHeaderLayout == SecurityHeaderLayout.Strict), this.SecurityStandardsManager.SecureConversationDriver.RenewAction.Value, this.SecurityStandardsManager.SecureConversationDriver.RenewResponseAction.Value);
|
|
int sctPriority = Int32.MaxValue;
|
|
TolerateFaultsMode faultMode = this.TolerateTransportFailures ? TolerateFaultsMode.Always : TolerateFaultsMode.Never;
|
|
lock (ThisLock)
|
|
{
|
|
if (this.ChannelBuilder.CanBuildChannelListener<IDuplexSessionChannel>())
|
|
{
|
|
result = ServerReliableChannelBinder<IDuplexSessionChannel>.CreateBinder(this.ChannelBuilder, remoteAddress, sctFilter, sctPriority, faultMode,
|
|
this.CloseTimeout, this.SendTimeout);
|
|
}
|
|
else if (this.ChannelBuilder.CanBuildChannelListener<IDuplexChannel>())
|
|
{
|
|
result = ServerReliableChannelBinder<IDuplexChannel>.CreateBinder(this.ChannelBuilder, remoteAddress, sctFilter, sctPriority, faultMode,
|
|
this.CloseTimeout, this.SendTimeout);
|
|
}
|
|
else if (this.ChannelBuilder.CanBuildChannelListener<IReplyChannel>())
|
|
{
|
|
result = ServerReliableChannelBinder<IReplyChannel>.CreateBinder(this.ChannelBuilder, remoteAddress, sctFilter, sctPriority, faultMode,
|
|
this.CloseTimeout, this.SendTimeout);
|
|
}
|
|
}
|
|
if (result == null)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException());
|
|
}
|
|
|
|
result.Open(this.OpenTimeout);
|
|
SessionInitiationMessageHandler handler = new SessionInitiationMessageHandler(result, this, sessionToken);
|
|
handler.BeginReceive(TimeSpan.MaxValue);
|
|
return result;
|
|
}
|
|
|
|
void OnTokenIssued(SecurityToken issuedToken, EndpointAddress tokenRequestor)
|
|
{
|
|
this.communicationObject.ThrowIfClosed();
|
|
if (!this.acceptNewWork)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new EndpointNotFoundException(SR.GetString(SR.SecurityListenerClosing)));
|
|
}
|
|
SecurityContextSecurityToken issuedSecurityContextToken = issuedToken as SecurityContextSecurityToken;
|
|
if (issuedSecurityContextToken == null)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.GetString(SR.SessionTokenIsNotSecurityContextToken, issuedToken.GetType(), typeof(SecurityContextSecurityToken))));
|
|
}
|
|
IServerReliableChannelBinder channelBinder = CreateChannelBinder(issuedSecurityContextToken, tokenRequestor ?? EndpointAddress.AnonymousAddress);
|
|
bool wasSessionAdded = false;
|
|
try
|
|
{
|
|
this.AddPendingSession(issuedSecurityContextToken.ContextId, channelBinder);
|
|
wasSessionAdded = true;
|
|
}
|
|
finally
|
|
{
|
|
if (!wasSessionAdded)
|
|
{
|
|
channelBinder.Abort();
|
|
}
|
|
}
|
|
}
|
|
|
|
void OnTimer(object state)
|
|
{
|
|
if (this.communicationObject.State == CommunicationState.Closed
|
|
|| this.communicationObject.State == CommunicationState.Faulted)
|
|
{
|
|
return;
|
|
}
|
|
try
|
|
{
|
|
this.ClearPendingSessions();
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
if (Fx.IsFatal(e))
|
|
{
|
|
throw;
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
if (this.communicationObject.State != CommunicationState.Closed
|
|
&& this.communicationObject.State != CommunicationState.Closing
|
|
&& this.communicationObject.State != CommunicationState.Faulted)
|
|
{
|
|
this.inactivityTimer.Set(this.inactivityTimeout);
|
|
}
|
|
}
|
|
}
|
|
|
|
void AddPendingSession(UniqueId sessionId, IServerReliableChannelBinder channelBinder)
|
|
{
|
|
lock (ThisLock)
|
|
{
|
|
if ((GetPendingSessionCount() + 1) > this.MaximumPendingSessions)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new QuotaExceededException(SR.GetString(SR.SecuritySessionLimitReached)));
|
|
}
|
|
if (this.pendingSessions1.ContainsKey(sessionId) || this.pendingSessions2.ContainsKey(sessionId))
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new MessageSecurityException(SR.GetString(SR.SecuritySessionAlreadyPending, sessionId)));
|
|
}
|
|
this.pendingSessions1.Add(sessionId, channelBinder);
|
|
}
|
|
SecurityTraceRecordHelper.TracePendingSessionAdded(sessionId, this.Uri);
|
|
if (TD.SecuritySessionRatioIsEnabled())
|
|
{
|
|
TD.SecuritySessionRatio(GetPendingSessionCount(), this.MaximumPendingSessions);
|
|
}
|
|
}
|
|
|
|
void TryCloseBinder(IServerReliableChannelBinder binder, TimeSpan timeout)
|
|
{
|
|
bool abortBinder = false;
|
|
try
|
|
{
|
|
binder.Close(timeout);
|
|
}
|
|
catch (CommunicationException e)
|
|
{
|
|
abortBinder = true;
|
|
DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
|
|
}
|
|
catch (TimeoutException e)
|
|
{
|
|
abortBinder = true;
|
|
|
|
if (TD.CloseTimeoutIsEnabled())
|
|
{
|
|
TD.CloseTimeout(e.Message);
|
|
}
|
|
DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
|
|
}
|
|
finally
|
|
{
|
|
if (abortBinder)
|
|
{
|
|
binder.Abort();
|
|
}
|
|
}
|
|
}
|
|
|
|
// this method should be called by the timer under ThisLock
|
|
void ClearPendingSessions()
|
|
{
|
|
lock (ThisLock)
|
|
{
|
|
if (this.pendingSessions1.Count == 0 && this.pendingSessions2.Count == 0)
|
|
{
|
|
return;
|
|
}
|
|
foreach (UniqueId sessionId in this.pendingSessions2.Keys)
|
|
{
|
|
IServerReliableChannelBinder channelBinder = this.pendingSessions2[sessionId];
|
|
try
|
|
{
|
|
TryCloseBinder(channelBinder, this.CloseTimeout);
|
|
this.SessionTokenCache.RemoveAllContexts(sessionId);
|
|
}
|
|
catch (CommunicationException e)
|
|
{
|
|
DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
|
|
}
|
|
catch (TimeoutException e)
|
|
{
|
|
if (TD.CloseTimeoutIsEnabled())
|
|
{
|
|
TD.CloseTimeout(e.Message);
|
|
}
|
|
DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
|
|
}
|
|
catch (ObjectDisposedException e)
|
|
{
|
|
DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
|
|
}
|
|
SecurityTraceRecordHelper.TracePendingSessionClosed(sessionId, this.Uri);
|
|
}
|
|
this.pendingSessions2.Clear();
|
|
Dictionary<UniqueId, IServerReliableChannelBinder> temp = this.pendingSessions2;
|
|
this.pendingSessions2 = this.pendingSessions1;
|
|
this.pendingSessions1 = temp;
|
|
}
|
|
}
|
|
|
|
bool RemovePendingSession(UniqueId sessionId)
|
|
{
|
|
bool result;
|
|
lock (ThisLock)
|
|
{
|
|
if (this.pendingSessions1.ContainsKey(sessionId))
|
|
{
|
|
this.pendingSessions1.Remove(sessionId);
|
|
result = true;
|
|
}
|
|
else if (pendingSessions2.ContainsKey(sessionId))
|
|
{
|
|
this.pendingSessions2.Remove(sessionId);
|
|
result = true;
|
|
}
|
|
else
|
|
{
|
|
result = false;
|
|
}
|
|
}
|
|
if (result)
|
|
{
|
|
SecurityTraceRecordHelper.TracePendingSessionActivated(sessionId, this.Uri);
|
|
if (TD.SecuritySessionRatioIsEnabled())
|
|
{
|
|
TD.SecuritySessionRatio(GetPendingSessionCount(), this.MaximumPendingSessions);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
IServerSecuritySessionChannel FindSessionChannel(UniqueId sessionId)
|
|
{
|
|
IServerSecuritySessionChannel result;
|
|
lock (ThisLock)
|
|
{
|
|
this.activeSessions.TryGetValue(sessionId, out result);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
void AddSessionChannel(UniqueId sessionId, IServerSecuritySessionChannel channel)
|
|
{
|
|
lock (ThisLock)
|
|
{
|
|
this.activeSessions.Add(sessionId, channel);
|
|
}
|
|
}
|
|
|
|
void RemoveSessionChannel(string sessionId)
|
|
{
|
|
RemoveSessionChannel(new UniqueId(sessionId));
|
|
}
|
|
|
|
void RemoveSessionChannel(UniqueId sessionId)
|
|
{
|
|
lock (ThisLock)
|
|
{
|
|
this.activeSessions.Remove(sessionId);
|
|
}
|
|
SecurityTraceRecordHelper.TraceActiveSessionRemoved(sessionId, this.Uri);
|
|
}
|
|
|
|
class SessionInitiationMessageHandler
|
|
{
|
|
static AsyncCallback receiveCallback = Fx.ThunkCallback(new AsyncCallback(ReceiveCallback));
|
|
IServerReliableChannelBinder channelBinder;
|
|
SecuritySessionServerSettings settings;
|
|
SecurityContextSecurityToken sessionToken;
|
|
bool processedInitiation = false;
|
|
|
|
public SessionInitiationMessageHandler(IServerReliableChannelBinder channelBinder, SecuritySessionServerSettings settings, SecurityContextSecurityToken sessionToken)
|
|
{
|
|
this.channelBinder = channelBinder;
|
|
this.settings = settings;
|
|
this.sessionToken = sessionToken;
|
|
}
|
|
|
|
public IAsyncResult BeginReceive(TimeSpan timeout)
|
|
{
|
|
return this.channelBinder.BeginTryReceive(timeout, receiveCallback, this);
|
|
}
|
|
|
|
public void ProcessMessage(IAsyncResult result)
|
|
{
|
|
bool threwException = false;
|
|
try
|
|
{
|
|
RequestContext requestContext;
|
|
if (!this.channelBinder.EndTryReceive(result, out requestContext))
|
|
{
|
|
// we should never have timed out since the receive was called with an Infinite timeout
|
|
// if we did then do a BeginReceive and return
|
|
this.BeginReceive(TimeSpan.MaxValue);
|
|
return;
|
|
}
|
|
|
|
if (requestContext == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
Message message = requestContext.RequestMessage;
|
|
lock (this.settings.ThisLock)
|
|
{
|
|
if (this.settings.communicationObject.State != CommunicationState.Opened)
|
|
{
|
|
((IDisposable)requestContext).Dispose();
|
|
return;
|
|
}
|
|
if (this.processedInitiation)
|
|
{
|
|
return;
|
|
}
|
|
this.processedInitiation = true;
|
|
}
|
|
if (!this.settings.RemovePendingSession(this.sessionToken.ContextId))
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new CommunicationException(SR.GetString(SR.SecuritySessionNotPending, this.sessionToken.ContextId)));
|
|
}
|
|
if (this.settings.channelAcceptor is SecuritySessionChannelAcceptor<IReplySessionChannel>)
|
|
{
|
|
SecuritySessionChannelAcceptor<IReplySessionChannel> replyAcceptor = ((SecuritySessionChannelAcceptor<IReplySessionChannel>)this.settings.channelAcceptor);
|
|
SecurityReplySessionChannel replySessionChannel = new SecurityReplySessionChannel(this.settings,
|
|
this.channelBinder,
|
|
sessionToken,
|
|
replyAcceptor.ListenerSecurityState,
|
|
this.settings.SettingsLifetimeManager);
|
|
settings.AddSessionChannel(this.sessionToken.ContextId, replySessionChannel);
|
|
replySessionChannel.StartReceiving(requestContext);
|
|
replyAcceptor.EnqueueAndDispatch(replySessionChannel);
|
|
}
|
|
else if (this.settings.channelAcceptor is SecuritySessionChannelAcceptor<IDuplexSessionChannel>)
|
|
{
|
|
SecuritySessionChannelAcceptor<IDuplexSessionChannel> duplexAcceptor = ((SecuritySessionChannelAcceptor<IDuplexSessionChannel>)this.settings.channelAcceptor);
|
|
ServerSecurityDuplexSessionChannel duplexSessionChannel = new ServerSecurityDuplexSessionChannel(this.settings,
|
|
this.channelBinder,
|
|
sessionToken,
|
|
duplexAcceptor.ListenerSecurityState,
|
|
this.settings.SettingsLifetimeManager);
|
|
settings.AddSessionChannel(this.sessionToken.ContextId, duplexSessionChannel);
|
|
duplexSessionChannel.StartReceiving(requestContext);
|
|
duplexAcceptor.EnqueueAndDispatch(duplexSessionChannel);
|
|
}
|
|
else
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new EndpointNotFoundException(SR.GetString(SR.SecuritySessionListenerNotFound, message.Headers.Action)));
|
|
}
|
|
}
|
|
#pragma warning suppress 56500 // covered by FxCOP
|
|
catch (Exception e)
|
|
{
|
|
threwException = true;
|
|
if (Fx.IsFatal(e))
|
|
{
|
|
throw;
|
|
}
|
|
DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
|
|
}
|
|
finally
|
|
{
|
|
if (threwException)
|
|
{
|
|
this.channelBinder.Abort();
|
|
}
|
|
}
|
|
}
|
|
|
|
static void ReceiveCallback(IAsyncResult result)
|
|
{
|
|
((SessionInitiationMessageHandler)result.AsyncState).ProcessMessage(result);
|
|
}
|
|
}
|
|
|
|
interface IInputQueueChannelAcceptor
|
|
{
|
|
int PendingCount { get; }
|
|
}
|
|
|
|
class SecuritySessionChannelAcceptor<T> : InputQueueChannelAcceptor<T>, IInputQueueChannelAcceptor
|
|
where T : class, IChannel
|
|
{
|
|
object listenerState;
|
|
|
|
public SecuritySessionChannelAcceptor(ChannelListenerBase manager, object listenerState)
|
|
: base(manager)
|
|
{
|
|
this.listenerState = listenerState;
|
|
}
|
|
|
|
public object ListenerSecurityState
|
|
{
|
|
get
|
|
{
|
|
return this.listenerState;
|
|
}
|
|
}
|
|
|
|
int IInputQueueChannelAcceptor.PendingCount
|
|
{
|
|
get { return this.PendingCount; }
|
|
}
|
|
}
|
|
|
|
interface IServerSecuritySessionChannel
|
|
{
|
|
void RenewSessionToken(SecurityContextSecurityToken newToken, SecurityContextSecurityToken supportingToken);
|
|
}
|
|
|
|
abstract class ServerSecuritySessionChannel : ChannelBase, IServerSecuritySessionChannel
|
|
{
|
|
FaultCode renewFaultCode;
|
|
FaultReason renewFaultReason;
|
|
FaultCode sessionAbortedFaultCode;
|
|
FaultReason sessionAbortedFaultReason;
|
|
|
|
// Double-checked locking pattern requires volatile for read/write synchronization
|
|
volatile bool areFaultCodesInitialized;
|
|
IServerReliableChannelBinder channelBinder;
|
|
SecurityProtocol securityProtocol;
|
|
// This is used to sign outgoing messages
|
|
SecurityContextSecurityToken currentSessionToken;
|
|
UniqueId sessionId;
|
|
// These are renewed tokens that have not been used as yet
|
|
List<SecurityContextSecurityToken> futureSessionTokens;
|
|
SecuritySessionServerSettings settings;
|
|
RequestContext initialRequestContext;
|
|
volatile bool isInputClosed;
|
|
ThreadNeutralSemaphore receiveLock;
|
|
MessageVersion messageVersion;
|
|
SecurityListenerSettingsLifetimeManager settingsLifetimeManager;
|
|
volatile bool hasSecurityStateReference;
|
|
|
|
protected ServerSecuritySessionChannel(SecuritySessionServerSettings settings,
|
|
IServerReliableChannelBinder channelBinder,
|
|
SecurityContextSecurityToken sessionToken,
|
|
object listenerSecurityProtocolState,
|
|
SecurityListenerSettingsLifetimeManager settingsLifetimeManager)
|
|
: base(settings.SecurityChannelListener)
|
|
{
|
|
this.settings = settings;
|
|
this.channelBinder = channelBinder;
|
|
this.messageVersion = settings.MessageVersion;
|
|
this.channelBinder.Faulted += this.OnInnerFaulted;
|
|
this.securityProtocol = this.Settings.SessionProtocolFactory.CreateSecurityProtocol(null, null, listenerSecurityProtocolState, true, TimeSpan.Zero);
|
|
if (!(this.securityProtocol is IAcceptorSecuritySessionProtocol))
|
|
{
|
|
Fx.Assert("Security protocol must be IAcceptorSecuritySessionProtocol.");
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ProtocolMisMatch, "IAcceptorSecuritySessionProtocol", this.GetType().ToString())));
|
|
}
|
|
this.currentSessionToken = sessionToken;
|
|
this.sessionId = sessionToken.ContextId;
|
|
this.futureSessionTokens = new List<SecurityContextSecurityToken>(1);
|
|
((IAcceptorSecuritySessionProtocol)this.securityProtocol).SetOutgoingSessionToken(sessionToken);
|
|
((IAcceptorSecuritySessionProtocol)this.securityProtocol).SetSessionTokenAuthenticator(this.sessionId, this.settings.SessionTokenAuthenticator, this.settings.SessionTokenResolver);
|
|
this.settingsLifetimeManager = settingsLifetimeManager;
|
|
this.receiveLock = new ThreadNeutralSemaphore(1);
|
|
}
|
|
|
|
protected SecuritySessionServerSettings Settings
|
|
{
|
|
get
|
|
{
|
|
return this.settings;
|
|
}
|
|
}
|
|
|
|
protected virtual bool CanDoSecurityCorrelation
|
|
{
|
|
get
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
internal IServerReliableChannelBinder ChannelBinder
|
|
{
|
|
get
|
|
{
|
|
return this.channelBinder;
|
|
}
|
|
}
|
|
|
|
internal TimeSpan InternalSendTimeout
|
|
{
|
|
get
|
|
{
|
|
return this.DefaultSendTimeout;
|
|
}
|
|
}
|
|
|
|
public EndpointAddress LocalAddress
|
|
{
|
|
get
|
|
{
|
|
return this.channelBinder.LocalAddress;
|
|
}
|
|
}
|
|
|
|
protected override void OnOpen(TimeSpan timeout)
|
|
{
|
|
this.securityProtocol.Open(timeout);
|
|
if (this.CanDoSecurityCorrelation)
|
|
{
|
|
((IAcceptorSecuritySessionProtocol)this.securityProtocol).ReturnCorrelationState = true;
|
|
}
|
|
lock (ThisLock)
|
|
{
|
|
// if an abort happened concurrently with the open, then return
|
|
if (this.State == CommunicationState.Closed || this.State == CommunicationState.Closing)
|
|
{
|
|
return;
|
|
}
|
|
this.settingsLifetimeManager.AddReference();
|
|
this.hasSecurityStateReference = true;
|
|
}
|
|
}
|
|
|
|
protected override IAsyncResult OnBeginOpen(TimeSpan timeout, AsyncCallback callback, object state)
|
|
{
|
|
OnOpen(timeout);
|
|
return new CompletedAsyncResult(callback, state);
|
|
}
|
|
|
|
protected override void OnEndOpen(IAsyncResult result)
|
|
{
|
|
CompletedAsyncResult.End(result);
|
|
}
|
|
|
|
protected virtual void AbortCore()
|
|
{
|
|
if (this.channelBinder != null)
|
|
{
|
|
this.channelBinder.Abort();
|
|
}
|
|
if (this.securityProtocol != null)
|
|
{
|
|
this.securityProtocol.Close(true, TimeSpan.Zero);
|
|
}
|
|
this.Settings.SessionTokenCache.RemoveAllContexts(this.currentSessionToken.ContextId);
|
|
bool abortLifetimeManager = false;
|
|
lock (ThisLock)
|
|
{
|
|
if (hasSecurityStateReference)
|
|
{
|
|
abortLifetimeManager = true;
|
|
hasSecurityStateReference = false;
|
|
}
|
|
}
|
|
if (abortLifetimeManager)
|
|
{
|
|
this.settingsLifetimeManager.Abort();
|
|
}
|
|
}
|
|
|
|
protected virtual void CloseCore(TimeSpan timeout)
|
|
{
|
|
TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
|
|
|
|
try
|
|
{
|
|
if (this.channelBinder != null)
|
|
{
|
|
this.channelBinder.Close(timeoutHelper.RemainingTime());
|
|
}
|
|
if (this.securityProtocol != null)
|
|
{
|
|
this.securityProtocol.Close(false, timeoutHelper.RemainingTime());
|
|
}
|
|
bool closeLifetimeManager = false;
|
|
lock (ThisLock)
|
|
{
|
|
if (hasSecurityStateReference)
|
|
{
|
|
closeLifetimeManager = true;
|
|
hasSecurityStateReference = false;
|
|
}
|
|
}
|
|
if (closeLifetimeManager)
|
|
{
|
|
this.settingsLifetimeManager.Close(timeoutHelper.RemainingTime());
|
|
}
|
|
}
|
|
catch (CommunicationObjectAbortedException)
|
|
{
|
|
if (this.State != CommunicationState.Closed)
|
|
{
|
|
throw;
|
|
}
|
|
// a parallel thread aborted the channel. Ignore the exception
|
|
}
|
|
|
|
this.Settings.SessionTokenCache.RemoveAllContexts(this.currentSessionToken.ContextId);
|
|
}
|
|
|
|
protected virtual IAsyncResult BeginCloseCore(TimeSpan timeout, AsyncCallback callback, object state)
|
|
{
|
|
return new CloseCoreAsyncResult(this, timeout, callback, state);
|
|
}
|
|
|
|
protected virtual void EndCloseCore(IAsyncResult result)
|
|
{
|
|
CloseCoreAsyncResult.End(result);
|
|
}
|
|
|
|
protected abstract void OnCloseMessageReceived(RequestContext requestContext, Message message, SecurityProtocolCorrelationState correlationState, TimeSpan timeout);
|
|
|
|
protected abstract void OnCloseResponseMessageReceived(RequestContext requestContext, Message message, SecurityProtocolCorrelationState correlationState, TimeSpan timeout);
|
|
|
|
public void RenewSessionToken(SecurityContextSecurityToken newToken, SecurityContextSecurityToken supportingToken)
|
|
{
|
|
ThrowIfClosedOrNotOpen();
|
|
// enforce that the token being renewed is the current session token
|
|
lock (ThisLock)
|
|
{
|
|
if (supportingToken.ContextId != this.currentSessionToken.ContextId || supportingToken.KeyGeneration != this.currentSessionToken.KeyGeneration)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new MessageSecurityException(SR.GetString(SR.CurrentSessionTokenNotRenewed, supportingToken.KeyGeneration, this.currentSessionToken.KeyGeneration)));
|
|
}
|
|
if (this.futureSessionTokens.Count == this.Settings.MaximumPendingKeysPerSession)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new MessageSecurityException(SR.GetString(SR.TooManyPendingSessionKeys)));
|
|
}
|
|
this.futureSessionTokens.Add(newToken);
|
|
}
|
|
SecurityTraceRecordHelper.TraceNewServerSessionKeyIssued(newToken, supportingToken, GetLocalUri());
|
|
}
|
|
|
|
protected Uri GetLocalUri()
|
|
{
|
|
if (this.channelBinder.LocalAddress == null)
|
|
return null;
|
|
else
|
|
return this.channelBinder.LocalAddress.Uri;
|
|
}
|
|
|
|
void OnInnerFaulted(IReliableChannelBinder sender, Exception exception)
|
|
{
|
|
this.Fault(exception);
|
|
}
|
|
|
|
SecurityContextSecurityToken GetSessionToken(SecurityMessageProperty securityProperty)
|
|
{
|
|
SecurityContextSecurityToken sct = (securityProperty.ProtectionToken != null) ? securityProperty.ProtectionToken.SecurityToken as SecurityContextSecurityToken : null;
|
|
if (sct != null && sct.ContextId == this.sessionId)
|
|
{
|
|
return sct;
|
|
}
|
|
if (securityProperty.HasIncomingSupportingTokens)
|
|
{
|
|
for (int i = 0; i < securityProperty.IncomingSupportingTokens.Count; ++i)
|
|
{
|
|
if (securityProperty.IncomingSupportingTokens[i].SecurityTokenAttachmentMode == SecurityTokenAttachmentMode.Endorsing)
|
|
{
|
|
sct = (securityProperty.IncomingSupportingTokens[i].SecurityToken as SecurityContextSecurityToken);
|
|
if (sct != null && sct.ContextId == this.sessionId)
|
|
{
|
|
return sct;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
bool CheckIncomingToken(RequestContext requestContext, Message message, SecurityProtocolCorrelationState correlationState, TimeSpan timeout)
|
|
{
|
|
SecurityMessageProperty securityProperty = message.Properties.Security;
|
|
// this is guaranteed to be non-null and matches the session ID since the binding checked it
|
|
SecurityContextSecurityToken incomingToken = GetSessionToken(securityProperty);
|
|
if (incomingToken == null)
|
|
{
|
|
throw TraceUtility.ThrowHelperWarning(new MessageSecurityException(SR.GetString(SR.NoSessionTokenPresentInMessage)), message);
|
|
}
|
|
// the incoming token's key should have been issued within keyRenewalPeriod time in the past
|
|
// if not, send back a renewal fault. However if this is a session close message then its ok to not require the client
|
|
// to renew the key in order to send the close.
|
|
if (incomingToken.KeyExpirationTime < DateTime.UtcNow &&
|
|
message.Headers.Action != this.settings.SecurityStandardsManager.SecureConversationDriver.CloseAction.Value)
|
|
{
|
|
if (this.settings.CanRenewSession)
|
|
{
|
|
SendRenewFault(requestContext, correlationState, timeout);
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new SessionKeyExpiredException(SR.GetString(SR.SecurityContextKeyExpired, incomingToken.ContextId, incomingToken.KeyGeneration)));
|
|
}
|
|
}
|
|
// this is a valid token. If it corresponds to a newly issued session token, make it the current
|
|
// session token.
|
|
lock (ThisLock)
|
|
{
|
|
if (this.futureSessionTokens.Count > 0 && incomingToken.KeyGeneration != this.currentSessionToken.KeyGeneration)
|
|
{
|
|
bool changedCurrentSessionToken = false;
|
|
for (int i = 0; i < this.futureSessionTokens.Count; ++i)
|
|
{
|
|
if (futureSessionTokens[i].KeyGeneration == incomingToken.KeyGeneration)
|
|
{
|
|
// let the current token expire after KeyRollover time interval
|
|
DateTime keyRolloverTime = TimeoutHelper.Add(DateTime.UtcNow, this.settings.KeyRolloverInterval);
|
|
this.settings.SessionTokenCache.UpdateContextCachingTime(this.currentSessionToken, keyRolloverTime);
|
|
this.currentSessionToken = futureSessionTokens[i];
|
|
futureSessionTokens.RemoveAt(i);
|
|
((IAcceptorSecuritySessionProtocol)this.securityProtocol).SetOutgoingSessionToken(this.currentSessionToken);
|
|
changedCurrentSessionToken = true;
|
|
break;
|
|
}
|
|
}
|
|
if (changedCurrentSessionToken)
|
|
{
|
|
SecurityTraceRecordHelper.TraceServerSessionKeyUpdated(this.currentSessionToken, GetLocalUri());
|
|
// remove all renewed tokens that will never be used.
|
|
for (int i = 0; i < futureSessionTokens.Count; ++i)
|
|
{
|
|
this.Settings.SessionTokenCache.RemoveContext(futureSessionTokens[i].ContextId, futureSessionTokens[i].KeyGeneration);
|
|
}
|
|
this.futureSessionTokens.Clear();
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public void StartReceiving(RequestContext initialRequestContext)
|
|
{
|
|
if (this.initialRequestContext != null)
|
|
{
|
|
Fx.Assert("The initial request context was already specified.");
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.AttemptToCreateMultipleRequestContext)));
|
|
}
|
|
this.initialRequestContext = initialRequestContext;
|
|
}
|
|
|
|
public RequestContext ReceiveRequest()
|
|
{
|
|
return this.ReceiveRequest(this.DefaultReceiveTimeout);
|
|
}
|
|
|
|
public RequestContext ReceiveRequest(TimeSpan timeout)
|
|
{
|
|
RequestContext requestContext;
|
|
if (this.TryReceiveRequest(timeout, out requestContext))
|
|
{
|
|
return requestContext;
|
|
}
|
|
else
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new TimeoutException());
|
|
}
|
|
}
|
|
|
|
public IAsyncResult BeginReceiveRequest(AsyncCallback callback, object state)
|
|
{
|
|
return this.BeginReceiveRequest(this.DefaultReceiveTimeout, callback, state);
|
|
}
|
|
|
|
public IAsyncResult BeginReceiveRequest(TimeSpan timeout, AsyncCallback callback, object state)
|
|
{
|
|
return this.BeginTryReceiveRequest(timeout, callback, state);
|
|
}
|
|
|
|
public RequestContext EndReceiveRequest(IAsyncResult result)
|
|
{
|
|
RequestContext requestContext;
|
|
if (this.EndTryReceiveRequest(result, out requestContext))
|
|
{
|
|
return requestContext;
|
|
}
|
|
else
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new TimeoutException());
|
|
}
|
|
}
|
|
|
|
public IAsyncResult BeginTryReceiveRequest(TimeSpan timeout, AsyncCallback callback, object state)
|
|
{
|
|
return new ReceiveRequestAsyncResult(this, timeout, callback, state);
|
|
}
|
|
|
|
public bool EndTryReceiveRequest(IAsyncResult result, out RequestContext requestContext)
|
|
{
|
|
return ReceiveRequestAsyncResult.EndAsRequestContext(result, out requestContext);
|
|
}
|
|
|
|
public bool TryReceiveRequest(TimeSpan timeout, out RequestContext requestContext)
|
|
{
|
|
ThrowIfFaulted();
|
|
TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
|
|
|
|
if (!this.receiveLock.TryEnter(timeoutHelper.RemainingTime()))
|
|
{
|
|
requestContext = null;
|
|
return false;
|
|
}
|
|
try
|
|
{
|
|
while (true)
|
|
{
|
|
if (isInputClosed || this.State == CommunicationState.Faulted)
|
|
{
|
|
break;
|
|
}
|
|
|
|
// schedule another Receive if the timeout has not been reached
|
|
if (timeoutHelper.RemainingTime() == TimeSpan.Zero)
|
|
{
|
|
requestContext = null;
|
|
return false;
|
|
}
|
|
|
|
RequestContext innerRequestContext;
|
|
if (initialRequestContext != null)
|
|
{
|
|
innerRequestContext = initialRequestContext;
|
|
initialRequestContext = null;
|
|
}
|
|
else
|
|
{
|
|
if (!channelBinder.TryReceive(timeoutHelper.RemainingTime(), out innerRequestContext))
|
|
{
|
|
requestContext = null;
|
|
return false;
|
|
}
|
|
}
|
|
if (innerRequestContext == null)
|
|
{
|
|
// the channel could have been aborted or closed
|
|
break;
|
|
}
|
|
if (this.isInputClosed && innerRequestContext.RequestMessage != null)
|
|
{
|
|
Message message = innerRequestContext.RequestMessage;
|
|
try
|
|
{
|
|
ProtocolException error = ProtocolException.ReceiveShutdownReturnedNonNull(message);
|
|
throw TraceUtility.ThrowHelperWarning(error, message);
|
|
}
|
|
finally
|
|
{
|
|
message.Close();
|
|
innerRequestContext.Abort();
|
|
}
|
|
}
|
|
SecurityProtocolCorrelationState correlationState = null;
|
|
bool isSecurityProcessingFailure;
|
|
Message requestMessage = ProcessRequestContext(innerRequestContext, timeoutHelper.RemainingTime(), out correlationState, out isSecurityProcessingFailure);
|
|
if (requestMessage != null)
|
|
{
|
|
requestContext = new SecuritySessionRequestContext(innerRequestContext, requestMessage, correlationState, this);
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
this.receiveLock.Exit();
|
|
}
|
|
ThrowIfFaulted();
|
|
requestContext = null;
|
|
return true;
|
|
}
|
|
|
|
public Message Receive()
|
|
{
|
|
return this.Receive(this.DefaultReceiveTimeout);
|
|
}
|
|
|
|
public Message Receive(TimeSpan timeout)
|
|
{
|
|
Message message;
|
|
if (this.TryReceive(timeout, out message))
|
|
{
|
|
return message;
|
|
}
|
|
else
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new TimeoutException());
|
|
}
|
|
}
|
|
|
|
public IAsyncResult BeginReceive(AsyncCallback callback, object state)
|
|
{
|
|
return this.BeginReceive(this.DefaultReceiveTimeout, callback, state);
|
|
}
|
|
|
|
public IAsyncResult BeginReceive(TimeSpan timeout, AsyncCallback callback, object state)
|
|
{
|
|
return this.BeginTryReceive(timeout, callback, state);
|
|
}
|
|
|
|
public Message EndReceive(IAsyncResult result)
|
|
{
|
|
Message message;
|
|
if (this.EndTryReceive(result, out message))
|
|
{
|
|
return message;
|
|
}
|
|
else
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new TimeoutException());
|
|
}
|
|
}
|
|
|
|
public IAsyncResult BeginTryReceive(TimeSpan timeout, AsyncCallback callback, object state)
|
|
{
|
|
return new ReceiveRequestAsyncResult(this, timeout, callback, state);
|
|
}
|
|
|
|
public bool EndTryReceive(IAsyncResult result, out Message message)
|
|
{
|
|
return ReceiveRequestAsyncResult.EndAsMessage(result, out message);
|
|
}
|
|
|
|
public bool TryReceive(TimeSpan timeout, out Message message)
|
|
{
|
|
TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
|
|
RequestContext requestContext;
|
|
|
|
if (this.TryReceiveRequest(timeoutHelper.RemainingTime(), out requestContext))
|
|
{
|
|
if (requestContext != null)
|
|
{
|
|
message = requestContext.RequestMessage;
|
|
try
|
|
{
|
|
requestContext.Close(timeoutHelper.RemainingTime());
|
|
}
|
|
catch (TimeoutException e)
|
|
{
|
|
DiagnosticUtility.TraceHandledException(e, System.Diagnostics.TraceEventType.Information);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
message = null;
|
|
}
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
message = null;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public override T GetProperty<T>()
|
|
{
|
|
if (typeof(T) == typeof(FaultConverter) && (this.channelBinder != null))
|
|
{
|
|
return new SecurityChannelFaultConverter(this.channelBinder.Channel) as T;
|
|
}
|
|
|
|
T result = base.GetProperty<T>();
|
|
if ((result == null) && (channelBinder != null) && (channelBinder.Channel != null))
|
|
{
|
|
result = channelBinder.Channel.GetProperty<T>();
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void SendFaultIfRequired(Exception e, Message unverifiedMessage, RequestContext requestContext, TimeSpan timeout)
|
|
{
|
|
try
|
|
{
|
|
// return if the underlying channel does not implement IDuplexSession or IReply
|
|
if (!(this.channelBinder.Channel is IReplyChannel) && !(this.channelBinder.Channel is IDuplexSessionChannel))
|
|
{
|
|
return;
|
|
}
|
|
|
|
MessageFault fault = SecurityUtils.CreateSecurityMessageFault(e, this.securityProtocol.SecurityProtocolFactory.StandardsManager);
|
|
if (fault == null)
|
|
{
|
|
return;
|
|
}
|
|
TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
|
|
try
|
|
{
|
|
using (Message faultMessage = Message.CreateMessage(unverifiedMessage.Version, fault, unverifiedMessage.Version.Addressing.DefaultFaultAction))
|
|
{
|
|
if (unverifiedMessage.Headers.MessageId != null)
|
|
faultMessage.InitializeReply(unverifiedMessage);
|
|
requestContext.Reply(faultMessage, timeoutHelper.RemainingTime());
|
|
requestContext.Close(timeoutHelper.RemainingTime());
|
|
}
|
|
}
|
|
catch (CommunicationException ex)
|
|
{
|
|
DiagnosticUtility.TraceHandledException(ex, TraceEventType.Information);
|
|
}
|
|
catch (TimeoutException ex)
|
|
{
|
|
if (TD.CloseTimeoutIsEnabled())
|
|
{
|
|
TD.CloseTimeout(e.Message);
|
|
}
|
|
DiagnosticUtility.TraceHandledException(ex, TraceEventType.Information);
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
unverifiedMessage.Close();
|
|
requestContext.Abort();
|
|
}
|
|
}
|
|
|
|
bool ShouldWrapException(Exception e)
|
|
{
|
|
return ((e is FormatException) || (e is XmlException));
|
|
}
|
|
|
|
Message ProcessRequestContext(RequestContext requestContext, TimeSpan timeout, out SecurityProtocolCorrelationState correlationState, out bool isSecurityProcessingFailure)
|
|
{
|
|
correlationState = null;
|
|
isSecurityProcessingFailure = false;
|
|
if (requestContext == null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
Message result = null;
|
|
Message message = requestContext.RequestMessage;
|
|
bool cleanupContextState = true;
|
|
try
|
|
{
|
|
TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
|
|
Message unverifiedMessage = message;
|
|
Exception securityException = null;
|
|
try
|
|
{
|
|
correlationState = VerifyIncomingMessage(ref message, timeoutHelper.RemainingTime());
|
|
}
|
|
catch (MessageSecurityException e)
|
|
{
|
|
isSecurityProcessingFailure = true;
|
|
securityException = e;
|
|
}
|
|
if (securityException != null)
|
|
{
|
|
// SendFaultIfRequired closes the unverified message and context
|
|
SendFaultIfRequired(securityException, unverifiedMessage, requestContext, timeoutHelper.RemainingTime());
|
|
cleanupContextState = false;
|
|
return null;
|
|
}
|
|
else if (CheckIncomingToken(requestContext, message, correlationState, timeoutHelper.RemainingTime()))
|
|
{
|
|
if (message.Headers.Action == this.Settings.SecurityStandardsManager.SecureConversationDriver.CloseAction.Value)
|
|
{
|
|
SecurityTraceRecordHelper.TraceServerSessionCloseReceived(this.currentSessionToken, GetLocalUri());
|
|
this.isInputClosed = true;
|
|
// OnCloseMessageReceived is responsible for closing the message and requestContext if required.
|
|
this.OnCloseMessageReceived(requestContext, message, correlationState, timeoutHelper.RemainingTime());
|
|
correlationState = null;
|
|
}
|
|
else if (message.Headers.Action == this.Settings.SecurityStandardsManager.SecureConversationDriver.CloseResponseAction.Value)
|
|
{
|
|
SecurityTraceRecordHelper.TraceServerSessionCloseResponseReceived(this.currentSessionToken, GetLocalUri());
|
|
this.isInputClosed = true;
|
|
// OnCloseResponseMessageReceived is responsible for closing the message and requestContext if required.
|
|
this.OnCloseResponseMessageReceived(requestContext, message, correlationState, timeoutHelper.RemainingTime());
|
|
correlationState = null;
|
|
}
|
|
else
|
|
{
|
|
result = message;
|
|
}
|
|
cleanupContextState = false;
|
|
}
|
|
}
|
|
#pragma warning suppress 56500 // covered by FxCOP
|
|
catch (Exception e)
|
|
{
|
|
if ((e is CommunicationException) || (e is TimeoutException) || (Fx.IsFatal(e)) || !ShouldWrapException(e))
|
|
{
|
|
throw;
|
|
}
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new MessageSecurityException(SR.GetString(SR.MessageSecurityVerificationFailed), e));
|
|
}
|
|
finally
|
|
{
|
|
if (cleanupContextState)
|
|
{
|
|
if (requestContext.RequestMessage != null)
|
|
{
|
|
requestContext.RequestMessage.Close();
|
|
}
|
|
requestContext.Abort();
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
internal void CheckOutgoingToken()
|
|
{
|
|
lock (ThisLock)
|
|
{
|
|
if (this.currentSessionToken.KeyExpirationTime < DateTime.UtcNow)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SessionKeyExpiredException(SR.GetString(SR.SecuritySessionKeyIsStale)));
|
|
}
|
|
}
|
|
}
|
|
|
|
internal void SecureApplicationMessage(ref Message message, TimeSpan timeout, SecurityProtocolCorrelationState correlationState)
|
|
{
|
|
ThrowIfFaulted();
|
|
ThrowIfClosedOrNotOpen();
|
|
CheckOutgoingToken();
|
|
this.securityProtocol.SecureOutgoingMessage(ref message, timeout, correlationState);
|
|
}
|
|
|
|
internal SecurityProtocolCorrelationState VerifyIncomingMessage(ref Message message, TimeSpan timeout)
|
|
{
|
|
return this.securityProtocol.VerifyIncomingMessage(ref message, timeout, null);
|
|
}
|
|
|
|
void PrepareReply(Message request, Message reply)
|
|
{
|
|
if (request.Headers.ReplyTo != null)
|
|
{
|
|
request.Headers.ReplyTo.ApplyTo(reply);
|
|
}
|
|
else if (request.Headers.From != null)
|
|
{
|
|
request.Headers.From.ApplyTo(reply);
|
|
}
|
|
if (request.Headers.MessageId != null)
|
|
{
|
|
reply.Headers.RelatesTo = request.Headers.MessageId;
|
|
}
|
|
TraceUtility.CopyActivity(request, reply);
|
|
if (TraceUtility.PropagateUserActivity || TraceUtility.ShouldPropagateActivity)
|
|
{
|
|
TraceUtility.AddActivityHeader(reply);
|
|
}
|
|
}
|
|
|
|
protected void InitializeFaultCodesIfRequired()
|
|
{
|
|
if (!areFaultCodesInitialized)
|
|
{
|
|
lock (ThisLock)
|
|
{
|
|
if (!areFaultCodesInitialized)
|
|
{
|
|
SecurityStandardsManager standardsManager = this.securityProtocol.SecurityProtocolFactory.StandardsManager;
|
|
SecureConversationDriver scDriver = standardsManager.SecureConversationDriver;
|
|
renewFaultCode = FaultCode.CreateSenderFaultCode(scDriver.RenewNeededFaultCode.Value, scDriver.Namespace.Value);
|
|
renewFaultReason = new FaultReason(SR.GetString(SR.SecurityRenewFaultReason), System.Globalization.CultureInfo.InvariantCulture);
|
|
sessionAbortedFaultCode = FaultCode.CreateSenderFaultCode(DotNetSecurityStrings.SecuritySessionAbortedFault, DotNetSecurityStrings.Namespace);
|
|
sessionAbortedFaultReason = new FaultReason(SR.GetString(SR.SecuritySessionAbortedFaultReason), System.Globalization.CultureInfo.InvariantCulture);
|
|
areFaultCodesInitialized = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void SendRenewFault(RequestContext requestContext, SecurityProtocolCorrelationState correlationState, TimeSpan timeout)
|
|
{
|
|
Message message = requestContext.RequestMessage;
|
|
try
|
|
{
|
|
InitializeFaultCodesIfRequired();
|
|
MessageFault renewFault = MessageFault.CreateFault(renewFaultCode, renewFaultReason);
|
|
Message response;
|
|
if (message.Headers.MessageId != null)
|
|
{
|
|
response = Message.CreateMessage(message.Version, renewFault, DotNetSecurityStrings.SecuritySessionFaultAction);
|
|
response.InitializeReply(message);
|
|
}
|
|
else
|
|
{
|
|
response = Message.CreateMessage(message.Version, renewFault, DotNetSecurityStrings.SecuritySessionFaultAction);
|
|
}
|
|
try
|
|
{
|
|
PrepareReply(message, response);
|
|
TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
|
|
this.securityProtocol.SecureOutgoingMessage(ref response, timeoutHelper.RemainingTime(), correlationState);
|
|
response.Properties.AllowOutputBatching = false;
|
|
SendMessage(requestContext, response, timeoutHelper.RemainingTime());
|
|
}
|
|
finally
|
|
{
|
|
response.Close();
|
|
}
|
|
SecurityTraceRecordHelper.TraceSessionRenewalFaultSent(this.currentSessionToken, GetLocalUri(), message);
|
|
}
|
|
catch (CommunicationException e)
|
|
{
|
|
SecurityTraceRecordHelper.TraceRenewFaultSendFailure(this.currentSessionToken, GetLocalUri(), e);
|
|
}
|
|
catch (TimeoutException e)
|
|
{
|
|
SecurityTraceRecordHelper.TraceRenewFaultSendFailure(this.currentSessionToken, GetLocalUri(), e);
|
|
}
|
|
}
|
|
|
|
Message ProcessCloseRequest(Message request)
|
|
{
|
|
RequestSecurityToken rst;
|
|
XmlDictionaryReader bodyReader = request.GetReaderAtBodyContents();
|
|
using (bodyReader)
|
|
{
|
|
rst = this.Settings.SecurityStandardsManager.TrustDriver.CreateRequestSecurityToken(bodyReader);
|
|
request.ReadFromBodyContentsToEnd(bodyReader);
|
|
}
|
|
if (rst.RequestType != null && rst.RequestType != this.Settings.SecurityStandardsManager.TrustDriver.RequestTypeClose)
|
|
{
|
|
throw TraceUtility.ThrowHelperWarning(new MessageSecurityException(SR.GetString(SR.InvalidRstRequestType, rst.RequestType)), request);
|
|
}
|
|
if (rst.CloseTarget == null)
|
|
{
|
|
throw TraceUtility.ThrowHelperWarning(new MessageSecurityException(SR.GetString(SR.NoCloseTargetSpecified)), request);
|
|
}
|
|
SecurityContextKeyIdentifierClause sctSkiClause = rst.CloseTarget as SecurityContextKeyIdentifierClause;
|
|
if (sctSkiClause == null || !SecuritySessionSecurityTokenAuthenticator.DoesSkiClauseMatchSigningToken(sctSkiClause, request))
|
|
{
|
|
throw TraceUtility.ThrowHelperWarning(new MessageSecurityException(SR.GetString(SR.BadCloseTarget, rst.CloseTarget)), request);
|
|
}
|
|
RequestSecurityTokenResponse rstr = new RequestSecurityTokenResponse(this.Settings.SecurityStandardsManager);
|
|
rstr.Context = rst.Context;
|
|
rstr.IsRequestedTokenClosed = true;
|
|
rstr.MakeReadOnly();
|
|
BodyWriter bodyWriter = rstr;
|
|
if (this.Settings.SecurityStandardsManager.MessageSecurityVersion.TrustVersion == TrustVersion.WSTrust13)
|
|
{
|
|
List<RequestSecurityTokenResponse> rstrList = new List<RequestSecurityTokenResponse>(1);
|
|
rstrList.Add(rstr);
|
|
RequestSecurityTokenResponseCollection rstrc = new RequestSecurityTokenResponseCollection(rstrList, this.Settings.SecurityStandardsManager);
|
|
bodyWriter = rstrc;
|
|
}
|
|
Message response = Message.CreateMessage(request.Version, ActionHeader.Create(this.Settings.SecurityStandardsManager.SecureConversationDriver.CloseResponseAction, request.Version.Addressing), bodyWriter);
|
|
PrepareReply(request, response);
|
|
return response;
|
|
}
|
|
|
|
internal Message CreateCloseResponse(Message message, SecurityProtocolCorrelationState correlationState, TimeSpan timeout)
|
|
{
|
|
using (message)
|
|
{
|
|
Message response = this.ProcessCloseRequest(message);
|
|
this.securityProtocol.SecureOutgoingMessage(ref response, timeout, correlationState);
|
|
response.Properties.AllowOutputBatching = false;
|
|
return response;
|
|
}
|
|
}
|
|
|
|
internal void TraceSessionClosedResponseSuccess()
|
|
{
|
|
SecurityTraceRecordHelper.TraceSessionClosedResponseSent(this.currentSessionToken, GetLocalUri());
|
|
}
|
|
|
|
internal void TraceSessionClosedResponseFailure(Exception e)
|
|
{
|
|
SecurityTraceRecordHelper.TraceSessionClosedResponseSendFailure(this.currentSessionToken, GetLocalUri(), e);
|
|
}
|
|
|
|
internal void TraceSessionClosedSuccess()
|
|
{
|
|
SecurityTraceRecordHelper.TraceSessionClosedSent(this.currentSessionToken, GetLocalUri());
|
|
}
|
|
|
|
internal void TraceSessionClosedFailure(Exception e)
|
|
{
|
|
SecurityTraceRecordHelper.TraceSessionCloseSendFailure(this.currentSessionToken, GetLocalUri(), e);
|
|
}
|
|
|
|
// SendCloseResponse closes the message and underlying context if the operation completes successfully
|
|
protected void SendCloseResponse(RequestContext requestContext, Message closeResponse, TimeSpan timeout)
|
|
{
|
|
try
|
|
{
|
|
TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
|
|
using (closeResponse)
|
|
{
|
|
SendMessage(requestContext, closeResponse, timeoutHelper.RemainingTime());
|
|
}
|
|
TraceSessionClosedResponseSuccess();
|
|
}
|
|
catch (CommunicationException e)
|
|
{
|
|
TraceSessionClosedResponseFailure(e);
|
|
}
|
|
catch (TimeoutException e)
|
|
{
|
|
TraceSessionClosedResponseFailure(e);
|
|
}
|
|
}
|
|
|
|
// BeginSendCloseResponse closes the message and underlying context if the operation completes successfully
|
|
internal IAsyncResult BeginSendCloseResponse(RequestContext requestContext, Message closeResponse, TimeSpan timeout, AsyncCallback callback, object state)
|
|
{
|
|
try
|
|
{
|
|
TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
|
|
return this.BeginSendMessage(requestContext, closeResponse, timeoutHelper.RemainingTime(), callback, state);
|
|
}
|
|
catch (CommunicationException e)
|
|
{
|
|
TraceSessionClosedResponseFailure(e);
|
|
}
|
|
catch (TimeoutException e)
|
|
{
|
|
TraceSessionClosedResponseFailure(e);
|
|
}
|
|
return new CompletedAsyncResult(callback, state);
|
|
}
|
|
|
|
// EndSendCloseResponse closes the message and underlying context if the operation completes successfully
|
|
internal void EndSendCloseResponse(IAsyncResult result)
|
|
{
|
|
if (result is CompletedAsyncResult)
|
|
{
|
|
CompletedAsyncResult.End(result);
|
|
return;
|
|
}
|
|
try
|
|
{
|
|
this.EndSendMessage(result);
|
|
}
|
|
catch (CommunicationException e)
|
|
{
|
|
TraceSessionClosedResponseFailure(e);
|
|
}
|
|
catch (TimeoutException e)
|
|
{
|
|
TraceSessionClosedResponseFailure(e);
|
|
}
|
|
}
|
|
|
|
internal Message CreateCloseMessage(TimeSpan timeout)
|
|
{
|
|
RequestSecurityToken rst = new RequestSecurityToken(this.Settings.SecurityStandardsManager);
|
|
rst.RequestType = this.Settings.SecurityStandardsManager.TrustDriver.RequestTypeClose;
|
|
rst.CloseTarget = this.Settings.IssuedSecurityTokenParameters.CreateKeyIdentifierClause(this.currentSessionToken, SecurityTokenReferenceStyle.External);
|
|
rst.MakeReadOnly();
|
|
Message closeMessage = Message.CreateMessage(this.messageVersion, ActionHeader.Create(this.Settings.SecurityStandardsManager.SecureConversationDriver.CloseAction, this.messageVersion.Addressing), rst);
|
|
RequestReplyCorrelator.PrepareRequest(closeMessage);
|
|
if (this.LocalAddress != null)
|
|
{
|
|
closeMessage.Headers.ReplyTo = this.LocalAddress;
|
|
}
|
|
else
|
|
{
|
|
if (closeMessage.Version.Addressing == AddressingVersion.WSAddressing10)
|
|
{
|
|
closeMessage.Headers.ReplyTo = null;
|
|
}
|
|
else if (closeMessage.Version.Addressing == AddressingVersion.WSAddressingAugust2004)
|
|
{
|
|
closeMessage.Headers.ReplyTo = EndpointAddress.AnonymousAddress;
|
|
}
|
|
else
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
|
|
new ProtocolException(SR.GetString(SR.AddressingVersionNotSupported, closeMessage.Version.Addressing)));
|
|
}
|
|
}
|
|
this.securityProtocol.SecureOutgoingMessage(ref closeMessage, timeout, null);
|
|
closeMessage.Properties.AllowOutputBatching = false;
|
|
return closeMessage;
|
|
}
|
|
|
|
protected void SendClose(TimeSpan timeout)
|
|
{
|
|
try
|
|
{
|
|
TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
|
|
using (Message closeMessage = CreateCloseMessage(timeoutHelper.RemainingTime()))
|
|
{
|
|
SendMessage(null, closeMessage, timeoutHelper.RemainingTime());
|
|
}
|
|
TraceSessionClosedSuccess();
|
|
}
|
|
catch (CommunicationException e)
|
|
{
|
|
TraceSessionClosedFailure(e);
|
|
}
|
|
catch (TimeoutException e)
|
|
{
|
|
TraceSessionClosedFailure(e);
|
|
}
|
|
}
|
|
|
|
internal IAsyncResult BeginSendClose(TimeSpan timeout, AsyncCallback callback, object state)
|
|
{
|
|
try
|
|
{
|
|
TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
|
|
Message closeMessage = CreateCloseMessage(timeoutHelper.RemainingTime());
|
|
return this.BeginSendMessage(null, closeMessage, timeoutHelper.RemainingTime(), callback, state);
|
|
}
|
|
catch (CommunicationException e)
|
|
{
|
|
TraceSessionClosedFailure(e);
|
|
}
|
|
catch (TimeoutException e)
|
|
{
|
|
TraceSessionClosedFailure(e);
|
|
}
|
|
return new CompletedAsyncResult(callback, state);
|
|
}
|
|
|
|
internal void EndSendClose(IAsyncResult result)
|
|
{
|
|
if (result is CompletedAsyncResult)
|
|
{
|
|
CompletedAsyncResult.End(result);
|
|
return;
|
|
}
|
|
try
|
|
{
|
|
this.EndSendMessage(result);
|
|
}
|
|
catch (CommunicationException e)
|
|
{
|
|
TraceSessionClosedFailure(e);
|
|
}
|
|
catch (TimeoutException e)
|
|
{
|
|
TraceSessionClosedFailure(e);
|
|
}
|
|
}
|
|
|
|
protected void SendMessage(RequestContext requestContext, Message message, TimeSpan timeout)
|
|
{
|
|
if (this.channelBinder.CanSendAsynchronously)
|
|
{
|
|
this.channelBinder.Send(message, timeout);
|
|
}
|
|
else if (requestContext != null)
|
|
{
|
|
TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
|
|
requestContext.Reply(message, timeoutHelper.RemainingTime());
|
|
requestContext.Close(timeoutHelper.RemainingTime());
|
|
}
|
|
}
|
|
|
|
internal IAsyncResult BeginSendMessage(RequestContext requestContext, Message response, TimeSpan timeout, AsyncCallback callback, object state)
|
|
{
|
|
return new SendMessageAsyncResult(this, requestContext, response, timeout, callback, state);
|
|
}
|
|
|
|
internal void EndSendMessage(IAsyncResult result)
|
|
{
|
|
SendMessageAsyncResult.End(result);
|
|
}
|
|
|
|
class SendMessageAsyncResult : AsyncResult
|
|
{
|
|
static AsyncCallback sendCallback = Fx.ThunkCallback(new AsyncCallback(SendCallback));
|
|
ServerSecuritySessionChannel sessionChannel;
|
|
TimeoutHelper timeoutHelper;
|
|
RequestContext requestContext;
|
|
Message message;
|
|
|
|
public SendMessageAsyncResult(ServerSecuritySessionChannel sessionChannel, RequestContext requestContext, Message message, TimeSpan timeout, AsyncCallback callback,
|
|
object state)
|
|
: base(callback, state)
|
|
{
|
|
this.sessionChannel = sessionChannel;
|
|
this.timeoutHelper = new TimeoutHelper(timeout);
|
|
this.requestContext = requestContext;
|
|
this.message = message;
|
|
|
|
bool closeMessage = true;
|
|
try
|
|
{
|
|
IAsyncResult result = this.BeginSend(message);
|
|
if (!result.CompletedSynchronously)
|
|
{
|
|
closeMessage = false;
|
|
return;
|
|
}
|
|
this.EndSend(result);
|
|
closeMessage = false;
|
|
}
|
|
finally
|
|
{
|
|
if (closeMessage)
|
|
{
|
|
this.message.Close();
|
|
}
|
|
}
|
|
Complete(true);
|
|
}
|
|
|
|
IAsyncResult BeginSend(Message response)
|
|
{
|
|
if (this.sessionChannel.channelBinder.CanSendAsynchronously)
|
|
{
|
|
return this.sessionChannel.channelBinder.BeginSend(response, timeoutHelper.RemainingTime(), sendCallback, this);
|
|
}
|
|
else if (requestContext != null)
|
|
{
|
|
return requestContext.BeginReply(response, sendCallback, this);
|
|
}
|
|
else
|
|
{
|
|
return new SendCompletedAsyncResult(sendCallback, this);
|
|
}
|
|
}
|
|
|
|
void EndSend(IAsyncResult result)
|
|
{
|
|
try
|
|
{
|
|
if (result is SendCompletedAsyncResult)
|
|
{
|
|
SendCompletedAsyncResult.End(result);
|
|
}
|
|
else if (this.sessionChannel.channelBinder.CanSendAsynchronously)
|
|
{
|
|
this.sessionChannel.channelBinder.EndSend(result);
|
|
}
|
|
else
|
|
{
|
|
this.requestContext.EndReply(result);
|
|
this.requestContext.Close(timeoutHelper.RemainingTime());
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
if (this.message != null)
|
|
{
|
|
this.message.Close();
|
|
}
|
|
}
|
|
}
|
|
|
|
static void SendCallback(IAsyncResult result)
|
|
{
|
|
if (result.CompletedSynchronously)
|
|
{
|
|
return;
|
|
}
|
|
SendMessageAsyncResult self = (SendMessageAsyncResult)(result.AsyncState);
|
|
Exception completionException = null;
|
|
try
|
|
{
|
|
self.EndSend(result);
|
|
}
|
|
#pragma warning suppress 56500 // covered by FxCOP
|
|
catch (Exception e)
|
|
{
|
|
if (Fx.IsFatal(e))
|
|
{
|
|
throw;
|
|
}
|
|
completionException = e;
|
|
}
|
|
self.Complete(false, completionException);
|
|
}
|
|
|
|
public static void End(IAsyncResult result)
|
|
{
|
|
AsyncResult.End<SendMessageAsyncResult>(result);
|
|
}
|
|
|
|
class SendCompletedAsyncResult : CompletedAsyncResult
|
|
{
|
|
public SendCompletedAsyncResult(AsyncCallback callback, object state)
|
|
: base(callback, state)
|
|
{
|
|
}
|
|
|
|
new public static void End(IAsyncResult result)
|
|
{
|
|
AsyncResult.End<SendCompletedAsyncResult>(result);
|
|
}
|
|
}
|
|
}
|
|
|
|
class CloseCoreAsyncResult : AsyncResult
|
|
{
|
|
static AsyncCallback channelBinderCloseCallback = Fx.ThunkCallback(new AsyncCallback(ChannelBinderCloseCallback));
|
|
static AsyncCallback settingsLifetimeManagerCloseCallback = Fx.ThunkCallback(new AsyncCallback(SettingsLifetimeManagerCloseCallback));
|
|
|
|
ServerSecuritySessionChannel channel;
|
|
TimeoutHelper timeoutHelper;
|
|
|
|
public CloseCoreAsyncResult(ServerSecuritySessionChannel channel, TimeSpan timeout, AsyncCallback callback, object state)
|
|
: base(callback, state)
|
|
{
|
|
this.channel = channel;
|
|
this.timeoutHelper = new TimeoutHelper(timeout);
|
|
bool completeSelf = false;
|
|
if (this.channel.channelBinder != null)
|
|
{
|
|
try
|
|
{
|
|
IAsyncResult result = this.channel.channelBinder.BeginClose(timeoutHelper.RemainingTime(), channelBinderCloseCallback, this);
|
|
if (!result.CompletedSynchronously)
|
|
{
|
|
return;
|
|
}
|
|
this.channel.channelBinder.EndClose(result);
|
|
}
|
|
catch (CommunicationObjectAbortedException)
|
|
{
|
|
if (this.channel.State != CommunicationState.Closed)
|
|
{
|
|
throw;
|
|
}
|
|
else
|
|
{
|
|
completeSelf = true;
|
|
}
|
|
}
|
|
}
|
|
if (!completeSelf)
|
|
{
|
|
completeSelf = this.OnChannelBinderClosed();
|
|
}
|
|
if (completeSelf)
|
|
{
|
|
RemoveSessionTokenFromCache();
|
|
Complete(true);
|
|
}
|
|
}
|
|
|
|
void RemoveSessionTokenFromCache()
|
|
{
|
|
this.channel.Settings.SessionTokenCache.RemoveAllContexts(this.channel.currentSessionToken.ContextId);
|
|
}
|
|
|
|
static void ChannelBinderCloseCallback(IAsyncResult result)
|
|
{
|
|
if (result.CompletedSynchronously)
|
|
{
|
|
return;
|
|
}
|
|
CloseCoreAsyncResult self = (CloseCoreAsyncResult)(result.AsyncState);
|
|
bool completeSelf = false;
|
|
Exception completionException = null;
|
|
try
|
|
{
|
|
try
|
|
{
|
|
self.channel.channelBinder.EndClose(result);
|
|
}
|
|
catch (CommunicationObjectAbortedException)
|
|
{
|
|
if (self.channel.State != CommunicationState.Closed)
|
|
{
|
|
throw;
|
|
}
|
|
completeSelf = true;
|
|
}
|
|
if (!completeSelf)
|
|
{
|
|
completeSelf = self.OnChannelBinderClosed();
|
|
}
|
|
if (completeSelf)
|
|
{
|
|
self.RemoveSessionTokenFromCache();
|
|
}
|
|
}
|
|
#pragma warning suppress 56500 // covered by FxCOP
|
|
catch (Exception e)
|
|
{
|
|
if (Fx.IsFatal(e))
|
|
{
|
|
throw;
|
|
}
|
|
completeSelf = true;
|
|
completionException = e;
|
|
}
|
|
if (completeSelf)
|
|
{
|
|
self.Complete(false, completionException);
|
|
}
|
|
}
|
|
|
|
bool OnChannelBinderClosed()
|
|
{
|
|
try
|
|
{
|
|
if (this.channel.securityProtocol != null)
|
|
{
|
|
this.channel.securityProtocol.Close(false, timeoutHelper.RemainingTime());
|
|
}
|
|
bool closeLifetimeManager = false;
|
|
lock (this.channel.ThisLock)
|
|
{
|
|
if (this.channel.hasSecurityStateReference)
|
|
{
|
|
closeLifetimeManager = true;
|
|
this.channel.hasSecurityStateReference = false;
|
|
}
|
|
}
|
|
if (!closeLifetimeManager)
|
|
{
|
|
return true;
|
|
}
|
|
IAsyncResult result = this.channel.settingsLifetimeManager.BeginClose(timeoutHelper.RemainingTime(), settingsLifetimeManagerCloseCallback, this);
|
|
if (!result.CompletedSynchronously)
|
|
{
|
|
return false;
|
|
}
|
|
this.channel.settingsLifetimeManager.EndClose(result);
|
|
return true;
|
|
}
|
|
catch (CommunicationObjectAbortedException)
|
|
{
|
|
if (channel.State != CommunicationState.Closed)
|
|
{
|
|
throw;
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
|
|
static void SettingsLifetimeManagerCloseCallback(IAsyncResult result)
|
|
{
|
|
if (result.CompletedSynchronously)
|
|
{
|
|
return;
|
|
}
|
|
CloseCoreAsyncResult self = (CloseCoreAsyncResult)(result.AsyncState);
|
|
bool removeSessionToken = false;
|
|
Exception completionException = null;
|
|
try
|
|
{
|
|
self.channel.settingsLifetimeManager.EndClose(result);
|
|
removeSessionToken = true;
|
|
}
|
|
catch (CommunicationObjectAbortedException)
|
|
{
|
|
if (self.channel.State != CommunicationState.Closed)
|
|
{
|
|
throw;
|
|
}
|
|
removeSessionToken = true;
|
|
}
|
|
#pragma warning suppress 56500 // covered by FxCOP
|
|
catch (Exception e)
|
|
{
|
|
if (Fx.IsFatal(e))
|
|
{
|
|
throw;
|
|
}
|
|
completionException = e;
|
|
}
|
|
finally
|
|
{
|
|
if (removeSessionToken)
|
|
{
|
|
self.RemoveSessionTokenFromCache();
|
|
}
|
|
}
|
|
self.Complete(false, completionException);
|
|
}
|
|
|
|
public static void End(IAsyncResult result)
|
|
{
|
|
AsyncResult.End<CloseCoreAsyncResult>(result);
|
|
}
|
|
}
|
|
|
|
protected class SoapSecurityInputSession : ISecureConversationSession, IInputSession
|
|
{
|
|
ServerSecuritySessionChannel channel;
|
|
UniqueId securityContextTokenId;
|
|
EndpointIdentity remoteIdentity;
|
|
SecurityKeyIdentifierClause sessionTokenIdentifier;
|
|
SecurityStandardsManager standardsManager;
|
|
|
|
public SoapSecurityInputSession(SecurityContextSecurityToken sessionToken,
|
|
SecuritySessionServerSettings settings, ServerSecuritySessionChannel channel)
|
|
{
|
|
this.channel = channel;
|
|
this.securityContextTokenId = sessionToken.ContextId;
|
|
Claim identityClaim = SecurityUtils.GetPrimaryIdentityClaim(sessionToken.AuthorizationPolicies);
|
|
if (identityClaim != null)
|
|
{
|
|
this.remoteIdentity = EndpointIdentity.CreateIdentity(identityClaim);
|
|
}
|
|
this.sessionTokenIdentifier = settings.IssuedSecurityTokenParameters.CreateKeyIdentifierClause(sessionToken, SecurityTokenReferenceStyle.External);
|
|
this.standardsManager = settings.SessionProtocolFactory.StandardsManager;
|
|
}
|
|
|
|
public string Id
|
|
{
|
|
get
|
|
{
|
|
return this.securityContextTokenId.ToString();
|
|
}
|
|
}
|
|
|
|
public EndpointIdentity RemoteIdentity
|
|
{
|
|
get
|
|
{
|
|
return this.remoteIdentity;
|
|
}
|
|
}
|
|
|
|
public void WriteSessionTokenIdentifier(XmlDictionaryWriter writer)
|
|
{
|
|
this.channel.ThrowIfDisposedOrNotOpen();
|
|
this.standardsManager.SecurityTokenSerializer.WriteKeyIdentifierClause(writer, this.sessionTokenIdentifier);
|
|
}
|
|
|
|
public bool TryReadSessionTokenIdentifier(XmlReader reader)
|
|
{
|
|
this.channel.ThrowIfDisposedOrNotOpen();
|
|
if (!this.standardsManager.SecurityTokenSerializer.CanReadKeyIdentifierClause(reader))
|
|
{
|
|
return false;
|
|
}
|
|
SecurityContextKeyIdentifierClause incomingTokenIdentifier = this.standardsManager.SecurityTokenSerializer.ReadKeyIdentifierClause(reader) as SecurityContextKeyIdentifierClause;
|
|
return incomingTokenIdentifier != null && incomingTokenIdentifier.Matches(this.securityContextTokenId, null);
|
|
}
|
|
}
|
|
|
|
class ReceiveRequestAsyncResult : AsyncResult
|
|
{
|
|
static FastAsyncCallback onWait = new FastAsyncCallback(OnWait);
|
|
static AsyncCallback onReceive = Fx.ThunkCallback(new AsyncCallback(OnReceive));
|
|
ServerSecuritySessionChannel channel;
|
|
RequestContext innerRequestContext;
|
|
Message requestMessage;
|
|
SecurityProtocolCorrelationState correlationState;
|
|
bool expired;
|
|
TimeoutHelper timeoutHelper;
|
|
|
|
public ReceiveRequestAsyncResult(ServerSecuritySessionChannel channel, TimeSpan timeout, AsyncCallback callback, object state)
|
|
: base(callback, state)
|
|
{
|
|
channel.ThrowIfFaulted();
|
|
this.timeoutHelper = new TimeoutHelper(timeout);
|
|
this.channel = channel;
|
|
if (!channel.receiveLock.EnterAsync(this.timeoutHelper.RemainingTime(), onWait, this))
|
|
{
|
|
return;
|
|
}
|
|
|
|
bool completeSelf = false;
|
|
bool throwing = true;
|
|
try
|
|
{
|
|
completeSelf = WaitComplete();
|
|
throwing = false;
|
|
}
|
|
finally
|
|
{
|
|
if (throwing)
|
|
{
|
|
this.channel.receiveLock.Exit();
|
|
}
|
|
}
|
|
|
|
if (completeSelf)
|
|
{
|
|
Complete(true);
|
|
}
|
|
}
|
|
|
|
static void OnWait(object state, Exception asyncException)
|
|
{
|
|
ReceiveRequestAsyncResult self = (ReceiveRequestAsyncResult)state;
|
|
bool completeSelf = false;
|
|
Exception completionException = asyncException;
|
|
if (completionException != null)
|
|
{
|
|
completeSelf = true;
|
|
}
|
|
else
|
|
{
|
|
try
|
|
{
|
|
completeSelf = self.WaitComplete();
|
|
}
|
|
#pragma warning suppress 56500 // covered by FxCOP
|
|
catch (Exception e)
|
|
{
|
|
if (Fx.IsFatal(e))
|
|
{
|
|
throw;
|
|
}
|
|
|
|
completeSelf = true;
|
|
completionException = e;
|
|
}
|
|
}
|
|
|
|
if (completeSelf)
|
|
{
|
|
self.Complete(false, completionException);
|
|
}
|
|
}
|
|
|
|
bool WaitComplete()
|
|
{
|
|
if (channel.isInputClosed)
|
|
{
|
|
return true;
|
|
}
|
|
channel.ThrowIfFaulted();
|
|
|
|
ServiceModelActivity activity = DiagnosticUtility.ShouldUseActivity &&
|
|
channel.initialRequestContext != null ?
|
|
TraceUtility.ExtractActivity(channel.initialRequestContext.RequestMessage) : null;
|
|
|
|
using (ServiceModelActivity.BoundOperation(activity))
|
|
{
|
|
if (channel.initialRequestContext != null)
|
|
{
|
|
innerRequestContext = channel.initialRequestContext;
|
|
channel.initialRequestContext = null;
|
|
bool isSecurityProcessingFailure;
|
|
requestMessage = channel.ProcessRequestContext(innerRequestContext, timeoutHelper.RemainingTime(), out this.correlationState, out isSecurityProcessingFailure);
|
|
if (requestMessage != null || channel.isInputClosed)
|
|
{
|
|
this.expired = false;
|
|
return true;
|
|
}
|
|
}
|
|
if (this.timeoutHelper.RemainingTime() == TimeSpan.Zero)
|
|
{
|
|
this.expired = true;
|
|
return true;
|
|
}
|
|
IAsyncResult result = channel.ChannelBinder.BeginTryReceive(this.timeoutHelper.RemainingTime(), onReceive, this);
|
|
if (!result.CompletedSynchronously)
|
|
return false;
|
|
|
|
return CompleteReceive(result);
|
|
}
|
|
}
|
|
|
|
bool CompleteReceive(IAsyncResult result)
|
|
{
|
|
while (true)
|
|
{
|
|
this.expired = !channel.ChannelBinder.EndTryReceive(result, out this.innerRequestContext);
|
|
if (this.expired || innerRequestContext == null)
|
|
break;
|
|
|
|
bool isSecurityProcessingFailure;
|
|
requestMessage = channel.ProcessRequestContext(innerRequestContext, timeoutHelper.RemainingTime(), out this.correlationState, out isSecurityProcessingFailure);
|
|
if (requestMessage != null)
|
|
{
|
|
if (channel.isInputClosed)
|
|
{
|
|
ProtocolException error = ProtocolException.ReceiveShutdownReturnedNonNull(requestMessage);
|
|
try
|
|
{
|
|
throw TraceUtility.ThrowHelperWarning(error, requestMessage);
|
|
}
|
|
finally
|
|
{
|
|
requestMessage.Close();
|
|
innerRequestContext.Abort();
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (channel.isInputClosed || channel.State == CommunicationState.Faulted)
|
|
break;
|
|
|
|
// retry the receive unless the timeout is reached
|
|
if (this.timeoutHelper.RemainingTime() == TimeSpan.Zero)
|
|
{
|
|
this.expired = true;
|
|
break;
|
|
}
|
|
|
|
result = channel.ChannelBinder.BeginTryReceive(this.timeoutHelper.RemainingTime(), onReceive, this);
|
|
|
|
if (!result.CompletedSynchronously)
|
|
return false;
|
|
}
|
|
this.channel.ThrowIfFaulted();
|
|
return true;
|
|
}
|
|
|
|
new void Complete(bool synchronous)
|
|
{
|
|
try
|
|
{
|
|
this.channel.receiveLock.Exit();
|
|
}
|
|
#pragma warning suppress 56500 // covered by FxCOP
|
|
catch (Exception e)
|
|
{
|
|
if (Fx.IsFatal(e))
|
|
{
|
|
throw;
|
|
}
|
|
|
|
if (DiagnosticUtility.ShouldTraceWarning)
|
|
{
|
|
TraceUtility.TraceEvent(TraceEventType.Warning, TraceCode.AsyncCallbackThrewException, SR.GetString(SR.TraceCodeAsyncCallbackThrewException), e.ToString());
|
|
}
|
|
|
|
}
|
|
|
|
base.Complete(synchronous);
|
|
}
|
|
|
|
new void Complete(bool synchronous, Exception exception)
|
|
{
|
|
try
|
|
{
|
|
this.channel.receiveLock.Exit();
|
|
}
|
|
#pragma warning suppress 56500 // covered by FxCOP
|
|
catch (Exception e)
|
|
{
|
|
if (Fx.IsFatal(e))
|
|
{
|
|
throw;
|
|
}
|
|
|
|
if (DiagnosticUtility.ShouldTraceWarning)
|
|
{
|
|
TraceUtility.TraceEvent(TraceEventType.Warning, TraceCode.AsyncCallbackThrewException, SR.GetString(SR.TraceCodeAsyncCallbackThrewException), e.ToString());
|
|
}
|
|
|
|
}
|
|
|
|
base.Complete(synchronous, exception);
|
|
}
|
|
|
|
static ReceiveRequestAsyncResult End(IAsyncResult result)
|
|
{
|
|
return AsyncResult.End<ReceiveRequestAsyncResult>(result);
|
|
}
|
|
|
|
public static bool EndAsMessage(IAsyncResult result, out Message message)
|
|
{
|
|
ReceiveRequestAsyncResult receiveResult = End(result);
|
|
message = receiveResult.requestMessage;
|
|
// if the message is not null, then dispose the inner request context
|
|
// if the message is null its either a close or protocol fault in which case the request context
|
|
// will be closed by the channel
|
|
if (message != null)
|
|
{
|
|
if (receiveResult.innerRequestContext != null)
|
|
{
|
|
try
|
|
{
|
|
receiveResult.innerRequestContext.Close(receiveResult.timeoutHelper.RemainingTime());
|
|
}
|
|
catch (TimeoutException e)
|
|
{
|
|
DiagnosticUtility.TraceHandledException(e, System.Diagnostics.TraceEventType.Information);
|
|
}
|
|
}
|
|
}
|
|
return !receiveResult.expired;
|
|
}
|
|
|
|
public static bool EndAsRequestContext(IAsyncResult result, out RequestContext requestContext)
|
|
{
|
|
ReceiveRequestAsyncResult receiveResult = End(result);
|
|
|
|
if (receiveResult.requestMessage == null)
|
|
{
|
|
requestContext = null;
|
|
}
|
|
else
|
|
{
|
|
requestContext = new SecuritySessionRequestContext(receiveResult.innerRequestContext, receiveResult.requestMessage, receiveResult.correlationState, receiveResult.channel);
|
|
}
|
|
|
|
return !receiveResult.expired;
|
|
}
|
|
|
|
static void OnReceive(IAsyncResult result)
|
|
{
|
|
if (result.CompletedSynchronously)
|
|
return;
|
|
|
|
ReceiveRequestAsyncResult self = (ReceiveRequestAsyncResult)result.AsyncState;
|
|
bool completeSelf = false;
|
|
Exception completionException = null;
|
|
try
|
|
{
|
|
completeSelf = self.CompleteReceive(result);
|
|
}
|
|
#pragma warning suppress 56500 // covered by FxCOP
|
|
catch (Exception e)
|
|
{
|
|
if (Fx.IsFatal(e))
|
|
{
|
|
throw;
|
|
}
|
|
|
|
completeSelf = true;
|
|
completionException = e;
|
|
}
|
|
if (completeSelf)
|
|
{
|
|
self.Complete(false, completionException);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
abstract class ServerSecuritySimplexSessionChannel : ServerSecuritySessionChannel
|
|
{
|
|
SoapSecurityInputSession session;
|
|
bool receivedClose;
|
|
bool canSendCloseResponse;
|
|
bool sentCloseResponse;
|
|
RequestContext closeRequestContext;
|
|
Message closeResponse;
|
|
InterruptibleWaitObject inputSessionClosedHandle = new InterruptibleWaitObject(false);
|
|
|
|
public ServerSecuritySimplexSessionChannel(
|
|
SecuritySessionServerSettings settings,
|
|
IServerReliableChannelBinder channelBinder,
|
|
SecurityContextSecurityToken sessionToken,
|
|
object listenerSecurityState, SecurityListenerSettingsLifetimeManager settingsLifetimeManager)
|
|
: base(settings, channelBinder, sessionToken, listenerSecurityState, settingsLifetimeManager)
|
|
{
|
|
this.session = new SoapSecurityInputSession(sessionToken, settings, this);
|
|
}
|
|
|
|
public IInputSession Session
|
|
{
|
|
get
|
|
{
|
|
return this.session;
|
|
}
|
|
}
|
|
|
|
void CleanupPendingCloseState()
|
|
{
|
|
lock (ThisLock)
|
|
{
|
|
if (this.closeResponse != null)
|
|
{
|
|
this.closeResponse.Close();
|
|
this.closeResponse = null;
|
|
}
|
|
if (this.closeRequestContext != null)
|
|
{
|
|
this.closeRequestContext.Abort();
|
|
this.closeRequestContext = null;
|
|
}
|
|
}
|
|
}
|
|
|
|
protected override void AbortCore()
|
|
{
|
|
base.AbortCore();
|
|
this.Settings.RemoveSessionChannel(this.session.Id);
|
|
CleanupPendingCloseState();
|
|
}
|
|
|
|
protected override void CloseCore(TimeSpan timeout)
|
|
{
|
|
base.CloseCore(timeout);
|
|
this.inputSessionClosedHandle.Abort(this);
|
|
this.Settings.RemoveSessionChannel(this.session.Id);
|
|
}
|
|
|
|
protected override void EndCloseCore(IAsyncResult result)
|
|
{
|
|
base.EndCloseCore(result);
|
|
this.inputSessionClosedHandle.Abort(this);
|
|
this.Settings.RemoveSessionChannel(this.session.Id);
|
|
}
|
|
|
|
protected override void OnAbort()
|
|
{
|
|
AbortCore();
|
|
this.inputSessionClosedHandle.Abort(this);
|
|
}
|
|
|
|
protected override void OnFaulted()
|
|
{
|
|
this.AbortCore();
|
|
this.inputSessionClosedHandle.Fault(this);
|
|
base.OnFaulted();
|
|
}
|
|
|
|
bool ShouldSendCloseResponseOnClose(out RequestContext pendingCloseRequestContext, out Message pendingCloseResponse)
|
|
{
|
|
bool sendCloseResponse = false;
|
|
lock (ThisLock)
|
|
{
|
|
this.canSendCloseResponse = true;
|
|
if (!this.sentCloseResponse && this.receivedClose && this.closeResponse != null)
|
|
{
|
|
this.sentCloseResponse = true;
|
|
sendCloseResponse = true;
|
|
pendingCloseRequestContext = this.closeRequestContext;
|
|
pendingCloseResponse = this.closeResponse;
|
|
this.closeResponse = null;
|
|
this.closeRequestContext = null;
|
|
}
|
|
else
|
|
{
|
|
canSendCloseResponse = false;
|
|
pendingCloseRequestContext = null;
|
|
pendingCloseResponse = null;
|
|
}
|
|
}
|
|
return sendCloseResponse;
|
|
}
|
|
|
|
bool SendCloseResponseOnCloseIfRequired(TimeSpan timeout)
|
|
{
|
|
bool aborted = false;
|
|
RequestContext pendingCloseRequestContext;
|
|
Message pendingCloseResponse;
|
|
bool sendCloseResponse = ShouldSendCloseResponseOnClose(out pendingCloseRequestContext, out pendingCloseResponse);
|
|
TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
|
|
bool cleanupCloseState = true;
|
|
if (sendCloseResponse)
|
|
{
|
|
try
|
|
{
|
|
this.SendCloseResponse(pendingCloseRequestContext, pendingCloseResponse, timeoutHelper.RemainingTime());
|
|
this.inputSessionClosedHandle.Set();
|
|
cleanupCloseState = false;
|
|
}
|
|
catch (CommunicationObjectAbortedException)
|
|
{
|
|
if (this.State != CommunicationState.Closed)
|
|
{
|
|
throw;
|
|
}
|
|
aborted = true;
|
|
}
|
|
finally
|
|
{
|
|
if (cleanupCloseState)
|
|
{
|
|
if (pendingCloseResponse != null)
|
|
{
|
|
pendingCloseResponse.Close();
|
|
}
|
|
if (pendingCloseRequestContext != null)
|
|
{
|
|
pendingCloseRequestContext.Abort();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return aborted;
|
|
}
|
|
|
|
protected override void OnClose(TimeSpan timeout)
|
|
{
|
|
TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
|
|
|
|
// send a close response if one was not sent yet
|
|
bool wasAborted = SendCloseResponseOnCloseIfRequired(timeoutHelper.RemainingTime());
|
|
if (wasAborted)
|
|
{
|
|
return;
|
|
}
|
|
|
|
bool wasInputSessionClosed = this.WaitForInputSessionClose(timeoutHelper.RemainingTime(), out wasAborted);
|
|
if (wasAborted)
|
|
{
|
|
return;
|
|
}
|
|
if (!wasInputSessionClosed)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new TimeoutException(SR.GetString(SR.ServiceSecurityCloseTimeout, timeoutHelper.OriginalTimeout)));
|
|
}
|
|
else
|
|
{
|
|
this.CloseCore(timeoutHelper.RemainingTime());
|
|
}
|
|
}
|
|
|
|
protected override IAsyncResult OnBeginClose(TimeSpan timeout, AsyncCallback callback, object state)
|
|
{
|
|
RequestContext pendingCloseRequestContext;
|
|
Message pendingCloseResponse;
|
|
bool sendCloseResponse = ShouldSendCloseResponseOnClose(out pendingCloseRequestContext, out pendingCloseResponse);
|
|
return new CloseAsyncResult(this, sendCloseResponse, pendingCloseRequestContext, pendingCloseResponse, timeout, callback, state);
|
|
}
|
|
|
|
protected override void OnEndClose(IAsyncResult result)
|
|
{
|
|
CloseAsyncResult.End(result);
|
|
}
|
|
|
|
bool WaitForInputSessionClose(TimeSpan timeout, out bool wasAborted)
|
|
{
|
|
Message message;
|
|
TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
|
|
wasAborted = false;
|
|
try
|
|
{
|
|
if (this.TryReceive(timeoutHelper.RemainingTime(), out message))
|
|
{
|
|
if (message != null)
|
|
{
|
|
using (message)
|
|
{
|
|
ProtocolException error = ProtocolException.ReceiveShutdownReturnedNonNull(message);
|
|
throw TraceUtility.ThrowHelperWarning(error, message);
|
|
}
|
|
}
|
|
return this.inputSessionClosedHandle.Wait(timeoutHelper.RemainingTime(), false);
|
|
}
|
|
}
|
|
catch (CommunicationObjectAbortedException)
|
|
{
|
|
if (this.State != CommunicationState.Closed)
|
|
{
|
|
throw;
|
|
}
|
|
wasAborted = true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
protected override void OnCloseResponseMessageReceived(RequestContext requestContext, Message message, SecurityProtocolCorrelationState correlationState, TimeSpan timeout)
|
|
{
|
|
// we dont expect a close-response for non-duplex security session
|
|
message.Close();
|
|
requestContext.Abort();
|
|
this.Fault(new ProtocolException(SR.GetString(SR.UnexpectedSecuritySessionCloseResponse)));
|
|
}
|
|
|
|
protected override void OnCloseMessageReceived(RequestContext requestContext, Message message, SecurityProtocolCorrelationState correlationState, TimeSpan timeout)
|
|
{
|
|
if (this.State == CommunicationState.Created)
|
|
{
|
|
Fx.Assert("ServerSecuritySimplexSessionChannel.OnCloseMessageReceived (this.State == Created)");
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ServerReceivedCloseMessageStateIsCreated, this.GetType().ToString())));
|
|
}
|
|
|
|
if (SendCloseResponseOnCloseReceivedIfRequired(requestContext, message, correlationState, timeout))
|
|
{
|
|
this.inputSessionClosedHandle.Set();
|
|
}
|
|
}
|
|
|
|
bool SendCloseResponseOnCloseReceivedIfRequired(RequestContext requestContext, Message message, SecurityProtocolCorrelationState correlationState, TimeSpan timeout)
|
|
{
|
|
bool sendCloseResponse = false;
|
|
ServiceModelActivity activity = DiagnosticUtility.ShouldUseActivity ? TraceUtility.ExtractActivity(message) : null;
|
|
bool cleanupContext = true;
|
|
try
|
|
{
|
|
TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
|
|
Message localCloseResponse = null;
|
|
lock (ThisLock)
|
|
{
|
|
if (!this.receivedClose)
|
|
{
|
|
this.receivedClose = true;
|
|
localCloseResponse = CreateCloseResponse(message, correlationState, timeoutHelper.RemainingTime());
|
|
if (canSendCloseResponse)
|
|
{
|
|
this.sentCloseResponse = true;
|
|
sendCloseResponse = true;
|
|
}
|
|
else
|
|
{
|
|
// save the close requestContext to reply later
|
|
this.closeRequestContext = requestContext;
|
|
this.closeResponse = localCloseResponse;
|
|
cleanupContext = false;
|
|
}
|
|
}
|
|
}
|
|
if (sendCloseResponse)
|
|
{
|
|
this.SendCloseResponse(requestContext, localCloseResponse, timeoutHelper.RemainingTime());
|
|
cleanupContext = false;
|
|
}
|
|
else if (cleanupContext)
|
|
{
|
|
requestContext.Close(timeoutHelper.RemainingTime());
|
|
cleanupContext = false;
|
|
}
|
|
return sendCloseResponse;
|
|
}
|
|
finally
|
|
{
|
|
message.Close();
|
|
if (cleanupContext)
|
|
{
|
|
requestContext.Abort();
|
|
}
|
|
if (DiagnosticUtility.ShouldUseActivity && (activity != null))
|
|
{
|
|
activity.Stop();
|
|
}
|
|
}
|
|
}
|
|
|
|
class CloseAsyncResult : AsyncResult
|
|
{
|
|
static readonly AsyncCallback sendCloseResponseCallback = Fx.ThunkCallback(new AsyncCallback(SendCloseResponseCallback));
|
|
static readonly AsyncCallback receiveCallback = Fx.ThunkCallback(new AsyncCallback(ReceiveCallback));
|
|
static readonly AsyncCallback waitCallback = Fx.ThunkCallback(new AsyncCallback(WaitForInputSessionCloseCallback));
|
|
static readonly AsyncCallback closeCoreCallback = Fx.ThunkCallback(new AsyncCallback(CloseCoreCallback));
|
|
|
|
ServerSecuritySimplexSessionChannel sessionChannel;
|
|
TimeoutHelper timeoutHelper;
|
|
RequestContext closeRequestContext;
|
|
Message closeResponse;
|
|
|
|
public CloseAsyncResult(ServerSecuritySimplexSessionChannel sessionChannel, bool sendCloseResponse, RequestContext closeRequestContext, Message closeResponse, TimeSpan timeout, AsyncCallback callback, object state)
|
|
: base(callback, state)
|
|
{
|
|
this.timeoutHelper = new TimeoutHelper(timeout);
|
|
this.sessionChannel = sessionChannel;
|
|
this.closeRequestContext = closeRequestContext;
|
|
this.closeResponse = closeResponse;
|
|
bool wasChannelAborted = false;
|
|
bool completeSelf = this.OnSendCloseResponse(sendCloseResponse, out wasChannelAborted);
|
|
if (wasChannelAborted || completeSelf)
|
|
{
|
|
Complete(true);
|
|
}
|
|
}
|
|
|
|
bool OnSendCloseResponse(bool shouldSendCloseResponse, out bool wasChannelAborted)
|
|
{
|
|
wasChannelAborted = false;
|
|
try
|
|
{
|
|
if (shouldSendCloseResponse)
|
|
{
|
|
bool cleanupCloseState = true;
|
|
try
|
|
{
|
|
IAsyncResult result = this.sessionChannel.BeginSendCloseResponse(closeRequestContext, closeResponse, timeoutHelper.RemainingTime(), sendCloseResponseCallback, this);
|
|
if (!result.CompletedSynchronously)
|
|
{
|
|
cleanupCloseState = false;
|
|
return false;
|
|
}
|
|
this.sessionChannel.EndSendCloseResponse(result);
|
|
this.sessionChannel.inputSessionClosedHandle.Set();
|
|
}
|
|
finally
|
|
{
|
|
if (cleanupCloseState)
|
|
{
|
|
CleanupCloseState();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch (CommunicationObjectAbortedException)
|
|
{
|
|
if (this.sessionChannel.State != CommunicationState.Closed)
|
|
{
|
|
throw;
|
|
}
|
|
wasChannelAborted = true;
|
|
}
|
|
if (wasChannelAborted)
|
|
{
|
|
return true;
|
|
}
|
|
return this.OnReceiveNullMessage(out wasChannelAborted);
|
|
}
|
|
|
|
void CleanupCloseState()
|
|
{
|
|
if (this.closeResponse != null)
|
|
{
|
|
this.closeResponse.Close();
|
|
}
|
|
if (this.closeRequestContext != null)
|
|
{
|
|
this.closeRequestContext.Abort();
|
|
}
|
|
}
|
|
|
|
static void SendCloseResponseCallback(IAsyncResult result)
|
|
{
|
|
if (result.CompletedSynchronously)
|
|
{
|
|
return;
|
|
}
|
|
CloseAsyncResult thisResult = (CloseAsyncResult)result.AsyncState;
|
|
bool completeSelf = false;
|
|
Exception completionException = null;
|
|
try
|
|
{
|
|
bool wasAborted = false;
|
|
try
|
|
{
|
|
thisResult.sessionChannel.EndSendCloseResponse(result);
|
|
thisResult.sessionChannel.inputSessionClosedHandle.Set();
|
|
}
|
|
catch (CommunicationObjectAbortedException)
|
|
{
|
|
if (thisResult.sessionChannel.State != CommunicationState.Closed)
|
|
{
|
|
throw;
|
|
}
|
|
wasAborted = true;
|
|
completeSelf = true;
|
|
}
|
|
finally
|
|
{
|
|
thisResult.CleanupCloseState();
|
|
}
|
|
if (!wasAborted)
|
|
{
|
|
completeSelf = thisResult.OnReceiveNullMessage(out wasAborted);
|
|
}
|
|
}
|
|
#pragma warning suppress 56500 // covered by FxCOP
|
|
catch (Exception e)
|
|
{
|
|
if (Fx.IsFatal(e))
|
|
{
|
|
throw;
|
|
}
|
|
|
|
completeSelf = true;
|
|
completionException = e;
|
|
}
|
|
if (completeSelf)
|
|
{
|
|
thisResult.Complete(false, completionException);
|
|
}
|
|
}
|
|
|
|
bool OnReceiveNullMessage(out bool wasChannelAborted)
|
|
{
|
|
wasChannelAborted = false;
|
|
bool receivedMessage = false;
|
|
Message message = null;
|
|
try
|
|
{
|
|
IAsyncResult result = this.sessionChannel.BeginTryReceive(this.timeoutHelper.RemainingTime(), receiveCallback, this);
|
|
if (!result.CompletedSynchronously)
|
|
{
|
|
return false;
|
|
}
|
|
receivedMessage = this.sessionChannel.EndTryReceive(result, out message);
|
|
}
|
|
catch (CommunicationObjectAbortedException)
|
|
{
|
|
if (this.sessionChannel.State != CommunicationState.Closed)
|
|
{
|
|
throw;
|
|
}
|
|
// another thread aborted the channel
|
|
wasChannelAborted = true;
|
|
}
|
|
if (wasChannelAborted)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (receivedMessage)
|
|
{
|
|
return this.OnMessageReceived(message);
|
|
}
|
|
else
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new TimeoutException(SR.GetString(SR.ServiceSecurityCloseTimeout, timeoutHelper.OriginalTimeout)));
|
|
}
|
|
}
|
|
|
|
static void ReceiveCallback(IAsyncResult result)
|
|
{
|
|
if (result.CompletedSynchronously)
|
|
{
|
|
return;
|
|
}
|
|
CloseAsyncResult thisResult = (CloseAsyncResult)result.AsyncState;
|
|
bool completeSelf = false;
|
|
Exception completionException = null;
|
|
try
|
|
{
|
|
Message message = null;
|
|
bool wasAborted = false;
|
|
bool receivedMessage = false;
|
|
try
|
|
{
|
|
receivedMessage = thisResult.sessionChannel.EndTryReceive(result, out message);
|
|
}
|
|
catch (CommunicationObjectAbortedException)
|
|
{
|
|
if (thisResult.sessionChannel.State != CommunicationState.Closed)
|
|
{
|
|
throw;
|
|
}
|
|
wasAborted = true;
|
|
completeSelf = true;
|
|
}
|
|
if (!wasAborted)
|
|
{
|
|
if (receivedMessage)
|
|
{
|
|
completeSelf = thisResult.OnMessageReceived(message);
|
|
}
|
|
else
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new TimeoutException(SR.GetString(SR.ServiceSecurityCloseTimeout, thisResult.timeoutHelper.OriginalTimeout)));
|
|
}
|
|
}
|
|
}
|
|
#pragma warning suppress 56500 // covered by FxCOP
|
|
catch (Exception e)
|
|
{
|
|
if (Fx.IsFatal(e))
|
|
{
|
|
throw;
|
|
}
|
|
|
|
completeSelf = true;
|
|
completionException = e;
|
|
}
|
|
if (completeSelf)
|
|
{
|
|
thisResult.Complete(false, completionException);
|
|
}
|
|
}
|
|
|
|
bool OnMessageReceived(Message message)
|
|
{
|
|
if (message != null)
|
|
{
|
|
using (message)
|
|
{
|
|
ProtocolException error = ProtocolException.ReceiveShutdownReturnedNonNull(message);
|
|
throw TraceUtility.ThrowHelperWarning(error, message);
|
|
}
|
|
}
|
|
bool inputSessionClosed = false;
|
|
try
|
|
{
|
|
IAsyncResult result = this.sessionChannel.inputSessionClosedHandle.BeginWait(this.timeoutHelper.RemainingTime(), true, waitCallback, this);
|
|
if (!result.CompletedSynchronously)
|
|
{
|
|
return false;
|
|
}
|
|
this.sessionChannel.inputSessionClosedHandle.EndWait(result);
|
|
inputSessionClosed = true;
|
|
}
|
|
catch (CommunicationObjectAbortedException)
|
|
{
|
|
if (this.sessionChannel.State != CommunicationState.Closed)
|
|
{
|
|
throw;
|
|
}
|
|
// a parallel thread aborted the channel
|
|
return true;
|
|
}
|
|
catch (TimeoutException)
|
|
{
|
|
inputSessionClosed = false;
|
|
}
|
|
|
|
return this.OnWaitOver(inputSessionClosed);
|
|
}
|
|
|
|
static void WaitForInputSessionCloseCallback(IAsyncResult result)
|
|
{
|
|
if (result.CompletedSynchronously)
|
|
{
|
|
return;
|
|
}
|
|
CloseAsyncResult thisResult = (CloseAsyncResult)result.AsyncState;
|
|
bool completeSelf = false;
|
|
Exception completionException = null;
|
|
try
|
|
{
|
|
bool inputSessionClosed = false;
|
|
bool wasChannelAborted = false;
|
|
try
|
|
{
|
|
thisResult.sessionChannel.inputSessionClosedHandle.EndWait(result);
|
|
inputSessionClosed = true;
|
|
}
|
|
catch (TimeoutException)
|
|
{
|
|
inputSessionClosed = false;
|
|
}
|
|
catch (CommunicationObjectAbortedException)
|
|
{
|
|
if (thisResult.sessionChannel.State != CommunicationState.Closed)
|
|
{
|
|
throw;
|
|
}
|
|
wasChannelAborted = true;
|
|
completeSelf = true;
|
|
}
|
|
if (!wasChannelAborted)
|
|
{
|
|
completeSelf = thisResult.OnWaitOver(inputSessionClosed);
|
|
}
|
|
}
|
|
#pragma warning suppress 56500 // covered by FxCOP
|
|
catch (Exception e)
|
|
{
|
|
if (Fx.IsFatal(e))
|
|
{
|
|
throw;
|
|
}
|
|
completeSelf = true;
|
|
completionException = e;
|
|
}
|
|
if (completeSelf)
|
|
{
|
|
thisResult.Complete(false, completionException);
|
|
}
|
|
}
|
|
|
|
bool OnWaitOver(bool closeCompleted)
|
|
{
|
|
if (closeCompleted)
|
|
{
|
|
IAsyncResult result = this.sessionChannel.BeginCloseCore(this.timeoutHelper.RemainingTime(), closeCoreCallback, this);
|
|
if (!result.CompletedSynchronously)
|
|
{
|
|
return false;
|
|
}
|
|
this.sessionChannel.EndCloseCore(result);
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new TimeoutException(SR.GetString(SR.ServiceSecurityCloseTimeout, this.timeoutHelper.OriginalTimeout)));
|
|
}
|
|
}
|
|
|
|
static void CloseCoreCallback(IAsyncResult result)
|
|
{
|
|
if (result.CompletedSynchronously)
|
|
{
|
|
return;
|
|
}
|
|
CloseAsyncResult self = (CloseAsyncResult)(result.AsyncState);
|
|
Exception completionException = null;
|
|
try
|
|
{
|
|
self.sessionChannel.EndCloseCore(result);
|
|
}
|
|
#pragma warning suppress 56500 // covered by FxCOP
|
|
catch (Exception e)
|
|
{
|
|
if (Fx.IsFatal(e))
|
|
{
|
|
throw;
|
|
}
|
|
completionException = e;
|
|
}
|
|
self.Complete(false, completionException);
|
|
}
|
|
|
|
public static void End(IAsyncResult result)
|
|
{
|
|
AsyncResult.End<CloseAsyncResult>(result);
|
|
}
|
|
}
|
|
}
|
|
|
|
class SecurityReplySessionChannel : ServerSecuritySimplexSessionChannel, IReplySessionChannel
|
|
{
|
|
public SecurityReplySessionChannel(
|
|
SecuritySessionServerSettings settings,
|
|
IServerReliableChannelBinder channelBinder,
|
|
SecurityContextSecurityToken sessionToken,
|
|
object listenerSecurityState, SecurityListenerSettingsLifetimeManager settingsLifetimeManager)
|
|
: base(settings, channelBinder, sessionToken, listenerSecurityState, settingsLifetimeManager)
|
|
{
|
|
}
|
|
|
|
protected override bool CanDoSecurityCorrelation
|
|
{
|
|
get
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
public bool WaitForRequest(TimeSpan timeout)
|
|
{
|
|
return this.ChannelBinder.WaitForRequest(timeout);
|
|
}
|
|
|
|
public IAsyncResult BeginWaitForRequest(TimeSpan timeout, AsyncCallback callback, object state)
|
|
{
|
|
return this.ChannelBinder.BeginWaitForRequest(timeout, callback, state);
|
|
}
|
|
|
|
public bool EndWaitForRequest(IAsyncResult result)
|
|
{
|
|
return this.ChannelBinder.EndWaitForRequest(result);
|
|
}
|
|
}
|
|
|
|
class SecuritySessionRequestContext : RequestContextBase
|
|
{
|
|
RequestContext requestContext;
|
|
ServerSecuritySessionChannel channel;
|
|
SecurityProtocolCorrelationState correlationState;
|
|
|
|
public SecuritySessionRequestContext(RequestContext requestContext, Message requestMessage, SecurityProtocolCorrelationState correlationState, ServerSecuritySessionChannel channel)
|
|
: base(requestMessage, channel.InternalCloseTimeout, channel.InternalSendTimeout)
|
|
{
|
|
this.requestContext = requestContext;
|
|
this.correlationState = correlationState;
|
|
this.channel = channel;
|
|
}
|
|
|
|
protected override void OnAbort()
|
|
{
|
|
this.requestContext.Abort();
|
|
}
|
|
|
|
protected override void OnClose(TimeSpan timeout)
|
|
{
|
|
this.requestContext.Close(timeout);
|
|
}
|
|
|
|
protected override void OnReply(Message message, TimeSpan timeout)
|
|
{
|
|
TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
|
|
if (message != null)
|
|
{
|
|
this.channel.SecureApplicationMessage(ref message, timeoutHelper.RemainingTime(), correlationState);
|
|
}
|
|
this.requestContext.Reply(message, timeoutHelper.RemainingTime());
|
|
}
|
|
|
|
protected override IAsyncResult OnBeginReply(Message message, TimeSpan timeout, AsyncCallback callback, object state)
|
|
{
|
|
TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
|
|
if (message != null)
|
|
{
|
|
this.channel.SecureApplicationMessage(ref message, timeoutHelper.RemainingTime(), correlationState);
|
|
}
|
|
return this.requestContext.BeginReply(message, timeoutHelper.RemainingTime(), callback, state);
|
|
}
|
|
|
|
protected override void OnEndReply(IAsyncResult result)
|
|
{
|
|
this.requestContext.EndReply(result);
|
|
}
|
|
}
|
|
|
|
class ServerSecurityDuplexSessionChannel : ServerSecuritySessionChannel, IDuplexSessionChannel
|
|
{
|
|
SoapSecurityServerDuplexSession session;
|
|
bool isInputClosed;
|
|
bool isOutputClosed;
|
|
bool sentClose;
|
|
bool receivedClose;
|
|
RequestContext closeRequestContext;
|
|
Message closeResponseMessage;
|
|
InterruptibleWaitObject outputSessionCloseHandle = new InterruptibleWaitObject(true);
|
|
InterruptibleWaitObject inputSessionCloseHandle = new InterruptibleWaitObject(false);
|
|
|
|
public ServerSecurityDuplexSessionChannel(
|
|
SecuritySessionServerSettings settings,
|
|
IServerReliableChannelBinder channelBinder,
|
|
SecurityContextSecurityToken sessionToken,
|
|
object listenerSecurityState, SecurityListenerSettingsLifetimeManager settingsLifetimeManager)
|
|
: base(settings, channelBinder, sessionToken, listenerSecurityState, settingsLifetimeManager)
|
|
{
|
|
this.session = new SoapSecurityServerDuplexSession(sessionToken, settings, this);
|
|
}
|
|
|
|
public EndpointAddress RemoteAddress
|
|
{
|
|
get
|
|
{
|
|
return this.ChannelBinder.RemoteAddress;
|
|
}
|
|
}
|
|
|
|
public Uri Via
|
|
{
|
|
get
|
|
{
|
|
return this.RemoteAddress.Uri;
|
|
}
|
|
}
|
|
|
|
public IDuplexSession Session
|
|
{
|
|
get
|
|
{
|
|
return this.session;
|
|
}
|
|
}
|
|
|
|
public void Send(Message message)
|
|
{
|
|
this.Send(message, this.DefaultSendTimeout);
|
|
}
|
|
|
|
public void Send(Message message, TimeSpan timeout)
|
|
{
|
|
CheckOutputOpen();
|
|
TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
|
|
this.SecureApplicationMessage(ref message, timeoutHelper.RemainingTime(), null);
|
|
this.ChannelBinder.Send(message, timeoutHelper.RemainingTime());
|
|
}
|
|
|
|
public IAsyncResult BeginSend(Message message, AsyncCallback callback, object state)
|
|
{
|
|
return this.BeginSend(message, this.DefaultSendTimeout, callback, state);
|
|
}
|
|
|
|
public IAsyncResult BeginSend(Message message, TimeSpan timeout, AsyncCallback callback, object state)
|
|
{
|
|
CheckOutputOpen();
|
|
TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
|
|
this.SecureApplicationMessage(ref message, timeoutHelper.RemainingTime(), null);
|
|
return this.ChannelBinder.BeginSend(message, timeoutHelper.RemainingTime(), callback, state);
|
|
}
|
|
|
|
public void EndSend(IAsyncResult result)
|
|
{
|
|
this.ChannelBinder.EndSend(result);
|
|
}
|
|
|
|
protected override void AbortCore()
|
|
{
|
|
base.AbortCore();
|
|
this.Settings.RemoveSessionChannel(this.session.Id);
|
|
CleanupPendingCloseState();
|
|
}
|
|
|
|
void CleanupPendingCloseState()
|
|
{
|
|
lock (ThisLock)
|
|
{
|
|
if (this.closeResponseMessage != null)
|
|
{
|
|
this.closeResponseMessage.Close();
|
|
this.closeResponseMessage = null;
|
|
}
|
|
if (this.closeRequestContext != null)
|
|
{
|
|
this.closeRequestContext.Abort();
|
|
this.closeRequestContext = null;
|
|
}
|
|
}
|
|
}
|
|
|
|
protected override void OnAbort()
|
|
{
|
|
AbortCore();
|
|
this.inputSessionCloseHandle.Abort(this);
|
|
this.outputSessionCloseHandle.Abort(this);
|
|
}
|
|
|
|
protected override void OnFaulted()
|
|
{
|
|
this.AbortCore();
|
|
this.inputSessionCloseHandle.Fault(this);
|
|
this.outputSessionCloseHandle.Fault(this);
|
|
base.OnFaulted();
|
|
}
|
|
|
|
protected override void CloseCore(TimeSpan timeout)
|
|
{
|
|
base.CloseCore(timeout);
|
|
this.inputSessionCloseHandle.Abort(this);
|
|
this.outputSessionCloseHandle.Abort(this);
|
|
this.Settings.RemoveSessionChannel(this.session.Id);
|
|
}
|
|
|
|
protected override void EndCloseCore(IAsyncResult result)
|
|
{
|
|
base.EndCloseCore(result);
|
|
this.inputSessionCloseHandle.Abort(this);
|
|
this.outputSessionCloseHandle.Abort(this);
|
|
this.Settings.RemoveSessionChannel(this.session.Id);
|
|
}
|
|
|
|
protected void CheckOutputOpen()
|
|
{
|
|
ThrowIfClosedOrNotOpen();
|
|
lock (ThisLock)
|
|
{
|
|
if (this.isOutputClosed)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new CommunicationException(SR.GetString(SR.OutputNotExpected)));
|
|
}
|
|
}
|
|
}
|
|
|
|
protected override IAsyncResult OnBeginClose(TimeSpan timeout, AsyncCallback callback, object state)
|
|
{
|
|
return new CloseAsyncResult(this, timeout, callback, state);
|
|
}
|
|
|
|
protected override void OnEndClose(IAsyncResult result)
|
|
{
|
|
CloseAsyncResult.End(result);
|
|
}
|
|
|
|
internal bool WaitForOutputSessionClose(TimeSpan timeout, out bool wasAborted)
|
|
{
|
|
wasAborted = false;
|
|
try
|
|
{
|
|
return this.outputSessionCloseHandle.Wait(timeout, false);
|
|
}
|
|
catch (CommunicationObjectAbortedException)
|
|
{
|
|
if (this.State != CommunicationState.Closed) throw;
|
|
wasAborted = true;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
protected override void OnClose(TimeSpan timeout)
|
|
{
|
|
TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
|
|
|
|
// step 1: close output session
|
|
this.CloseOutputSession(timeoutHelper.RemainingTime());
|
|
|
|
// if the channel was aborted while closing the output session, return
|
|
if (this.State == CommunicationState.Closed)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// step 2: wait for input session to be closed
|
|
bool wasAborted;
|
|
bool didInputSessionClose = this.WaitForInputSessionClose(timeoutHelper.RemainingTime(), out wasAborted);
|
|
if (wasAborted)
|
|
{
|
|
return;
|
|
}
|
|
if (!didInputSessionClose)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new TimeoutException(SR.GetString(SR.ServiceSecurityCloseTimeout, timeoutHelper.OriginalTimeout)));
|
|
}
|
|
|
|
// wait for any concurrent CloseOutputSessions to finish
|
|
bool didOutputSessionClose = this.WaitForOutputSessionClose(timeoutHelper.RemainingTime(), out wasAborted);
|
|
if (wasAborted)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (!didOutputSessionClose)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new TimeoutException(SR.GetString(SR.ServiceSecurityCloseOutputSessionTimeout, timeoutHelper.OriginalTimeout)));
|
|
}
|
|
|
|
this.CloseCore(timeoutHelper.RemainingTime());
|
|
}
|
|
|
|
bool WaitForInputSessionClose(TimeSpan timeout, out bool wasAborted)
|
|
{
|
|
TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
|
|
Message message;
|
|
wasAborted = false;
|
|
try
|
|
{
|
|
if (!this.TryReceive(timeoutHelper.RemainingTime(), out message))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (message != null)
|
|
{
|
|
using (message)
|
|
{
|
|
ProtocolException error = ProtocolException.ReceiveShutdownReturnedNonNull(message);
|
|
throw TraceUtility.ThrowHelperWarning(error, message);
|
|
}
|
|
}
|
|
|
|
// wait for remote close
|
|
if (!this.inputSessionCloseHandle.Wait(timeoutHelper.RemainingTime(), false))
|
|
{
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
lock (ThisLock)
|
|
{
|
|
if (!(this.isInputClosed))
|
|
{
|
|
Fx.Assert("Shutdown request was not received.");
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ShutdownRequestWasNotReceived)));
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
catch (CommunicationObjectAbortedException)
|
|
{
|
|
if (this.State != CommunicationState.Closed)
|
|
{
|
|
throw;
|
|
}
|
|
wasAborted = true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
protected override void OnCloseMessageReceived(RequestContext requestContext, Message message, SecurityProtocolCorrelationState correlationState, TimeSpan timeout)
|
|
{
|
|
if (this.State == CommunicationState.Created)
|
|
{
|
|
Fx.Assert("ServerSecurityDuplexSessionChannel.OnCloseMessageReceived (this.State == Created)");
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ServerReceivedCloseMessageStateIsCreated, this.GetType().ToString())));
|
|
}
|
|
|
|
TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
|
|
bool setInputSessionCloseHandle = false;
|
|
bool cleanupContext = true;
|
|
try
|
|
{
|
|
lock (ThisLock)
|
|
{
|
|
this.receivedClose = true;
|
|
if (!this.isInputClosed)
|
|
{
|
|
this.isInputClosed = true;
|
|
setInputSessionCloseHandle = true;
|
|
|
|
if (!this.isOutputClosed)
|
|
{
|
|
this.closeRequestContext = requestContext;
|
|
// CreateCloseResponse closes the message passed in
|
|
this.closeResponseMessage = CreateCloseResponse(message, null, timeoutHelper.RemainingTime());
|
|
cleanupContext = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (setInputSessionCloseHandle)
|
|
{
|
|
this.inputSessionCloseHandle.Set();
|
|
}
|
|
if (cleanupContext)
|
|
{
|
|
requestContext.Close(timeoutHelper.RemainingTime());
|
|
cleanupContext = false;
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
message.Close();
|
|
if (cleanupContext)
|
|
{
|
|
requestContext.Abort();
|
|
}
|
|
}
|
|
}
|
|
|
|
protected override void OnCloseResponseMessageReceived(RequestContext requestContext, Message message, SecurityProtocolCorrelationState correlationState, TimeSpan timeout)
|
|
{
|
|
bool cleanupContext = true;
|
|
try
|
|
{
|
|
bool isCloseResponseExpected = false;
|
|
bool setInputSessionCloseHandle = false;
|
|
lock (ThisLock)
|
|
{
|
|
isCloseResponseExpected = this.sentClose;
|
|
if (isCloseResponseExpected && !this.isInputClosed)
|
|
{
|
|
this.isInputClosed = true;
|
|
setInputSessionCloseHandle = true;
|
|
}
|
|
}
|
|
if (!isCloseResponseExpected)
|
|
{
|
|
this.Fault(new ProtocolException(SR.GetString(SR.UnexpectedSecuritySessionCloseResponse)));
|
|
return;
|
|
}
|
|
if (setInputSessionCloseHandle)
|
|
{
|
|
this.inputSessionCloseHandle.Set();
|
|
}
|
|
|
|
requestContext.Close(timeout);
|
|
cleanupContext = false;
|
|
}
|
|
finally
|
|
{
|
|
message.Close();
|
|
if (cleanupContext)
|
|
{
|
|
requestContext.Abort();
|
|
}
|
|
}
|
|
}
|
|
|
|
void DetermineCloseOutputSessionMessage(out bool sendClose, out bool sendCloseResponse, out Message pendingCloseResponseMessage, out RequestContext pendingCloseRequestContext)
|
|
{
|
|
sendClose = false;
|
|
sendCloseResponse = false;
|
|
pendingCloseResponseMessage = null;
|
|
pendingCloseRequestContext = null;
|
|
lock (ThisLock)
|
|
{
|
|
if (!this.isOutputClosed)
|
|
{
|
|
this.isOutputClosed = true;
|
|
if (this.receivedClose)
|
|
{
|
|
if (this.closeResponseMessage != null)
|
|
{
|
|
pendingCloseResponseMessage = this.closeResponseMessage;
|
|
pendingCloseRequestContext = this.closeRequestContext;
|
|
this.closeResponseMessage = null;
|
|
this.closeRequestContext = null;
|
|
sendCloseResponse = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
sendClose = true;
|
|
this.sentClose = true;
|
|
}
|
|
this.outputSessionCloseHandle.Reset();
|
|
}
|
|
}
|
|
}
|
|
|
|
void CloseOutputSession(TimeSpan timeout)
|
|
{
|
|
bool sendClose = false;
|
|
bool sendCloseResponse = false;
|
|
Message pendingCloseResponseMessage;
|
|
RequestContext pendingCloseRequestContext;
|
|
try
|
|
{
|
|
DetermineCloseOutputSessionMessage(out sendClose, out sendCloseResponse, out pendingCloseResponseMessage, out pendingCloseRequestContext);
|
|
if (sendCloseResponse)
|
|
{
|
|
bool cleanupCloseState = true;
|
|
try
|
|
{
|
|
this.SendCloseResponse(pendingCloseRequestContext, pendingCloseResponseMessage, timeout);
|
|
cleanupCloseState = false;
|
|
}
|
|
finally
|
|
{
|
|
if (cleanupCloseState)
|
|
{
|
|
pendingCloseResponseMessage.Close();
|
|
pendingCloseRequestContext.Abort();
|
|
}
|
|
}
|
|
}
|
|
else if (sendClose)
|
|
{
|
|
this.SendClose(timeout);
|
|
}
|
|
}
|
|
catch (CommunicationObjectAbortedException)
|
|
{
|
|
if (this.State != CommunicationState.Closed) throw;
|
|
// a parallel thread aborted the channel. ignore the exception
|
|
}
|
|
finally
|
|
{
|
|
if (sendClose || sendCloseResponse)
|
|
{
|
|
this.outputSessionCloseHandle.Set();
|
|
}
|
|
}
|
|
}
|
|
|
|
IAsyncResult BeginCloseOutputSession(TimeSpan timeout, AsyncCallback callback, object state)
|
|
{
|
|
return new CloseOutputSessionAsyncResult(this, timeout, callback, state);
|
|
}
|
|
|
|
void EndCloseOutputSession(IAsyncResult result)
|
|
{
|
|
CloseOutputSessionAsyncResult.End(result);
|
|
}
|
|
|
|
public bool WaitForMessage(TimeSpan timeout)
|
|
{
|
|
return this.ChannelBinder.WaitForRequest(timeout);
|
|
}
|
|
|
|
public IAsyncResult BeginWaitForMessage(TimeSpan timeout, AsyncCallback callback, object state)
|
|
{
|
|
return this.ChannelBinder.BeginWaitForRequest(timeout, callback, state);
|
|
}
|
|
|
|
public bool EndWaitForMessage(IAsyncResult result)
|
|
{
|
|
return this.ChannelBinder.EndWaitForRequest(result);
|
|
}
|
|
|
|
class CloseOutputSessionAsyncResult : AsyncResult
|
|
{
|
|
static AsyncCallback sendCallback = Fx.ThunkCallback(new AsyncCallback(SendCallback));
|
|
ServerSecurityDuplexSessionChannel sessionChannel;
|
|
TimeoutHelper timeoutHelper;
|
|
bool sendClose;
|
|
bool sendCloseResponse;
|
|
Message closeResponseMessage;
|
|
RequestContext closeRequestContext;
|
|
|
|
public CloseOutputSessionAsyncResult(ServerSecurityDuplexSessionChannel sessionChannel, TimeSpan timeout, AsyncCallback callback, object state)
|
|
: base(callback, state)
|
|
{
|
|
this.sessionChannel = sessionChannel;
|
|
this.timeoutHelper = new TimeoutHelper(timeout);
|
|
this.sessionChannel.DetermineCloseOutputSessionMessage(out sendClose, out sendCloseResponse, out closeResponseMessage, out closeRequestContext);
|
|
if (!sendClose && !sendCloseResponse)
|
|
{
|
|
Complete(true);
|
|
return;
|
|
}
|
|
bool doCleanup = true;
|
|
try
|
|
{
|
|
IAsyncResult result = this.BeginSend(sendCallback, this);
|
|
if (!result.CompletedSynchronously)
|
|
{
|
|
doCleanup = false;
|
|
return;
|
|
}
|
|
this.EndSend(result);
|
|
}
|
|
finally
|
|
{
|
|
if (doCleanup)
|
|
{
|
|
Cleanup();
|
|
}
|
|
}
|
|
Complete(true);
|
|
}
|
|
|
|
static void SendCallback(IAsyncResult result)
|
|
{
|
|
if (result.CompletedSynchronously)
|
|
{
|
|
return;
|
|
}
|
|
CloseOutputSessionAsyncResult self = (CloseOutputSessionAsyncResult)(result.AsyncState);
|
|
Exception completionException = null;
|
|
try
|
|
{
|
|
self.EndSend(result);
|
|
}
|
|
#pragma warning suppress 56500 // covered by FxCOP
|
|
catch (Exception e)
|
|
{
|
|
if (Fx.IsFatal(e))
|
|
{
|
|
throw;
|
|
}
|
|
completionException = e;
|
|
}
|
|
self.Cleanup();
|
|
self.Complete(false, completionException);
|
|
}
|
|
|
|
IAsyncResult BeginSend(AsyncCallback callback, object state)
|
|
{
|
|
if (this.sendClose)
|
|
{
|
|
return this.sessionChannel.BeginSendClose(timeoutHelper.RemainingTime(), callback, state);
|
|
}
|
|
else
|
|
{
|
|
return this.sessionChannel.BeginSendCloseResponse(this.closeRequestContext, this.closeResponseMessage, this.timeoutHelper.RemainingTime(), callback, state);
|
|
}
|
|
}
|
|
|
|
void EndSend(IAsyncResult result)
|
|
{
|
|
if (this.sendClose)
|
|
{
|
|
this.sessionChannel.EndSendClose(result);
|
|
}
|
|
else
|
|
{
|
|
this.sessionChannel.EndSendCloseResponse(result);
|
|
}
|
|
}
|
|
|
|
void Cleanup()
|
|
{
|
|
if (this.closeResponseMessage != null)
|
|
{
|
|
this.closeResponseMessage.Close();
|
|
}
|
|
if (this.closeRequestContext != null)
|
|
{
|
|
this.closeRequestContext.Abort();
|
|
}
|
|
this.sessionChannel.outputSessionCloseHandle.Set();
|
|
}
|
|
|
|
public static void End(IAsyncResult result)
|
|
{
|
|
AsyncResult.End<CloseOutputSessionAsyncResult>(result);
|
|
}
|
|
}
|
|
|
|
class CloseAsyncResult : AsyncResult
|
|
{
|
|
static readonly AsyncCallback receiveCallback = Fx.ThunkCallback(new AsyncCallback(ReceiveCallback));
|
|
static readonly AsyncCallback inputSessionWaitCallback = Fx.ThunkCallback(new AsyncCallback(WaitForInputSessionCloseCallback));
|
|
static readonly AsyncCallback closeOutputSessionCallback = Fx.ThunkCallback(new AsyncCallback(CloseOutputSessionCallback));
|
|
static readonly AsyncCallback outputSessionWaitCallback = Fx.ThunkCallback(new AsyncCallback(WaitForOutputSessionCloseCallback));
|
|
static readonly AsyncCallback closeCoreCallback = Fx.ThunkCallback(new AsyncCallback(CloseCoreCallback));
|
|
ServerSecurityDuplexSessionChannel sessionChannel;
|
|
TimeoutHelper timeoutHelper;
|
|
|
|
public CloseAsyncResult(ServerSecurityDuplexSessionChannel sessionChannel, TimeSpan timeout, AsyncCallback callback, object state)
|
|
: base(callback, state)
|
|
{
|
|
this.timeoutHelper = new TimeoutHelper(timeout);
|
|
this.sessionChannel = sessionChannel;
|
|
|
|
bool wasAborted = false;
|
|
try
|
|
{
|
|
IAsyncResult result = this.sessionChannel.BeginCloseOutputSession(timeoutHelper.RemainingTime(), closeOutputSessionCallback, this);
|
|
if (!result.CompletedSynchronously)
|
|
{
|
|
return;
|
|
}
|
|
this.sessionChannel.EndCloseOutputSession(result);
|
|
}
|
|
catch (CommunicationObjectAbortedException)
|
|
{
|
|
if (sessionChannel.State != CommunicationState.Closed) throw;
|
|
// a parallel thread must have aborted the channel. No need to close
|
|
wasAborted = true;
|
|
}
|
|
if (wasAborted || this.OnOutputSessionClosed())
|
|
{
|
|
Complete(true);
|
|
}
|
|
}
|
|
|
|
static void CloseOutputSessionCallback(IAsyncResult result)
|
|
{
|
|
if (result.CompletedSynchronously)
|
|
{
|
|
return;
|
|
}
|
|
CloseAsyncResult thisResult = (CloseAsyncResult)result.AsyncState;
|
|
bool completeSelf = false;
|
|
Exception completionException = null;
|
|
try
|
|
{
|
|
bool wasAborted = false;
|
|
try
|
|
{
|
|
thisResult.sessionChannel.Session.EndCloseOutputSession(result);
|
|
}
|
|
catch (CommunicationObjectAbortedException)
|
|
{
|
|
if (thisResult.sessionChannel.State != CommunicationState.Closed)
|
|
{
|
|
throw;
|
|
}
|
|
completeSelf = true;
|
|
wasAborted = true;
|
|
}
|
|
if (!wasAborted)
|
|
{
|
|
completeSelf = thisResult.OnOutputSessionClosed();
|
|
}
|
|
}
|
|
#pragma warning suppress 56500 // covered by FxCOP
|
|
catch (Exception e)
|
|
{
|
|
if (Fx.IsFatal(e))
|
|
{
|
|
throw;
|
|
}
|
|
|
|
completeSelf = true;
|
|
completionException = e;
|
|
}
|
|
if (completeSelf)
|
|
{
|
|
thisResult.Complete(false, completionException);
|
|
}
|
|
}
|
|
|
|
bool OnOutputSessionClosed()
|
|
{
|
|
bool wasAborted = false;
|
|
Message message = null;
|
|
bool receivedMessage = false;
|
|
try
|
|
{
|
|
IAsyncResult result = this.sessionChannel.BeginTryReceive(this.timeoutHelper.RemainingTime(), receiveCallback, this);
|
|
if (!result.CompletedSynchronously)
|
|
{
|
|
return false;
|
|
}
|
|
receivedMessage = this.sessionChannel.EndTryReceive(result, out message);
|
|
}
|
|
catch (CommunicationObjectAbortedException)
|
|
{
|
|
if (this.sessionChannel.State != CommunicationState.Closed)
|
|
{
|
|
throw;
|
|
}
|
|
wasAborted = true;
|
|
}
|
|
if (wasAborted)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (receivedMessage)
|
|
{
|
|
return this.OnMessageReceived(message);
|
|
}
|
|
else
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new TimeoutException(SR.GetString(SR.ServiceSecurityCloseTimeout, this.timeoutHelper.OriginalTimeout)));
|
|
}
|
|
}
|
|
|
|
static void ReceiveCallback(IAsyncResult result)
|
|
{
|
|
if (result.CompletedSynchronously)
|
|
{
|
|
return;
|
|
}
|
|
CloseAsyncResult thisResult = (CloseAsyncResult)result.AsyncState;
|
|
bool completeSelf = false;
|
|
Exception completionException = null;
|
|
try
|
|
{
|
|
Message message = null;
|
|
bool receivedRequest = false;
|
|
bool wasAborted = false;
|
|
try
|
|
{
|
|
receivedRequest = thisResult.sessionChannel.EndTryReceive(result, out message);
|
|
}
|
|
catch (CommunicationObjectAbortedException)
|
|
{
|
|
if (thisResult.sessionChannel.State != CommunicationState.Closed)
|
|
{
|
|
throw;
|
|
}
|
|
completeSelf = true;
|
|
wasAborted = true;
|
|
}
|
|
if (!wasAborted)
|
|
{
|
|
if (receivedRequest)
|
|
{
|
|
completeSelf = thisResult.OnMessageReceived(message);
|
|
}
|
|
else
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new TimeoutException(SR.GetString(SR.ServiceSecurityCloseTimeout, thisResult.timeoutHelper.OriginalTimeout)));
|
|
}
|
|
}
|
|
}
|
|
#pragma warning suppress 56500 // covered by FxCOP
|
|
catch (Exception e)
|
|
{
|
|
if (Fx.IsFatal(e))
|
|
{
|
|
throw;
|
|
}
|
|
|
|
completeSelf = true;
|
|
completionException = e;
|
|
}
|
|
if (completeSelf)
|
|
{
|
|
thisResult.Complete(false, completionException);
|
|
}
|
|
}
|
|
|
|
bool OnMessageReceived(Message message)
|
|
{
|
|
if (message != null)
|
|
{
|
|
using (message)
|
|
{
|
|
ProtocolException error = ProtocolException.ReceiveShutdownReturnedNonNull(message);
|
|
throw TraceUtility.ThrowHelperWarning(error, message);
|
|
}
|
|
}
|
|
bool wasAborted = false;
|
|
bool inputSessionClosed = false;
|
|
try
|
|
{
|
|
IAsyncResult result = this.sessionChannel.inputSessionCloseHandle.BeginWait(this.timeoutHelper.RemainingTime(), inputSessionWaitCallback, this);
|
|
if (!result.CompletedSynchronously)
|
|
{
|
|
return false;
|
|
}
|
|
try
|
|
{
|
|
this.sessionChannel.inputSessionCloseHandle.EndWait(result);
|
|
inputSessionClosed = true;
|
|
}
|
|
catch (TimeoutException)
|
|
{
|
|
inputSessionClosed = false;
|
|
}
|
|
}
|
|
catch (CommunicationObjectAbortedException)
|
|
{
|
|
if (this.sessionChannel.State != CommunicationState.Closed)
|
|
{
|
|
throw;
|
|
}
|
|
wasAborted = true;
|
|
}
|
|
if (wasAborted)
|
|
{
|
|
return true;
|
|
}
|
|
return this.OnInputSessionWaitOver(inputSessionClosed);
|
|
}
|
|
|
|
static void WaitForInputSessionCloseCallback(IAsyncResult result)
|
|
{
|
|
if (result.CompletedSynchronously)
|
|
{
|
|
return;
|
|
}
|
|
CloseAsyncResult thisResult = (CloseAsyncResult)result.AsyncState;
|
|
bool completeSelf = false;
|
|
Exception completionException = null;
|
|
bool inputSessionClosed = false;
|
|
try
|
|
{
|
|
bool wasAborted = false;
|
|
try
|
|
{
|
|
thisResult.sessionChannel.inputSessionCloseHandle.EndWait(result);
|
|
inputSessionClosed = true;
|
|
}
|
|
catch (TimeoutException)
|
|
{
|
|
inputSessionClosed = false;
|
|
}
|
|
catch (CommunicationObjectAbortedException)
|
|
{
|
|
if (thisResult.sessionChannel.State != CommunicationState.Closed)
|
|
{
|
|
throw;
|
|
}
|
|
wasAborted = true;
|
|
completeSelf = true;
|
|
}
|
|
if (!wasAborted)
|
|
{
|
|
completeSelf = thisResult.OnInputSessionWaitOver(inputSessionClosed);
|
|
}
|
|
|
|
}
|
|
#pragma warning suppress 56500 // covered by FxCOP
|
|
catch (Exception e)
|
|
{
|
|
if (Fx.IsFatal(e))
|
|
{
|
|
throw;
|
|
}
|
|
|
|
completeSelf = true;
|
|
completionException = e;
|
|
}
|
|
if (completeSelf)
|
|
{
|
|
thisResult.Complete(false, completionException);
|
|
}
|
|
}
|
|
|
|
bool OnInputSessionWaitOver(bool inputSessionClosed)
|
|
{
|
|
if (inputSessionClosed)
|
|
{
|
|
lock (this.sessionChannel.ThisLock)
|
|
{
|
|
if (!(this.sessionChannel.isInputClosed))
|
|
{
|
|
Fx.Assert("Shutdown request was not received.");
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ShutdownRequestWasNotReceived)));
|
|
}
|
|
}
|
|
bool outputSessionClosed = false;
|
|
bool wasAborted = false;
|
|
try
|
|
{
|
|
IAsyncResult result = this.sessionChannel.outputSessionCloseHandle.BeginWait(timeoutHelper.RemainingTime(), true, outputSessionWaitCallback, this);
|
|
if (!result.CompletedSynchronously)
|
|
{
|
|
return false;
|
|
}
|
|
this.sessionChannel.outputSessionCloseHandle.EndWait(result);
|
|
outputSessionClosed = true;
|
|
}
|
|
catch (CommunicationObjectAbortedException)
|
|
{
|
|
if (this.sessionChannel.State != CommunicationState.Closed) throw;
|
|
wasAborted = true;
|
|
}
|
|
catch (TimeoutException)
|
|
{
|
|
outputSessionClosed = false;
|
|
}
|
|
if (wasAborted)
|
|
{
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return this.OnOutputSessionWaitOver(outputSessionClosed);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new TimeoutException(SR.GetString(SR.ServiceSecurityCloseTimeout, timeoutHelper.OriginalTimeout)));
|
|
}
|
|
}
|
|
|
|
static void WaitForOutputSessionCloseCallback(IAsyncResult result)
|
|
{
|
|
if (result.CompletedSynchronously)
|
|
{
|
|
return;
|
|
}
|
|
CloseAsyncResult thisResult = (CloseAsyncResult)result.AsyncState;
|
|
bool completeSelf = false;
|
|
Exception completionException = null;
|
|
bool outputSessionClosed = false;
|
|
try
|
|
{
|
|
bool wasAborted = false;
|
|
try
|
|
{
|
|
thisResult.sessionChannel.outputSessionCloseHandle.EndWait(result);
|
|
outputSessionClosed = true;
|
|
}
|
|
catch (CommunicationObjectAbortedException)
|
|
{
|
|
if (thisResult.sessionChannel.State != CommunicationState.Closed)
|
|
{
|
|
throw;
|
|
}
|
|
wasAborted = true;
|
|
completeSelf = true;
|
|
}
|
|
catch (TimeoutException)
|
|
{
|
|
outputSessionClosed = false;
|
|
}
|
|
if (!wasAborted)
|
|
{
|
|
completeSelf = thisResult.OnOutputSessionWaitOver(outputSessionClosed);
|
|
}
|
|
}
|
|
#pragma warning suppress 56500 // covered by FxCOP
|
|
catch (Exception e)
|
|
{
|
|
if (Fx.IsFatal(e))
|
|
{
|
|
throw;
|
|
}
|
|
|
|
completeSelf = true;
|
|
completionException = e;
|
|
}
|
|
if (completeSelf)
|
|
{
|
|
thisResult.Complete(false, completionException);
|
|
}
|
|
}
|
|
|
|
bool OnOutputSessionWaitOver(bool outputSessionClosed)
|
|
{
|
|
if (outputSessionClosed)
|
|
{
|
|
IAsyncResult result = this.sessionChannel.BeginCloseCore(this.timeoutHelper.RemainingTime(), closeCoreCallback, this);
|
|
if (!result.CompletedSynchronously)
|
|
{
|
|
return false;
|
|
}
|
|
this.sessionChannel.EndCloseCore(result);
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new TimeoutException(SR.GetString(SR.ServiceSecurityCloseOutputSessionTimeout, timeoutHelper.OriginalTimeout)));
|
|
}
|
|
}
|
|
|
|
static void CloseCoreCallback(IAsyncResult result)
|
|
{
|
|
if (result.CompletedSynchronously)
|
|
{
|
|
return;
|
|
}
|
|
CloseAsyncResult self = (CloseAsyncResult)(result.AsyncState);
|
|
Exception completionException = null;
|
|
try
|
|
{
|
|
self.sessionChannel.EndCloseCore(result);
|
|
}
|
|
#pragma warning suppress 56500 // covered by FxCOP
|
|
catch (Exception e)
|
|
{
|
|
if (Fx.IsFatal(e))
|
|
{
|
|
throw;
|
|
}
|
|
completionException = e;
|
|
}
|
|
self.Complete(false, completionException);
|
|
}
|
|
|
|
public static void End(IAsyncResult result)
|
|
{
|
|
AsyncResult.End<CloseAsyncResult>(result);
|
|
}
|
|
}
|
|
|
|
class SoapSecurityServerDuplexSession : SoapSecurityInputSession, IDuplexSession
|
|
{
|
|
ServerSecurityDuplexSessionChannel channel;
|
|
|
|
public SoapSecurityServerDuplexSession(SecurityContextSecurityToken sessionToken, SecuritySessionServerSettings settings, ServerSecurityDuplexSessionChannel channel)
|
|
: base(sessionToken, settings, channel)
|
|
{
|
|
this.channel = channel;
|
|
}
|
|
|
|
public void CloseOutputSession()
|
|
{
|
|
this.CloseOutputSession(this.channel.DefaultCloseTimeout);
|
|
}
|
|
|
|
public void CloseOutputSession(TimeSpan timeout)
|
|
{
|
|
this.channel.ThrowIfFaulted();
|
|
this.channel.ThrowIfNotOpened();
|
|
Exception pendingException = null;
|
|
try
|
|
{
|
|
this.channel.CloseOutputSession(timeout);
|
|
}
|
|
#pragma warning suppress 56500 // covered by FxCOP
|
|
catch (Exception e)
|
|
{
|
|
if (Fx.IsFatal(e))
|
|
{
|
|
throw;
|
|
}
|
|
pendingException = e;
|
|
}
|
|
if (pendingException != null)
|
|
{
|
|
this.channel.Fault(pendingException);
|
|
if (pendingException is CommunicationException)
|
|
{
|
|
throw pendingException;
|
|
}
|
|
else
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(pendingException);
|
|
}
|
|
}
|
|
}
|
|
|
|
public IAsyncResult BeginCloseOutputSession(AsyncCallback callback, object state)
|
|
{
|
|
return this.BeginCloseOutputSession(this.channel.DefaultCloseTimeout, callback, state);
|
|
}
|
|
|
|
public IAsyncResult BeginCloseOutputSession(TimeSpan timeout, AsyncCallback callback, object state)
|
|
{
|
|
this.channel.ThrowIfFaulted();
|
|
this.channel.ThrowIfNotOpened();
|
|
Exception pendingException = null;
|
|
try
|
|
{
|
|
return this.channel.BeginCloseOutputSession(timeout, callback, state);
|
|
}
|
|
#pragma warning suppress 56500 // covered by FxCOP
|
|
catch (Exception e)
|
|
{
|
|
if (Fx.IsFatal(e))
|
|
{
|
|
throw;
|
|
}
|
|
pendingException = e;
|
|
}
|
|
if (pendingException != null)
|
|
{
|
|
this.channel.Fault(pendingException);
|
|
if (pendingException is CommunicationException)
|
|
{
|
|
throw pendingException;
|
|
}
|
|
else
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(pendingException);
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public void EndCloseOutputSession(IAsyncResult result)
|
|
{
|
|
Exception pendingException = null;
|
|
try
|
|
{
|
|
this.channel.EndCloseOutputSession(result);
|
|
}
|
|
#pragma warning suppress 56500 // covered by FxCOP
|
|
catch (Exception e)
|
|
{
|
|
if (Fx.IsFatal(e))
|
|
{
|
|
throw;
|
|
}
|
|
pendingException = e;
|
|
}
|
|
if (pendingException != null)
|
|
{
|
|
this.channel.Fault(pendingException);
|
|
if (pendingException is CommunicationException)
|
|
{
|
|
throw pendingException;
|
|
}
|
|
else
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(pendingException);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
internal class SecuritySessionDemuxFailureHandler : IChannelDemuxFailureHandler
|
|
{
|
|
SecurityStandardsManager standardsManager;
|
|
|
|
public SecuritySessionDemuxFailureHandler(SecurityStandardsManager standardsManager)
|
|
{
|
|
if (standardsManager == null)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("standardsManager");
|
|
}
|
|
this.standardsManager = standardsManager;
|
|
}
|
|
|
|
public void HandleDemuxFailure(Message message)
|
|
{
|
|
if (message == null)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("message");
|
|
}
|
|
if (DiagnosticUtility.ShouldTraceWarning)
|
|
{
|
|
TraceUtility.TraceEvent(TraceEventType.Warning, TraceCode.SecuritySessionDemuxFailure, SR.GetString(SR.TraceCodeSecuritySessionDemuxFailure), message);
|
|
}
|
|
}
|
|
|
|
public Message CreateSessionDemuxFaultMessage(Message message)
|
|
{
|
|
MessageFault fault = SecurityUtils.CreateSecurityContextNotFoundFault(this.standardsManager, message.Headers.Action);
|
|
Message faultMessage = Message.CreateMessage(message.Version, fault, message.Version.Addressing.DefaultFaultAction);
|
|
if (message.Headers.MessageId != null)
|
|
{
|
|
faultMessage.InitializeReply(message);
|
|
}
|
|
return faultMessage;
|
|
}
|
|
|
|
IAsyncResult BeginHandleDemuxFailure<TFaultContext>(Message message, TFaultContext faultContext, AsyncCallback callback, object state)
|
|
{
|
|
this.HandleDemuxFailure(message);
|
|
Message fault = CreateSessionDemuxFaultMessage(message);
|
|
return new SendFaultAsyncResult<TFaultContext>(fault, faultContext, callback, state);
|
|
}
|
|
|
|
public IAsyncResult BeginHandleDemuxFailure(Message message, RequestContext faultContext, AsyncCallback callback, object state)
|
|
{
|
|
return BeginHandleDemuxFailure<RequestContext>(message, faultContext, callback, state);
|
|
}
|
|
|
|
public IAsyncResult BeginHandleDemuxFailure(Message message, IOutputChannel faultContext, AsyncCallback callback, object state)
|
|
{
|
|
return BeginHandleDemuxFailure<IOutputChannel>(message, faultContext, callback, state);
|
|
}
|
|
|
|
public void EndHandleDemuxFailure(IAsyncResult result)
|
|
{
|
|
if (result is SendFaultAsyncResult<RequestContext>)
|
|
{
|
|
SendFaultAsyncResult<RequestContext>.End(result);
|
|
}
|
|
else if (result is SendFaultAsyncResult<IOutputChannel>)
|
|
{
|
|
SendFaultAsyncResult<IOutputChannel>.End(result);
|
|
}
|
|
else
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.GetString(SR.InvalidAsyncResult), "result"));
|
|
}
|
|
}
|
|
|
|
class SendFaultAsyncResult<TFaultContext> : AsyncResult
|
|
{
|
|
Message message;
|
|
static AsyncCallback sendCallback = Fx.ThunkCallback(new AsyncCallback(SendCallback));
|
|
TFaultContext faultContext;
|
|
|
|
public SendFaultAsyncResult(Message fault, TFaultContext faultContext, AsyncCallback callback, object state)
|
|
: base(callback, state)
|
|
{
|
|
this.faultContext = faultContext;
|
|
this.message = fault;
|
|
IAsyncResult result = BeginSend(fault);
|
|
if (!result.CompletedSynchronously)
|
|
{
|
|
return;
|
|
}
|
|
EndSend(result);
|
|
Complete(true);
|
|
}
|
|
|
|
IAsyncResult BeginSend(Message message)
|
|
{
|
|
bool throwing = true;
|
|
try
|
|
{
|
|
IAsyncResult result = null;
|
|
if (faultContext is RequestContext)
|
|
{
|
|
result = ((RequestContext)(object)faultContext).BeginReply(message, sendCallback, this);
|
|
}
|
|
else
|
|
{
|
|
result = ((IOutputChannel)faultContext).BeginSend(message, sendCallback, this);
|
|
}
|
|
throwing = false;
|
|
return result;
|
|
}
|
|
finally
|
|
{
|
|
if (throwing && message != null)
|
|
{
|
|
message.Close();
|
|
}
|
|
}
|
|
}
|
|
|
|
void EndSend(IAsyncResult result)
|
|
{
|
|
using (this.message)
|
|
{
|
|
if (faultContext is RequestContext)
|
|
{
|
|
((RequestContext)(object)faultContext).EndReply(result);
|
|
}
|
|
else
|
|
{
|
|
((IOutputChannel)faultContext).EndSend(result);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void SendCallback(IAsyncResult result)
|
|
{
|
|
if (result.CompletedSynchronously)
|
|
{
|
|
return;
|
|
}
|
|
SendFaultAsyncResult<TFaultContext> self = (SendFaultAsyncResult<TFaultContext>)result.AsyncState;
|
|
Exception completionException = null;
|
|
try
|
|
{
|
|
self.EndSend(result);
|
|
}
|
|
#pragma warning suppress 56500 // covered by FxCOP
|
|
catch (Exception e)
|
|
{
|
|
if (Fx.IsFatal(e))
|
|
{
|
|
throw;
|
|
}
|
|
|
|
completionException = e;
|
|
}
|
|
self.Complete(false, completionException);
|
|
}
|
|
|
|
internal static void End(IAsyncResult result)
|
|
{
|
|
AsyncResult.End<SendFaultAsyncResult<TFaultContext>>(result);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|