| @@ -76,6 +76,11 @@ namespace GSF.Communication · 75 unmodified lines |
| 76 | 76 | /// </summary> |
| 77 | 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 | 84 | /// <summary> |
| 80 | 85 | /// Performs application-defined tasks associated with |
| 81 | 86 | /// freeing, releasing, or resetting unmanaged resources. |
| @@ -93,11 +98,14 @@ namespace GSF.Communication · 11 unmodified lines |
| 93 | 98 | private class TlsClientInfo |
| 94 | 99 | { |
| 95 | 100 | public TransportProvider<TlsSocket> Client; |
| 101 | + public ICancellationToken TimeoutToken; |
| 102 | + |
| 103 | + public int Sending; |
| 96 | 104 | public object SendLock; |
| 97 | 105 | public ConcurrentQueue<TlsServerPayload> SendQueue; |
| 98 | 106 | public ShortSynchronizedOperation DumpPayloadsOperation; |
| 99 | | − public int Sending; |
| 100 | 107 | |
| 108 | + public NegotiateStream NegotiateStream; |
| 101 | 109 | public WindowsPrincipal ClientPrincipal; |
| 102 | 110 | } |
| 103 | 111 | |
| @@ -995,7 +1003,6 @@ namespace GSF.Communication · 891 unmodified lines |
| 995 | 1003 | private void ProcessAccept(SocketAsyncEventArgs acceptArgs) |
| 996 | 1004 | { |
| 997 | 1005 | TransportProvider<TlsSocket> client = new TransportProvider<TlsSocket>(); |
| 998 | | − IPEndPoint remoteEndPoint = null; |
| 999 | 1006 | NetworkStream netStream; |
| 1000 | 1007 | |
| 1001 | 1008 | try |
| @@ -1025,9 +1032,6 @@ namespace GSF.Communication · 23 unmodified lines |
| 1025 | 1032 | |
| 1026 | 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 | 1035 | if (MaxClientConnections != -1 && ClientIDs.Length >= MaxClientConnections) |
| 1032 | 1036 | { |
| 1033 | 1037 | // Reject client connection since limit has been reached. |
| @@ -1042,11 +1046,43 @@ namespace GSF.Communication · 8 unmodified lines |
| 1042 | 1046 | client.Provider = new TlsSocket |
| 1043 | 1047 | { |
| 1044 | 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 | 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 | 1088 | finally |
| @@ -1067,13 +1103,10 @@ namespace GSF.Communication · 14 unmodified lines |
| 1067 | 1103 | catch (Exception ex) |
| 1068 | 1104 | { |
| 1069 | 1105 | // Notify of the exception. |
| 1070 | | − if ((object)remoteEndPoint != null) |
| 1071 | | − { |
| 1072 | | − string clientAddress = remoteEndPoint.Address.ToString(); |
| 1073 | | − string errorMessage = $"Unable to accept connection to client [{clientAddress}]: {ex.Message}"; |
| 1074 | | − OnClientConnectingException(new Exception(errorMessage, ex)); |
| 1075 | | − } |
| 1076 | | − |
| 1106 | + IPEndPoint remoteEndPoint = client.Provider?.RemoteEndPoint; |
| 1107 | + string clientAddress = remoteEndPoint?.Address.ToString() ?? "UNKNOWN"; |
| 1108 | + string errorMessage = $"Unable to accept connection to client [{clientAddress}]: {ex.Message}"; |
| 1109 | + OnClientConnectingException(new Exception(errorMessage, ex)); |
| 1077 | 1110 | TerminateConnection(client, false); |
| 1078 | 1111 | } |
| 1079 | 1112 | } |
| @@ -1083,12 +1116,15 @@ namespace GSF.Communication · 3 unmodified lines |
| 1083 | 1116 | /// </summary> |
| 1084 | 1117 | private void ProcessTlsAuthentication(IAsyncResult asyncResult) |
| 1085 | 1118 | { |
| 1086 | | − TransportProvider<TlsSocket> client = (TransportProvider<TlsSocket>)asyncResult.AsyncState; |
| 1087 | | − IPEndPoint remoteEndPoint = client.Provider.Socket.RemoteEndPoint as IPEndPoint; |
| 1119 | + TlsClientInfo clientInfo = (TlsClientInfo)asyncResult.AsyncState; |
| 1120 | + TransportProvider<TlsSocket> client = clientInfo.Client; |
| 1088 | 1121 | SslStream stream = client.Provider.SslStream; |
| 1089 | 1122 | |
| 1090 | 1123 | try |
| 1091 | 1124 | { |
| 1125 | + if (!clientInfo.TimeoutToken.Cancel()) |
| 1126 | + throw new SocketException((int)SocketError.TimedOut); |
| 1127 | + |
| 1092 | 1128 | stream.EndAuthenticateAsServer(asyncResult); |
| 1093 | 1129 | |
| 1094 | 1130 | if (EnabledSslProtocols != SslProtocols.None) |
| @@ -1103,41 +1139,13 @@ namespace GSF.Communication · 8 unmodified lines |
| 1103 | 1139 | if (m_integratedSecurity) |
| 1104 | 1140 | { |
| 1105 | 1141 | #if !MONO |
| 1106 | | − NegotiateStream negotiateStream = new NegotiateStream(stream, true); |
| 1107 | | − negotiateStream.BeginAuthenticateAsServer(ProcessIntegratedSecurityAuthentication, Tuple.Create(client, negotiateStream)); |
| 1142 | + clientInfo.NegotiateStream = new NegotiateStream(stream, true); |
| 1143 | + clientInfo.TimeoutToken = new Action(() => client.Provider.Socket.Dispose()).DelayAndExecute(15000); |
| 1144 | + clientInfo.NegotiateStream.BeginAuthenticateAsServer(ProcessIntegratedSecurityAuthentication, clientInfo); |
| 1108 | 1145 | #endif |
| 1109 | 1146 | } |
| 1110 | 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 | 1149 | // We can proceed further with receiving data from the client. |
| 1142 | 1150 | m_clientInfoLookup.TryAdd(client.ID, clientInfo); |
| 1143 | 1151 | |
| @@ -1148,12 +1156,10 @@ namespace GSF.Communication · 4 unmodified lines |
| 1148 | 1156 | catch (Exception ex) |
| 1149 | 1157 | { |
| 1150 | 1158 | // Notify of the exception. |
| 1151 | | − if ((object)remoteEndPoint != null) |
| 1152 | | − { |
| 1153 | | − string clientAddress = remoteEndPoint.Address.ToString(); |
| 1154 | | − string errorMessage = $"Unable to authenticate connection to client [{clientAddress}]: {CertificateChecker.ReasonForFailure ?? ex.Message}"; |
| 1155 | | − OnClientConnectingException(new Exception(errorMessage, ex)); |
| 1156 | | − } |
| 1159 | + IPEndPoint remoteEndPoint = client.Provider.RemoteEndPoint; |
| 1160 | + string clientAddress = remoteEndPoint.Address.ToString(); |
| 1161 | + string errorMessage = $"Unable to authenticate connection to client [{clientAddress}]: {CertificateChecker.ReasonForFailure ?? ex.Message}"; |
| 1162 | + OnClientConnectingException(new Exception(errorMessage, ex)); |
| 1157 | 1163 | TerminateConnection(client, false); |
| 1158 | 1164 | } |
| 1159 | 1165 | } |
| @@ -1161,20 +1167,26 @@ namespace GSF.Communication · 1 unmodified line |
| 1161 | 1167 | #if !MONO |
| 1162 | 1168 | private void ProcessIntegratedSecurityAuthentication(IAsyncResult asyncResult) |
| 1163 | 1169 | { |
| 1164 | | − Tuple<TransportProvider<TlsSocket>, NegotiateStream> state = (Tuple<TransportProvider<TlsSocket>, NegotiateStream>)asyncResult.AsyncState; |
| 1165 | | − TransportProvider<TlsSocket> client = state.Item1; |
| 1166 | | − IPEndPoint remoteEndPoint = client.Provider.Socket.RemoteEndPoint as IPEndPoint; |
| 1167 | | − NegotiateStream negotiateStream = state.Item2; |
| 1168 | | − WindowsPrincipal clientPrincipal = null; |
| 1170 | + TlsClientInfo clientInfo = (TlsClientInfo)asyncResult.AsyncState; |
| 1171 | + TransportProvider<TlsSocket> client = clientInfo.Client; |
| 1172 | + NegotiateStream negotiateStream = clientInfo.NegotiateStream; |
| 1173 | + IPEndPoint remoteEndPoint = client.Provider.RemoteEndPoint; |
| 1174 | + WindowsPrincipal clientPrincipal; |
| 1169 | 1175 | |
| 1170 | 1176 | try |
| 1171 | 1177 | { |
| 1178 | + if (!clientInfo.TimeoutToken.Cancel()) |
| 1179 | + throw new SocketException((int)SocketError.TimedOut); |
| 1180 | + |
| 1172 | 1181 | try |
| 1173 | 1182 | { |
| 1174 | 1183 | negotiateStream.EndAuthenticateAsServer(asyncResult); |
| 1175 | 1184 | |
| 1176 | 1185 | if (negotiateStream.RemoteIdentity is WindowsIdentity) |
| 1186 | + { |
| 1177 | 1187 | clientPrincipal = new WindowsPrincipal((WindowsIdentity)negotiateStream.RemoteIdentity); |
| 1188 | + clientInfo.ClientPrincipal = clientPrincipal; |
| 1189 | + } |
| 1178 | 1190 | } |
| 1179 | 1191 | catch (InvalidCredentialException) |
| 1180 | 1192 | { |
| @@ -1182,36 +1194,6 @@ namespace GSF.Communication · 1 unmodified line |
| 1182 | 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 | 1197 | // We can proceed further with receiving data from the client. |
| 1216 | 1198 | m_clientInfoLookup.TryAdd(client.ID, clientInfo); |
| 1217 | 1199 | |
| @@ -1221,12 +1203,9 @@ namespace GSF.Communication · 3 unmodified lines |
| 1221 | 1203 | catch (Exception ex) |
| 1222 | 1204 | { |
| 1223 | 1205 | // Notify of the exception. |
| 1224 | | − if ((object)remoteEndPoint != null) |
| 1225 | | − { |
| 1226 | | − string clientAddress = remoteEndPoint.Address.ToString(); |
| 1227 | | − string errorMessage = $"Unable to authenticate connection to client [{clientAddress}]: {ex.Message}"; |
| 1228 | | − OnClientConnectingException(new Exception(errorMessage, ex)); |
| 1229 | | − } |
| 1206 | + string clientAddress = remoteEndPoint.Address.ToString(); |
| 1207 | + string errorMessage = $"Unable to authenticate connection to client [{clientAddress}]: {ex.Message}"; |
| 1208 | + OnClientConnectingException(new Exception(errorMessage, ex)); |
| 1230 | 1209 | TerminateConnection(client, false); |
| 1231 | 1210 | } |
| 1232 | 1211 | finally |