File: FrameworkFork\System.ServiceModel\System\ServiceModel\NetTcpBinding.cs
Web Access
Project: src\src\dotnet-svcutil\lib\src\dotnet-svcutil-lib.csproj (dotnet-svcutil-lib)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
using System.ComponentModel;
using System.Diagnostics.Contracts;
using System.ServiceModel.Channels;
using Microsoft.Xml;
 
namespace System.ServiceModel
{
    public class NetTcpBinding : Binding
    {
        private OptionalReliableSession _reliableSession;
        // private BindingElements
        private TcpTransportBindingElement _transport;
        private BinaryMessageEncodingBindingElement _encoding;
        private TransactionFlowBindingElement _context;
        private ReliableSessionBindingElement _session;
        private long _maxBufferPoolSize;
        private NetTcpSecurity _security = new NetTcpSecurity();
 
        public NetTcpBinding() { Initialize(); }
        public NetTcpBinding(SecurityMode securityMode)
            : this()
        {
            _security.Mode = securityMode;
        }
 
        public NetTcpBinding(SecurityMode securityMode, bool reliableSessionEnabled)
            : this(securityMode)
        {
            this.ReliableSession.Enabled = reliableSessionEnabled;
        }
 
 
        private NetTcpBinding(TcpTransportBindingElement transport, BinaryMessageEncodingBindingElement encoding, TransactionFlowBindingElement context, ReliableSessionBindingElement session, NetTcpSecurity security)
            : this()
        {
            _security = security;
            this.ReliableSession.Enabled = session != null;
            InitializeFrom(transport, encoding, context, session);
        }
 
 
        private NetTcpBinding(TcpTransportBindingElement transport,
                      BinaryMessageEncodingBindingElement encoding,
                      NetTcpSecurity security)
            : this()
        {
            _security = security;
        }
 
        [DefaultValue(ConnectionOrientedTransportDefaults.TransferMode)]
        public TransferMode TransferMode
        {
            get { return _transport.TransferMode; }
            set { _transport.TransferMode = value; }
        }
 
        [DefaultValue(ConnectionOrientedTransportDefaults.HostNameComparisonMode)]
        public HostNameComparisonMode HostNameComparisonMode
        {
            get { return _transport.HostNameComparisonMode; }
            set { _transport.HostNameComparisonMode = value; }
        }
 
        [DefaultValue(TransportDefaults.MaxBufferPoolSize)]
        public long MaxBufferPoolSize
        {
            get { return _maxBufferPoolSize; }
            set
            {
                _maxBufferPoolSize = value;
            }
        }
 
        [DefaultValue(TransportDefaults.MaxBufferSize)]
        public int MaxBufferSize
        {
            get { return _transport.MaxBufferSize; }
            set { _transport.MaxBufferSize = value; }
        }
 
        [DefaultValue(TransportDefaults.MaxReceivedMessageSize)]
        public long MaxReceivedMessageSize
        {
            get { return _transport.MaxReceivedMessageSize; }
            set { _transport.MaxReceivedMessageSize = value; }
        }
 
        [DefaultValue(TcpTransportDefaults.PortSharingEnabled)]
        public bool PortSharingEnabled
        {
            get { return _transport.PortSharingEnabled; }
            set { _transport.PortSharingEnabled = value; }
        }
 
        public XmlDictionaryReaderQuotas ReaderQuotas
        {
            get { return _encoding.ReaderQuotas; }
            set
            {
                if (value == null)
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("value");
                value.CopyTo(_encoding.ReaderQuotas);
            }
        }
 
