Unified · Split
| @@ -109,11 +109,15 @@ namespace GSF.Communication · 108 unmodified lines | |||
| 109 | /// <returns>Length of the payload.</returns> | 109 | /// <returns>Length of the payload.</returns> |
| 110 | public static int ExtractLength(byte[] buffer, int length, byte[] marker) | 110 | public static int ExtractLength(byte[] buffer, int length, byte[] marker) |
| 111 | { | 111 | { |
| 112 | // Check to see if buffer is at least as big as the payload header and has the payload marker | 112 | // Check to see if buffer is at least as big as the payload header |
| 113 | if (length >= (marker.Length + LengthSegment) && HasHeader(buffer, marker)) | 113 | if (length < (marker.Length + LengthSegment)) |
| 114 | return BitConverter.ToInt32(buffer, marker.Length); | 114 | return -1; |
| 115 | 115 | ||
| 116 | return -1; | 116 | // Check to see if buffer has the payload marker |
| 117 | if (!HasHeader(buffer, marker)) | ||
| 118 | return 0; | ||
| 119 | |||
| 120 | return BitConverter.ToInt32(buffer, marker.Length); | ||
| 117 | } | 121 | } |
| 118 | } | 122 | } |
| 119 | } | 123 | } |
| @@ -167,6 +167,7 @@ namespace GSF.Communication · 166 unmodified lines | |||
| 167 | public int ConnectionAttempts; | 167 | public int ConnectionAttempts; |
| 168 | 168 | ||
| 169 | public CancellationToken Token = new CancellationToken(); | 169 | public CancellationToken Token = new CancellationToken(); |
| 170 | public ICancellationToken TimeoutToken; | ||
| 170 | 171 | ||
| 171 | public void Dispose() | 172 | public void Dispose() |
| 172 | { | 173 | { |
| @@ -744,6 +745,15 @@ namespace GSF.Communication · 571 unmodified lines | |||
| 744 | // send and receive operations on the socket | 745 | // send and receive operations on the socket |
| 745 | connectState.NetworkStream = new NetworkStream(connectState.Socket, false); | 746 | connectState.NetworkStream = new NetworkStream(connectState.Socket, false); |
| 746 | connectState.NegotiateStream = new NegotiateStream(connectState.NetworkStream, true); | 747 | connectState.NegotiateStream = new NegotiateStream(connectState.NetworkStream, true); |
| 748 | |||
| 749 | connectState.TimeoutToken = new Action(() => | ||
| 750 | { | ||
| 751 | SocketException ex = new SocketException((int)SocketError.TimedOut); | ||
| 752 | OnConnectionException(ex); | ||
| 753 | TerminateConnection(connectState.Token); | ||
| 754 | connectState.Dispose(); | ||
| 755 | }).DelayAndExecute(15000); | ||
| 756 | |||
| 747 | connectState.NegotiateStream.BeginAuthenticateAsClient(m_networkCredential ?? (NetworkCredential)CredentialCache.DefaultCredentials, string.Empty, ProcessIntegratedSecurityAuthentication, connectState); | 757 | connectState.NegotiateStream.BeginAuthenticateAsClient(m_networkCredential ?? (NetworkCredential)CredentialCache.DefaultCredentials, string.Empty, ProcessIntegratedSecurityAuthentication, connectState); |
| 748 | #endif | 758 | #endif |
| 749 | } | 759 | } |
| @@ -875,6 +885,10 @@ namespace GSF.Communication · 125 unmodified lines | |||
| 875 | // Get the connect state from the async result | 885 | // Get the connect state from the async result |
| 876 | connectState = (ConnectState)asyncResult.AsyncState; | 886 | connectState = (ConnectState)asyncResult.AsyncState; |
| 877 | 887 | ||
| 888 | // Attempt to cancel the timeout operation | ||
| 889 | if (!connectState.TimeoutToken.Cancel()) | ||
| 890 | return; | ||
| 891 | |||
| 878 | // Quit if this connection loop has been cancelled | 892 | // Quit if this connection loop has been cancelled |
| 879 | if (connectState.Token.Cancelled) | 893 | if (connectState.Token.Cancelled) |
| 880 | return; | 894 | return; |
| @@ -803,6 +803,7 @@ namespace GSF.Communication · 802 unmodified lines | |||
| 803 | TransportProvider<Socket> client = new TransportProvider<Socket>(); | 803 | TransportProvider<Socket> client = new TransportProvider<Socket>(); |
| 804 | SocketAsyncEventArgs receiveArgs = null; | 804 | SocketAsyncEventArgs receiveArgs = null; |
| 805 | WindowsPrincipal clientPrincipal = null; | 805 | WindowsPrincipal clientPrincipal = null; |
| 806 | IPEndPoint remoteEndPoint = null; | ||
| 806 | TcpClientInfo clientInfo; | 807 | TcpClientInfo clientInfo; |
| 807 | 808 | ||
| 808 | try | 809 | try |
| @@ -829,6 +830,7 @@ namespace GSF.Communication · 20 unmodified lines | |||
| 829 | // Process the newly connected client. | 830 | // Process the newly connected client. |
| 830 | client.Provider = acceptArgs.AcceptSocket; | 831 | client.Provider = acceptArgs.AcceptSocket; |
| 831 | client.Provider.ReceiveBufferSize = ReceiveBufferSize; | 832 | client.Provider.ReceiveBufferSize = ReceiveBufferSize; |
| 833 | remoteEndPoint = client.Provider.RemoteEndPoint as IPEndPoint; | ||
| 832 | 834 | ||
| 833 | // Set up SocketAsyncEventArgs for receive operations. | 835 | // Set up SocketAsyncEventArgs for receive operations. |
| 834 | receiveArgs = FastObjectFactory<SocketAsyncEventArgs>.CreateObjectFunction(); | 836 | receiveArgs = FastObjectFactory<SocketAsyncEventArgs>.CreateObjectFunction(); |
| @@ -838,9 +840,7 @@ namespace GSF.Communication · 3 unmodified lines | |||
| 838 | acceptArgs.AcceptSocket = null; | 840 | acceptArgs.AcceptSocket = null; |
| 839 | 841 | ||
| 840 | if (!m_tcpServer.AcceptAsync(acceptArgs)) | 842 | if (!m_tcpServer.AcceptAsync(acceptArgs)) |
| 841 | { | ||
| 842 | ThreadPool.QueueUserWorkItem(state => ProcessAccept(acceptArgs)); | 843 | ThreadPool.QueueUserWorkItem(state => ProcessAccept(acceptArgs)); |
| 843 | } | ||
| 844 | 844 | ||
| 845 | #if !MONO | 845 | #if !MONO |
| 846 | // Authenticate the connected client Windows credentials. | 846 | // Authenticate the connected client Windows credentials. |
| @@ -851,10 +851,15 @@ namespace GSF.Communication · 4 unmodified lines | |||
| 851 | 851 | ||
| 852 | try | 852 | try |
| 853 | { | 853 | { |
| 854 | socketStream = new NetworkStream(client.Provider); | 854 | socketStream = new NetworkStream(client.Provider, false); |
| 855 | authenticationStream = new NegotiateStream(socketStream); | 855 | authenticationStream = new NegotiateStream(socketStream, true); |
| 856 | |||
| 857 | ICancellationToken timeoutToken = new Action(() => client.Provider.Dispose()).DelayAndExecute(15000); | ||
| 856 | authenticationStream.AuthenticateAsServer(); | 858 | authenticationStream.AuthenticateAsServer(); |
| 857 | 859 | ||
| 860 | if (!timeoutToken.Cancel()) | ||
| 861 | throw new SocketException((int)SocketError.TimedOut); | ||
| 862 | |||
| 858 | if (authenticationStream.RemoteIdentity is WindowsIdentity) | 863 | if (authenticationStream.RemoteIdentity is WindowsIdentity) |
| 859 | clientPrincipal = new WindowsPrincipal((WindowsIdentity)authenticationStream.RemoteIdentity); | 864 | clientPrincipal = new WindowsPrincipal((WindowsIdentity)authenticationStream.RemoteIdentity); |
| 860 | } | 865 | } |
| @@ -940,22 +945,12 @@ namespace GSF.Communication · 79 unmodified lines | |||
| 940 | catch (Exception ex) | 945 | catch (Exception ex) |
| 941 | { | 946 | { |
| 942 | // Notify of the exception. | 947 | // Notify of the exception. |
| 943 | if ((object)client.Provider != null && (object)client.Provider.RemoteEndPoint != null) | 948 | string clientAddress = remoteEndPoint?.Address.ToString() ?? "UNKNOWN"; |
| 944 | { | 949 | string errorMessage = $"Unable to accept connection to client [{clientAddress}]: {ex.Message}"; |
| 945 | string clientAddress = ((IPEndPoint)client.Provider.RemoteEndPoint).Address.ToString(); | 950 | OnClientConnectingException(new Exception(errorMessage, ex)); |
| 946 | string errorMessage = $"Unable to accept connection to client [{clientAddress}]: {ex.Message}"; | ||
| 947 | OnClientConnectingException(new Exception(errorMessage, ex)); | ||
| 948 | } | ||
| 949 | else | ||
| 950 | { | ||
| 951 | string errorMessage = $"Unable to accept connection to client [unknown]: {ex.Message}"; | ||
| 952 | OnClientConnectingException(new Exception(errorMessage, ex)); | ||
| 953 | } | ||
| 954 | 951 | ||
| 955 | if ((object)receiveArgs != null) | 952 | if ((object)receiveArgs != null) |
| 956 | { | ||
| 957 | TerminateConnection(client, receiveArgs, false); | 953 | TerminateConnection(client, receiveArgs, false); |
| 958 | } | ||
| 959 | } | 954 | } |
| 960 | } | 955 | } |
| 961 | 956 | ||
| @@ -64,6 +64,7 @@ namespace GSF.Communication · 63 unmodified lines | |||
| 64 | public int ConnectionAttempts; | 64 | public int ConnectionAttempts; |
| 65 | 65 | ||
| 66 | public CancellationToken Token = new CancellationToken(); | 66 | public CancellationToken Token = new CancellationToken(); |
| 67 | public ICancellationToken TimeoutToken; | ||
| 67 | 68 | ||
| 68 | public void Dispose() | 69 | public void Dispose() |
| 69 | { | 70 | { |
| @@ -909,7 +910,17 @@ namespace GSF.Communication · 839 unmodified lines | |||
| 909 | endpoint = Regex.Match(m_connectData["server"], Transport.EndpointFormatRegex); | 910 | endpoint = Regex.Match(m_connectData["server"], Transport.EndpointFormatRegex); |
| 910 | 911 | ||
| 911 | if (!connectState.Token.Cancelled) | 912 | if (!connectState.Token.Cancelled) |
| 913 | { | ||
| 914 | connectState.TimeoutToken = new Action(() => | ||
| 915 | { | ||
| 916 | SocketException ex = new SocketException((int)SocketError.TimedOut); | ||
| 917 | OnConnectionException(ex); | ||
| 918 | TerminateConnection(connectState.Token); | ||
| 919 | connectState.Dispose(); | ||
| 920 | }).DelayAndExecute(15000); | ||
| 921 | |||
| 912 | connectState.SslStream.BeginAuthenticateAsClient(endpoint.Groups["host"].Value, m_clientCertificates, m_enabledSslProtocols, m_checkCertificateRevocation, ProcessTlsAuthentication, connectState); | 922 | connectState.SslStream.BeginAuthenticateAsClient(endpoint.Groups["host"].Value, m_clientCertificates, m_enabledSslProtocols, m_checkCertificateRevocation, ProcessTlsAuthentication, connectState); |
| 923 | } | ||
| 913 | } | 924 | } |
| 914 | catch (SocketException ex) | 925 | catch (SocketException ex) |
| 915 | { | 926 | { |
| @@ -968,6 +979,10 @@ namespace GSF.Communication · 52 unmodified lines | |||
| 968 | // Get the connect state from the async result | 979 | // Get the connect state from the async result |
| 969 | connectState = (ConnectState)asyncResult.AsyncState; | 980 | connectState = (ConnectState)asyncResult.AsyncState; |
| 970 | 981 | ||
| 982 | // Attempt to cancel the timeout operation | ||
| 983 | if (!connectState.TimeoutToken.Cancel()) | ||
| 984 | return; | ||
| 985 | |||
| 971 | // Quit if this connection loop has been cancelled | 986 | // Quit if this connection loop has been cancelled |
| 972 | if (connectState.Token.Cancelled) | 987 | if (connectState.Token.Cancelled) |
| 973 | return; | 988 | return; |
| @@ -995,6 +1010,15 @@ namespace GSF.Communication · 21 unmodified lines | |||
| 995 | 1010 | ||
| 996 | // Create the NegotiateStream to begin authentication of the user's Windows credentials | 1011 | // Create the NegotiateStream to begin authentication of the user's Windows credentials |
| 997 | connectState.NegotiateStream = new NegotiateStream(connectState.SslStream, true); | 1012 | connectState.NegotiateStream = new NegotiateStream(connectState.SslStream, true); |
| 1013 | |||
| 1014 | connectState.TimeoutToken = new Action(() => | ||
| 1015 | { | ||
| 1016 | SocketException ex = new SocketException((int)SocketError.TimedOut); | ||
| 1017 | OnConnectionException(ex); | ||
| 1018 | TerminateConnection(connectState.Token); | ||
| 1019 | connectState.Dispose(); | ||
| 1020 | }).DelayAndExecute(15000); | ||
| 1021 | |||
| 998 | connectState.NegotiateStream.BeginAuthenticateAsClient(m_networkCredential ?? (NetworkCredential)CredentialCache.DefaultCredentials, string.Empty, ProcessIntegratedSecurityAuthentication, connectState); | 1022 | connectState.NegotiateStream.BeginAuthenticateAsClient(m_networkCredential ?? (NetworkCredential)CredentialCache.DefaultCredentials, string.Empty, ProcessIntegratedSecurityAuthentication, connectState); |
| 999 | #endif | 1023 | #endif |
| 1000 | } | 1024 | } |
| @@ -1112,6 +1136,10 @@ namespace GSF.Communication · 111 unmodified lines | |||
| 1112 | // Get the connect state from the async result | 1136 | // Get the connect state from the async result |
| 1113 | connectState = (ConnectState)asyncResult.AsyncState; | 1137 | connectState = (ConnectState)asyncResult.AsyncState; |
| 1114 | 1138 | ||
| 1139 | // Attempt to cancel the timeout operation | ||
| 1140 | if (!connectState.TimeoutToken.Cancel()) | ||
| 1141 | return; | ||
| 1142 | |||
| 1115 | // Quit if this connection loop has been cancelled | 1143 | // Quit if this connection loop has been cancelled |
| 1116 | if (connectState.Token.Cancelled) | 1144 | if (connectState.Token.Cancelled) |
| 1117 | return; | 1145 | return; |
| @@ -76,6 +76,11 @@ namespace GSF.Communication · 75 unmodified lines | |||
| 76 | /// </summary> | 76 | /// </summary> |
| 77 | public SslStream SslStream; | 77 | public SslStream SslStream; |
| 78 | 78 | ||
| 79 | /// <summary> | ||
| 80 | /// The end point of the remote client connecting to this server. | ||
| 81 | /// </summary> | ||
| 82 | public IPEndPoint RemoteEndPoint; | ||
| 83 | |||
| 79 | /// <summary> | 84 | /// <summary> |
| 80 | /// Performs application-defined tasks associated with | 85 | /// Performs application-defined tasks associated with |
| 81 | /// freeing, releasing, or resetting unmanaged resources. | 86 | /// freeing, releasing, or resetting unmanaged resources. |
| @@ -93,11 +98,14 @@ namespace GSF.Communication · 11 unmodified lines | |||
| 93 | private class TlsClientInfo | 98 | private class TlsClientInfo |
| 94 | { | 99 | { |
| 95 | public TransportProvider<TlsSocket> Client; | 100 | public TransportProvider<TlsSocket> Client; |
| 101 | public ICancellationToken TimeoutToken; | ||
| 102 | |||
| 103 | public int Sending; | ||
| 96 | public object SendLock; | 104 | public object SendLock; |
| 97 | public ConcurrentQueue<TlsServerPayload> SendQueue; | 105 | public ConcurrentQueue<TlsServerPayload> SendQueue; |
| 98 | public ShortSynchronizedOperation DumpPayloadsOperation; | 106 | public ShortSynchronizedOperation DumpPayloadsOperation; |
| 99 | public int Sending; | ||
| 100 | 107 | ||
| 108 | public NegotiateStream NegotiateStream; | ||
| 101 | public WindowsPrincipal ClientPrincipal; | 109 | public WindowsPrincipal ClientPrincipal; |
| 102 | } | 110 | } |
| 103 | 111 | ||
| @@ -995,7 +1003,6 @@ namespace GSF.Communication · 891 unmodified lines | |||
| 995 | private void ProcessAccept(SocketAsyncEventArgs acceptArgs) | 1003 | private void ProcessAccept(SocketAsyncEventArgs acceptArgs) |
| 996 | { | 1004 | { |
| 997 | TransportProvider<TlsSocket> client = new TransportProvider<TlsSocket>(); | 1005 | TransportProvider<TlsSocket> client = new TransportProvider<TlsSocket>(); |
| 998 | IPEndPoint remoteEndPoint = null; | ||
| 999 | NetworkStream netStream; | 1006 | NetworkStream netStream; |
| 1000 | 1007 | ||
| 1001 | try | 1008 | try |
| @@ -1025,9 +1032,6 @@ namespace GSF.Communication · 23 unmodified lines | |||
| 1025 | 1032 | ||
| 1026 | try | 1033 | try |
| 1027 | { | 1034 | { |
| 1028 | // Get the remote end point in case we need to display useful error messages | ||
| 1029 | remoteEndPoint = acceptArgs.AcceptSocket.RemoteEndPoint as IPEndPoint; | ||
| 1030 | |||
| 1031 | if (MaxClientConnections != -1 && ClientIDs.Length >= MaxClientConnections) | 1035 | if (MaxClientConnections != -1 && ClientIDs.Length >= MaxClientConnections) |
| 1032 | { | 1036 | { |
| 1033 | // Reject client connection since limit has been reached. | 1037 | // Reject client connection since limit has been reached. |
| @@ -1042,11 +1046,43 @@ namespace GSF.Communication · 8 unmodified lines | |||
| 1042 | client.Provider = new TlsSocket | 1046 | client.Provider = new TlsSocket |
| 1043 | { | 1047 | { |
| 1044 | Socket = acceptArgs.AcceptSocket, | 1048 | Socket = acceptArgs.AcceptSocket, |
| 1045 | SslStream = new SslStream(netStream, false, m_remoteCertificateValidationCallback ?? CertificateChecker.ValidateRemoteCertificate, m_localCertificateSelectionCallback) | 1049 | SslStream = new SslStream(netStream, false, m_remoteCertificateValidationCallback ?? CertificateChecker.ValidateRemoteCertificate, m_localCertificateSelectionCallback), |
| 1050 | RemoteEndPoint = acceptArgs.AcceptSocket.RemoteEndPoint as IPEndPoint | ||
| 1046 | }; | 1051 | }; |
| 1047 | 1052 | ||
| 1048 | client.Provider.Socket.ReceiveBufferSize = ReceiveBufferSize; | 1053 | client.Provider.Socket.ReceiveBufferSize = ReceiveBufferSize; |
| 1049 | client.Provider.SslStream.BeginAuthenticateAsServer(m_certificate, m_requireClientCertificate, m_enabledSslProtocols, m_checkCertificateRevocation, ProcessTlsAuthentication, client); | 1054 | |
| 1055 | TlsClientInfo clientInfo = new TlsClientInfo | ||
| 1056 | { | ||
| 1057 | Client = client, | ||
| 1058 | SendLock = new object(), | ||
| 1059 | SendQueue = new ConcurrentQueue<TlsServerPayload>() | ||
| 1060 | }; | ||
| 1061 | |||
| 1062 | // Create operation to dump send queue payloads when the queue grows too large. | ||
| 1063 | clientInfo.DumpPayloadsOperation = new ShortSynchronizedOperation(() => | ||
| 1064 | { | ||
| 1065 | TlsServerPayload payload; | ||
| 1066 | |||
| 1067 | // Check to see if the client has reached the maximum send queue size. | ||
| 1068 | if (m_maxSendQueueSize > 0 && clientInfo.SendQueue.Count >= m_maxSendQueueSize) | ||
| 1069 | { | ||
| 1070 | for (int i = 0; i < m_maxSendQueueSize; i++) | ||
| 1071 | { | ||
| 1072 | if (clientInfo.SendQueue.TryDequeue(out payload)) | ||
| 1073 | { | ||
| 1074 | payload.WaitHandle.Set(); | ||
| 1075 | payload.WaitHandle.Dispose(); | ||
| 1076 | payload.WaitHandle = null; | ||
| 1077 | } | ||
| 1078 | } | ||
| 1079 | |||
| 1080 | throw new InvalidOperationException($"Client {clientInfo.Client.ID} connected to TCP server reached maximum send queue size. {m_maxSendQueueSize} payloads dumped from the queue."); | ||
| 1081 | } | ||
| 1082 | }, ex => OnSendClientDataException(clientInfo.Client.ID, ex)); | ||
| 1083 | |||
| 1084 | clientInfo.TimeoutToken = new Action(() => client.Provider.Socket.Dispose()).DelayAndExecute(15000); | ||
| 1085 | client.Provider.SslStream.BeginAuthenticateAsServer(m_certificate, m_requireClientCertificate, m_enabledSslProtocols, m_checkCertificateRevocation, ProcessTlsAuthentication, clientInfo); | ||
| 1050 | } | 1086 | } |
| 1051 | } | 1087 | } |
| 1052 | finally | 1088 | finally |
| @@ -1067,13 +1103,10 @@ namespace GSF.Communication · 14 unmodified lines | |||
| 1067 | catch (Exception ex) | 1103 | catch (Exception ex) |
| 1068 | { | 1104 | { |
| 1069 | // Notify of the exception. | 1105 | // Notify of the exception. |
| 1070 | if ((object)remoteEndPoint != null) | 1106 | IPEndPoint remoteEndPoint = client.Provider?.RemoteEndPoint; |
| 1071 | { | 1107 | string clientAddress = remoteEndPoint?.Address.ToString() ?? "UNKNOWN"; |
| 1072 | string clientAddress = remoteEndPoint.Address.ToString(); | 1108 | string errorMessage = $"Unable to accept connection to client [{clientAddress}]: {ex.Message}"; |
| 1073 | string errorMessage = $"Unable to accept connection to client [{clientAddress}]: {ex.Message}"; | 1109 | OnClientConnectingException(new Exception(errorMessage, ex)); |
| 1074 | OnClientConnectingException(new Exception(errorMessage, ex)); | ||
| 1075 | } | ||
| 1076 | |||
| 1077 | TerminateConnection(client, false); | 1110 | TerminateConnection(client, false); |
| 1078 | } | 1111 | } |
| 1079 | } | 1112 | } |
| @@ -1083,12 +1116,15 @@ namespace GSF.Communication · 3 unmodified lines | |||
| 1083 | /// </summary> | 1116 | /// </summary> |
| 1084 | private void ProcessTlsAuthentication(IAsyncResult asyncResult) | 1117 | private void ProcessTlsAuthentication(IAsyncResult asyncResult) |
| 1085 | { | 1118 | { |
| 1086 | TransportProvider<TlsSocket> client = (TransportProvider<TlsSocket>)asyncResult.AsyncState; | 1119 | TlsClientInfo clientInfo = (TlsClientInfo)asyncResult.AsyncState; |
| 1087 | IPEndPoint remoteEndPoint = client.Provider.Socket.RemoteEndPoint as IPEndPoint; | 1120 | TransportProvider<TlsSocket> client = clientInfo.Client; |
| 1088 | SslStream stream = client.Provider.SslStream; | 1121 | SslStream stream = client.Provider.SslStream; |
| 1089 | 1122 | ||
| 1090 | try | 1123 | try |
| 1091 | { | 1124 | { |
| 1125 | if (!clientInfo.TimeoutToken.Cancel()) | ||
| 1126 | throw new SocketException((int)SocketError.TimedOut); | ||
| 1127 | |||
| 1092 | stream.EndAuthenticateAsServer(asyncResult); | 1128 | stream.EndAuthenticateAsServer(asyncResult); |
| 1093 | 1129 | ||
| 1094 | if (EnabledSslProtocols != SslProtocols.None) | 1130 | if (EnabledSslProtocols != SslProtocols.None) |
| @@ -1103,41 +1139,13 @@ namespace GSF.Communication · 8 unmodified lines | |||
| 1103 | if (m_integratedSecurity) | 1139 | if (m_integratedSecurity) |
| 1104 | { | 1140 | { |
| 1105 | #if !MONO | 1141 | #if !MONO |
| 1106 | NegotiateStream negotiateStream = new NegotiateStream(stream, true); | 1142 | clientInfo.NegotiateStream = new NegotiateStream(stream, true); |
| 1107 | negotiateStream.BeginAuthenticateAsServer(ProcessIntegratedSecurityAuthentication, Tuple.Create(client, negotiateStream)); | 1143 | clientInfo.TimeoutToken = new Action(() => client.Provider.Socket.Dispose()).DelayAndExecute(15000); |
| 1144 | clientInfo.NegotiateStream.BeginAuthenticateAsServer(ProcessIntegratedSecurityAuthentication, clientInfo); | ||
| 1108 | #endif | 1145 | #endif |
| 1109 | } | 1146 | } |
| 1110 | else | 1147 | else |
| 1111 | { | 1148 | { |
| 1112 | TlsClientInfo clientInfo = new TlsClientInfo | ||
| 1113 | { | ||
| 1114 | Client = client, | ||
| 1115 | SendLock = new object(), | ||
| 1116 | SendQueue = new ConcurrentQueue<TlsServerPayload>() | ||
| 1117 | }; | ||
| 1118 | |||
| 1119 | // Create operation to dump send queue payloads when the queue grows too large. | ||
| 1120 | clientInfo.DumpPayloadsOperation = new ShortSynchronizedOperation(() => | ||
| 1121 | { | ||
| 1122 | TlsServerPayload payload; | ||
| 1123 | |||
| 1124 | // Check to see if the client has reached the maximum send queue size. | ||
| 1125 | if (m_maxSendQueueSize > 0 && clientInfo.SendQueue.Count >= m_maxSendQueueSize) | ||
| 1126 | { | ||
| 1127 | for (int i = 0; i < m_maxSendQueueSize; i++) | ||
| 1128 | { | ||
| 1129 | if (clientInfo.SendQueue.TryDequeue(out payload)) | ||
| 1130 | { | ||
| 1131 | payload.WaitHandle.Set(); | ||
| 1132 | payload.WaitHandle.Dispose(); | ||
| 1133 | payload.WaitHandle = null; | ||
| 1134 | } | ||
| 1135 | } | ||
| 1136 | |||
| 1137 | throw new InvalidOperationException($"Client {clientInfo.Client.ID} connected to TCP server reached maximum send queue size. {m_maxSendQueueSize} payloads dumped from the queue."); | ||
| 1138 | } | ||
| 1139 | }, ex => OnSendClientDataException(clientInfo.Client.ID, ex)); | ||
| 1140 | |||
| 1141 | // We can proceed further with receiving data from the client. | 1149 | // We can proceed further with receiving data from the client. |
| 1142 | m_clientInfoLookup.TryAdd(client.ID, clientInfo); | 1150 | m_clientInfoLookup.TryAdd(client.ID, clientInfo); |
| 1143 | 1151 | ||
| @@ -1148,12 +1156,10 @@ namespace GSF.Communication · 4 unmodified lines | |||
| 1148 | catch (Exception ex) | 1156 | catch (Exception ex) |
| 1149 | { | 1157 | { |
| 1150 | // Notify of the exception. | 1158 | // Notify of the exception. |
| 1151 | if ((object)remoteEndPoint != null) | 1159 | IPEndPoint remoteEndPoint = client.Provider.RemoteEndPoint; |
| 1152 | { | 1160 | string clientAddress = remoteEndPoint.Address.ToString(); |
| 1153 | string clientAddress = remoteEndPoint.Address.ToString(); | 1161 | string errorMessage = $"Unable to authenticate connection to client [{clientAddress}]: {CertificateChecker.ReasonForFailure ?? ex.Message}"; |
| 1154 | string errorMessage = $"Unable to authenticate connection to client [{clientAddress}]: {CertificateChecker.ReasonForFailure ?? ex.Message}"; | 1162 | OnClientConnectingException(new Exception(errorMessage, ex)); |
| 1155 | OnClientConnectingException(new Exception(errorMessage, ex)); | ||
| 1156 | } | ||
| 1157 | TerminateConnection(client, false); | 1163 | TerminateConnection(client, false); |
| 1158 | } | 1164 | } |
| 1159 | } | 1165 | } |
| @@ -1161,20 +1167,26 @@ namespace GSF.Communication · 1 unmodified line | |||
| 1161 | #if !MONO | 1167 | #if !MONO |
| 1162 | private void ProcessIntegratedSecurityAuthentication(IAsyncResult asyncResult) | 1168 | private void ProcessIntegratedSecurityAuthentication(IAsyncResult asyncResult) |
| 1163 | { | 1169 | { |
| 1164 | Tuple<TransportProvider<TlsSocket>, NegotiateStream> state = (Tuple<TransportProvider<TlsSocket>, NegotiateStream>)asyncResult.AsyncState; | 1170 | TlsClientInfo clientInfo = (TlsClientInfo)asyncResult.AsyncState; |
| 1165 | TransportProvider<TlsSocket> client = state.Item1; | 1171 | TransportProvider<TlsSocket> client = clientInfo.Client; |
| 1166 | IPEndPoint remoteEndPoint = client.Provider.Socket.RemoteEndPoint as IPEndPoint; | 1172 | NegotiateStream negotiateStream = clientInfo.NegotiateStream; |
| 1167 | NegotiateStream negotiateStream = state.Item2; | 1173 | IPEndPoint remoteEndPoint = client.Provider.RemoteEndPoint; |
| 1168 | WindowsPrincipal clientPrincipal = null; | 1174 | WindowsPrincipal clientPrincipal; |
| 1169 | 1175 | ||
| 1170 | try | 1176 | try |
| 1171 | { | 1177 | { |
| 1178 | if (!clientInfo.TimeoutToken.Cancel()) | ||
| 1179 | throw new SocketException((int)SocketError.TimedOut); | ||
| 1180 | |||
| 1172 | try | 1181 | try |
| 1173 | { | 1182 | { |
| 1174 | negotiateStream.EndAuthenticateAsServer(asyncResult); | 1183 | negotiateStream.EndAuthenticateAsServer(asyncResult); |
| 1175 | 1184 | ||
| 1176 | if (negotiateStream.RemoteIdentity is WindowsIdentity) | 1185 | if (negotiateStream.RemoteIdentity is WindowsIdentity) |
| 1186 | { | ||
| 1177 | clientPrincipal = new WindowsPrincipal((WindowsIdentity)negotiateStream.RemoteIdentity); | 1187 | clientPrincipal = new WindowsPrincipal((WindowsIdentity)negotiateStream.RemoteIdentity); |
| 1188 | clientInfo.ClientPrincipal = clientPrincipal; | ||
| 1189 | } | ||
| 1178 | } | 1190 | } |
| 1179 | catch (InvalidCredentialException) | 1191 | catch (InvalidCredentialException) |
| 1180 | { | 1192 | { |
| @@ -1182,36 +1194,6 @@ namespace GSF.Communication · 1 unmodified line | |||
| 1182 | throw; | 1194 | throw; |
| 1183 | } | 1195 | } |
| 1184 | 1196 | ||
| 1185 | TlsClientInfo clientInfo = new TlsClientInfo | ||
| 1186 | { | ||
| 1187 | Client = client, | ||
| 1188 | SendLock = new object(), | ||
| 1189 | SendQueue = new ConcurrentQueue<TlsServerPayload>(), | ||
| 1190 | ClientPrincipal = clientPrincipal | ||
| 1191 | }; | ||
| 1192 | |||
| 1193 | // Create operation to dump send queue payloads when the queue grows too large. | ||
| 1194 | clientInfo.DumpPayloadsOperation = new ShortSynchronizedOperation(() => | ||
| 1195 | { | ||
| 1196 | TlsServerPayload payload; | ||
| 1197 | |||
| 1198 | // Check to see if the client has reached the maximum send queue size. | ||
| 1199 | if (m_maxSendQueueSize > 0 && clientInfo.SendQueue.Count >= m_maxSendQueueSize) | ||
| 1200 | { | ||
| 1201 | for (int i = 0; i < m_maxSendQueueSize; i++) | ||
| 1202 | { | ||
| 1203 | if (clientInfo.SendQueue.TryDequeue(out payload)) | ||
| 1204 | { | ||
| 1205 | payload.WaitHandle.Set(); | ||
| 1206 | payload.WaitHandle.Dispose(); | ||
| 1207 | payload.WaitHandle = null; | ||
| 1208 | } | ||
| 1209 | } | ||
| 1210 | |||
| 1211 | throw new InvalidOperationException($"Client {clientInfo.Client.ID} connected to TCP server reached maximum send queue size. {m_maxSendQueueSize} payloads dumped from the queue."); | ||
| 1212 | } | ||
| 1213 | }, ex => OnSendClientDataException(clientInfo.Client.ID, ex)); | ||
| 1214 | |||
| 1215 | // We can proceed further with receiving data from the client. | 1197 | // We can proceed further with receiving data from the client. |
| 1216 | m_clientInfoLookup.TryAdd(client.ID, clientInfo); | 1198 | m_clientInfoLookup.TryAdd(client.ID, clientInfo); |
| 1217 | 1199 | ||
| @@ -1221,12 +1203,9 @@ namespace GSF.Communication · 3 unmodified lines | |||
| 1221 | catch (Exception ex) | 1203 | catch (Exception ex) |
| 1222 | { | 1204 | { |
| 1223 | // Notify of the exception. | 1205 | // Notify of the exception. |
| 1224 | if ((object)remoteEndPoint != null) | 1206 | string clientAddress = remoteEndPoint.Address.ToString(); |
| 1225 | { | 1207 | string errorMessage = $"Unable to authenticate connection to client [{clientAddress}]: {ex.Message}"; |
| 1226 | string clientAddress = remoteEndPoint.Address.ToString(); | 1208 | OnClientConnectingException(new Exception(errorMessage, ex)); |
| 1227 | string errorMessage = $"Unable to authenticate connection to client [{clientAddress}]: {ex.Message}"; | ||
| 1228 | OnClientConnectingException(new Exception(errorMessage, ex)); | ||
| 1229 | } | ||
| 1230 | TerminateConnection(client, false); | 1209 | TerminateConnection(client, false); |
| 1231 | } | 1210 | } |
| 1232 | finally | 1211 | finally |
| @@ -540,18 +540,17 @@ namespace GSF.ServiceProcess · 539 unmodified lines | |||
| 540 | /// </summary> | 540 | /// </summary> |
| 541 | public void Connect() | 541 | public void Connect() |
| 542 | { | 542 | { |
| 543 | if ((object)m_remotingClient == null) | 543 | ClientBase remotingClient = m_remotingClient; |
| 544 | |||
| 545 | if ((object)remotingClient == null) | ||
| 544 | throw new InvalidOperationException("RemotingClient property of ClientHelper component is not set"); | 546 | throw new InvalidOperationException("RemotingClient property of ClientHelper component is not set"); |
| 545 | 547 | ||
| 546 | m_authenticationComplete = false; | 548 | m_authenticationComplete = false; |
| 547 | 549 | ||
| 548 | // Wait for connection. | 550 | // Wait for connection. |
| 549 | m_remotingClient.Connect(); | 551 | remotingClient.Connect(); |
| 550 | |||
| 551 | const int MaxWaitPeriods = 50; // Will wait a maximum of 5 seconds before assuming failure | ||
| 552 | int attempts = 0; | ||
| 553 | 552 | ||
| 554 | while (!m_authenticationComplete && m_remotingClient.Enabled && attempts++ < MaxWaitPeriods) | 553 | while (remotingClient.Enabled && !m_authenticationComplete) |
| 555 | { | 554 | { |
| 556 | // Wait for authentication. | 555 | // Wait for authentication. |
| 557 | Thread.Sleep(100); | 556 | Thread.Sleep(100); |
| @@ -561,14 +560,10 @@ namespace GSF.ServiceProcess · 3 unmodified lines | |||
| 561 | return; | 560 | return; |
| 562 | } | 561 | } |
| 563 | 562 | ||
| 564 | // It is possible that local domain account will not be accepted and/or recognized by remote | 563 | if (!m_authenticationComplete) |
| 565 | // server when attempting to use integrated security - in these cases the sent client info | 564 | UpdateStatus(UpdateType.Alarm, "Connection was terminated before completing authentication.\r\n\r\n"); |
| 566 | // message can be rejected so the ServiceHelper will neither respond with success or failure. | ||
| 567 | // We treat this as an authentication failure so that credentials can be requested. | ||
| 568 | if (attempts >= MaxWaitPeriods) | ||
| 569 | OnAuthenticationFailure(); | ||
| 570 | 565 | ||
| 571 | if (m_remotingClient.Enabled) | 566 | if (remotingClient.Enabled) |
| 572 | m_attemptReconnection = true; | 567 | m_attemptReconnection = true; |
| 573 | } | 568 | } |
| 574 | 569 | ||
| @@ -777,7 +772,6 @@ namespace GSF.ServiceProcess · 202 unmodified lines | |||
| 777 | status.Append(m_remotingClient.Status); | 772 | status.Append(m_remotingClient.Status); |
| 778 | status.AppendLine(); | 773 | status.AppendLine(); |
| 779 | UpdateStatus(UpdateType.Information, "{0}", status.ToString()); | 774 | UpdateStatus(UpdateType.Information, "{0}", status.ToString()); |
| 780 | SendRequest(StatusMessageFilter); | ||
| 781 | } | 775 | } |
| 782 | 776 | ||
| 783 | private void RemotingClient_ConnectionException(object sender, EventArgs<Exception> e) | 777 | private void RemotingClient_ConnectionException(object sender, EventArgs<Exception> e) |
| @@ -848,6 +842,7 @@ namespace GSF.ServiceProcess · 64 unmodified lines | |||
| 848 | UpdateStatus(UpdateType.Alarm, "{0}", response.Message); | 842 | UpdateStatus(UpdateType.Alarm, "{0}", response.Message); |
| 849 | break; | 843 | break; |
| 850 | case "AUTHENTICATIONSUCCESS": | 844 | case "AUTHENTICATIONSUCCESS": |
| 845 | SendRequest(StatusMessageFilter); | ||
| 851 | OnAuthenticationSuccess(); | 846 | OnAuthenticationSuccess(); |
| 852 | break; | 847 | break; |
| 853 | case "AUTHENTICATIONFAILURE": | 848 | case "AUTHENTICATIONFAILURE": |