dev
dart 441 lines 10.7 KB
Raw
1 import 'dart:async';
2 import 'dart:convert';
3 import 'dart:io';
4 import 'dart:typed_data';
5 import 'package:cw_core/utils/proxy_logger/abstract.dart';
6 import 'package:cw_core/utils/proxy_socket/abstract.dart';
7 import 'package:cw_core/utils/tor/abstract.dart';
8 import 'package:http/http.dart';
9 import 'package:socks5_proxy/socks_client.dart';
10 import 'package:http/io_client.dart' as ioc;
11
12 class ProxyWrapper {
13 static final ProxyWrapper _proxyWrapper = ProxyWrapper._internal();
14 static ProxyLogger? logger;
15
16 factory ProxyWrapper() {
17 return _proxyWrapper;
18 }
19
20 ProxyWrapper._internal();
21 Future<ProxySocket> getSocksSocket(bool sslEnabled, String host, int port,
22 {Duration? connectionTimeout}) async {
23 logger?.log(
24 uri: Uri(
25 scheme: sslEnabled ? "https" : "http",
26 host: host,
27 port: port,
28 ),
29 method: RequestMethod.newProxySocket,
30 body: Uint8List(0),
31 response: null,
32 network: requestNetwork(),
33 error: null);
34 return ProxySocket.connect(sslEnabled, ProxyAddress(host: host, port: port),
35 connectionTimeout: connectionTimeout);
36 }
37
38 RequestNetwork requestNetwork() {
39 return CakeTor.instance!.started ? RequestNetwork.tor : RequestNetwork.clearnet;
40 }
41
42 ioc.IOClient getHttpIOClient({int? portOverride, bool internal = false}) {
43 if (!internal) {
44 logger?.log(
45 uri: null,
46 method: RequestMethod.newHttpIOClient,
47 body: Uint8List(0),
48 response: null,
49 network: requestNetwork(),
50 error: null,
51 );
52 }
53 // ignore: deprecated_member_use_from_same_package
54 final httpClient = ProxyWrapper().getHttpClient(portOverride: portOverride, internal: true);
55 return ioc.IOClient(httpClient);
56 }
57
58 int getPort() => CakeTor.instance!.port;
59
60 @Deprecated(
61 'Use ProxyWrapper().get/post/put methods instead, and provide proper clearnet and onion uri.')
62 HttpClient getHttpClient({int? portOverride, bool internal = false}) {
63 if (!internal) {
64 logger?.log(
65 uri: null,
66 method: RequestMethod.newProxySocket,
67 body: Uint8List(0),
68 response: null,
69 network: requestNetwork(),
70 error: null);
71 }
72 if (CakeTor.instance!.started) {
73 // Assign connection factory.
74 final client = HttpClient();
75 SocksTCPClient.assignToHttpClient(client, [
76 ProxySettings(
77 InternetAddress.loopbackIPv4,
78 CakeTor.instance!.port,
79 password: null,
80 ),
81 ]);
82 return client;
83 } else {
84 return HttpClient();
85 }
86 }
87
88 Future<Response> _make({
89 required RequestMethod method,
90 required ioc.IOClient client,
91 required Uri uri,
92 required Map<String, String>? headers,
93 String? body,
94 }) async {
95 Object? error;
96 Response? resp;
97 try {
98 switch (method) {
99 case RequestMethod.get:
100 resp = await client.get(
101 uri,
102 headers: headers,
103 );
104 break;
105 case RequestMethod.delete:
106 resp = await client.delete(
107 uri,
108 headers: headers,
109 body: body,
110 );
111 break;
112 case RequestMethod.post:
113 resp = await client.post(
114 uri,
115 headers: headers,
116 body: body,
117 );
118 break;
119 case RequestMethod.put:
120 resp = await client.put(
121 uri,
122 headers: headers,
123 body: body,
124 );
125 break;
126 case RequestMethod.newHttpClient:
127 case RequestMethod.newHttpIOClient:
128 case RequestMethod.newProxySocket:
129 throw UnimplementedError();
130 }
131 return resp;
132 } catch (e) {
133 error = e;
134 rethrow;
135 } finally {
136 logger?.log(
137 uri: uri,
138 method: RequestMethod.get,
139 body: utf8.encode(body ?? ''),
140 response: resp,
141 network: requestNetwork(),
142 error: error?.toString(),
143 );
144 }
145 }
146
147 Future<Response> get({
148 Map<String, String>? headers,
149 int? portOverride,
150 Uri? clearnetUri,
151 Uri? onionUri,
152 }) async {
153 ioc.IOClient? torClient;
154 bool torEnabled = CakeTor.instance!.started;
155
156 if (CakeTor.instance!.started) {
157 torEnabled = true;
158 } else {
159 torEnabled = false;
160 }
161
162 // if tor is enabled, try to connect to the onion url first:
163 if (torEnabled) {
164 try {
165 // ignore: deprecated_member_use_from_same_package
166 torClient = await getHttpIOClient(portOverride: portOverride, internal: true);
167 } catch (_) {
168 rethrow;
169 }
170
171 if (onionUri != null) {
172 try {
173 return await _make(
174 method: RequestMethod.get,
175 client: torClient,
176 uri: onionUri,
177 headers: headers,
178 );
179 } catch (_) {
180 rethrow;
181 }
182 }
183
184 if (clearnetUri != null) {
185 try {
186 return await _make(
187 method: RequestMethod.get,
188 client: torClient,
189 uri: clearnetUri,
190 headers: headers,
191 );
192 } catch (_) {
193 rethrow;
194 }
195 }
196 }
197
198 if (clearnetUri != null) {
199 try {
200 return HttpOverrides.runZoned(
201 () async {
202 return await _make(
203 method: RequestMethod.get,
204 client: ioc.IOClient(),
205 uri: clearnetUri,
206 headers: headers,
207 );
208 },
209 );
210 } catch (_) {
211 // we weren't able to get a response:
212 rethrow;
213 }
214 }
215
216 throw Exception("Unable to connect to server");
217 }
218
219 Future<Response> post({
220 Map<String, String>? headers,
221 int? portOverride,
222 Uri? clearnetUri,
223 Uri? onionUri,
224 String? body,
225 bool allowMitmMoneroBypassSSLCheck = false,
226 }) async {
227 HttpClient? torHttpClient;
228 HttpClient cleatnetHttpClient = HttpClient();
229 if (allowMitmMoneroBypassSSLCheck) {
230 cleatnetHttpClient.badCertificateCallback =
231 ((X509Certificate cert, String host, int port) => true);
232 }
233
234 ioc.IOClient clearnetClient = ioc.IOClient(cleatnetHttpClient);
235
236 bool torEnabled = CakeTor.instance!.started;
237
238 if (torEnabled) {
239 try {
240 // ignore: deprecated_member_use_from_same_package
241 torHttpClient = await getHttpClient(portOverride: portOverride);
242 } catch (_) {
243 rethrow;
244 }
245 if (allowMitmMoneroBypassSSLCheck) {
246 torHttpClient.badCertificateCallback =
247 ((X509Certificate cert, String host, int port) => true);
248 }
249 if (onionUri != null) {
250 try {
251 return await _make(
252 method: RequestMethod.post,
253 client: ioc.IOClient(torHttpClient),
254 uri: onionUri,
255 headers: headers,
256 body: body,
257 );
258 } catch (_) {
259 rethrow;
260 }
261 }
262
263 if (clearnetUri != null) {
264 try {
265 return await _make(
266 method: RequestMethod.post,
267 client: ioc.IOClient(torHttpClient),
268 uri: clearnetUri,
269 headers: headers,
270 body: body,
271 );
272 } catch (_) {
273 rethrow;
274 }
275 }
276 }
277
278 if (clearnetUri != null) {
279 try {
280 return HttpOverrides.runZoned(
281 () async {
282 return await _make(
283 method: RequestMethod.post,
284 client: clearnetClient,
285 uri: clearnetUri,
286 headers: headers,
287 body: body,
288 );
289 },
290 );
291 } catch (_) {
292 rethrow;
293 }
294 }
295
296 throw Exception("Unable to connect to server");
297 }
298
299 Future<Response> put({
300 Map<String, String>? headers,
301 int? portOverride,
302 Uri? clearnetUri,
303 Uri? onionUri,
304 String? body,
305 }) async {
306 ioc.IOClient? torClient;
307 bool torEnabled = CakeTor.instance!.started;
308
309 if (torEnabled) {
310 try {
311 // ignore: deprecated_member_use_from_same_package
312 torClient = await getHttpIOClient(portOverride: portOverride, internal: true);
313 } catch (_) {}
314
315 if (onionUri != null) {
316 try {
317 return await _make(
318 method: RequestMethod.put,
319 client: torClient!,
320 uri: onionUri,
321 headers: headers,
322 body: body,
323 );
324 } catch (_) {
325 rethrow;
326 }
327 }
328
329 if (clearnetUri != null) {
330 try {
331 return await _make(
332 method: RequestMethod.put,
333 client: torClient!,
334 uri: clearnetUri,
335 headers: headers,
336 body: body,
337 );
338 } catch (_) {
339 rethrow;
340 }
341 }
342 }
343
344 if (clearnetUri != null) {
345 try {
346 return HttpOverrides.runZoned(
347 () async {
348 return await _make(
349 method: RequestMethod.put,
350 client: ioc.IOClient(),
351 uri: clearnetUri,
352 headers: headers,
353 body: body,
354 );
355 },
356 );
357 } catch (_) {
358 // we weren't able to get a response:
359 rethrow;
360 }
361 }
362
363 throw Exception("Unable to connect to server");
364 }
365
366 Future<Response> delete({
367 Map<String, String>? headers,
368 int? portOverride,
369 Uri? clearnetUri,
370 Uri? onionUri,
371 }) async {
372 ioc.IOClient? torClient;
373 bool torEnabled = CakeTor.instance!.started;
374
375 if (CakeTor.instance!.started) {
376 torEnabled = true;
377 } else {
378 torEnabled = false;
379 }
380
381 // if tor is enabled, try to connect to the onion url first:
382 if (torEnabled) {
383 try {
384 // ignore: deprecated_member_use_from_same_package
385 torClient = await getHttpIOClient(portOverride: portOverride, internal: true);
386 } catch (_) {
387 rethrow;
388 }
389
390 if (onionUri != null) {
391 try {
392 return await _make(
393 method: RequestMethod.delete,
394 client: torClient,
395 uri: onionUri,
396 headers: headers,
397 );
398 } catch (_) {
399 rethrow;
400 }
401 }
402
403 if (clearnetUri != null) {
404 try {
405 return await _make(
406 method: RequestMethod.delete,
407 client: torClient,
408 uri: clearnetUri,
409 headers: headers,
410 );
411 } catch (_) {
412 rethrow;
413 }
414 }
415 }
416
417 if (clearnetUri != null) {
418 try {
419 return HttpOverrides.runZoned(
420 () async {
421 return await _make(
422 method: RequestMethod.delete,
423 client: ioc.IOClient(),
424 uri: clearnetUri,
425 headers: headers,
426 );
427 },
428 );
429 } catch (_) {
430 // we weren't able to get a response:
431 rethrow;
432 }
433 }
434
435 throw Exception("Unable to connect to server");
436 }
437 }
438
439 class CakeTor {
440 static CakeTorInstance? instance;
441 }