CheckCircuit – Blame information for rev 2
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 1 | using CommandLine.Text; |
2 | using CommandLine; |
||
3 | using System; |
||
4 | using System.Collections.Concurrent; |
||
5 | using System.Collections.Generic; |
||
6 | using System.IO; |
||
7 | using System.Linq; |
||
8 | using System.Net; |
||
9 | using System.Net.Sockets; |
||
10 | using System.Runtime.Intrinsics.Arm; |
||
11 | using System.Text; |
||
12 | using System.Threading.Tasks; |
||
13 | using CheckCircuit.CommandLine; |
||
14 | using System.Threading; |
||
15 | |||
16 | namespace CheckCircuit |
||
17 | { |
||
18 | internal class Program |
||
19 | { |
||
20 | |||
21 | public static TcpClient TcpClient { get; set; } |
||
22 | |||
23 | public static ConcurrentDictionary<IntPtr, TcpClient> tcpClients { get; set; } |
||
24 | public static bool Verbose { get; private set; } |
||
25 | |||
26 | static int Main(string[] args) |
||
27 | { |
||
28 | var CancellationTokenSource = new CancellationTokenSource(); |
||
29 | var CancellationToken = CancellationTokenSource.Token; |
||
30 | |||
31 | tcpClients = new ConcurrentDictionary<IntPtr, TcpClient>(); |
||
32 | |||
33 | return Parser.Default.ParseArguments<ConnectOptions>(args) |
||
34 | .MapResult(opts => RunConnectAndReturnExitCode(opts, CancellationToken), errs => 1); |
||
35 | } |
||
36 | |||
37 | private static int RunConnectAndReturnExitCode(ConnectOptions opts, CancellationToken cancellationToken) |
||
38 | { |
||
39 | Verbose = opts.Verbose; |
||
40 | |||
41 | if(!IPEndPoint.TryParse(opts.Listen, out var listenEndPoint)) |
||
42 | { |
||
43 | Console.WriteLine("Bad listen HOST:PORT format."); |
||
44 | return 1; |
||
45 | } |
||
46 | var server = StartServer(listenEndPoint, cancellationToken); |
||
47 | |||
48 | if(!IPEndPoint.TryParse(opts.Connect, out var connectEndPoint)) |
||
49 | { |
||
50 | Console.WriteLine("Bad connect HOST:PORT format."); |
||
51 | return 1; |
||
52 | } |
||
53 | |||
54 | if(string.IsNullOrEmpty(opts.Password)) |
||
55 | { |
||
2 | office | 56 | Console.WriteLine("Bad password string."); |
1 | office | 57 | return 1; |
58 | } |
||
59 | |||
60 | var client = StartClient(connectEndPoint, opts.Password, cancellationToken); |
||
61 | |||
62 | Task.WhenAll(new[] { server, client }).Wait(cancellationToken); |
||
63 | |||
64 | return 0; |
||
65 | } |
||
66 | |||
67 | private static async Task StartServer(IPEndPoint listen, CancellationToken cancellationToken) |
||
68 | { |
||
69 | var tcpServer = new TcpListener(listen); |
||
70 | tcpServer.Start(); |
||
71 | |||
72 | do |
||
73 | { |
||
74 | var tcpClient = await tcpServer.AcceptTcpClientAsync(); |
||
75 | var handle = tcpClient.Client.Handle; |
||
76 | if (!tcpClients.ContainsKey(handle)) |
||
77 | { |
||
78 | tcpClients.TryAdd(handle, tcpClient); |
||
79 | } |
||
80 | |||
81 | } while (!cancellationToken.IsCancellationRequested); |
||
82 | } |
||
83 | |||
84 | private static async Task StartClient(IPEndPoint connect, string password, CancellationToken cancellationToken) |
||
85 | { |
||
86 | try |
||
87 | { |
||
88 | using (var tcpClient = new TcpClient()) |
||
89 | { |
||
90 | await tcpClient.ConnectAsync(connect.Address, connect.Port, cancellationToken); |
||
91 | using (var tcpClientStream = tcpClient.GetStream()) |
||
92 | { |
||
93 | using (var streamReader = new StreamReader(tcpClientStream)) |
||
94 | { |
||
95 | using (var streamWriter = new StreamWriter(tcpClientStream)) |
||
96 | { |
||
97 | do |
||
98 | { |
||
99 | if(!await Authenticate(streamReader, streamWriter, password)) |
||
100 | { |
||
101 | if (Verbose) |
||
102 | { |
||
103 | Console.WriteLine("Authentication failed, please check OR port and password."); |
||
104 | } |
||
105 | await SendPayload(tcpClients, "0", cancellationToken); |
||
106 | await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken); |
||
107 | continue; |
||
108 | } |
||
109 | |||
110 | do |
||
111 | { |
||
112 | try |
||
113 | { |
||
114 | if (await IsCircuitFormed(streamReader, streamWriter)) |
||
115 | { |
||
116 | await SendPayload(tcpClients, "1", cancellationToken); |
||
117 | await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken); |
||
118 | continue; |
||
119 | } |
||
120 | |||
121 | await SendPayload(tcpClients, "0", cancellationToken); |
||
122 | await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken); |
||
123 | } |
||
124 | catch(ArgumentException) |
||
125 | { |
||
126 | await SendPayload(tcpClients, "0", cancellationToken); |
||
127 | await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken); |
||
128 | } |
||
129 | |||
130 | } while (tcpClient.Connected && !cancellationToken.IsCancellationRequested); |
||
131 | } while (tcpClient.Connected && !cancellationToken.IsCancellationRequested); |
||
132 | } |
||
133 | } |
||
134 | } |
||
135 | } |
||
136 | } |
||
137 | catch (Exception exception) |
||
138 | { |
||
139 | if (Verbose) |
||
140 | { |
||
141 | Console.WriteLine(exception); |
||
142 | } |
||
143 | } |
||
144 | } |
||
145 | |||
146 | public static async Task<bool> Authenticate(StreamReader sr, StreamWriter sw, string password) |
||
147 | { |
||
148 | var readLineTask = sr.ReadLineAsync(); |
||
149 | await sw.WriteLineAsync($"AUTHENTICATE \"{password}\""); |
||
150 | await sw.FlushAsync(); |
||
151 | var readLine = await readLineTask; |
||
152 | |||
153 | return string.Equals(readLine, @"250 OK", StringComparison.Ordinal); |
||
154 | } |
||
155 | |||
156 | public static async Task<bool> IsCircuitFormed(StreamReader sr, StreamWriter sw) |
||
157 | { |
||
158 | var readLineTask = sr.ReadLineAsync(); |
||
159 | await sw.WriteLineAsync(@"GETINFO status/circuit-established"); |
||
160 | await sw.FlushAsync(); |
||
161 | var readLine = await readLineTask; |
||
162 | |||
163 | var success = string.Equals(readLine, @"250-status/circuit-established=1", StringComparison.Ordinal); |
||
164 | readLine = await sr.ReadLineAsync(); |
||
165 | if(string.Equals(readLine, @"250 OK", StringComparison.Ordinal)) |
||
166 | { |
||
167 | return success; |
||
168 | } |
||
169 | |||
170 | throw new ArgumentException("Unable to read response from control port."); |
||
171 | } |
||
172 | |||
173 | public static async Task SendPayload(ConcurrentDictionary<IntPtr, TcpClient> clients, string payload, CancellationToken CancellationToken) |
||
174 | { |
||
175 | foreach (var (handle, client) in clients) |
||
176 | { |
||
177 | if(CancellationToken.IsCancellationRequested) |
||
178 | { |
||
179 | throw new TaskCanceledException(); |
||
180 | } |
||
181 | |||
182 | try |
||
183 | { |
||
184 | if (!client.Connected) |
||
185 | { |
||
186 | continue; |
||
187 | } |
||
188 | |||
189 | using (var networkStream = client.GetStream()) |
||
190 | { |
||
191 | using (var sw = new StreamWriter(networkStream)) |
||
192 | { |
||
193 | await sw.WriteLineAsync(payload); |
||
194 | |||
195 | } |
||
196 | } |
||
197 | } |
||
198 | catch(Exception exception) |
||
199 | { |
||
200 | if (Verbose) |
||
201 | { |
||
202 | Console.WriteLine(exception); |
||
203 | } |
||
204 | } |
||
205 | finally |
||
206 | { |
||
207 | clients.TryRemove(handle, out _); |
||
208 | try |
||
209 | { |
||
210 | client.Close(); |
||
211 | } |
||
212 | catch(ObjectDisposedException exception) |
||
213 | { |
||
214 | if (Verbose) |
||
215 | { |
||
216 | Console.WriteLine(exception); |
||
217 | } |
||
218 | } |
||
219 | } |
||
220 | } |
||
221 | } |
||
222 | } |
||
223 | |||
224 | |||
225 | } |