private async Task <WatsonMessage> MessageReadAsync(ClientMetadata client) { /* * * Do not catch exceptions, let them get caught by the data reader * to destroy the connection * */ WatsonMessage msg = null; if (_Mode == Mode.Ssl) { msg = new WatsonMessage(client.SslStream, Debug); await msg.Build(); } else if (_Mode == Mode.Tcp) { msg = new WatsonMessage(client.NetworkStream, Debug); await msg.Build(); } else { throw new ArgumentException("Unknown mode: " + _Mode.ToString()); } return(msg); }
private async Task <bool> StartTls(ClientMetadata client) { try { // the two bools in this should really be contruction paramaters // maybe re-use mutualAuthentication and acceptInvalidCerts ? await client.SslStream.AuthenticateAsServerAsync(_SslCertificate, true, SslProtocols.Tls12, false); if (!client.SslStream.IsEncrypted) { Log("*** StartTls stream from " + client.IpPort + " not encrypted"); client.Dispose(); return(false); } if (!client.SslStream.IsAuthenticated) { Log("*** StartTls stream from " + client.IpPort + " not authenticated"); client.Dispose(); return(false); } if (MutuallyAuthenticate && !client.SslStream.IsMutuallyAuthenticated) { Log("*** StartTls stream from " + client.IpPort + " failed mutual authentication"); client.Dispose(); return(false); } } catch (IOException ex) { // Some type of problem initiating the SSL connection switch (ex.Message) { case "Authentication failed because the remote party has closed the transport stream.": case "Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host.": Log("*** StartTls IOException " + client.IpPort + " closed the connection."); break; case "The handshake failed due to an unexpected packet format.": Log("*** StartTls IOException " + client.IpPort + " disconnected, invalid handshake."); break; default: Log("*** StartTls IOException from " + client.IpPort + Environment.NewLine + ex.ToString()); break; } client.Dispose(); return(false); } catch (Exception ex) { Log("*** StartTls Exception from " + client.IpPort + Environment.NewLine + ex.ToString()); client.Dispose(); return(false); } return(true); }
private bool IsConnected(ClientMetadata client) { if (client.Tcp.Connected) { if ((client.Tcp.Client.Poll(0, SelectMode.SelectWrite)) && (!client.Tcp.Client.Poll(0, SelectMode.SelectError))) { byte[] buffer = new byte[1]; if (client.Tcp.Client.Receive(buffer, SocketFlags.Peek) == 0) { return(false); } else { return(true); } } else { return(false); } } else { return(false); } }
private void FinalizeConnection(ClientMetadata client) { #region Add-to-Client-List if (!AddClient(client)) { Log("*** FinalizeConnection unable to add client " + client.IpPort); client.Dispose(); return; } // Do not decrement in this block, decrement is done by the connection reader int activeCount = Interlocked.Increment(ref _ActiveClients); #endregion #region Start-Data-Receiver Log("*** FinalizeConnection starting data receiver for " + client.IpPort + " (now " + activeCount + " clients)"); if (_ClientConnected != null) { Task.Run(() => _ClientConnected(client.IpPort)); } Task.Run(async() => await DataReceiver(client)); #endregion }
private bool MessageWrite(ClientMetadata client, WatsonMessage msg, byte[] data) { if (client == null) { throw new ArgumentNullException(nameof(client)); } if (msg == null) { throw new ArgumentNullException(nameof(msg)); } int dataLen = 0; if (data != null) { dataLen = data.Length; } byte[] headerBytes = msg.ToHeaderBytes(dataLen); _SendLock.Wait(); try { if (_Mode == Mode.Tcp) { client.NetworkStream.Write(headerBytes, 0, headerBytes.Length); if (dataLen > 0) { client.NetworkStream.Write(data, 0, dataLen); } client.NetworkStream.Flush(); } else if (_Mode == Mode.Ssl) { client.SslStream.Write(headerBytes, 0, headerBytes.Length); if (dataLen > 0) { client.SslStream.Write(data, 0, dataLen); } client.SslStream.Flush(); } else { throw new ArgumentException("Unknown mode: " + _Mode.ToString()); } return(true); } catch (Exception) { Log("*** MessageWrite " + client.IpPort + " disconnected due to exception"); return(false); } finally { _SendLock.Release(); } }
private bool RemoveClient(ClientMetadata client) { _Clients.TryRemove(client.IpPort, out ClientMetadata removedClient); _UnauthenticatedClients.TryRemove(client.IpPort, out DateTime dt); Log("*** RemoveClient removed client " + client.IpPort); return(true); }
private bool AddClient(ClientMetadata client) { _Clients.TryRemove(client.IpPort, out ClientMetadata removedClient); _Clients.TryAdd(client.IpPort, client); Log("*** AddClient added client " + client.IpPort); return(true); }
private bool AddClient(ClientMetadata client) { if (!_Clients.TryRemove(client.IpPort, out ClientMetadata removedClient)) { // do nothing, it probably did not exist anyway } _Clients.TryAdd(client.IpPort, client); Log("*** AddClient added client " + client.IpPort); return(true); }
private async Task <bool> MessageWriteAsync(ClientMetadata client, byte[] data) { try { #region Format-Message string header = ""; byte[] headerBytes; byte[] message; if (data == null || data.Length < 1) { header += "0:"; } else { header += data.Length + ":"; } headerBytes = Encoding.UTF8.GetBytes(header); int messageLen = headerBytes.Length; if (data != null && data.Length > 0) { messageLen += data.Length; } message = new byte[messageLen]; Buffer.BlockCopy(headerBytes, 0, message, 0, headerBytes.Length); if (data != null && data.Length > 0) { Buffer.BlockCopy(data, 0, message, headerBytes.Length, data.Length); } #endregion #region Send-Message-Async var clientStream = client.Tcp.GetStream(); await clientStream.WriteAsync(message, 0, message.Length); await clientStream.FlushAsync(); return(true); #endregion } catch (Exception) { Log("*** MessageWriteAsync " + client.IpPort() + " disconnected due to exception"); return(false); } }
private bool RemoveClient(ClientMetadata client) { if (!_Clients.TryRemove(client.IpPort, out ClientMetadata removedClient)) { Log("*** RemoveClient unable to remove client " + client.IpPort); return(false); } else { Log("*** RemoveClient removed client " + client.IpPort); return(true); } }
private bool AddClient(ClientMetadata client) { ClientMetadata removedClient; if (!Clients.TryRemove(client.IpPort(), out removedClient)) { // do nothing, it probably did not exist anyway } Clients.TryAdd(client.IpPort(), client); Log("AddClient added client " + client.IpPort()); return(true); }
private async Task DataReceiver(ClientMetadata client, CancellationToken?cancelToken = null) { try { #region Wait-for-Data while (true) { cancelToken?.ThrowIfCancellationRequested(); try { if (!IsConnected(client)) { break; } byte[] data = await MessageReadAsync(client); if (data == null) { // no message available await Task.Delay(30); continue; } if (MessageReceived != null) { var unawaited = Task.Run(() => MessageReceived(client.IpPort(), data)); } } catch (Exception) { break; } } #endregion } finally { ActiveClients--; RemoveClient(client); if (ClientDisconnected != null) { var unawaited = Task.Run(() => ClientDisconnected(client.IpPort())); } Log("DataReceiver client " + client.IpPort() + " disconnected (now " + ActiveClients + " clients active)"); } }
private async Task DataReceiver(ClientMetadata client) { try { #region Wait-for-Data while (true) { try { if (!IsConnected(client)) { break; } byte[] data = await MessageReadAsync(client); if (data == null) { // no message available await Task.Delay(30); continue; } if (_MessageReceived != null) { Task <bool> unawaited = Task.Run(() => _MessageReceived(client.IpPort, data)); } } catch (Exception) { break; } } #endregion } finally { int activeCount = Interlocked.Decrement(ref _ActiveClients); RemoveClient(client); if (_ClientDisconnected != null) { Task <bool> unawaited = Task.Run(() => _ClientDisconnected(client.IpPort)); } Log("*** DataReceiver client " + client.IpPort + " disconnected (now " + activeCount + " clients active)"); client.Dispose(); } }
private async Task <bool> MessageWriteAsync(ClientMetadata client, WatsonMessage msg, byte[] data) { int dataLen = 0; MemoryStream ms = new MemoryStream(); if (data != null && data.Length > 0) { dataLen = data.Length; ms.Write(data, 0, data.Length); ms.Seek(0, SeekOrigin.Begin); } return(await MessageWriteAsync(client, msg, dataLen, ms)); }
private bool RemoveClient(ClientMetadata client) { ClientMetadata removedClient; if (!Clients.TryRemove(client.IpPort(), out removedClient)) { Log("RemoveClient unable to remove client " + client.IpPort()); return(false); } else { Log("RemoveClient removed client " + client.IpPort()); return(true); } }
private async Task <bool> MessageWriteAsync(ClientMetadata client, WatsonMessage msg) { if (client == null) { throw new ArgumentNullException(nameof(client)); } if (msg == null) { throw new ArgumentNullException(nameof(msg)); } byte[] msgBytes = msg.ToBytes(); await _SendLock.WaitAsync(); try { if (_Mode == Mode.Tcp) { await client.NetworkStream.WriteAsync(msgBytes, 0, msgBytes.Length); await client.NetworkStream.FlushAsync(); } else if (_Mode == Mode.Ssl) { await client.SslStream.WriteAsync(msgBytes, 0, msgBytes.Length); await client.SslStream.FlushAsync(); } else { throw new ArgumentException("Unknown mode: " + _Mode.ToString()); } return(true); } catch (Exception) { Log("*** MessageWriteAsync " + client.IpPort + " disconnected due to exception"); return(false); } finally { _SendLock.Release(); } }
private void FinalizeConnection(ClientMetadata client) { #region Add-to-Client-List if (!AddClient(client)) { Log("*** FinalizeConnection unable to add client " + client.IpPort); client.Dispose(); return; } // Do not decrement in this block, decrement is done by the connection reader int activeCount = Interlocked.Increment(ref _ActiveClients); #endregion #region Request-Authentication if (!String.IsNullOrEmpty(PresharedKey)) { Log("*** FinalizeConnection soliciting authentication material from " + client.IpPort); _UnauthenticatedClients.TryAdd(client.IpPort, DateTime.Now); byte[] data = Encoding.UTF8.GetBytes("Authentication required"); WatsonMessage authMsg = new WatsonMessage(); authMsg.Status = MessageStatus.AuthRequired; authMsg.Data = null; authMsg.ContentLength = 0; MessageWrite(client, authMsg, null); } #endregion #region Start-Data-Receiver Log("*** FinalizeConnection starting data receiver for " + client.IpPort + " (now " + activeCount + " clients)"); if (ClientConnected != null) { Task.Run(() => ClientConnected(client.IpPort)); } Task.Run(async() => await DataReceiver(client)); #endregion }
private bool MessageWrite(ClientMetadata client, WatsonMessage msg, byte[] data) { int dataLen = 0; MemoryStream ms = new MemoryStream(); if (data != null && data.Length > 0) { dataLen = data.Length; ms.Write(data, 0, data.Length); ms.Seek(0, SeekOrigin.Begin); } else { ms = new MemoryStream(new byte[0]); } return(MessageWrite(client, msg, dataLen, ms)); }
private void FinalizeConnection(ClientMetadata client) { #region Add-to-Client-List ClientMetadata removed = null; DateTime ts; _Clients.TryRemove(client.IpPort, out removed); _ClientsLastSeen.TryRemove(client.IpPort, out ts); _Clients.TryAdd(client.IpPort, client); _ClientsLastSeen.TryAdd(client.IpPort, DateTime.Now); #endregion Add-to-Client-List #region Request-Authentication if (!String.IsNullOrEmpty(PresharedKey)) { Log("*** FinalizeConnection soliciting authentication material from " + client.IpPort); _UnauthenticatedClients.TryAdd(client.IpPort, DateTime.Now); byte[] data = Encoding.UTF8.GetBytes("Authentication required"); WatsonMessage authMsg = new WatsonMessage(); authMsg.Status = MessageStatus.AuthRequired; authMsg.Data = null; authMsg.ContentLength = 0; MessageWrite(client, authMsg, null); } #endregion Request-Authentication #region Start-Data-Receiver Log("*** FinalizeConnection starting data receiver for " + client.IpPort); if (ClientConnected != null) { Task.Run(() => ClientConnected(client.IpPort)); } Task.Run(async() => await DataReceiver(client, client.Token)); #endregion Start-Data-Receiver }
private async Task AcceptConnections() { Listener.Start(); while (true) { #region Accept-Connection Token.ThrowIfCancellationRequested(); TcpClient client = await Listener.AcceptTcpClientAsync(); client.LingerState.Enabled = false; #endregion #region Get-Tuple-and-Check-IP string clientIp = ((IPEndPoint)client.Client.RemoteEndPoint).Address.ToString(); int clientPort = ((IPEndPoint)client.Client.RemoteEndPoint).Port; if (PermittedIps != null && PermittedIps.Count > 0) { if (!PermittedIps.Contains(clientIp)) { Log("*** AcceptConnections rejecting connection from " + clientIp + " (not permitted)"); client.Close(); return; } } Log("AcceptConnections accepted connection from " + clientIp + ":" + clientPort); #endregion var unawaited = Task.Run(() => { #region Add-to-Client-List ActiveClients++; // Do not decrement in this block, decrement is done by the connection reader ClientMetadata currClient = new ClientMetadata(client); if (!AddClient(currClient)) { Log("*** AcceptConnections unable to add client " + currClient.IpPort()); client.Close(); return; } #endregion #region Start-Data-Receiver CancellationToken dataReceiverToken = default(CancellationToken); Log("AcceptConnections starting data receiver for " + currClient.IpPort() + " (now " + ActiveClients + " clients)"); if (ClientConnected != null) { Task.Run(() => ClientConnected(currClient.IpPort())); } Task.Run(async() => await DataReceiver(currClient, dataReceiverToken), dataReceiverToken); #endregion }, Token); } }
private bool IsConnected(ClientMetadata client) { if (client.TcpClient.Connected) { byte[] tmp = new byte[1]; bool success = false; bool sendLocked = false; bool readLocked = false; try { client.WriteLock.Wait(1); sendLocked = true; client.TcpClient.Client.Send(tmp, 0, 0); success = true; } catch (ObjectDisposedException) { } catch (IOException) { } catch (SocketException se) { if (se.NativeErrorCode.Equals(10035)) { success = true; } } catch (Exception e) { Log("*** IsConnected " + client.IpPort + " exception using send: " + e.Message); success = false; } finally { if (sendLocked) { client.WriteLock.Release(); } } if (success) { return(true); } try { client.ReadLock.Wait(1); readLocked = true; if ((client.TcpClient.Client.Poll(0, SelectMode.SelectWrite)) && (!client.TcpClient.Client.Poll(0, SelectMode.SelectError))) { byte[] buffer = new byte[1]; if (client.TcpClient.Client.Receive(buffer, SocketFlags.Peek) == 0) { return(false); } else { return(true); } } else { return(false); } } catch (Exception e) { Log("*** IsConnected " + client.IpPort + " exception using poll/peek: " + e.Message); return(false); } finally { if (readLocked) { client.ReadLock.Release(); } } } else { return(false); } }
private async Task AcceptConnections() { _Listener.Start(); while (!_Token.IsCancellationRequested) { string clientIpPort = String.Empty; try { #region Accept-Connection-and-Validate-IP TcpClient tcpClient = await _Listener.AcceptTcpClientAsync(); tcpClient.LingerState.Enabled = false; string clientIp = ((IPEndPoint)tcpClient.Client.RemoteEndPoint).Address.ToString(); if (PermittedIPs != null && PermittedIPs.Count > 0) { if (!PermittedIPs.Contains(clientIp)) { Log("*** AcceptConnections rejecting connection from " + clientIp + " (not permitted)"); tcpClient.Close(); continue; } } ClientMetadata client = new ClientMetadata(tcpClient); clientIpPort = client.IpPort; #endregion Accept-Connection-and-Validate-IP if (_Mode == Mode.Tcp) { Task unawaited = Task.Run(() => FinalizeConnection(client), _Token); } else if (_Mode == Mode.Ssl) { if (AcceptInvalidCertificates) { client.SslStream = new SslStream(client.NetworkStream, false, new RemoteCertificateValidationCallback(AcceptCertificate)); } else { client.SslStream = new SslStream(client.NetworkStream, false); } Task unawaited = Task.Run(() => { Task <bool> success = StartTls(client); if (success.Result) { FinalizeConnection(client); } }, _Token); } else { throw new ArgumentException("Unknown mode: " + _Mode.ToString()); } Log("*** AcceptConnections accepted connection from " + client.IpPort); } catch (Exception e) { Log("*** AcceptConnections exception " + clientIpPort + " " + e.Message); } } }
private async Task <bool> MessageWriteAsync(ClientMetadata client, WatsonMessage msg, long contentLength, Stream stream) { if (client == null) { throw new ArgumentNullException(nameof(client)); } if (msg == null) { throw new ArgumentNullException(nameof(msg)); } if (contentLength > 0) { if (stream == null || !stream.CanRead) { throw new ArgumentException("Cannot read from supplied stream."); } } byte[] headerBytes = msg.ToHeaderBytes(contentLength); int bytesRead = 0; long bytesRemaining = contentLength; byte[] buffer = new byte[_ReadStreamBufferSize]; client.WriteLock.Wait(1); try { if (_Mode == Mode.Tcp) { await client.NetworkStream.WriteAsync(headerBytes, 0, headerBytes.Length); if (contentLength > 0) { while (bytesRemaining > 0) { bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length); if (bytesRead > 0) { await client.NetworkStream.WriteAsync(buffer, 0, bytesRead); bytesRemaining -= bytesRead; } } } await client.NetworkStream.FlushAsync(); } else if (_Mode == Mode.Ssl) { await client.SslStream.WriteAsync(headerBytes, 0, headerBytes.Length); if (contentLength > 0) { while (bytesRemaining > 0) { bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length); if (bytesRead > 0) { await client.SslStream.WriteAsync(buffer, 0, bytesRead); bytesRemaining -= bytesRead; } } } await client.SslStream.FlushAsync(); } return(true); } catch (Exception e) { Log("*** MessageWriteAsync " + client.IpPort + " disconnected due to exception: " + e.Message); return(false); } finally { client.WriteLock.Release(); } }
private async Task DataReceiver(ClientMetadata client) { try { #region Wait-for-Data while (true) { try { if (!IsConnected(client)) { break; } WatsonMessage msg = await MessageReadAsync(client); if (msg == null) { // no message available await Task.Delay(30); continue; } if (!String.IsNullOrEmpty(PresharedKey)) { if (_UnauthenticatedClients.ContainsKey(client.IpPort)) { Log("*** DataReceiver message received from unauthenticated endpoint: " + client.IpPort); if (msg.Status == MessageStatus.AuthRequested) { // check preshared key if (msg.PresharedKey != null && msg.PresharedKey.Length > 0) { string clientPsk = Encoding.UTF8.GetString(msg.PresharedKey).Trim(); if (PresharedKey.Trim().Equals(clientPsk)) { if (Debug) { Log("DataReceiver accepted authentication from " + client.IpPort); } _UnauthenticatedClients.TryRemove(client.IpPort, out DateTime dt); byte[] data = Encoding.UTF8.GetBytes("Authentication successful"); WatsonMessage authMsg = new WatsonMessage(data, Debug); authMsg.Status = MessageStatus.AuthSuccess; MessageWrite(client, authMsg, null); continue; } else { if (Debug) { Log("DataReceiver declined authentication from " + client.IpPort); } byte[] data = Encoding.UTF8.GetBytes("Authentication declined"); WatsonMessage authMsg = new WatsonMessage(data, Debug); authMsg.Status = MessageStatus.AuthFailure; MessageWrite(client, authMsg, null); continue; } } else { if (Debug) { Log("DataReceiver no authentication material from " + client.IpPort); } byte[] data = Encoding.UTF8.GetBytes("No authentication material"); WatsonMessage authMsg = new WatsonMessage(data, Debug); authMsg.Status = MessageStatus.AuthFailure; MessageWrite(client, authMsg, null); continue; } } else { // decline the message if (Debug) { Log("DataReceiver no authentication material from " + client.IpPort); } byte[] data = Encoding.UTF8.GetBytes("Authentication required"); WatsonMessage authMsg = new WatsonMessage(data, Debug); authMsg.Status = MessageStatus.AuthRequired; MessageWrite(client, authMsg, null); continue; } } } if (ReadDataStream) { if (MessageReceived != null) { Task <bool> unawaited = Task.Run(() => MessageReceived(client.IpPort, msg.Data)); } } else { if (StreamReceived != null) { StreamReceived(client.IpPort, msg.ContentLength, msg.DataStream); } } } catch (Exception) { break; } } #endregion } finally { int activeCount = Interlocked.Decrement(ref _ActiveClients); RemoveClient(client); if (ClientDisconnected != null) { Task <bool> unawaited = Task.Run(() => ClientDisconnected(client.IpPort)); } Log("*** DataReceiver client " + client.IpPort + " disconnected (now " + activeCount + " clients active)"); client.Dispose(); } }
private async Task <byte[]> MessageReadAsync(ClientMetadata client) { /* * * Do not catch exceptions, let them get caught by the data reader * to destroy the connection * */ #region Variables int bytesRead = 0; int sleepInterval = 25; int maxTimeout = 500; int currentTimeout = 0; bool timeout = false; NetworkStream ClientStream = client.Tcp.GetStream(); byte[] headerBytes; string header = ""; long contentLength; byte[] contentBytes; if (!ClientStream.CanRead) { return(null); } if (!ClientStream.DataAvailable) { return(null); } #endregion #region Read-Header using (MemoryStream headerMs = new MemoryStream()) { #region Read-Header-Bytes byte[] headerBuffer = new byte[1]; timeout = false; currentTimeout = 0; int read = 0; while ((read = await ClientStream.ReadAsync(headerBuffer, 0, headerBuffer.Length)) > 0) { if (read > 0) { await headerMs.WriteAsync(headerBuffer, 0, read); bytesRead += read; currentTimeout = 0; if (bytesRead > 1) { if ((int)headerBuffer[0] == 58) { break; } } } else { if (currentTimeout >= maxTimeout) { timeout = true; break; } else { currentTimeout += sleepInterval; await Task.Delay(sleepInterval); } } } if (timeout) { Log("*** MessageReadAsync timeout " + currentTimeout + "ms/" + maxTimeout + "ms exceeded while reading header after reading " + bytesRead + " bytes"); return(null); } headerBytes = headerMs.ToArray(); if (headerBytes == null || headerBytes.Length < 1) { return(null); } #endregion #region Process-Header header = Encoding.UTF8.GetString(headerBytes); header = header.Replace(":", ""); if (!Int64.TryParse(header, out contentLength)) { Log("*** MessageReadAsync malformed message from " + client.IpPort() + " (message header not an integer)"); return(null); } #endregion } #endregion #region Read-Data using (MemoryStream dataMs = new MemoryStream()) { long bytesRemaining = contentLength; timeout = false; currentTimeout = 0; int read = 0; byte[] buffer; long bufferSize = 2048; if (bufferSize > bytesRemaining) { bufferSize = bytesRemaining; } buffer = new byte[bufferSize]; while ((read = await ClientStream.ReadAsync(buffer, 0, buffer.Length)) > 0) { if (read > 0) { dataMs.Write(buffer, 0, read); bytesRead = bytesRead + read; bytesRemaining = bytesRemaining - read; currentTimeout = 0; // reduce buffer size if number of bytes remaining is // less than the pre-defined buffer size of 2KB if (bytesRemaining < bufferSize) { bufferSize = bytesRemaining; } buffer = new byte[bufferSize]; // check if read fully if (bytesRemaining == 0) { break; } if (bytesRead == contentLength) { break; } } if (!ClientStream.DataAvailable) { while (true) { if (currentTimeout >= maxTimeout) { timeout = true; break; } else { currentTimeout += sleepInterval; await Task.Delay(sleepInterval); } } if (timeout) { break; } } } if (timeout) { Log("*** MessageReadAsync timeout " + currentTimeout + "ms/" + maxTimeout + "ms exceeded while reading content after reading " + bytesRead + " bytes"); return(null); } contentBytes = dataMs.ToArray(); } #endregion #region Check-Content-Bytes if (contentBytes == null || contentBytes.Length < 1) { Log("*** MessageReadAsync " + client.IpPort() + " no content read"); return(null); } if (contentBytes.Length != contentLength) { Log("*** MessageReadAsync " + client.IpPort() + " content length " + contentBytes.Length + " bytes does not match header value " + contentLength + ", discarding"); return(null); } #endregion return(contentBytes); }
private async Task DataReceiver(ClientMetadata client, CancellationToken token) { string header = "[" + client.IpPort + "]"; while (true) { try { token.ThrowIfCancellationRequested(); if (!IsConnected(client)) { break; } WatsonMessage msg = null; bool buildSuccess = false; if (_Mode == Mode.Ssl) { msg = new WatsonMessage(client.SslStream, Debug); } else if (_Mode == Mode.Tcp) { msg = new WatsonMessage(client.NetworkStream, Debug); } if (_MessageReceived != null) { buildSuccess = await msg.Build(); } else if (_StreamReceived != null) { buildSuccess = await msg.BuildStream(); } else { break; } if (!buildSuccess) { break; } if (msg == null) { // no message available await Task.Delay(30); continue; } if (!String.IsNullOrEmpty(PresharedKey)) { if (_UnauthenticatedClients.ContainsKey(client.IpPort)) { Log(header + " message received from unauthenticated endpoint"); if (msg.Status == MessageStatus.AuthRequested) { // check preshared key if (msg.PresharedKey != null && msg.PresharedKey.Length > 0) { string clientPsk = Encoding.UTF8.GetString(msg.PresharedKey).Trim(); if (PresharedKey.Trim().Equals(clientPsk)) { Log(header + " accepted authentication"); _UnauthenticatedClients.TryRemove(client.IpPort, out DateTime dt); byte[] data = Encoding.UTF8.GetBytes("Authentication successful"); WatsonMessage authMsg = new WatsonMessage(data, Debug); authMsg.Status = MessageStatus.AuthSuccess; MessageWrite(client, authMsg, null); continue; } else { Log(header + " declined authentication"); byte[] data = Encoding.UTF8.GetBytes("Authentication declined"); WatsonMessage authMsg = new WatsonMessage(data, Debug); authMsg.Status = MessageStatus.AuthFailure; MessageWrite(client, authMsg, null); continue; } } else { Log(header + " no authentication material"); byte[] data = Encoding.UTF8.GetBytes("No authentication material"); WatsonMessage authMsg = new WatsonMessage(data, Debug); authMsg.Status = MessageStatus.AuthFailure; MessageWrite(client, authMsg, null); continue; } } else { // decline the message Log(header + " no authentication material"); byte[] data = Encoding.UTF8.GetBytes("Authentication required"); WatsonMessage authMsg = new WatsonMessage(data, Debug); authMsg.Status = MessageStatus.AuthRequired; MessageWrite(client, authMsg, null); continue; } } } if (msg.Status == MessageStatus.Disconnecting) { Log(header + " sent notification of disconnection"); break; } if (msg.Status == MessageStatus.Removed) { Log(header + " sent notification of removal"); break; } if (_MessageReceived != null) { // does not need to be awaited, because the stream has been fully read Task unawaited = Task.Run(() => _MessageReceived(client.IpPort, msg.Data)); } else if (_StreamReceived != null) { // must be awaited, the stream has not been fully read await _StreamReceived(client.IpPort, msg.ContentLength, msg.DataStream); } else { break; } UpdateClientLastSeen(client.IpPort); } catch (Exception e) { Log(Environment.NewLine + "[" + client.IpPort + "] Data receiver exception:" + Environment.NewLine + e.ToString() + Environment.NewLine); break; } } Log(header + " data receiver terminated"); if (ClientDisconnected != null) { Task unawaited = null; if (_ClientsKicked.ContainsKey(client.IpPort)) { unawaited = Task.Run(() => ClientDisconnected(client.IpPort, DisconnectReason.Kicked)); } else if (_ClientsTimedout.ContainsKey(client.IpPort)) { unawaited = Task.Run(() => ClientDisconnected(client.IpPort, DisconnectReason.Timeout)); } else { unawaited = Task.Run(() => ClientDisconnected(client.IpPort, DisconnectReason.Normal)); } } DateTime removedTs; _Clients.TryRemove(client.IpPort, out ClientMetadata removedClient); _ClientsLastSeen.TryRemove(client.IpPort, out removedTs); _ClientsKicked.TryRemove(client.IpPort, out removedTs); _ClientsTimedout.TryRemove(client.IpPort, out removedTs); _UnauthenticatedClients.TryRemove(client.IpPort, out removedTs); Log(header + " disposing"); client.Dispose(); }
private async Task AcceptConnections() { Listener.Start(); while (true) { #region Accept-Connection Token.ThrowIfCancellationRequested(); TcpClient tcpClient = await Listener.AcceptTcpClientAsync(); tcpClient.LingerState.Enabled = false; #endregion #region Get-Tuple-and-Check-IP string clientIp = ((IPEndPoint)tcpClient.Client.RemoteEndPoint).Address.ToString(); int clientPort = ((IPEndPoint)tcpClient.Client.RemoteEndPoint).Port; if (PermittedIps != null && PermittedIps.Count > 0) { if (!PermittedIps.Contains(clientIp)) { Log("*** AcceptConnections rejecting connection from " + clientIp + " (not permitted)"); tcpClient.Close(); return; } } Log("AcceptConnections accepted connection from " + clientIp + ":" + clientPort); #endregion #region Initialize-and-Authenticate SslStream sslStream = null; if (AcceptInvalidCerts) { // accept invalid certs sslStream = new SslStream(tcpClient.GetStream(), false, new RemoteCertificateValidationCallback(AcceptCertificate)); } else { // do not accept invalid SSL certificates sslStream = new SslStream(tcpClient.GetStream(), false); } sslStream.AuthenticateAsServer(SslCertificate, true, SslProtocols.Tls12, false); if (!sslStream.IsEncrypted) { Log("*** AcceptConnections stream from " + clientIp + " not encrypted"); tcpClient.Close(); return; } if (!sslStream.IsAuthenticated) { Log("*** AcceptConnections stream from " + clientIp + " not authenticated"); tcpClient.Close(); return; } if (MutuallyAuthenticate && !sslStream.IsMutuallyAuthenticated) { Log("*** AcceptConnections stream from " + clientIp + " failed mutual authentication"); tcpClient.Close(); return; } #endregion var unawaited = Task.Run(() => { #region Add-to-Client-List ActiveClients++; // Do not decrement in this block, decrement is done by the connection reader ClientMetadata currClient = new ClientMetadata(tcpClient, sslStream); if (!AddClient(currClient)) { Log("*** AcceptConnections unable to add client " + currClient.IpPort()); tcpClient.Close(); return; } #endregion #region Start-Data-Receiver CancellationToken dataReceiverToken = default(CancellationToken); Log("AcceptConnections starting data receiver for " + currClient.IpPort() + " (now " + ActiveClients + " clients)"); if (ClientConnected != null) { Task.Run(() => ClientConnected(currClient.IpPort())); } Task.Run(async() => await DataReceiver(currClient, dataReceiverToken), dataReceiverToken); #endregion }, Token); } }
private async Task AcceptConnections() { _Listener.Start(); while (!_Token.IsCancellationRequested) { string clientIpPort = String.Empty; try { #region Accept-Connection TcpClient tcpClient = await _Listener.AcceptTcpClientAsync(); tcpClient.LingerState.Enabled = false; #endregion #region Get-Tuple-and-Check-IP string clientIp = ((IPEndPoint)tcpClient.Client.RemoteEndPoint).Address.ToString(); if (_PermittedIps != null && _PermittedIps.Count > 0) { if (!_PermittedIps.Contains(clientIp)) { Log("*** AcceptConnections rejecting connection from " + clientIp + " (not permitted)"); tcpClient.Close(); continue; } } #endregion ClientMetadata client = new ClientMetadata(tcpClient); clientIpPort = client.IpPort; Log("*** AcceptConnections accepted connection from " + client.IpPort); Task unawaited = Task.Run(() => { FinalizeConnection(client); }, _Token); } catch (ObjectDisposedException ex) { // Listener stopped ? if so, clientIpPort will be empty Log("*** AcceptConnections ObjectDisposedException from " + clientIpPort + Environment.NewLine + ex.ToString()); } catch (SocketException ex) { switch (ex.Message) { case "An existing connection was forcibly closed by the remote host": Log("*** AcceptConnections SocketException " + clientIpPort + " closed the connection."); break; default: Log("*** AcceptConnections SocketException from " + clientIpPort + Environment.NewLine + ex.ToString()); break; } } catch (Exception ex) { Log("*** AcceptConnections Exception from " + clientIpPort + Environment.NewLine + ex.ToString()); } } }