CheckCircuit – Rev 2
?pathlinks?
using CommandLine.Text;
using CommandLine;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Runtime.Intrinsics.Arm;
using System.Text;
using System.Threading.Tasks;
using CheckCircuit.CommandLine;
using System.Threading;
namespace CheckCircuit
{
internal class Program
{
public static TcpClient TcpClient { get; set; }
public static ConcurrentDictionary<IntPtr, TcpClient> tcpClients { get; set; }
public static bool Verbose { get; private set; }
static int Main(string[] args)
{
var CancellationTokenSource = new CancellationTokenSource();
var CancellationToken = CancellationTokenSource.Token;
tcpClients = new ConcurrentDictionary<IntPtr, TcpClient>();
return Parser.Default.ParseArguments<ConnectOptions>(args)
.MapResult(opts => RunConnectAndReturnExitCode(opts, CancellationToken), errs => 1);
}
private static int RunConnectAndReturnExitCode(ConnectOptions opts, CancellationToken cancellationToken)
{
Verbose = opts.Verbose;
if(!IPEndPoint.TryParse(opts.Listen, out var listenEndPoint))
{
Console.WriteLine("Bad listen HOST:PORT format.");
return 1;
}
var server = StartServer(listenEndPoint, cancellationToken);
if(!IPEndPoint.TryParse(opts.Connect, out var connectEndPoint))
{
Console.WriteLine("Bad connect HOST:PORT format.");
return 1;
}
if(string.IsNullOrEmpty(opts.Password))
{
Console.WriteLine("Bad password string.");
return 1;
}
var client = StartClient(connectEndPoint, opts.Password, cancellationToken);
Task.WhenAll(new[] { server, client }).Wait(cancellationToken);
return 0;
}
private static async Task StartServer(IPEndPoint listen, CancellationToken cancellationToken)
{
var tcpServer = new TcpListener(listen);
tcpServer.Start();
do
{
var tcpClient = await tcpServer.AcceptTcpClientAsync();
var handle = tcpClient.Client.Handle;
if (!tcpClients.ContainsKey(handle))
{
tcpClients.TryAdd(handle, tcpClient);
}
} while (!cancellationToken.IsCancellationRequested);
}
private static async Task StartClient(IPEndPoint connect, string password, CancellationToken cancellationToken)
{
try
{
using (var tcpClient = new TcpClient())
{
await tcpClient.ConnectAsync(connect.Address, connect.Port, cancellationToken);
using (var tcpClientStream = tcpClient.GetStream())
{
using (var streamReader = new StreamReader(tcpClientStream))
{
using (var streamWriter = new StreamWriter(tcpClientStream))
{
do
{
if(!await Authenticate(streamReader, streamWriter, password))
{
if (Verbose)
{
Console.WriteLine("Authentication failed, please check OR port and password.");
}
await SendPayload(tcpClients, "0", cancellationToken);
await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken);
continue;
}
do
{
try
{
if (await IsCircuitFormed(streamReader, streamWriter))
{
await SendPayload(tcpClients, "1", cancellationToken);
await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken);
continue;
}
await SendPayload(tcpClients, "0", cancellationToken);
await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken);
}
catch(ArgumentException)
{
await SendPayload(tcpClients, "0", cancellationToken);
await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken);
}
} while (tcpClient.Connected && !cancellationToken.IsCancellationRequested);
} while (tcpClient.Connected && !cancellationToken.IsCancellationRequested);
}
}
}
}
}
catch (Exception exception)
{
if (Verbose)
{
Console.WriteLine(exception);
}
}
}
public static async Task<bool> Authenticate(StreamReader sr, StreamWriter sw, string password)
{
var readLineTask = sr.ReadLineAsync();
await sw.WriteLineAsync($"AUTHENTICATE \"{password}\"");
await sw.FlushAsync();
var readLine = await readLineTask;
return string.Equals(readLine, @"250 OK", StringComparison.Ordinal);
}
public static async Task<bool> IsCircuitFormed(StreamReader sr, StreamWriter sw)
{
var readLineTask = sr.ReadLineAsync();
await sw.WriteLineAsync(@"GETINFO status/circuit-established");
await sw.FlushAsync();
var readLine = await readLineTask;
var success = string.Equals(readLine, @"250-status/circuit-established=1", StringComparison.Ordinal);
readLine = await sr.ReadLineAsync();
if(string.Equals(readLine, @"250 OK", StringComparison.Ordinal))
{
return success;
}
throw new ArgumentException("Unable to read response from control port.");
}
public static async Task SendPayload(ConcurrentDictionary<IntPtr, TcpClient> clients, string payload, CancellationToken CancellationToken)
{
foreach (var (handle, client) in clients)
{
if(CancellationToken.IsCancellationRequested)
{
throw new TaskCanceledException();
}
try
{
if (!client.Connected)
{
continue;
}
using (var networkStream = client.GetStream())
{
using (var sw = new StreamWriter(networkStream))
{
await sw.WriteLineAsync(payload);
}
}
}
catch(Exception exception)
{
if (Verbose)
{
Console.WriteLine(exception);
}
}
finally
{
clients.TryRemove(handle, out _);
try
{
client.Close();
}
catch(ObjectDisposedException exception)
{
if (Verbose)
{
Console.WriteLine(exception);
}
}
}
}
}
}
}