        public OptionalReliableSession ReliableSession
        {
            get
            {
                return _reliableSession;
            }
            set
            {
                if (value == null)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("value"));
                }
                _reliableSession.CopySettings(value);
            }
        }
 
        public override string Scheme { get { return _transport.Scheme; } }
 
        public EnvelopeVersion EnvelopeVersion
        {
            get { return EnvelopeVersion.Soap12; }
        }
 
        public NetTcpSecurity Security
        {
            get { return _security; }
            set
            {
                if (value == null)
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("value");
                _security = value;
            }
        }
 
        public bool TransactionFlow
        {
            get { return _context.Transactions; }
            set { _context.Transactions = value; }
        }
 
        public TransactionProtocol TransactionProtocol
        {
            get { return _context.TransactionProtocol; }
            set { _context.TransactionProtocol = value; }
        }
 
        private static TransactionFlowBindingElement GetDefaultTransactionFlowBindingElement()
        {
            return new TransactionFlowBindingElement(NetTcpDefaults.TransactionsEnabled);
        }
 
        private void Initialize()
        {
            _transport = new TcpTransportBindingElement();
            _encoding = new BinaryMessageEncodingBindingElement();
            _context = new TransactionFlowBindingElement(NetTcpDefaults.TransactionsEnabled);
            _session = new ReliableSessionBindingElement();
            _reliableSession = new OptionalReliableSession(_session);
 
            // NetNative and CoreCLR initialize to what TransportBindingElement does in the desktop
            // This property is not available in shipped contracts
            _maxBufferPoolSize = TransportDefaults.MaxBufferPoolSize;
        }
 
        private void InitializeFrom(TcpTransportBindingElement transport, BinaryMessageEncodingBindingElement encoding, TransactionFlowBindingElement context, ReliableSessionBindingElement session)
        {
            // TODO: Fx.Assert(transport != null, "Invalid (null) transport value.");
            // TODO: Fx.Assert(encoding != null, "Invalid (null) encoding value.");
            // TODO: Fx.Assert(context != null, "Invalid (null) context value.");
            // TODO: Fx.Assert(security != null, "Invalid (null) security value.");
 
            // transport
            this.HostNameComparisonMode = transport.HostNameComparisonMode;
            this.MaxBufferPoolSize = transport.MaxBufferPoolSize;
            this.MaxBufferSize = transport.MaxBufferSize;
 
            this.MaxReceivedMessageSize = transport.MaxReceivedMessageSize;
            this.PortSharingEnabled = transport.PortSharingEnabled;
            this.TransferMode = transport.TransferMode;
 
            // encoding
            this.ReaderQuotas = encoding.ReaderQuotas;
 
            // context
            this.TransactionFlow = context.Transactions;
            this.TransactionProtocol = context.TransactionProtocol;
 
            //session
            if (session != null)
            {
                // only set properties that have standard binding manifestations
                _session.InactivityTimeout = session.InactivityTimeout;
                _session.Ordered = session.Ordered;
            }
        }
 
        // check that properties of the HttpTransportBindingElement and 
        // MessageEncodingBindingElement not exposed as properties on BasicHttpBinding 
        // match default values of the binding elements
        private bool IsBindingElementsMatch(TcpTransportBindingElement transport, BinaryMessageEncodingBindingElement encoding)
        {
            if (!_transport.IsMatch(transport))
                return false;
 
            if (!_encoding.IsMatch(encoding))
                return false;
 
            if (!_context.IsMatch(_context))
                return false;
 
            if (_reliableSession.Enabled)
            {
                if (!_session.IsMatch(_session))
                    return false;
            }
 
            return true;
        }
 
        // check that properties of the HttpTransportBindingElement and 
        // MessageEncodingBindingElement not exposed as properties on BasicHttpBinding 
        // match default values of the binding elements
        private bool IsBindingElementsMatch(TcpTransportBindingElement transport, BinaryMessageEncodingBindingElement encoding, TransactionFlowBindingElement context, ReliableSessionBindingElement session)
        {
            if (!_transport.IsMatch(transport))
                return false;
            if (!_encoding.IsMatch(encoding))
                return false;
            if (!_context.IsMatch(context))
                return false;
            if (_reliableSession.Enabled)
            {
                if (!_session.IsMatch(session))
                    return false;
            }
            else if (session != null)
                return false;
 
            return true;
        }
 
        private void CheckSettings()
        {
#if FEATURE_NETNATIVE // In .NET Native, some settings for the binding security are not supported; this check is not necessary for CoreCLR
                      
            NetTcpSecurity security = this.Security;
            if (security == null)
            {
                return;
            }
 
            SecurityMode mode = security.Mode;
            if (mode == SecurityMode.None)
            {
                return;
            }
            else if (mode == SecurityMode.Message)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.Format(SR.UnsupportedSecuritySetting, "Mode", mode)));
            }
 
            // Message.ClientCredentialType = Certificate, IssuedToken or Windows are not supported.
            if (mode == SecurityMode.TransportWithMessageCredential)
            {
                MessageSecurityOverTcp message = security.Message;
                if (message != null)
                {
                    MessageCredentialType mct = message.ClientCredentialType;
                    if ((mct == MessageCredentialType.Certificate) || (mct == MessageCredentialType.IssuedToken) || (mct == MessageCredentialType.Windows))
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.Format(SR.UnsupportedSecuritySetting, "Message.ClientCredentialType", mct)));
                    }
                }
            }
 
            // Transport.ClientCredentialType = Certificate is not supported.
            Contract.Assert((mode == SecurityMode.Transport) || (mode == SecurityMode.TransportWithMessageCredential), "Unexpected SecurityMode value: " + mode);
            TcpTransportSecurity transport = security.Transport;
            if ((transport != null) && (transport.ClientCredentialType == TcpClientCredentialType.Certificate))
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.Format(SR.UnsupportedSecuritySetting, "Transport.ClientCredentialType", transport.ClientCredentialType)));
            }
