811 lines · 30.6 KB · C#
RawDownload
//******************************************************************************************************//  ClientConnection.cs - Gbtc////  Copyright © 2012, Grid Protection Alliance.  All Rights Reserved.////  Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See//  the NOTICE file distributed with this work for additional information regarding copyright ownership.//  The GPA licenses this file to you under the MIT License (MIT), the "License"; you may//  not use this file except in compliance with the License. You may obtain a copy of the License at:////      http://www.opensource.org/licenses/MIT////  Unless agreed to in writing, the subject software distributed under the License is distributed on an//  "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the//  License for the specific language governing permissions and limitations.////  Code Modification History://  ----------------------------------------------------------------------------------------------------//  06/24/2011 - Ritchie//       Generated original version of source code.//  12/20/2012 - Starlynn Danyelle Gilliam//       Modified Header.////****************************************************************************************************** using System;using System.Collections.Generic;using System.Net;using System.Net.Sockets;using System.Security.Cryptography;using System.Text;using GSF.Communication;using GSF.Diagnostics;using GSF.IO;using GSF.Security.Cryptography;using GSF.Threading; namespace GSF.TimeSeries.Transport{    /// <summary>    /// Represents a <see cref="DataSubscriber"/> client connection to the <see cref="DataPublisher"/>.    /// </summary>    public class ClientConnection : IProvideStatus, IDisposable    {        #region [ Members ]         // Constants        private const int EvenKey = 0;      // Even key/IV index        private const int OddKey = 1;       // Odd key/IV index        private const int KeyIndex = 0;     // Index of cipher key component in keyIV array        private const int IVIndex = 1;      // Index of initialization vector component in keyIV array         // Fields        private DataPublisher m_parent;        private readonly Guid m_clientID;        private Guid m_subscriberID;        private readonly string m_connectionID;        private readonly string m_hostName;        private string m_subscriberAcronym;        private string m_subscriberName;        private string m_sharedSecret;        private string m_subscriberInfo;        private IClientSubscription m_subscription;        private volatile bool m_authenticated;        private volatile byte[][][] m_keyIVs;        private volatile int m_cipherIndex;        private List<IPAddress> m_validIPAddresses;        private IPAddress m_ipAddress;        private IServer m_commandChannel;        private UdpServer m_dataChannel;        private string m_configurationString;        private bool m_connectionEstablished;        private bool m_isSubscribed;        private Ticks m_lastCipherKeyUpdateTime;        private SharedTimer m_pingTimer;        private SharedTimer m_reconnectTimer;        private OperationalModes m_operationalModes;        private Encoding m_encoding;        private bool m_disposed;        private bool m_clientNotFoundExceptionExceptionOccurred;         #endregion         #region [ Constructors ]         /// <summary>        /// Creates a new <see cref="ClientConnection"/> instance.        /// </summary>        /// <param name="parent">Parent data publisher.</param>        /// <param name="clientID">Client ID of associated connection.</param>        /// <param name="commandChannel"><see cref="TcpServer"/> command channel used to lookup connection information.</param>        public ClientConnection(DataPublisher parent, Guid clientID, IServer commandChannel)        {            m_parent = parent;            m_clientID = clientID;            m_commandChannel = commandChannel;            m_subscriberID = clientID;            m_keyIVs = null;            m_cipherIndex = 0;             // Setup ping timer            m_pingTimer = Common.TimerScheduler.CreateTimer(5000);            m_pingTimer.AutoReset = true;            m_pingTimer.Elapsed += m_pingTimer_Elapsed;            m_pingTimer.Start();             // Setup reconnect timer            m_reconnectTimer = Common.TimerScheduler.CreateTimer(1000);            m_reconnectTimer.AutoReset = false;            m_reconnectTimer.Elapsed += m_reconnectTimer_Elapsed;             // Attempt to lookup remote connection identification for logging purposes            try            {                Socket commandChannelSocket = GetCommandChannelSocket();                IPEndPoint remoteEndPoint = null;                 if ((object)commandChannel != null)                    remoteEndPoint = commandChannelSocket.RemoteEndPoint as IPEndPoint;                 if ((object)remoteEndPoint != null)                {                    m_ipAddress = remoteEndPoint.Address;                     if (remoteEndPoint.AddressFamily == AddressFamily.InterNetworkV6)                        m_connectionID = "[" + m_ipAddress + "]:" + remoteEndPoint.Port;                    else                        m_connectionID = m_ipAddress + ":" + remoteEndPoint.Port;                     try                    {                        IPHostEntry ipHost = Dns.GetHostEntry(remoteEndPoint.Address);                         if (!string.IsNullOrWhiteSpace(ipHost.HostName))                        {                            m_hostName = ipHost.HostName;                            m_connectionID = m_hostName + " (" + m_connectionID + ")";                        }                    }                     // Just ignoring possible DNS lookup failures...                    catch (ArgumentNullException)                    {                        // The hostNameOrAddress parameter is null.                     }                    catch (ArgumentOutOfRangeException)                    {                        // The length of hostNameOrAddress parameter is greater than 255 characters.                     }                    catch (ArgumentException)                    {                        // The hostNameOrAddress parameter is an invalid IP address.                     }                    catch (SocketException)                    {                        // An error was encountered when resolving the hostNameOrAddress parameter.                        }                }            }            catch            {                // At worst we'll just use the client GUID for identification                m_connectionID = m_subscriberID == Guid.Empty ? clientID.ToString() : m_subscriberID.ToString();            }             if (string.IsNullOrWhiteSpace(m_connectionID))                m_connectionID = "unavailable";             if (string.IsNullOrWhiteSpace(m_hostName))            {                if ((object)m_ipAddress != null)                    m_hostName = m_ipAddress.ToString();                else                    m_hostName = m_connectionID;            }             if ((object)m_ipAddress == null)                m_ipAddress = IPAddress.None;        }         /// <summary>        /// Releases the unmanaged resources before the <see cref="ClientConnection"/> object is reclaimed by <see cref="GC"/>.        /// </summary>        ~ClientConnection()        {            Dispose(false);        }         #endregion         #region [ Properties ]         /// <summary>        /// Gets client ID of this <see cref="ClientConnection"/>.        /// </summary>        public Guid ClientID => m_clientID;         /// <summary>        /// Gets or sets reference to <see cref="UdpServer"/> data channel, attaching to or detaching from events as needed, associated with this <see cref="ClientConnection"/>.        /// </summary>        public UdpServer DataChannel        {            get            {                return m_dataChannel;            }            set            {                m_connectionEstablished = (object)value != null;                 if ((object)m_dataChannel != null)                {                    // Detach from events on existing data channel reference                    m_dataChannel.ClientConnectingException -= m_dataChannel_ClientConnectingException;                    m_dataChannel.SendClientDataException -= m_dataChannel_SendClientDataException;                    m_dataChannel.ServerStarted -= m_dataChannel_ServerStarted;                    m_dataChannel.ServerStopped -= m_dataChannel_ServerStopped;                     if (m_dataChannel != value)                        m_dataChannel.Dispose();                }                 // Assign new data channel reference                m_dataChannel = value;                 if ((object)m_dataChannel != null)                {                    // Save UDP settings so channel can be reestablished if needed                    m_configurationString = m_dataChannel.ConfigurationString;                     // Attach to events on new data channel reference                    m_dataChannel.ClientConnectingException += m_dataChannel_ClientConnectingException;                    m_dataChannel.SendClientDataException += m_dataChannel_SendClientDataException;                    m_dataChannel.ServerStarted += m_dataChannel_ServerStarted;                    m_dataChannel.ServerStopped += m_dataChannel_ServerStopped;                }            }        }         /// <summary>        /// Gets <see cref="IServer"/> command channel.        /// </summary>        public IServer CommandChannel => m_commandChannel;         /// <summary>        /// Gets <see cref="IServer"/> publication channel - that is, data channel if defined otherwise command channel.        /// </summary>        public IServer PublishChannel => (object)m_dataChannel == null ? m_commandChannel : m_dataChannel;         /// <summary>        /// Gets connected state of the associated client socket.        /// </summary>        public bool IsConnected        {            get            {                Socket commandChannelSocket;                bool isConnected = false;                 try                {                    commandChannelSocket = GetCommandChannelSocket();                     if ((object)commandChannelSocket != null)                        isConnected = commandChannelSocket.Connected;                }                catch                {                    isConnected = false;                }                 return isConnected;            }        }         /// <summary>        /// Gets or sets IsSubscribed state.        /// </summary>        public bool IsSubscribed        {            get            {                return m_isSubscribed;            }            set            {                m_isSubscribed = value;            }        }         /// <summary>        /// Gets or sets flag that indicates if the socket exception for "No client found for ID [Guid]" has been thrown.        /// </summary>        /// <returns>        /// Since this message might be thrown many times before the communications channel has had a chance to disconnect        /// the socket, it is best to stop attempting to send data when this error has been encountered.        /// </returns>        public bool ClientNotFoundExceptionOccurred        {            // Users have encountered issues when a client disconnects where many thousands of exceptions get thrown, every 3ms.            // This can cause the entire system to become unresponsive and causes all devices to reset (no data).            // System only recovers when the client disconnect process finally executes as this can take some time to occur.            get            {                return m_clientNotFoundExceptionExceptionOccurred;            }            set            {                m_clientNotFoundExceptionExceptionOccurred = value;            }        }         /// <summary>        /// Gets or sets the <see cref="Guid"/> based subscriber ID of this <see cref="ClientConnection"/>.        /// </summary>        public Guid SubscriberID        {            get            {                return m_subscriberID;            }            set            {                m_subscriberID = value;            }        }         /// <summary>        /// Gets or sets the subscriber acronym of this <see cref="ClientConnection"/>.        /// </summary>        public string SubscriberAcronym        {            get            {                return m_subscriberAcronym;            }            set            {                m_subscriberAcronym = value;            }        }         /// <summary>        /// Gets or sets the subscriber name of this <see cref="ClientConnection"/>.        /// </summary>        public string SubscriberName        {            get            {                return m_subscriberName;            }            set            {                m_subscriberName = value;            }        }         /// <summary>        /// Gets or sets subscriber info for this <see cref="ClientConnection"/>.        /// </summary>        public string SubscriberInfo        {            get            {                if (string.IsNullOrWhiteSpace(m_subscriberInfo))                    return m_subscriberName;                 return m_subscriberInfo;            }            set            {                if (string.IsNullOrWhiteSpace(value))                {                    m_subscriberInfo = null;                }                else                {                    Dictionary<string, string> settings = value.ParseKeyValuePairs();                    string source, version, buildDate;                     settings.TryGetValue("source", out source);                    settings.TryGetValue("version", out version);                    settings.TryGetValue("buildDate", out buildDate);                     m_subscriberInfo = $"{source.ToNonNullNorWhiteSpace("unknown source")} version {version.ToNonNullNorWhiteSpace("?.?.?.?")} built on {buildDate.ToNonNullNorWhiteSpace("undefined date")}";                }            }        }         /// <summary>        /// Gets the connection identification of this <see cref="ClientConnection"/>.        /// </summary>        public string ConnectionID => m_connectionID;         /// <summary>        /// Gets or sets authenticated state of this <see cref="ClientConnection"/>.        /// </summary>        public bool Authenticated        {            get            {                return m_authenticated;            }            set            {                m_authenticated = value;            }        }         /// <summary>        /// Gets or sets shared secret used to lookup cipher keys only known to client and server.        /// </summary>        public string SharedSecret        {            get            {                return m_sharedSecret;            }            set            {                m_sharedSecret = value;            }        }         /// <summary>        /// Gets active and standby keys and initialization vectors.        /// </summary>        public byte[][][] KeyIVs => m_keyIVs;         /// <summary>        /// Gets current cipher index.        /// </summary>        public int CipherIndex => m_cipherIndex;         /// <summary>        /// Gets time of last cipher key update.        /// </summary>        public Ticks LastCipherKeyUpdateTime => m_lastCipherKeyUpdateTime;         /// <summary>        /// Gets or sets the list of valid IP addresses that this client can connect from.        /// </summary>        public List<IPAddress> ValidIPAddresses        {            get            {                return m_validIPAddresses;            }            set            {                m_validIPAddresses = value;            }        }         /// <summary>        /// Gets the IP address of the remote client connection.        /// </summary>        public IPAddress IPAddress => m_ipAddress;         /// <summary>        /// Gets or sets subscription associated with this <see cref="ClientConnection"/>.        /// </summary>        public IClientSubscription Subscription        {            get            {                return m_subscription;            }            set            {                m_subscription = value;                 if ((object)m_subscription != null)                    m_subscription.Name = m_hostName;            }        }         /// <summary>        /// Gets the subscriber name of this <see cref="ClientConnection"/>.        /// </summary>        public string Name => SubscriberName;         /// <summary>        /// Gets or sets a set of flags that define ways in        /// which the subscriber and publisher communicate.        /// </summary>        public OperationalModes OperationalModes        {            get            {                return m_operationalModes;            }            set            {                m_operationalModes = value;                 switch ((OperationalEncoding)(value & OperationalModes.EncodingMask))                {                    case OperationalEncoding.Unicode:                        m_encoding = Encoding.Unicode;

Lines 1–500 of 811.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t