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