#endif // FEATURE_NETNATIVE
        }
 
        public override BindingElementCollection CreateBindingElements()
        {
            this.CheckSettings();
 
            // return collection of BindingElements
            BindingElementCollection bindingElements = new BindingElementCollection();
            // order of BindingElements is important
            bindingElements.Add(_context);
            // add session
            if (_reliableSession.Enabled)
                bindingElements.Add(_session);
            // add security (*optional)
            SecurityBindingElement wsSecurity = CreateMessageSecurity();
            if (wsSecurity != null)
                bindingElements.Add(wsSecurity);
            // add encoding
            bindingElements.Add(_encoding);
            // add transport security
            BindingElement transportSecurity = CreateTransportSecurity();
            if (transportSecurity != null)
            {
                bindingElements.Add(transportSecurity);
            }
 
            // add transport (tcp)
            bindingElements.Add(_transport);
 
            return bindingElements.Clone();
        }
 
        internal static bool TryCreate(BindingElementCollection elements, out Binding binding)
        {
            binding = null;
            if (elements.Count > 6)
                return false;
 
            // collect all binding elements
            TcpTransportBindingElement transport = null;
            BinaryMessageEncodingBindingElement encoding = null;
            TransactionFlowBindingElement context = null;
            ReliableSessionBindingElement session = null;
            SecurityBindingElement wsSecurity = null;
            BindingElement transportSecurity = null;
 
            foreach (BindingElement element in elements)
            {
                if (element is SecurityBindingElement)
                    wsSecurity = element as SecurityBindingElement;
                else if (element is TransportBindingElement)
                    transport = element as TcpTransportBindingElement;
                else if (element is MessageEncodingBindingElement)
                    encoding = element as BinaryMessageEncodingBindingElement;
                else if (element is TransactionFlowBindingElement)
                    context = element as TransactionFlowBindingElement;
                else if (element is ReliableSessionBindingElement)
                    session = element as ReliableSessionBindingElement;
                else
                {
                    if (transportSecurity != null)
                        return false;
                    transportSecurity = element;
                }
            }
 
            if (transport == null)
                return false;
            if (encoding == null)
                return false;
            if (context == null)
                context = GetDefaultTransactionFlowBindingElement();
 
            TcpTransportSecurity tcpTransportSecurity = new TcpTransportSecurity();
            UnifiedSecurityMode mode = GetModeFromTransportSecurity(transportSecurity);
 
            NetTcpSecurity security;
            if (!TryCreateSecurity(wsSecurity, mode, session != null, transportSecurity, tcpTransportSecurity, out security))
                return false;
 
            if (!SetTransportSecurity(transportSecurity, security.Mode, tcpTransportSecurity))
                return false;
 
            NetTcpBinding netTcpBinding = new NetTcpBinding(transport, encoding, context, session, security);
            if (!netTcpBinding.IsBindingElementsMatch(transport, encoding, context, session))
                return false;
 
            binding = netTcpBinding;
            return true;
        }
 
 
        private BindingElement CreateTransportSecurity()
        {
            return _security.CreateTransportSecurity();
        }
 
        private static UnifiedSecurityMode GetModeFromTransportSecurity(BindingElement transport)
        {
            return NetTcpSecurity.GetModeFromTransportSecurity(transport);
        }
 
        private static bool SetTransportSecurity(BindingElement transport, SecurityMode mode, TcpTransportSecurity transportSecurity)
        {
            return NetTcpSecurity.SetTransportSecurity(transport, mode, transportSecurity);
        }
 
        private SecurityBindingElement CreateMessageSecurity()
        {
            if (_security.Mode == SecurityMode.Message)
            {
                throw ExceptionHelper.PlatformNotSupported(nameof(SecurityMode.Message));
            }
            if (_security.Mode == SecurityMode.TransportWithMessageCredential)
            {
                return _security.CreateMessageSecurity(ReliableSession.Enabled);
            }
            else
            {
                return null;
            }
        }
 
        private static bool TryCreateSecurity(SecurityBindingElement sbe, UnifiedSecurityMode mode, bool isReliableSession, BindingElement transportSecurity, TcpTransportSecurity tcpTransportSecurity, out NetTcpSecurity security)
        {
            if (sbe != null)
                mode &= UnifiedSecurityMode.Message | UnifiedSecurityMode.TransportWithMessageCredential;
            else
                mode &= ~(UnifiedSecurityMode.Message | UnifiedSecurityMode.TransportWithMessageCredential);
 
            SecurityMode securityMode = SecurityModeHelper.ToSecurityMode(mode);
            // TODO: Fx.Assert(SecurityModeHelper.IsDefined(securityMode), string.Format("Invalid SecurityMode value: {0}.", securityMode.ToString()));
 
            if (NetTcpSecurity.TryCreate(sbe, securityMode, isReliableSession, transportSecurity, tcpTransportSecurity, out security))
                return true;
 
            return false;
        }
    }
}