The Labs \ Source Viewer \ SSCLI \ System.Runtime.Remoting.Channels.Tcp \ TcpClientTransportSink

  1. // ==++==
  2. //
  3. //
  4. // Copyright (c) 2006 Microsoft Corporation. All rights reserved.
  5. //
  6. // The use and distribution terms for this software are contained in the file
  7. // named license.txt, which can be found in the root of this distribution.
  8. // By using this software in any fashion, you are agreeing to be bound by the
  9. // terms of this license.
  10. //
  11. // You must not remove this notice, or any other, from this software.
  12. //
  13. //
  14. // ==--==
  15. //===========================================================================
  16. // File: TcpClientChannel.cs
  17. //
  18. // Summary: Implements a channel that transmits method calls over TCP.
  19. //
  20. //==========================================================================
  21. using System;
  22. using System.Collections;
  23. using System.IO;
  24. using System.Net;
  25. using System.Net.Security;
  26. using System.Net.Sockets;
  27. using System.Runtime.Remoting;
  28. using System.Runtime.Remoting.Channels;
  29. using System.Runtime.Remoting.Messaging;
  30. using System.Security.Cryptography.X509Certificates;
  31. using System.Threading;
  32. using System.Text;
  33. using System.Globalization;
  34. using System.Security.Principal;
  35. using System.Security.Permissions;
  36. namespace System.Runtime.Remoting.Channels.Tcp
  37. {
  38.    
  39.     public class TcpClientChannel : IChannelSender, ISecurableChannel
  40.     {
  41.         private int _channelPriority = 1;
  42.         // channel priority
  43.         private string _channelName = "tcp";
  44.         // channel name
  45.         private bool _secure = false;
  46.         private IDictionary _prop = null;
  47.        
  48.         private IClientChannelSinkProvider _sinkProvider = null;
  49.         // sink chain provider
  50.        
  51.         public TcpClientChannel()
  52.         {
  53.             SetupChannel();
  54.         }
  55.         // TcpClientChannel
  56.        
  57.         public TcpClientChannel(string name, IClientChannelSinkProvider sinkProvider)
  58.         {
  59.             _channelName = name;
  60.             _sinkProvider = sinkProvider;
  61.            
  62.             SetupChannel();
  63.         }
  64.        
  65.        
  66.         // constructor used by config file
  67.         public TcpClientChannel(IDictionary properties, IClientChannelSinkProvider sinkProvider)
  68.         {
  69.             if (properties != null) {
  70.                 _prop = properties;
  71.                 foreach (DictionaryEntry entry in properties) {
  72.                     switch ((string)entry.Key) {
  73.                         case "name":
  74.                             _channelName = (string)entry.Value;
  75.                             break;
  76.                         case "priority":
  77.                             _channelPriority = Convert.ToInt32(entry.Value, CultureInfo.InvariantCulture);
  78.                             break;
  79.                         case "secure":
  80.                             _secure = Convert.ToBoolean(entry.Value, CultureInfo.InvariantCulture);
  81.                             break;
  82.                         default:
  83.                             break;
  84.                     }
  85.                 }
  86.             }
  87.            
  88.             _sinkProvider = sinkProvider;
  89.             SetupChannel();
  90.         }
  91.         // TcpClientChannel
  92.        
  93.         private void SetupChannel()
  94.         {
  95.             if (_sinkProvider != null) {
  96.                 CoreChannel.AppendProviderToClientProviderChain(_sinkProvider, new TcpClientTransportSinkProvider(_prop));
  97.             }
  98.             else
  99.                 _sinkProvider = CreateDefaultClientProviderChain();
  100.         }
  101.         // SetupChannel
  102.         //
  103.         // ISecurableChannel implementation
  104.         //
  105.         public bool IsSecured {
  106.             [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.Infrastructure, Infrastructure = true)]
  107.             get { return _secure; }
  108.             [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.Infrastructure, Infrastructure = true)]
  109.             set { _secure = value; }
  110.         }
  111.        
  112.         //
  113.         // IChannel implementation
  114.         //
  115.        
  116.         public int ChannelPriority {
  117.             [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.Infrastructure, Infrastructure = true)]
  118.             get { return _channelPriority; }
  119.         }
  120.        
  121.         public string ChannelName {
  122.             [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.Infrastructure, Infrastructure = true)]
  123.             get { return _channelName; }
  124.         }
  125.        
  126.         // returns channelURI and places object uri into out parameter
  127.         [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.Infrastructure, Infrastructure = true)]
  128.         public string Parse(string url, out string objectURI)
  129.         {
  130.             return TcpChannelHelper.ParseURL(url, out objectURI);
  131.         }
  132.         // Parse
  133.         //
  134.         // end of IChannel implementation
  135.         //
  136.        
  137.        
  138.        
  139.         //
  140.         // IChannelSender implementation
  141.         //
  142.        
  143.         [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.Infrastructure, Infrastructure = true)]
  144.         public virtual IMessageSink CreateMessageSink(string url, object remoteChannelData, out string objectURI)
  145.         {
  146.             // Set the out parameters
  147.             objectURI = null;
  148.             string channelURI = null;
  149.            
  150.            
  151.             // Is this a well known object?
  152.             if (url != null) {
  153.                 // Parse returns null if this is not one of our url's
  154.                 channelURI = Parse(url, out objectURI);
  155.             }
  156.             // determine if we want to connect based on the channel data
  157.             else {
  158.                 if (remoteChannelData != null) {
  159.                     if (remoteChannelData is IChannelDataStore) {
  160.                         IChannelDataStore cds = (IChannelDataStore)remoteChannelData;
  161.                        
  162.                         // see if this is an tcp uri
  163.                         string simpleChannelUri = Parse(cds.ChannelUris[0], out objectURI);
  164.                         if (simpleChannelUri != null)
  165.                             channelURI = cds.ChannelUris[0];
  166.                     }
  167.                 }
  168.             }
  169.            
  170.             if (null != channelURI) {
  171.                 if (url == null)
  172.                     url = channelURI;
  173.                
  174.                 IClientChannelSink sink = _sinkProvider.CreateSink(this, url, remoteChannelData);
  175.                
  176.                 // return sink after making sure that it implements IMessageSink
  177.                 IMessageSink msgSink = sink as IMessageSink;
  178.                 if ((sink != null) && (msgSink == null)) {
  179.                     throw new RemotingException(CoreChannel.GetResourceString("Remoting_Channels_ChannelSinkNotMsgSink"));
  180.                 }
  181.                
  182.                 return msgSink;
  183.             }
  184.            
  185.             return null;
  186.         }
  187.         // CreateMessageSink
  188.        
  189.         //
  190.         // end of IChannelSender implementation
  191.         //
  192.        
  193.         private IClientChannelSinkProvider CreateDefaultClientProviderChain()
  194.         {
  195.             IClientChannelSinkProvider chain = new BinaryClientFormatterSinkProvider();
  196.             IClientChannelSinkProvider sink = chain;
  197.            
  198.             sink.Next = new TcpClientTransportSinkProvider(_prop);
  199.            
  200.             return chain;
  201.         }
  202.         // CreateDefaultClientProviderChain
  203.     }
  204.     // class TcpClientChannel
  205.    
  206.    
  207.    
  208.     internal class TcpClientTransportSinkProvider : IClientChannelSinkProvider
  209.     {
  210.         IDictionary _prop = null;
  211.         internal TcpClientTransportSinkProvider(IDictionary properties)
  212.         {
  213.             _prop = properties;
  214.         }
  215.        
  216.         public IClientChannelSink CreateSink(IChannelSender channel, string url, object remoteChannelData)
  217.         {
  218.             // url is set to the channel uri in CreateMessageSink
  219.             TcpClientTransportSink sink = new TcpClientTransportSink(url, (TcpClientChannel)channel);
  220.             if (_prop != null) {
  221.                 // Tansfer all properties from the channel to the sink
  222.                 foreach (object key in _prop.Keys) {
  223.                     sink[key] = _prop[key];
  224.                 }
  225.             }
  226.             return sink;
  227.         }
  228.        
  229.         public IClientChannelSinkProvider Next {
  230.             get { return null; }
  231.             set {
  232.                 throw new NotSupportedException();
  233.             }
  234.         }
  235.     }
  236.     // class TcpClientTransportSinkProvider
  237.    
  238.    
  239.     internal class TcpClientTransportSink : BaseChannelSinkWithProperties, IClientChannelSink
  240.     {
  241.         // socket cache
  242.         internal SocketCache ClientSocketCache;
  243.         private bool authSet = false;
  244.        
  245.         private SocketHandler CreateSocketHandler(Socket socket, SocketCache socketCache, string machinePortAndSid)
  246.         {
  247.             Stream netStream = new SocketStream(socket);
  248.            
  249.             // Check if authentication is requested
  250.             if (_channel.IsSecured) {
  251.                 throw new NotSupportedException();
  252.             }
  253.            
  254.             return new TcpClientSocketHandler(socket, machinePortAndSid, netStream, this);
  255.         }
  256.         // CreateSocketHandler
  257.        
  258.         // returns the connectiongroupname else the Sid for current credentials
  259.         private string GetSid()
  260.         {
  261.             if (_connectionGroupName != null) {
  262.                 return _connectionGroupName;
  263.             }
  264.             return null;
  265.         }
  266.        
  267.         // transport sink state
  268.         private string m_machineName;
  269.         private int m_port;
  270.         private TcpClientChannel _channel = null;
  271.        
  272.         private string _machineAndPort;
  273.         private const string UserNameKey = "username";
  274.         private const string PasswordKey = "password";
  275.         private const string DomainKey = "domain";
  276.         private const string ProtectionLevelKey = "protectionlevel";
  277.         private const string ConnectionGroupNameKey = "connectiongroupname";
  278.         private const string SocketCacheTimeoutKey = "socketcachetimeout";
  279.         private const string ReceiveTimeoutKey = "timeout";
  280.         private const string SocketCachePolicyKey = "socketcachepolicy";
  281.         private const string SPNKey = "serviceprincipalname";
  282.         private const string RetryCountKey = "retrycount";
  283.        
  284.         // property values
  285.         private string _securityUserName = null;
  286.         private string _securityPassword = null;
  287.         private string _securityDomain = null;
  288.         private string _connectionGroupName = null;
  289.         private TimeSpan _socketCacheTimeout = TimeSpan.FromSeconds(10);
  290.         // default timeout is 5 seconds
  291.         private int _receiveTimeout = 0;
  292.         // default timeout is infinite
  293.         private SocketCachePolicy _socketCachePolicy = SocketCachePolicy.Default;
  294.         // default is v1.0 behaviour
  295.         private string _spn = string.Empty;
  296.         private int _retryCount = 1;
  297.         // defualt retry is 1 to keep it equivalent with v1.1
  298.         private ProtectionLevel _protectionLevel = ProtectionLevel.EncryptAndSign;
  299.         private static ICollection s_keySet = null;
  300.        
  301.         internal TcpClientTransportSink(string channelURI, TcpClientChannel channel)
  302.         {
  303.             string objectURI;
  304.             _channel = channel;
  305.             string simpleChannelUri = TcpChannelHelper.ParseURL(channelURI, out objectURI);
  306.             ClientSocketCache = new SocketCache(new SocketHandlerFactory(this.CreateSocketHandler), _socketCachePolicy, _socketCacheTimeout);
  307.             // extract machine name and port
  308.             Uri uri = new Uri(simpleChannelUri);
  309.            
  310.             if (uri.IsDefaultPort) {
  311.                 // If there is no colon, then there is no port number.
  312.                 throw new RemotingException(String.Format(CultureInfo.CurrentCulture, CoreChannel.GetResourceString("Remoting_Tcp_UrlMustHavePort"), channelURI));
  313.             }
  314.             m_machineName = uri.Host;
  315.             m_port = uri.Port;
  316.            
  317.             _machineAndPort = m_machineName + ":" + m_port;
  318.         }
  319.         // TcpClientTransportSink
  320.         public void ProcessMessage(IMessage msg, ITransportHeaders requestHeaders, Stream requestStream, out ITransportHeaders responseHeaders, out Stream responseStream)
  321.         {
  322.             InternalRemotingServices.RemotingTrace("TcpClientTransportSink::ProcessMessage");
  323.            
  324.             TcpClientSocketHandler clientSocket = SendRequestWithRetry(msg, requestHeaders, requestStream);
  325.            
  326.             // receive response
  327.             responseHeaders = clientSocket.ReadHeaders();
  328.             responseStream = clientSocket.GetResponseStream();
  329.            
  330.             // The client socket will be returned to the cache
  331.             // when the response stream is closed.
  332.            
  333.         }
  334.         // ProcessMessage
  335.        
  336.         public void AsyncProcessRequest(IClientChannelSinkStack sinkStack, IMessage msg, ITransportHeaders headers, Stream stream)
  337.         {
  338.             InternalRemotingServices.RemotingTrace("TcpClientTransportSink::AsyncProcessRequest");
  339.            
  340.             TcpClientSocketHandler clientSocket = SendRequestWithRetry(msg, headers, stream);
  341.            
  342.             if (clientSocket.OneWayRequest) {
  343.                 clientSocket.ReturnToCache();
  344.             }
  345.             else {
  346.                 // do an async read on the reply
  347.                 clientSocket.DataArrivedCallback = new WaitCallback(this.ReceiveCallback);
  348.                 clientSocket.DataArrivedCallbackState = sinkStack;
  349.                 clientSocket.BeginReadMessage();
  350.             }
  351.         }
  352.         // AsyncProcessRequest
  353.        
  354.         public void AsyncProcessResponse(IClientResponseChannelSinkStack sinkStack, object state, ITransportHeaders headers, Stream stream)
  355.         {
  356.             // We don't have to implement this since we are always last in the chain.
  357.             throw new NotSupportedException();
  358.         }
  359.         // AsyncProcessRequest
  360.        
  361.        
  362.         public Stream GetRequestStream(IMessage msg, ITransportHeaders headers)
  363.         {
  364.             // Currently, we require a memory stream be handed to us since we need
  365.             // the length before sending.
  366.             return null;
  367.         }
  368.         // GetRequestStream
  369.        
  370.         public IClientChannelSink NextChannelSink {
  371.             get { return null; }
  372.         }
  373.         // Next
  374.        
  375.         private TcpClientSocketHandler SendRequestWithRetry(IMessage msg, ITransportHeaders requestHeaders, Stream requestStream)
  376.         {
  377.             // If the stream is seekable, we can retry once on a failure to write.
  378.             long initialPosition = 0;
  379.             bool sendException = true;
  380.             bool bCanSeek = requestStream.CanSeek;
  381.             if (bCanSeek)
  382.                 initialPosition = requestStream.Position;
  383.            
  384.             TcpClientSocketHandler clientSocket = null;
  385.             // Add the sid string only if the channel is secure.
  386.             string machinePortAndSid = _machineAndPort + (_channel.IsSecured ? "/" + GetSid() : null);
  387.            
  388.             // The authentication config entries are only valid if secure is true
  389.             if (authSet && !_channel.IsSecured)
  390.                 throw new RemotingException(CoreChannel.GetResourceString("Remoting_Tcp_AuthenticationConfigClient"));
  391.            
  392.             // If explicitUserName is set but connectionGroupName isnt we will need to authenticate on each call
  393.             bool openNewAlways = (_channel.IsSecured) && (_securityUserName != null) && (_connectionGroupName == null);
  394.            
  395.             try {
  396.                 clientSocket = (TcpClientSocketHandler)ClientSocketCache.GetSocket(machinePortAndSid, openNewAlways);
  397.                 clientSocket.SendRequest(msg, requestHeaders, requestStream);
  398.             }
  399.             catch (SocketException) {
  400.                 // Retry sending if socketexception occured, stream is seekable, retrycount times
  401.                 for (int count = 0; (count < _retryCount) && (bCanSeek && sendException); count++) {
  402.                     // retry sending if possible
  403.                     try {
  404.                         // reset position...
  405.                         requestStream.Position = initialPosition;
  406.                        
  407.                         // ...and try again.
  408.                         clientSocket = (TcpClientSocketHandler)ClientSocketCache.GetSocket(machinePortAndSid, openNewAlways);
  409.                        
  410.                         clientSocket.SendRequest(msg, requestHeaders, requestStream);
  411.                         sendException = false;
  412.                     }
  413.                     catch (SocketException) {
  414.                     }
  415.                 }
  416.                 if (sendException) {
  417.                     throw;
  418.                 }
  419.             }
  420.            
  421.             requestStream.Close();
  422.            
  423.             return clientSocket;
  424.         }
  425.         // SendRequestWithRetry
  426.        
  427.         private void ReceiveCallback(object state)
  428.         {
  429.             TcpClientSocketHandler clientSocket = null;
  430.             IClientChannelSinkStack sinkStack = null;
  431.            
  432.             try {
  433.                 clientSocket = (TcpClientSocketHandler)state;
  434.                 sinkStack = (IClientChannelSinkStack)clientSocket.DataArrivedCallbackState;
  435.                
  436.                 ITransportHeaders responseHeaders = clientSocket.ReadHeaders();
  437.                 Stream responseStream = clientSocket.GetResponseStream();
  438.                
  439.                 // call down the sink chain
  440.                 sinkStack.AsyncProcessResponse(responseHeaders, responseStream);
  441.             }
  442.             catch (Exception e) {
  443.                 try {
  444.                     if (sinkStack != null)
  445.                         sinkStack.DispatchException(e);
  446.                 }
  447.                 catch {
  448.                     // Fatal Error.. ignore
  449.                 }
  450.             }
  451.             catch {
  452.                 try {
  453.                     if (sinkStack != null)
  454.                         sinkStack.DispatchException(new Exception(CoreChannel.GetResourceString("Remoting_nonClsCompliantException")));
  455.                 }
  456.                 catch {
  457.                     // Fatal Error.. ignore
  458.                 }
  459.             }
  460.            
  461.             // The client socket will be returned to the cache
  462.             // when the response stream is closed.
  463.            
  464.         }
  465.         // ReceiveCallback
  466.        
  467.        
  468.         //
  469.         // Properties
  470.         //
  471.         public override object this[object key]
  472.         {
  473.             get {
  474.                 string keyStr = key as string;
  475.                 if (keyStr == null)
  476.                     return null;
  477.                
  478.                 switch (keyStr.ToLower(CultureInfo.InvariantCulture)) {
  479.                     case UserNameKey:
  480.                         return _securityUserName;
  481.                     case PasswordKey:
  482.                         return null;
  483.                     case DomainKey:
  484.                         // Intentionally refuse to return password.
  485.                         return _securityDomain;
  486.                     case SocketCacheTimeoutKey:
  487.                         return _socketCacheTimeout;
  488.                     case ReceiveTimeoutKey:
  489.                         return _receiveTimeout;
  490.                     case SocketCachePolicyKey:
  491.                         return _socketCachePolicy.ToString();
  492.                     case RetryCountKey:
  493.                         return _retryCount;
  494.                     case ConnectionGroupNameKey:
  495.                         return _connectionGroupName;
  496.                     case ProtectionLevelKey:
  497.                         if (authSet)
  498.                             return _protectionLevel.ToString();
  499.                         break;
  500.                 }
  501.                 // switch (keyStr.ToLower(CultureInfo.InvariantCulture))
  502.                 return null;
  503.             }
  504.            
  505.             set {
  506.                 string keyStr = key as string;
  507.                 if (keyStr == null)
  508.                     return;
  509.                
  510.                 switch (keyStr.ToLower(CultureInfo.InvariantCulture)) {
  511.                     case UserNameKey:
  512.                         _securityUserName = (string)value;
  513.                         break;
  514.                     case PasswordKey:
  515.                         _securityPassword = (string)value;
  516.                         break;
  517.                     case DomainKey:
  518.                         _securityDomain = (string)value;
  519.                         break;
  520.                     case SocketCacheTimeoutKey:
  521.                         int timeout = Convert.ToInt32(value, CultureInfo.InvariantCulture);
  522.                         if (timeout < 0)
  523.                             throw new RemotingException(CoreChannel.GetResourceString("Remoting_Tcp_SocketTimeoutNegative"));
  524.                         _socketCacheTimeout = TimeSpan.FromSeconds(timeout);
  525.                         ClientSocketCache.SocketTimeout = _socketCacheTimeout;
  526.                         break;
  527.                     case ReceiveTimeoutKey:
  528.                         _receiveTimeout = Convert.ToInt32(value, CultureInfo.InvariantCulture);
  529.                         ClientSocketCache.ReceiveTimeout = _receiveTimeout;
  530.                         break;
  531.                     case SocketCachePolicyKey:
  532.                         _socketCachePolicy = (SocketCachePolicy)(value is SocketCachePolicy ? value : Enum.Parse(typeof(SocketCachePolicy), (string)value, true));
  533.                         ClientSocketCache.CachePolicy = _socketCachePolicy;
  534.                         break;
  535.                     case RetryCountKey:
  536.                         _retryCount = Convert.ToInt32(value, CultureInfo.InvariantCulture);
  537.                         break;
  538.                     case ConnectionGroupNameKey:
  539.                         _connectionGroupName = (string)value;
  540.                         break;
  541.                     case ProtectionLevelKey:
  542.                         _protectionLevel = (ProtectionLevel)(value is ProtectionLevel ? value : Enum.Parse(typeof(ProtectionLevel), (string)value, true));
  543.                         authSet = true;
  544.                         break;
  545.                     case SPNKey:
  546.                         _spn = (string)value;
  547.                         authSet = true;
  548.                         break;
  549.                 }
  550.                 // switch (keyStr.ToLower(CultureInfo.InvariantCulturey))
  551.             }
  552.         }
  553.         // this[]
  554.        
  555.         public override ICollection Keys {
  556.             get {
  557.                 if (s_keySet == null) {
  558.                     // Don't need to synchronize. Doesn't matter if the list gets
  559.                     // generated twice.
  560.                     ArrayList keys = new ArrayList(6);
  561.                     keys.Add(UserNameKey);
  562.                     keys.Add(PasswordKey);
  563.                     keys.Add(DomainKey);
  564.                     keys.Add(SocketCacheTimeoutKey);
  565.                     keys.Add(SocketCachePolicyKey);
  566.                     keys.Add(RetryCountKey);
  567.                     keys.Add(ProtectionLevelKey);
  568.                     keys.Add(ConnectionGroupNameKey);
  569.                     keys.Add(ReceiveTimeoutKey);
  570.                    
  571.                     s_keySet = keys;
  572.                 }
  573.                
  574.                 return s_keySet;
  575.             }
  576.         }
  577.         // Keys
  578.         //
  579.         // end of Properties
  580.         //
  581.        
  582.     }
  583.     // class TcpClientTransportSink
  584.    
  585. }
  586. // namespace namespace System.Runtime.Remoting.Channels.Tcp

Developer Fusion