Permalink
Browse files

Start working on a task that monitors the Tor process

  • Loading branch information...
nopara73 committed Oct 20, 2018
1 parent 24b1c70 commit eba37ba943cf402a65fd42187e16f466771f0d0a
@@ -135,6 +135,7 @@ public static void InitializeNoWallet()
TorManager = new TorProcessManager(Config.GetTorSocks5EndPoint(), TorLogsFile);
TorManager.Start(false, DataDir);
TorManager.StartMonitor(TimeSpan.FromSeconds(3), TimeSpan.FromSeconds(3), TimeSpan.FromMinutes(3), DataDir);
Logger.LogInfo<TorProcessManager>($"{nameof(TorProcessManager)} is initialized.");
@@ -329,6 +330,9 @@ public static async Task DisposeAsync()
RegTestMemPoolServingNode.Disconnect();
Logger.LogInfo($"{nameof(RegTestMemPoolServingNode)} is disposed.", nameof(Global));
}
TorManager?.Dispose();
Logger.LogInfo($"{nameof(TorManager)} is stopped.", nameof(Global));
}
catch (Exception ex)
{
@@ -45,7 +45,7 @@ public static StatusLine CreateNew(string statusLineString)
}
catch (Exception ex)
{
Logger.LogDebug<StatusLine>(ex);
Logger.LogTrace<StatusLine>(ex); // Often happens when internet connection is lost.
throw new NotSupportedException($"Invalid {nameof(StatusLine)}: {statusLineString}.", ex);
}
}
@@ -20,6 +20,8 @@ namespace WalletWasabi.TorSocks5
{
public class TorHttpClient : IDisposable
{
public static DateTimeOffset? TorDoesntWorkSince { get; private set; } = null; // This sets the first time the request failed with exception.
public Uri DestinationUri { get; }
public IPEndPoint TorSocks5EndPoint { get; }
@@ -64,7 +66,9 @@ public async Task<HttpResponseMessage> SendAsync(HttpMethod method, string relat
{
try
{
return await SendAsync(request);
HttpResponseMessage ret = await SendAsync(request);
TorDoesntWorkSince = null;
return ret;
}
catch (Exception ex)
{
@@ -76,7 +80,9 @@ public async Task<HttpResponseMessage> SendAsync(HttpMethod method, string relat
cancel.ThrowIfCancellationRequested();
try
{
return await SendAsync(request);
HttpResponseMessage ret2 = await SendAsync(request);
TorDoesntWorkSince = null;
return ret2;
}
// If we get ttlexpired then wait and retry again linux often do this.
catch (TorSocks5FailureResponseException ex2) when (ex2.RepField == RepField.TtlExpired)
@@ -97,14 +103,29 @@ public async Task<HttpResponseMessage> SendAsync(HttpMethod method, string relat
}
cancel.ThrowIfCancellationRequested();
return await SendAsync(request);
HttpResponseMessage ret3 = await SendAsync(request);
TorDoesntWorkSince = null;
return ret3;
}
}
}
catch (TaskCanceledException ex)
{
if (TorDoesntWorkSince == null)
{
TorDoesntWorkSince = DateTimeOffset.UtcNow;
}
throw new OperationCanceledException(ex.Message, ex, cancel);
}
catch
{
if (TorDoesntWorkSince == null)
{
TorDoesntWorkSince = DateTimeOffset.UtcNow;
}
throw;
}
}
/// <remarks>
@@ -11,17 +11,22 @@
namespace WalletWasabi.TorSocks5
{
public class TorProcessManager
public class TorProcessManager : IDisposable
{
public IPEndPoint TorSocks5EndPoint { get; }
public string LogFile { get; }
public Process TorProcess { get; private set; }
/// <param name="torSocks5EndPoint">Opt out Tor with null.</param>
/// <param name="logFile">Opt out of logging with null.</param>
public TorProcessManager(IPEndPoint torSocks5EndPoint, string logFile)
{
TorSocks5EndPoint = torSocks5EndPoint ?? new IPEndPoint(IPAddress.Loopback, 9050);
LogFile = logFile;
_running = 0;
Stop = new CancellationTokenSource();
TorProcess = null;
}
public void Start(bool ensureRunning, string dataDir)
@@ -122,7 +127,7 @@ public void Start(bool ensureRunning, string dataDir)
CreateNoWindow = true,
RedirectStandardOutput = true
};
Process.Start(torProcessStartInfo);
TorProcess = Process.Start(torProcessStartInfo);
Logger.LogInfo<TorProcessManager>($"Starting Tor process with Process.Start.");
}
else // Linux and OSX
@@ -188,5 +193,140 @@ public async Task<bool> IsTorRunningAsync()
return true;
}
}
#region Monitor
/// <summary>
/// 0: Not started, 1: Running, 2: Stopping, 3: Stopped
/// </summary>
private long _running;
public bool IsRunning => Interlocked.Read(ref _running) == 1;
public bool IsStopping => Interlocked.Read(ref _running) == 2;
private CancellationTokenSource Stop { get; }
public void StartMonitor(TimeSpan torMisbehaviorCheckPeriod, TimeSpan checkIfRunningAfterTorMisbehavedFor, TimeSpan tryRestartAfterTorMisbehavedFor, string dataDirToStartWith)
{
Logger.LogInfo<TorProcessManager>("Starting Tor monitor...");
Interlocked.Exchange(ref _running, 1);
Task.Run(async () =>
{
try
{
while (IsRunning)
{
try
{
// If stop was requested return.
if (IsRunning == false) return;
await Task.Delay(torMisbehaviorCheckPeriod, Stop.Token).ConfigureAwait(false);
if (!(TorHttpClient.TorDoesntWorkSince is null)) // If Tor misbehaves.
{
TimeSpan torMisbehavedFor = (DateTimeOffset.UtcNow - TorHttpClient.TorDoesntWorkSince) ?? TimeSpan.Zero;
if (torMisbehavedFor > tryRestartAfterTorMisbehavedFor)
{
Logger.LogInfo<TorProcessManager>($"Tor didn't work properly for {(int)torMisbehavedFor.TotalSeconds} seconds. Attempting to restart...");
Logger.LogInfo<TorProcessManager>($"Attempting to kill Tor...");
// Try killing, then starting Tor.
if (TorProcess != null)
{
new Thread(delegate () // Don't ask. This is the only way it worked on Win10
{
TorProcess?.Kill();
}).Start();
}
else if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
try
{
string runTorCmd = $"killall tor";
EnvironmentHelpers.ShellExec(runTorCmd, false); // Not always have permission and stuff.
}
catch (Exception ex)
{
Logger.LogInfo<TorProcessManager>($"Couldn't kill tor...");
Logger.LogDebug<TorProcessManager>(ex);
}
}
else
{
Logger.LogInfo<TorProcessManager>($"Couldn't kill Tor.");
}
Logger.LogInfo<TorProcessManager>($"Attempting to start Tor...");
// Give some time before trying to restart.
await Task.Delay(3000, Stop.Token).ConfigureAwait(false);
Start(true, dataDirToStartWith);
await Task.Delay(7000, Stop.Token).ConfigureAwait(false);
}
else if (torMisbehavedFor > checkIfRunningAfterTorMisbehavedFor)
{
Logger.LogInfo<TorProcessManager>($"Tor didn't work properly for {(int)torMisbehavedFor.TotalSeconds} seconds. Maybe it crashed. Attempting to start it...");
Start(true, dataDirToStartWith); // Try starting Tor, if doesn't work it'll be another issue.
await Task.Delay(7000, Stop.Token).ConfigureAwait(false);
}
}
}
catch (TaskCanceledException ex)
{
Logger.LogTrace<TorProcessManager>(ex);
}
catch (Exception ex)
{
Logger.LogDebug<TorProcessManager>(ex);
}
}
}
finally
{
if (IsStopping)
{
Interlocked.Exchange(ref _running, 3);
}
}
});
}
#region IDisposable Support
private volatile bool _disposedValue = false; // To detect redundant calls
protected virtual void Dispose(bool disposing)
{
if (!_disposedValue)
{
if (disposing)
{
if (IsRunning)
{
Interlocked.Exchange(ref _running, 2);
}
Stop?.Cancel();
while (IsStopping)
{
Task.Delay(50).GetAwaiter().GetResult(); // DO NOT MAKE IT ASYNC (.NET Core threading brainfart)
}
Stop?.Dispose();
}
_disposedValue = true;
}
}
// This code added to correctly implement the disposable pattern.
public void Dispose()
{
// Do not change this code. Put cleanup code in Dispose(bool disposing) above.
Dispose(true);
}
#endregion IDisposable Support
#endregion Monitor
}
}
@@ -95,18 +95,18 @@ internal async Task ConnectAsync()
}
// ex.Message must be checked, because I'm having difficulty catching SocketExceptionFactory+ExtendedSocketException
catch (Exception ex) when (ex.Message.StartsWith(
"No connection could be made because the target machine actively refused it")
|| ex.Message.StartsWith("Connection refused"))
"No connection could be made because the target machine actively refused it")
|| ex.Message.StartsWith("Connection refused"))
{
error = ex;
}
catch (SocketException ex) when(ex.ErrorCode == 10061)
catch (SocketException ex) when (ex.ErrorCode == 10061)
{
error = ex;
}
if (error!=null)
if (error != null)
throw new ConnectionException(
$"Couldn't connect to Tor SOCKSPort at {TorSocks5EndPoint.Address}:{TorSocks5EndPoint.Port}. Is Tor running?",error);
$"Couldn't connect to Tor SOCKSPort at {TorSocks5EndPoint.Address}:{TorSocks5EndPoint.Port}. Is Tor running?", error);
Stream = TcpClient.GetStream();
RemoteEndPoint = TcpClient.Client.RemoteEndPoint as IPEndPoint;

0 comments on commit eba37ba

Please sign in to comment.