Skip to content

Commit b4c8291

Browse files
Tweak diagnostics (#1241)
* Make it work (it needs the TRACE symbol defined - lots of head scratching without it) * Expose publicly, but still in DEBUG (to allow programmatic configuration necessary in Core) * Document how to use it * Tweak usage (add some logs, remove key/iv information, override ToString on some Message types) Co-authored-by: Wojciech Nagórski <[email protected]>
1 parent 58284d6 commit b4c8291

File tree

8 files changed

+107
-23
lines changed

8 files changed

+107
-23
lines changed

src/Renci.SshNet/Abstractions/DiagnosticAbstraction.cs

Lines changed: 53 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,65 @@
1-
using System.Diagnostics;
1+
using System.ComponentModel;
2+
using System.Diagnostics;
23

34
namespace Renci.SshNet.Abstractions
45
{
5-
internal static class DiagnosticAbstraction
6+
/// <summary>
7+
/// Provides access to the <see cref="System.Diagnostics"/> internals of SSH.NET.
8+
/// </summary>
9+
[EditorBrowsable(EditorBrowsableState.Never)]
10+
public static class DiagnosticAbstraction
611
{
7-
private static readonly SourceSwitch SourceSwitch = new SourceSwitch("SshNetSwitch");
8-
9-
public static bool IsEnabled(TraceEventType traceEventType)
10-
{
11-
return SourceSwitch.ShouldTrace(traceEventType);
12-
}
13-
14-
private static readonly TraceSource Loggging =
1512
#if DEBUG
16-
new TraceSource("SshNet.Logging", SourceLevels.All);
17-
#else
18-
new TraceSource("SshNet.Logging");
19-
#endif // DEBUG
13+
/// <summary>
14+
/// The <see cref="TraceSource"/> instance used by SSH.NET.
15+
/// </summary>
16+
/// <remarks>
17+
/// <para>
18+
/// Configuration on .NET Core must be done programmatically, e.g.
19+
/// <code>
20+
/// DiagnosticAbstraction.Source.Switch = new SourceSwitch("sourceSwitch", "Verbose");
21+
/// DiagnosticAbstraction.Source.Listeners.Remove("Default");
22+
/// DiagnosticAbstraction.Source.Listeners.Add(new ConsoleTraceListener());
23+
/// DiagnosticAbstraction.Source.Listeners.Add(new TextWriterTraceListener("trace.log"));
24+
/// </code>
25+
/// </para>
26+
/// <para>
27+
/// On .NET Framework, it is possible to configure via App.config, e.g.
28+
/// <code>
29+
/// <![CDATA[
30+
/// <configuration>
31+
/// <system.diagnostics>
32+
/// <trace autoflush="true"/>
33+
/// <sources>
34+
/// <source name="SshNet.Logging" switchValue="Verbose">
35+
/// <listeners>
36+
/// <remove name="Default" />
37+
/// <add name="console"
38+
/// type="System.Diagnostics.ConsoleTraceListener" />
39+
/// <add name="logFile"
40+
/// type="System.Diagnostics.TextWriterTraceListener"
41+
/// initializeData="SshNetTrace.log" />
42+
/// </listeners>
43+
/// </source>
44+
/// </sources>
45+
/// </system.diagnostics>
46+
/// </configuration>
47+
/// ]]>
48+
/// </code>
49+
/// </para>
50+
/// </remarks>
51+
public static readonly TraceSource Source = new TraceSource("SshNet.Logging");
52+
#endif
2053

54+
/// <summary>
55+
/// Logs a message to <see cref="Source"/> at the <see cref="TraceEventType.Verbose"/>
56+
/// level.
57+
/// </summary>
58+
/// <param name="text">The message to log.</param>
2159
[Conditional("DEBUG")]
2260
public static void Log(string text)
2361
{
24-
Loggging.TraceEvent(TraceEventType.Verbose,
62+
Source.TraceEvent(TraceEventType.Verbose,
2563
System.Environment.CurrentManagedThreadId,
2664
text);
2765
}

src/Renci.SshNet/Messages/Authentication/FailureMessage.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,5 +60,13 @@ internal override void Process(Session session)
6060
{
6161
session.OnUserAuthenticationFailureReceived(this);
6262
}
63+
64+
/// <inheritdoc/>
65+
public override string ToString()
66+
{
67+
#pragma warning disable MA0089 // Optimize string method usage
68+
return $"SSH_MSG_USERAUTH_FAILURE {string.Join(",", AllowedAuthentications)} ({nameof(PartialSuccess)}:{PartialSuccess})";
69+
#pragma warning restore MA0089 // Optimize string method usage
70+
}
6371
}
6472
}

src/Renci.SshNet/Messages/Authentication/PublicKeyMessage.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,5 +60,11 @@ protected override void SaveData()
6060
WriteBinaryString(PublicKeyAlgorithmName);
6161
WriteBinaryString(PublicKeyData);
6262
}
63+
64+
/// <inheritdoc/>
65+
public override string ToString()
66+
{
67+
return $"SSH_MSG_USERAUTH_PK_OK ({Ascii.GetString(PublicKeyAlgorithmName)})";
68+
}
6369
}
6470
}

