Move `TorProcessManager.IsRunningAsync` method to `TorSocks5Client` class.
MODIFY
WalletWasabi.Tests/IntegrationTests/TorTests.cs
WalletWasabi.Tests/IntegrationTests/TorTests.cs
@@ -6,11 +6,11 @@ using System.Text.RegularExpressions;
using System.Threading.Tasks;
using WalletWasabi.Tor;
using WalletWasabi.Tor.Http;
+using WalletWasabi.Tor.Socks5;
using Xunit;
namespace WalletWasabi.Tests.IntegrationTests
{
- // Tor must be running
public class TorTests : IAsyncLifetime
{
public async Task InitializeAsync()
@@ -184,8 +184,8 @@ namespace WalletWasabi.Tests.IntegrationTests
[Fact]
public async Task TorRunningAsync()
{
- Assert.True(await TorProcessManager.IsTorRunningAsync(new IPEndPoint(IPAddress.Loopback, 9050)));
- Assert.False(await TorProcessManager.IsTorRunningAsync(new IPEndPoint(IPAddress.Loopback, 9054)));
+ Assert.True(await new TorSocks5Client(new IPEndPoint(IPAddress.Loopback, 9050)).IsTorRunningAsync());
+ Assert.False(await new TorSocks5Client(new IPEndPoint(IPAddress.Loopback, 9054)).IsTorRunningAsync());
}
private static async Task<List<string>> QBitTestAsync(TorHttpClient client, int times, bool alterRequests = false)

MODIFY
WalletWasabi/Tor/Socks5/TorSocks5Client.cs
WalletWasabi/Tor/Socks5/TorSocks5Client.cs
using Nito.AsyncEx;
using System;
using System.IO;
-using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Threading.Tasks;
@@ -78,6 +77,24 @@ namespace WalletWasabi.Tor.Socks5
}
}
+ /// <summary>
+ /// Checks whether communication can be established with Tor over <see cref="TorSocks5EndPoint"/> endpoint.
+ /// </summary>
+ /// <returns></returns>
+ public async Task<bool> IsTorRunningAsync()
+ {
+ try
+ {
+ await ConnectAsync().ConfigureAwait(false);
+ await HandshakeAsync(isolateStream: true).ConfigureAwait(false);
+ }
+ catch (ConnectionException)
+ {
+ return false;
+ }
+ return true;
+ }
+
/// <summary>
/// IsolateSOCKSAuth must be on (on by default)
/// https://www.torproject.org/docs/tor-manual.html.en
@@ -96,6 +113,8 @@ namespace WalletWasabi.Tor.Socks5
/// <param name="identity">Isolates streams by identity. If identity is empty string, it won't isolate stream.</param>
internal async Task HandshakeAsync(string identity)
{
+ Logger.LogDebug($"> {nameof(identity)}={identity}");
+
if (TorSocks5EndPoint is null)
{
return;
@@ -113,10 +132,12 @@ namespace WalletWasabi.Tor.Socks5
var methodSelection = new MethodSelectionResponse();
methodSelection.FromBytes(receiveBuffer);
+
if (methodSelection.Ver != VerField.Socks5)
{
throw new NotSupportedException($"SOCKS{methodSelection.Ver.Value} not supported. Only SOCKS5 is supported.");
}
+
if (methodSelection.Method == MethodField.NoAcceptableMethods)
{
// https://www.ietf.org/rfc/rfc1928.txt
@@ -125,6 +146,7 @@ namespace WalletWasabi.Tor.Socks5
DisposeTcpClient();
throw new NotSupportedException("Tor's SOCKS5 proxy does not support any of the client's authentication methods.");
}
+
if (methodSelection.Method == MethodField.UsernamePassword)
{
// https://tools.ietf.org/html/rfc1929#section-2
@@ -132,10 +154,8 @@ namespace WalletWasabi.Tor.Socks5
// Username / Password Authentication protocol, the Username / Password
// sub-negotiation begins. This begins with the client producing a
// Username / Password request:
- var username = identity;
- var password = identity;
- var uName = new UNameField(username);
- var passwd = new PasswdField(password);
+ var uName = new UNameField(uName: identity);
+ var passwd = new PasswdField(passwd: identity);
var usernamePasswordRequest = new UsernamePasswordRequest(uName, passwd);
sendBuffer = usernamePasswordRequest.ToBytes();
@@ -159,6 +179,8 @@ namespace WalletWasabi.Tor.Socks5
throw new InvalidOperationException("Wrong username and/or password.");
}
}
+
+ Logger.LogDebug("<");
}
internal async Task ConnectToDestinationAsync(EndPoint destination, bool isRecursiveCall = false)

MODIFY
WalletWasabi/Tor/TorProcessManager.cs
WalletWasabi/Tor/TorProcessManager.cs
@@ -46,6 +46,7 @@ namespace WalletWasabi.Tor
Stop = new CancellationTokenSource();
TorProcess = null;
Settings = settings;
+ TorSocks5Client = new TorSocks5Client(torSocks5EndPoint);
IoHelpers.EnsureContainingDirectoryExists(Settings.LogFilePath);
}
@@ -58,6 +59,8 @@ namespace WalletWasabi.Tor
private TorSettings Settings { get; }
+ private TorSocks5Client TorSocks5Client { get; }
+
public bool IsRunning => Interlocked.Read(ref _monitorState) == StateRunning;
private CancellationTokenSource Stop { get; set; }
@@ -74,7 +77,7 @@ namespace WalletWasabi.Tor
try
{
// Is Tor already running? Either our Tor process from previous Wasabi Wallet run or possibly user's own Tor.
- bool isAlreadyRunning = await IsTorRunningAsync(TorSocks5EndPoint).ConfigureAwait(false);
+ bool isAlreadyRunning = await TorSocks5Client.IsTorRunningAsync().ConfigureAwait(false);
if (isAlreadyRunning)
{
@@ -129,7 +132,7 @@ namespace WalletWasabi.Tor
{
i++;
- bool isRunning = await IsTorRunningAsync(TorSocks5EndPoint).ConfigureAwait(false);
+ bool isRunning = await TorSocks5Client.IsTorRunningAsync().ConfigureAwait(false);
if (isRunning)
{
@@ -160,22 +163,6 @@ namespace WalletWasabi.Tor
return false;
}
- /// <param name="torSocks5EndPoint">Valid Tor end point.</param>
- public static async Task<bool> IsTorRunningAsync(EndPoint torSocks5EndPoint)
- {
- using var client = new TorSocks5Client(torSocks5EndPoint);
- try
- {
- await client.ConnectAsync().ConfigureAwait(false);
- await client.HandshakeAsync().ConfigureAwait(false);
- }
- catch (ConnectionException)
- {
- return false;
- }
- return true;
- }
-
#region Monitor
public void StartMonitor(TimeSpan torMisbehaviorCheckPeriod, TimeSpan checkIfRunningAfterTorMisbehavedFor, Uri fallBackTestRequestUri)

Move `TorProcessManager.IsRunningAsync` method to `TorSocks5Client` class.