diff --git a/src/Renci.SshNet/BaseClient.cs b/src/Renci.SshNet/BaseClient.cs index 56d2145cc..25d178f85 100644 --- a/src/Renci.SshNet/BaseClient.cs +++ b/src/Renci.SshNet/BaseClient.cs @@ -1,9 +1,9 @@ using System; +using System.Diagnostics; using System.Net.Sockets; using System.Threading; using System.Threading.Tasks; -using Renci.SshNet.Abstractions; using Renci.SshNet.Common; using Renci.SshNet.Messages.Transport; @@ -317,7 +317,7 @@ public async Task ConnectAsync(CancellationToken cancellationToken) /// The method was called after the client was disposed. public void Disconnect() { - DiagnosticAbstraction.Log("Disconnecting client."); + Diagnostic.Log("Disconnecting client.", TraceEventType.Verbose); CheckDisposed(); @@ -416,7 +416,7 @@ protected virtual void Dispose(bool disposing) if (disposing) { - DiagnosticAbstraction.Log("Disposing client."); + Diagnostic.Log("Disposing client.", TraceEventType.Verbose); Disconnect(); diff --git a/src/Renci.SshNet/Channels/Channel.cs b/src/Renci.SshNet/Channels/Channel.cs index 25975872d..162f20e73 100644 --- a/src/Renci.SshNet/Channels/Channel.cs +++ b/src/Renci.SshNet/Channels/Channel.cs @@ -1,9 +1,9 @@ using System; +using System.Diagnostics; using System.Globalization; using System.Net.Sockets; using System.Threading; -using Renci.SshNet.Abstractions; using Renci.SshNet.Common; using Renci.SshNet.Messages; using Renci.SshNet.Messages.Connection; @@ -556,7 +556,7 @@ protected virtual void Close() var closeWaitResult = _session.TryWait(_channelClosedWaitHandle, ConnectionInfo.ChannelCloseTimeout); if (closeWaitResult != WaitResult.Success) { - DiagnosticAbstraction.Log(string.Format("Wait for channel close not successful: {0:G}.", closeWaitResult)); + Diagnostic.Log(string.Format("Wait for channel close not successful: {0:G}.", closeWaitResult), TraceEventType.Warning); } } } diff --git a/src/Renci.SshNet/Channels/ChannelDirectTcpip.cs b/src/Renci.SshNet/Channels/ChannelDirectTcpip.cs index 6c521bce2..d60a34885 100644 --- a/src/Renci.SshNet/Channels/ChannelDirectTcpip.cs +++ b/src/Renci.SshNet/Channels/ChannelDirectTcpip.cs @@ -1,7 +1,9 @@ using System; +using System.Diagnostics; using System.Net; using System.Net.Sockets; using System.Threading; + using Renci.SshNet.Abstractions; using Renci.SshNet.Common; using Renci.SshNet.Messages.Connection; @@ -156,8 +158,7 @@ private void ShutdownSocket(SocketShutdown how) } catch (SocketException ex) { - // TODO: log as warning - DiagnosticAbstraction.Log("Failure shutting down socket: " + ex); + Diagnostic.Log("Failure shutting down socket: " + ex, TraceEventType.Warning); } } } diff --git a/src/Renci.SshNet/Channels/ChannelForwardedTcpip.cs b/src/Renci.SshNet/Channels/ChannelForwardedTcpip.cs index a8382015a..416f3f8b8 100644 --- a/src/Renci.SshNet/Channels/ChannelForwardedTcpip.cs +++ b/src/Renci.SshNet/Channels/ChannelForwardedTcpip.cs @@ -1,6 +1,8 @@ using System; +using System.Diagnostics; using System.Net; using System.Net.Sockets; + using Renci.SshNet.Abstractions; using Renci.SshNet.Common; using Renci.SshNet.Messages.Connection; @@ -138,8 +140,7 @@ private void ShutdownSocket(SocketShutdown how) } catch (SocketException ex) { - // TODO: log as warning - DiagnosticAbstraction.Log("Failure shutting down socket: " + ex); + Diagnostic.Log("Failure shutting down socket: " + ex, TraceEventType.Warning); } } } diff --git a/src/Renci.SshNet/Common/Extensions.cs b/src/Renci.SshNet/Common/Extensions.cs index ae390d604..35ce537c5 100644 --- a/src/Renci.SshNet/Common/Extensions.cs +++ b/src/Renci.SshNet/Common/Extensions.cs @@ -315,5 +315,12 @@ internal static bool IsConnected(this Socket socket) return socket.Connected; } + + internal static string Join(this IEnumerable values, string separator) + { + // A sly way to prevent analyzers asking to "use an overload with a char parameter" + // which is not available on all targets. + return string.Join(separator, values); + } } } diff --git a/src/Renci.SshNet/Connection/ConnectorBase.cs b/src/Renci.SshNet/Connection/ConnectorBase.cs index 384091b18..0d8949e04 100644 --- a/src/Renci.SshNet/Connection/ConnectorBase.cs +++ b/src/Renci.SshNet/Connection/ConnectorBase.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics; using System.Net; using System.Net.Sockets; using System.Threading; @@ -41,7 +42,7 @@ protected Socket SocketConnect(string host, int port, TimeSpan timeout) var ipAddress = Dns.GetHostAddresses(host)[0]; var ep = new IPEndPoint(ipAddress, port); - DiagnosticAbstraction.Log(string.Format("Initiating connection to '{0}:{1}'.", host, port)); + Diagnostic.Log(string.Format("Initiating connection to '{0}:{1}'.", host, port), TraceEventType.Information); var socket = SocketFactory.Create(ep.AddressFamily, SocketType.Stream, ProtocolType.Tcp); @@ -81,7 +82,7 @@ protected async Task SocketConnectAsync(string host, int port, Cancellat var ep = new IPEndPoint(ipAddress, port); - DiagnosticAbstraction.Log(string.Format("Initiating connection to '{0}:{1}'.", host, port)); + Diagnostic.Log(string.Format("Initiating connection to '{0}:{1}'.", host, port), TraceEventType.Information); var socket = SocketFactory.Create(ep.AddressFamily, SocketType.Stream, ProtocolType.Tcp); try diff --git a/src/Renci.SshNet/Abstractions/DiagnosticAbstraction.cs b/src/Renci.SshNet/Diagnostic.cs similarity index 61% rename from src/Renci.SshNet/Abstractions/DiagnosticAbstraction.cs rename to src/Renci.SshNet/Diagnostic.cs index bc1248dc0..e8584981c 100644 --- a/src/Renci.SshNet/Abstractions/DiagnosticAbstraction.cs +++ b/src/Renci.SshNet/Diagnostic.cs @@ -1,28 +1,24 @@ -using System.ComponentModel; +using System; using System.Diagnostics; -namespace Renci.SshNet.Abstractions +namespace Renci.SshNet { /// /// Provides access to the internals of SSH.NET. /// - [EditorBrowsable(EditorBrowsableState.Never)] - public static class DiagnosticAbstraction + public static class Diagnostic { /// /// The instance used by SSH.NET. /// /// /// - /// Currently, the library only traces events when compiled in Debug mode. - /// - /// /// Configuration on .NET Core must be done programmatically, e.g. /// - /// DiagnosticAbstraction.Source.Switch = new SourceSwitch("sourceSwitch", "Verbose"); - /// DiagnosticAbstraction.Source.Listeners.Remove("Default"); - /// DiagnosticAbstraction.Source.Listeners.Add(new ConsoleTraceListener()); - /// DiagnosticAbstraction.Source.Listeners.Add(new TextWriterTraceListener("trace.log")); + /// Diagnostics.Source.Switch = new SourceSwitch("sourceSwitch", nameof(SourceLevels.Verbose)); + /// Diagnostics.Source.Listeners.Remove("Default"); + /// Diagnostics.Source.Listeners.Add(new ConsoleTraceListener()); + /// Diagnostics.Source.Listeners.Add(new TextWriterTraceListener("SshNetTrace.log")); /// /// /// @@ -53,17 +49,19 @@ public static class DiagnosticAbstraction public static readonly TraceSource Source = new TraceSource("SshNet.Logging"); /// - /// Logs a message to at the - /// level. + /// Logs a message to with the specified event type. /// /// The message to log. - /// The trace event type. - [Conditional("DEBUG")] - public static void Log(string text, TraceEventType type = TraceEventType.Verbose) + /// The trace event type. + public static void Log(string text, TraceEventType eventType) + { + Source.TraceEvent(eventType, Environment.CurrentManagedThreadId, text); + } + + /// + public static bool IsEnabled(TraceEventType eventType) { - Source.TraceEvent(type, - System.Environment.CurrentManagedThreadId, - text); + return Source.Switch.ShouldTrace(eventType); } } } diff --git a/src/Renci.SshNet/ForwardedPortDynamic.cs b/src/Renci.SshNet/ForwardedPortDynamic.cs index 0c6e417a8..2d354850d 100644 --- a/src/Renci.SshNet/ForwardedPortDynamic.cs +++ b/src/Renci.SshNet/ForwardedPortDynamic.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics; using System.Globalization; using System.Linq; using System.Net; @@ -413,8 +414,7 @@ private void InternalStop(TimeSpan timeout) if (!_pendingChannelCountdown.Wait(timeout)) { - // TODO: log as warning - DiagnosticAbstraction.Log("Timeout waiting for pending channels in dynamic forwarded port to close."); + Diagnostic.Log("Timeout waiting for pending channels in dynamic forwarded port to close.", TraceEventType.Warning); } } diff --git a/src/Renci.SshNet/ForwardedPortLocal.cs b/src/Renci.SshNet/ForwardedPortLocal.cs index fce8f7fd7..e51d205a1 100644 --- a/src/Renci.SshNet/ForwardedPortLocal.cs +++ b/src/Renci.SshNet/ForwardedPortLocal.cs @@ -1,9 +1,9 @@ using System; +using System.Diagnostics; using System.Net; using System.Net.Sockets; using System.Threading; -using Renci.SshNet.Abstractions; using Renci.SshNet.Common; namespace Renci.SshNet @@ -399,8 +399,7 @@ private void InternalStop(TimeSpan timeout) if (!_pendingChannelCountdown.Wait(timeout)) { - // TODO: log as warning - DiagnosticAbstraction.Log("Timeout waiting for pending channels in local forwarded port to close."); + Diagnostic.Log("Timeout waiting for pending channels in local forwarded port to close.", TraceEventType.Warning); } } diff --git a/src/Renci.SshNet/ForwardedPortRemote.cs b/src/Renci.SshNet/ForwardedPortRemote.cs index 10430abc3..112193eef 100644 --- a/src/Renci.SshNet/ForwardedPortRemote.cs +++ b/src/Renci.SshNet/ForwardedPortRemote.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics; using System.Globalization; using System.Net; using System.Threading; @@ -213,8 +214,7 @@ protected override void StopPort(TimeSpan timeout) if (!_pendingChannelCountdown.Wait(timeout)) { - // TODO: log as warning - DiagnosticAbstraction.Log("Timeout waiting for pending channels in remote forwarded port to close."); + Diagnostic.Log("Timeout waiting for pending channels in remote forwarded port to close.", TraceEventType.Warning); } _status = ForwardedPortStatus.Stopped; diff --git a/src/Renci.SshNet/Messages/Transport/DisconnectMessage.cs b/src/Renci.SshNet/Messages/Transport/DisconnectMessage.cs index 88ad5a01a..eef36bf70 100644 --- a/src/Renci.SshNet/Messages/Transport/DisconnectMessage.cs +++ b/src/Renci.SshNet/Messages/Transport/DisconnectMessage.cs @@ -112,5 +112,11 @@ internal override void Process(Session session) { session.OnDisconnectReceived(this); } + + /// + public override string ToString() + { + return $"SSH_MSG_DISCONNECT ({ReasonCode}) {Description}"; + } } } diff --git a/src/Renci.SshNet/Security/KeyExchange.cs b/src/Renci.SshNet/Security/KeyExchange.cs index f24dcafe1..279fb242d 100644 --- a/src/Renci.SshNet/Security/KeyExchange.cs +++ b/src/Renci.SshNet/Security/KeyExchange.cs @@ -1,9 +1,9 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Security.Cryptography; -using Renci.SshNet.Abstractions; using Renci.SshNet.Common; using Renci.SshNet.Compression; using Renci.SshNet.Messages; @@ -71,12 +71,20 @@ public virtual void Start(Session session, KeyExchangeInitMessage message, bool SendMessage(session.ClientInitMessage); } - // Determine encryption algorithm + var sessionId = Session.ToHex(Session.SessionId); + + // Determine client encryption algorithm var clientEncryptionAlgorithmName = (from b in session.ConnectionInfo.Encryptions.Keys from a in message.EncryptionAlgorithmsClientToServer where a == b select a).FirstOrDefault(); + if (Diagnostic.IsEnabled(TraceEventType.Verbose)) + { + Diagnostic.Log($"[{sessionId}] Encryption client to server: we offer {session.ConnectionInfo.Encryptions.Keys.Join(",")}", TraceEventType.Verbose); + Diagnostic.Log($"[{sessionId}] Encryption client to server: they offer {message.EncryptionAlgorithmsClientToServer.Join(",")}", TraceEventType.Verbose); + } + if (string.IsNullOrEmpty(clientEncryptionAlgorithmName)) { throw new SshConnectionException("Client encryption algorithm not found", DisconnectReason.KeyExchangeFailed); @@ -84,11 +92,18 @@ from a in message.EncryptionAlgorithmsClientToServer session.ConnectionInfo.CurrentClientEncryption = clientEncryptionAlgorithmName; - // Determine encryption algorithm + // Determine server encryption algorithm var serverDecryptionAlgorithmName = (from b in session.ConnectionInfo.Encryptions.Keys from a in message.EncryptionAlgorithmsServerToClient where a == b select a).FirstOrDefault(); + + if (Diagnostic.IsEnabled(TraceEventType.Verbose)) + { + Diagnostic.Log($"[{sessionId}] Encryption server to client: we offer {session.ConnectionInfo.Encryptions.Keys.Join(",")}", TraceEventType.Verbose); + Diagnostic.Log($"[{sessionId}] Encryption server to client: they offer {message.EncryptionAlgorithmsServerToClient.Join(",")}", TraceEventType.Verbose); + } + if (string.IsNullOrEmpty(serverDecryptionAlgorithmName)) { throw new SshConnectionException("Server decryption algorithm not found", DisconnectReason.KeyExchangeFailed); @@ -101,6 +116,13 @@ from a in message.EncryptionAlgorithmsServerToClient from a in message.MacAlgorithmsClientToServer where a == b select a).FirstOrDefault(); + + if (Diagnostic.IsEnabled(TraceEventType.Verbose)) + { + Diagnostic.Log($"[{sessionId}] MAC client to server: we offer {session.ConnectionInfo.HmacAlgorithms.Keys.Join(",")}", TraceEventType.Verbose); + Diagnostic.Log($"[{sessionId}] MAC client to server: they offer {message.MacAlgorithmsClientToServer.Join(",")}", TraceEventType.Verbose); + } + if (string.IsNullOrEmpty(clientHmacAlgorithmName)) { throw new SshConnectionException("Server HMAC algorithm not found", DisconnectReason.KeyExchangeFailed); @@ -113,6 +135,13 @@ from a in message.MacAlgorithmsClientToServer from a in message.MacAlgorithmsServerToClient where a == b select a).FirstOrDefault(); + + if (Diagnostic.IsEnabled(TraceEventType.Verbose)) + { + Diagnostic.Log($"[{sessionId}] MAC server to client: we offer {session.ConnectionInfo.HmacAlgorithms.Keys.Join(",")}", TraceEventType.Verbose); + Diagnostic.Log($"[{sessionId}] MAC server to client: they offer {message.MacAlgorithmsServerToClient.Join(",")}", TraceEventType.Verbose); + } + if (string.IsNullOrEmpty(serverHmacAlgorithmName)) { throw new SshConnectionException("Server HMAC algorithm not found", DisconnectReason.KeyExchangeFailed); @@ -124,7 +153,14 @@ from a in message.MacAlgorithmsServerToClient var compressionAlgorithmName = (from b in session.ConnectionInfo.CompressionAlgorithms.Keys from a in message.CompressionAlgorithmsClientToServer where a == b - select a).LastOrDefault(); + select a).FirstOrDefault(); + + if (Diagnostic.IsEnabled(TraceEventType.Verbose)) + { + Diagnostic.Log($"[{sessionId}] Compression client to server: we offer {session.ConnectionInfo.CompressionAlgorithms.Keys.Join(",")}", TraceEventType.Verbose); + Diagnostic.Log($"[{sessionId}] Compression client to server: they offer {message.CompressionAlgorithmsClientToServer.Join(",")}", TraceEventType.Verbose); + } + if (string.IsNullOrEmpty(compressionAlgorithmName)) { throw new SshConnectionException("Compression algorithm not found", DisconnectReason.KeyExchangeFailed); @@ -136,7 +172,14 @@ from a in message.CompressionAlgorithmsClientToServer var decompressionAlgorithmName = (from b in session.ConnectionInfo.CompressionAlgorithms.Keys from a in message.CompressionAlgorithmsServerToClient where a == b - select a).LastOrDefault(); + select a).FirstOrDefault(); + + if (Diagnostic.IsEnabled(TraceEventType.Verbose)) + { + Diagnostic.Log($"[{sessionId}] Compression server to client: we offer {session.ConnectionInfo.CompressionAlgorithms.Keys.Join(",")}", TraceEventType.Verbose); + Diagnostic.Log($"[{sessionId}] Compression server to client: they offer {message.CompressionAlgorithmsClientToServer.Join(",")}", TraceEventType.Verbose); + } + if (string.IsNullOrEmpty(decompressionAlgorithmName)) { throw new SshConnectionException("Decompression algorithm not found", DisconnectReason.KeyExchangeFailed); @@ -182,9 +225,13 @@ public Cipher CreateServerCipher() serverKey = GenerateSessionKey(SharedKey, ExchangeHash, serverKey, _serverCipherInfo.KeySize / 8); - DiagnosticAbstraction.Log(string.Format("[{0}] Creating {1} server cipher.", + if (Diagnostic.IsEnabled(TraceEventType.Information)) + { + Diagnostic.Log(string.Format("[{0}] Creating {1} server cipher.", Session.ToHex(Session.SessionId), - Session.ConnectionInfo.CurrentServerEncryption)); + Session.ConnectionInfo.CurrentServerEncryption), + TraceEventType.Information); + } // Create server cipher return _serverCipherInfo.Cipher(serverKey, serverVector); @@ -207,9 +254,13 @@ public Cipher CreateClientCipher() clientKey = GenerateSessionKey(SharedKey, ExchangeHash, clientKey, _clientCipherInfo.KeySize / 8); - DiagnosticAbstraction.Log(string.Format("[{0}] Creating {1} client cipher.", + if (Diagnostic.IsEnabled(TraceEventType.Information)) + { + Diagnostic.Log(string.Format("[{0}] Creating {1} client cipher.", Session.ToHex(Session.SessionId), - Session.ConnectionInfo.CurrentClientEncryption)); + Session.ConnectionInfo.CurrentClientEncryption), + TraceEventType.Information); + } // Create client cipher return _clientCipherInfo.Cipher(clientKey, clientVector); @@ -231,9 +282,13 @@ public HashAlgorithm CreateServerHash() Hash(GenerateSessionKey(SharedKey, ExchangeHash, 'F', sessionId)), _serverHashInfo.KeySize / 8); - DiagnosticAbstraction.Log(string.Format("[{0}] Creating {1} server hmac algorithm.", + if (Diagnostic.IsEnabled(TraceEventType.Information)) + { + Diagnostic.Log(string.Format("[{0}] Creating {1} server hmac algorithm.", Session.ToHex(Session.SessionId), - Session.ConnectionInfo.CurrentServerHmacAlgorithm)); + Session.ConnectionInfo.CurrentServerHmacAlgorithm), + TraceEventType.Information); + } return _serverHashInfo.HashAlgorithm(serverKey); } @@ -254,9 +309,13 @@ public HashAlgorithm CreateClientHash() Hash(GenerateSessionKey(SharedKey, ExchangeHash, 'E', sessionId)), _clientHashInfo.KeySize / 8); - DiagnosticAbstraction.Log(string.Format("[{0}] Creating {1} client hmac algorithm.", + if (Diagnostic.IsEnabled(TraceEventType.Information)) + { + Diagnostic.Log(string.Format("[{0}] Creating {1} client hmac algorithm.", Session.ToHex(Session.SessionId), - Session.ConnectionInfo.CurrentClientHmacAlgorithm)); + Session.ConnectionInfo.CurrentClientHmacAlgorithm), + TraceEventType.Information); + } return _clientHashInfo.HashAlgorithm(clientKey); } @@ -274,9 +333,13 @@ public Compressor CreateCompressor() return null; } - DiagnosticAbstraction.Log(string.Format("[{0}] Creating {1} client compressor.", + if (Diagnostic.IsEnabled(TraceEventType.Information)) + { + Diagnostic.Log(string.Format("[{0}] Creating {1} client compressor.", Session.ToHex(Session.SessionId), - Session.ConnectionInfo.CurrentClientCompressionAlgorithm)); + Session.ConnectionInfo.CurrentClientCompressionAlgorithm), + TraceEventType.Information); + } var compressor = _compressorFactory(); @@ -298,9 +361,13 @@ public Compressor CreateDecompressor() return null; } - DiagnosticAbstraction.Log(string.Format("[{0}] Creating {1} server decompressor.", + if (Diagnostic.IsEnabled(TraceEventType.Information)) + { + Diagnostic.Log(string.Format("[{0}] Creating {1} server decompressor.", Session.ToHex(Session.SessionId), - Session.ConnectionInfo.CurrentServerCompressionAlgorithm)); + Session.ConnectionInfo.CurrentServerCompressionAlgorithm), + TraceEventType.Information); + } var decompressor = _decompressorFactory(); diff --git a/src/Renci.SshNet/ServiceFactory.cs b/src/Renci.SshNet/ServiceFactory.cs index ece94505b..89263ab7c 100644 --- a/src/Renci.SshNet/ServiceFactory.cs +++ b/src/Renci.SshNet/ServiceFactory.cs @@ -1,10 +1,10 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Net.Sockets; using System.Text; -using Renci.SshNet.Abstractions; using Renci.SshNet.Common; using Renci.SshNet.Connection; using Renci.SshNet.Messages.Transport; @@ -93,17 +93,25 @@ public IKeyExchange CreateKeyExchange(IDictionary> cl } // find an algorithm that is supported by both client and server - var keyExchangeAlgorithmFactory = (from c in clientAlgorithms + var keyExchangeAlgorithm = (from c in clientAlgorithms from s in serverAlgorithms where s == c.Key - select c.Value).FirstOrDefault(); + select c).FirstOrDefault(); - if (keyExchangeAlgorithmFactory is null) + if (Diagnostic.IsEnabled(TraceEventType.Verbose)) + { + Diagnostic.Log($"Key exchange algorithm: we offer {clientAlgorithms.Keys.Join(",")}", TraceEventType.Verbose); + Diagnostic.Log($"Key exchange algorithm: they offer {serverAlgorithms.Join(",")}", TraceEventType.Verbose); + } + + Diagnostic.Log($"Key exchange algorithm: using {keyExchangeAlgorithm.Key}", TraceEventType.Information); + + if (keyExchangeAlgorithm.Value is null) { throw new SshConnectionException("Failed to negotiate key exchange algorithm.", DisconnectReason.KeyExchangeFailed); } - return keyExchangeAlgorithmFactory(); + return keyExchangeAlgorithm.Value(); } /// @@ -159,7 +167,7 @@ public ISftpFileReader CreateSftpFileReader(string fileName, ISftpSession sftpSe fileSize = null; maxPendingReads = defaultMaxPendingReads; - DiagnosticAbstraction.Log(string.Format("Failed to obtain size of file. Allowing maximum {0} pending reads: {1}", maxPendingReads, ex)); + Diagnostic.Log(string.Format("Failed to obtain size of file. Allowing maximum {0} pending reads: {1}", maxPendingReads, ex), TraceEventType.Error); } return sftpSession.CreateFileReader(handle, sftpSession, chunkSize, maxPendingReads, fileSize); diff --git a/src/Renci.SshNet/Session.cs b/src/Renci.SshNet/Session.cs index 122e28d3d..4cf2e75e4 100644 --- a/src/Renci.SshNet/Session.cs +++ b/src/Renci.SshNet/Session.cs @@ -1,9 +1,12 @@ using System; +using System.Diagnostics; using System.Globalization; using System.Linq; using System.Net.Sockets; using System.Security.Cryptography; +#if !NET using System.Text; +#endif using System.Threading; using System.Threading.Tasks; @@ -616,7 +619,11 @@ public void Connect() ServerVersion = ConnectionInfo.ServerVersion = serverIdentification.ToString(); ConnectionInfo.ClientVersion = ClientVersion; - DiagnosticAbstraction.Log(string.Format("Server version '{0}'.", serverIdentification)); + if (Diagnostic.IsEnabled(TraceEventType.Information)) + { + Diagnostic.Log($"Our identification: '{ClientVersion}'.", TraceEventType.Information); + Diagnostic.Log($"Their identification: '{serverIdentification}'.", TraceEventType.Information); + } if (!(serverIdentification.ProtocolVersion.Equals("2.0") || serverIdentification.ProtocolVersion.Equals("1.99"))) { @@ -735,7 +742,11 @@ public async Task ConnectAsync(CancellationToken cancellationToken) ServerVersion = ConnectionInfo.ServerVersion = serverIdentification.ToString(); ConnectionInfo.ClientVersion = ClientVersion; - DiagnosticAbstraction.Log(string.Format("Server version '{0}'.", serverIdentification)); + if (Diagnostic.IsEnabled(TraceEventType.Information)) + { + Diagnostic.Log($"Our identification: '{ClientVersion}'.", TraceEventType.Information); + Diagnostic.Log($"Their identification: '{serverIdentification}'.", TraceEventType.Information); + } if (!(serverIdentification.ProtocolVersion.Equals("2.0") || serverIdentification.ProtocolVersion.Equals("1.99"))) { @@ -821,7 +832,10 @@ public async Task ConnectAsync(CancellationToken cancellationToken) /// public void Disconnect() { - DiagnosticAbstraction.Log(string.Format("[{0}] Disconnecting session.", ToHex(SessionId))); + if (Diagnostic.IsEnabled(TraceEventType.Information)) + { + Diagnostic.Log(string.Format("[{0}] Disconnecting session.", ToHex(SessionId)), TraceEventType.Information); + } // send SSH_MSG_DISCONNECT message, clear socket read buffer and dispose it Disconnect(DisconnectReason.ByApplication, "Connection terminated by the client."); @@ -1057,7 +1071,10 @@ internal void SendMessage(Message message) WaitOnHandle(_keyExchangeCompletedWaitHandle.WaitHandle); } - DiagnosticAbstraction.Log(string.Format("[{0}] Sending message '{1}' to server: '{2}'.", ToHex(SessionId), message.GetType().Name, message)); + if (Diagnostic.IsEnabled(TraceEventType.Verbose)) + { + Diagnostic.Log(string.Format("[{0}] Sending message '{1}' to server: '{2}'.", ToHex(SessionId), message.GetType().Name, message), TraceEventType.Verbose); + } var paddingMultiplier = _clientCipher is null ? (byte) 8 : Math.Max((byte) 8, _serverCipher.MinimumSize); var packetData = message.GetPacket(paddingMultiplier, _clientCompression); @@ -1168,15 +1185,16 @@ private bool TrySendMessage(Message message) SendMessage(message); return true; } - catch (SshException ex) - { - DiagnosticAbstraction.Log(string.Format("Failure sending message '{0}' to server: '{1}' => {2}", message.GetType().Name, message, ex)); - return false; - } - catch (SocketException ex) + catch (Exception ex) { - DiagnosticAbstraction.Log(string.Format("Failure sending message '{0}' to server: '{1}' => {2}", message.GetType().Name, message, ex)); - return false; + Diagnostic.Log(string.Format("Failure sending message '{0}' to server: '{1}' => {2}", message.GetType().Name, message, ex), TraceEventType.Error); + + if (ex is SshException or SocketException) + { + return false; + } + + throw; } } @@ -1324,7 +1342,10 @@ private void TrySendDisconnect(DisconnectReason reasonCode, string message) /// message. internal void OnDisconnectReceived(DisconnectMessage message) { - DiagnosticAbstraction.Log(string.Format("[{0}] Disconnect received: {1} {2}.", ToHex(SessionId), message.ReasonCode, message.Description)); + if (Diagnostic.IsEnabled(TraceEventType.Information)) + { + Diagnostic.Log(string.Format("[{0}] Disconnect received: {1} {2}.", ToHex(SessionId), message.ReasonCode, message.Description), TraceEventType.Information); + } // transition to disconnecting state to avoid throwing exceptions while cleaning up, and to // ensure any exceptions that are raised do not overwrite the SshConnectionException that we @@ -1423,7 +1444,10 @@ internal void OnKeyExchangeInitReceived(KeyExchangeInitMessage message) ConnectionInfo.CurrentKeyExchangeAlgorithm = _keyExchange.Name; - DiagnosticAbstraction.Log(string.Format("[{0}] Performing {1} key exchange.", ToHex(SessionId), ConnectionInfo.CurrentKeyExchangeAlgorithm)); + if (Diagnostic.IsEnabled(TraceEventType.Information)) + { + Diagnostic.Log(string.Format("[{0}] Performing {1} key exchange.", ToHex(SessionId), ConnectionInfo.CurrentKeyExchangeAlgorithm), TraceEventType.Information); + } _keyExchange.HostKeyReceived += KeyExchange_HostKeyReceived; @@ -1726,34 +1750,34 @@ private Message LoadMessage(byte[] data, int offset, int count) var message = _sshMessageFactory.Create(messageType); message.Load(data, offset + 1, count - 1); - DiagnosticAbstraction.Log(string.Format("[{0}] Received message '{1}' from server: '{2}'.", ToHex(SessionId), message.GetType().Name, message)); + if (Diagnostic.IsEnabled(TraceEventType.Verbose)) + { + Diagnostic.Log(string.Format("[{0}] Received message '{1}' from server: '{2}'.", ToHex(SessionId), message.GetType().Name, message), TraceEventType.Verbose); + } return message; } - private static string ToHex(byte[] bytes, int offset) + internal static string ToHex(byte[] bytes) { - var byteCount = bytes.Length - offset; + if (bytes is null) + { + return null; + } +#if NET + return Convert.ToHexString(bytes); +#else var builder = new StringBuilder(bytes.Length * 2); - for (var i = offset; i < byteCount; i++) + for (var i = 0; i < bytes.Length; i++) { var b = bytes[i]; _ = builder.Append(b.ToString("X2")); } return builder.ToString(); - } - - internal static string ToHex(byte[] bytes) - { - if (bytes is null) - { - return null; - } - - return ToHex(bytes, 0); +#endif } /// @@ -1870,7 +1894,7 @@ private void SocketDisconnectAndDispose() { try { - DiagnosticAbstraction.Log(string.Format("[{0}] Shutting down socket.", ToHex(SessionId))); + Diagnostic.Log(string.Format("[{0}] Shutting down socket.", ToHex(SessionId)), TraceEventType.Verbose); // Interrupt any pending reads; should be done outside of socket read lock as we // actually want shutdown the socket to make sure blocking reads are interrupted. @@ -1882,14 +1906,15 @@ private void SocketDisconnectAndDispose() } catch (SocketException ex) { - // TODO: log as warning - DiagnosticAbstraction.Log("Failure shutting down socket: " + ex); + Diagnostic.Log("Failure shutting down socket: " + ex, TraceEventType.Warning); } } - DiagnosticAbstraction.Log(string.Format("[{0}] Disposing socket.", ToHex(SessionId))); + Diagnostic.Log(string.Format("[{0}] Disposing socket.", ToHex(SessionId)), TraceEventType.Verbose); + _socket.Dispose(); - DiagnosticAbstraction.Log(string.Format("[{0}] Disposed socket.", ToHex(SessionId))); + Diagnostic.Log(string.Format("[{0}] Disposed socket.", ToHex(SessionId)), TraceEventType.Verbose); + _socket = null; } } @@ -1973,7 +1998,7 @@ private void RaiseError(Exception exp) { var connectionException = exp as SshConnectionException; - DiagnosticAbstraction.Log(string.Format("[{0}] Raised exception: {1}", ToHex(SessionId), exp)); + Diagnostic.Log(string.Format("[{0}] Raised exception: {1}", ToHex(SessionId), exp), TraceEventType.Warning); if (_isDisconnecting) { @@ -2000,7 +2025,8 @@ private void RaiseError(Exception exp) if (connectionException != null) { - DiagnosticAbstraction.Log(string.Format("[{0}] Disconnecting after exception: {1}", ToHex(SessionId), exp)); + Diagnostic.Log(string.Format("[{0}] Disconnecting after exception: {1}", ToHex(SessionId), exp), TraceEventType.Information); + Disconnect(connectionException.DisconnectReason, exp.ToString()); } } @@ -2052,7 +2078,7 @@ protected virtual void Dispose(bool disposing) if (disposing) { - DiagnosticAbstraction.Log(string.Format("[{0}] Disposing session.", ToHex(SessionId))); + Diagnostic.Log(string.Format("[{0}] Disposing session.", ToHex(SessionId)), TraceEventType.Verbose); Disconnect(); diff --git a/src/Renci.SshNet/Sftp/SftpFileReader.cs b/src/Renci.SshNet/Sftp/SftpFileReader.cs index 8d3ef211f..88fdf5af4 100644 --- a/src/Renci.SshNet/Sftp/SftpFileReader.cs +++ b/src/Renci.SshNet/Sftp/SftpFileReader.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Globalization; using System.Runtime.ExceptionServices; using System.Threading; @@ -278,7 +279,7 @@ private void Dispose(bool disposing) } catch (Exception ex) { - DiagnosticAbstraction.Log("Failure closing handle: " + ex); + Diagnostic.Log("Failure closing handle: " + ex, TraceEventType.Error); } } } diff --git a/src/Renci.SshNet/SubsystemSession.cs b/src/Renci.SshNet/SubsystemSession.cs index 70385d903..2c754a18e 100644 --- a/src/Renci.SshNet/SubsystemSession.cs +++ b/src/Renci.SshNet/SubsystemSession.cs @@ -1,9 +1,9 @@ using System; +using System.Diagnostics; using System.Globalization; using System.Runtime.ExceptionServices; using System.Threading; -using Renci.SshNet.Abstractions; using Renci.SshNet.Channels; using Renci.SshNet.Common; @@ -191,7 +191,7 @@ protected void RaiseError(Exception error) { _exception = error; - DiagnosticAbstraction.Log("Raised exception: " + error); + Diagnostic.Log("Raised exception: " + error, TraceEventType.Warning); _ = _errorOccuredWaitHandle?.Set(); diff --git a/test/Renci.SshNet.IntegrationTests/TestsFixtures/IntegrationTestBase.cs b/test/Renci.SshNet.IntegrationTests/TestsFixtures/IntegrationTestBase.cs index d08578e4e..dfe8f18bb 100644 --- a/test/Renci.SshNet.IntegrationTests/TestsFixtures/IntegrationTestBase.cs +++ b/test/Renci.SshNet.IntegrationTests/TestsFixtures/IntegrationTestBase.cs @@ -1,7 +1,5 @@ using System.Diagnostics; -using Renci.SshNet.Abstractions; - namespace Renci.SshNet.IntegrationTests.TestsFixtures { /// @@ -88,15 +86,15 @@ protected void CreateTestFile(string fileName, int size) protected void EnableTracing() { - DiagnosticAbstraction.Source.Switch = new SourceSwitch("sourceSwitch", nameof(SourceLevels.Verbose)); - DiagnosticAbstraction.Source.Listeners.Remove("Default"); - DiagnosticAbstraction.Source.Listeners.Add(new ConsoleTraceListener() { Name = "TestConsoleLogger" }); + Diagnostic.Source.Switch = new SourceSwitch("sourceSwitch", nameof(SourceLevels.Verbose)); + Diagnostic.Source.Listeners.Remove("Default"); + Diagnostic.Source.Listeners.Add(new ConsoleTraceListener() { Name = "TestConsoleLogger" }); } protected void DisableTracing() { - DiagnosticAbstraction.Source.Switch = new SourceSwitch("sourceSwitch", nameof(SourceLevels.Off)); - DiagnosticAbstraction.Source.Listeners.Remove("TestConsoleLogger"); + Diagnostic.Source.Switch = new SourceSwitch("sourceSwitch", nameof(SourceLevels.Off)); + Diagnostic.Source.Listeners.Remove("TestConsoleLogger"); } } }