src/Renci.SshNet/Messages/Authentication/RequestMessage.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,5 +106,11 @@ internal override void Process(Session session)
106106
{
107107
throw new NotImplementedException();
108108
}
109+
110+
/// <inheritdoc/>
111+
public override string ToString()
112+
{
113+
return $"SSH_MSG_USERAUTH_REQUEST ({MethodName})";
114+
}
109115
}
110116
}

src/Renci.SshNet/Messages/Authentication/RequestMessagePublicKey.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,5 +96,11 @@ protected override void SaveData()
9696
WriteBinaryString(Signature);
9797
}
9898
}
99+
100+
/// <inheritdoc/>
101+
public override string ToString()
102+
{
103+
return $"{base.ToString()} {Ascii.GetString(PublicKeyAlgorithmName)} {(Signature != null ? "with" : "without")} signature.";
104+
}
99105
}
100106
}

src/Renci.SshNet/Renci.SshNet.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
</PropertyGroup>
77

88
<PropertyGroup Condition=" '$(TargetFramework)' == 'net462' ">
9-
<DefineConstants>FEATURE_BINARY_SERIALIZATION;FEATURE_SOCKET_EAP;FEATURE_SOCKET_APM;FEATURE_DNS_SYNC;FEATURE_HASH_RIPEMD160_CREATE;FEATURE_HMAC_RIPEMD160</DefineConstants>
9+
<DefineConstants>$(DefineConstants);FEATURE_BINARY_SERIALIZATION;FEATURE_SOCKET_EAP;FEATURE_SOCKET_APM;FEATURE_DNS_SYNC;FEATURE_HASH_RIPEMD160_CREATE;FEATURE_HMAC_RIPEMD160</DefineConstants>
1010
</PropertyGroup>
1111

1212
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' or '$(TargetFramework)' == 'netstandard2.1' or '$(TargetFramework)' == 'net6.0' or '$(TargetFramework)' == 'net7.0' ">
@@ -18,6 +18,6 @@
1818
</ItemGroup>
1919

2020
<PropertyGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' or '$(TargetFramework)' == 'netstandard2.1' or '$(TargetFramework)' == 'net6.0' or '$(TargetFramework)' == 'net7.0' ">
21-
<DefineConstants>FEATURE_SOCKET_TAP;FEATURE_SOCKET_APM;FEATURE_SOCKET_EAP;FEATURE_DNS_SYNC;FEATURE_DNS_APM;FEATURE_DNS_TAP</DefineConstants>
21+
<DefineConstants>$(DefineConstants);FEATURE_SOCKET_TAP;FEATURE_SOCKET_APM;FEATURE_SOCKET_EAP;FEATURE_DNS_SYNC;FEATURE_DNS_APM;FEATURE_DNS_TAP</DefineConstants>
2222
</PropertyGroup>
2323
</Project>

src/Renci.SshNet/Security/KeyExchange.cs

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -183,11 +183,9 @@ public Cipher CreateServerCipher()
183183

184184
serverKey = GenerateSessionKey(SharedKey, ExchangeHash, serverKey, _serverCipherInfo.KeySize / 8);
185185

186-
DiagnosticAbstraction.Log(string.Format("[{0}] Creating server cipher (Name:{1},Key:{2},IV:{3})",
186+
DiagnosticAbstraction.Log(string.Format("[{0}] Creating {1} server cipher.",
187187
Session.ToHex(Session.SessionId),
188-
Session.ConnectionInfo.CurrentServerEncryption,
189-
Session.ToHex(serverKey),
190-
Session.ToHex(serverVector)));
188+
Session.ConnectionInfo.CurrentServerEncryption));
191189

192190
// Create server cipher
193191
return _serverCipherInfo.Cipher(serverKey, serverVector);
@@ -210,6 +208,10 @@ public Cipher CreateClientCipher()
210208

211209
clientKey = GenerateSessionKey(SharedKey, ExchangeHash, clientKey, _clientCipherInfo.KeySize / 8);
212210

211+
DiagnosticAbstraction.Log(string.Format("[{0}] Creating {1} client cipher.",
212+
Session.ToHex(Session.SessionId),
213+
Session.ConnectionInfo.CurrentClientEncryption));
214+
213215
// Create client cipher
214216
return _clientCipherInfo.Cipher(clientKey, clientVector);
215217
}
@@ -230,6 +232,10 @@ public HashAlgorithm CreateServerHash()
230232
Hash(GenerateSessionKey(SharedKey, ExchangeHash, 'F', sessionId)),
231233
_serverHashInfo.KeySize / 8);
232234

235+
DiagnosticAbstraction.Log(string.Format("[{0}] Creating {1} server hmac algorithm.",
236+
Session.ToHex(Session.SessionId),
237+
Session.ConnectionInfo.CurrentServerHmacAlgorithm));
238+
233239
return _serverHashInfo.HashAlgorithm(serverKey);
234240
}
235241

@@ -249,6 +255,10 @@ public HashAlgorithm CreateClientHash()
249255
Hash(GenerateSessionKey(SharedKey, ExchangeHash, 'E', sessionId)),
250256
_clientHashInfo.KeySize / 8);
251257

258+
DiagnosticAbstraction.Log(string.Format("[{0}] Creating {1} client hmac algorithm.",
259+
Session.ToHex(Session.SessionId),
260+
Session.ConnectionInfo.CurrentClientHmacAlgorithm));
261+
252262
return _clientHashInfo.HashAlgorithm(clientKey);
253263
}
254264

@@ -265,6 +275,10 @@ public Compressor CreateCompressor()
265275
return null;
266276
}
267277

278+
DiagnosticAbstraction.Log(string.Format("[{0}] Creating {1} client compressor.",
279+
Session.ToHex(Session.SessionId),
280+
Session.ConnectionInfo.CurrentClientCompressionAlgorithm));
281+
268282
var compressor = _compressionType.CreateInstance<Compressor>();
269283

270284
compressor.Init(Session);
@@ -285,6 +299,10 @@ public Compressor CreateDecompressor()
285299
return null;
286300
}
287301

302+
DiagnosticAbstraction.Log(string.Format("[{0}] Creating {1} server decompressor.",
303+
Session.ToHex(Session.SessionId),
304+
Session.ConnectionInfo.CurrentServerCompressionAlgorithm));
305+
288306
var decompressor = _decompressionType.CreateInstance<Compressor>();
289307

290308
decompressor.Init(Session);

src/Renci.SshNet/Session.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -616,7 +616,7 @@ public void Connect()
616616
ServerVersion = ConnectionInfo.ServerVersion = serverIdentification.ToString();
617617
ConnectionInfo.ClientVersion = ClientVersion;
618618

619-
DiagnosticAbstraction.Log(string.Format("Server version '{0}' on '{1}'.", serverIdentification.ProtocolVersion, serverIdentification.SoftwareVersion));
619+
DiagnosticAbstraction.Log(string.Format("Server version '{0}'.", serverIdentification));
620620

621621
if (!(serverIdentification.ProtocolVersion.Equals("2.0") || serverIdentification.ProtocolVersion.Equals("1.99")))
622622
{
@@ -728,7 +728,7 @@ public async Task ConnectAsync(CancellationToken cancellationToken)
728728
ServerVersion = ConnectionInfo.ServerVersion = serverIdentification.ToString();
729729
ConnectionInfo.ClientVersion = ClientVersion;
730730

731-
DiagnosticAbstraction.Log(string.Format("Server version '{0}' on '{1}'.", serverIdentification.ProtocolVersion, serverIdentification.SoftwareVersion));
731+
DiagnosticAbstraction.Log(string.Format("Server version '{0}'.", serverIdentification));
732732

733733
if (!(serverIdentification.ProtocolVersion.Equals("2.0") || serverIdentification.ProtocolVersion.Equals("1.99")))
734734
{
@@ -1397,6 +1397,8 @@ internal void OnKeyExchangeInitReceived(KeyExchangeInitMessage message)
13971397

13981398
ConnectionInfo.CurrentKeyExchangeAlgorithm = _keyExchange.Name;
13991399

1400+
DiagnosticAbstraction.Log(string.Format("[{0}] Performing {1} key exchange.", ToHex(SessionId), ConnectionInfo.CurrentKeyExchangeAlgorithm));
1401+
14001402
_keyExchange.HostKeyReceived += KeyExchange_HostKeyReceived;
14011403

14021404
// Start the algorithm implementation

0 commit comments

Comments
 (0)