2023-12-07 16:44:07 +00:00
|
|
|
import 'dart:io';
|
|
|
|
|
2024-01-08 17:33:04 +00:00
|
|
|
import 'package:cake_wallet/store/settings_store.dart';
|
|
|
|
import 'package:cake_wallet/view_model/settings/tor_connection.dart';
|
2023-12-07 16:44:07 +00:00
|
|
|
import 'package:socks5_proxy/socks.dart';
|
|
|
|
import 'package:tor/tor.dart';
|
|
|
|
|
2024-01-12 19:53:19 +00:00
|
|
|
// this is the only way to ensure we're making a non-tor connection:
|
|
|
|
class NullOverrides extends HttpOverrides {
|
|
|
|
NullOverrides();
|
|
|
|
|
|
|
|
@override
|
|
|
|
HttpClient createHttpClient(SecurityContext? context) {
|
|
|
|
return super.createHttpClient(context);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-12-07 16:44:07 +00:00
|
|
|
class ProxyWrapper {
|
2024-01-08 23:18:13 +00:00
|
|
|
ProxyWrapper({
|
|
|
|
required this.settingsStore,
|
|
|
|
});
|
|
|
|
|
|
|
|
late SettingsStore settingsStore;
|
2023-12-07 16:44:07 +00:00
|
|
|
|
2024-01-08 17:33:04 +00:00
|
|
|
HttpClient? _torClient;
|
2023-12-07 16:44:07 +00:00
|
|
|
|
2023-12-11 19:34:34 +00:00
|
|
|
static int get port => Tor.instance.port;
|
|
|
|
|
2023-12-12 20:29:06 +00:00
|
|
|
static bool get enabled => Tor.instance.enabled;
|
|
|
|
|
2024-01-08 17:33:04 +00:00
|
|
|
bool started = false;
|
2023-12-12 20:29:06 +00:00
|
|
|
|
2024-01-08 23:18:13 +00:00
|
|
|
// Method to get or create the Tor proxy instance
|
2024-01-10 18:00:58 +00:00
|
|
|
Future<HttpClient> getProxyHttpClient({int? portOverride}) async {
|
2023-12-07 16:44:07 +00:00
|
|
|
if (!started) {
|
|
|
|
started = true;
|
2024-01-08 17:33:04 +00:00
|
|
|
_torClient = HttpClient();
|
2023-12-07 16:44:07 +00:00
|
|
|
|
|
|
|
// Assign connection factory.
|
2024-01-08 17:33:04 +00:00
|
|
|
SocksTCPClient.assignToHttpClient(_torClient!, [
|
2023-12-07 16:44:07 +00:00
|
|
|
ProxySettings(
|
|
|
|
InternetAddress.loopbackIPv4,
|
2023-12-11 19:34:34 +00:00
|
|
|
portOverride ?? Tor.instance.port,
|
2023-12-07 16:44:07 +00:00
|
|
|
password: null,
|
|
|
|
),
|
|
|
|
]);
|
|
|
|
}
|
2024-01-08 17:33:04 +00:00
|
|
|
|
|
|
|
return _torClient!;
|
|
|
|
}
|
|
|
|
|
2024-01-12 19:53:19 +00:00
|
|
|
Future<HttpClientResponse> makeGet({
|
|
|
|
required HttpClient client,
|
|
|
|
required Uri uri,
|
|
|
|
required Map<String, String>? headers,
|
|
|
|
}) async {
|
|
|
|
final request = await client.getUrl(uri);
|
|
|
|
if (headers != null) {
|
|
|
|
headers.forEach((key, value) {
|
|
|
|
request.headers.add(key, value);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
return await request.close();
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<HttpClientResponse> makePost({
|
|
|
|
required HttpClient client,
|
|
|
|
required Uri uri,
|
|
|
|
required Map<String, String>? headers,
|
|
|
|
}) async {
|
|
|
|
final request = await client.postUrl(uri);
|
|
|
|
if (headers != null) {
|
|
|
|
headers.forEach((key, value) {
|
|
|
|
request.headers.add(key, value);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
return await request.close();
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<HttpClientResponse> get({
|
2024-01-10 18:00:58 +00:00
|
|
|
Map<String, String>? headers,
|
|
|
|
int? portOverride,
|
|
|
|
bool torOnly = false,
|
|
|
|
Uri? clearnetUri,
|
2024-01-12 19:53:19 +00:00
|
|
|
Uri? onionUri,
|
2024-01-10 18:00:58 +00:00
|
|
|
}) async {
|
2024-01-12 19:53:19 +00:00
|
|
|
HttpClient? torClient;
|
2024-01-10 18:00:58 +00:00
|
|
|
late bool torEnabled;
|
2024-01-08 17:33:04 +00:00
|
|
|
if (settingsStore.torConnectionMode == TorConnectionMode.onionOnly ||
|
|
|
|
settingsStore.torConnectionMode == TorConnectionMode.enabled) {
|
2024-01-12 19:53:19 +00:00
|
|
|
torClient = await getProxyHttpClient(portOverride: portOverride);
|
2024-01-10 18:00:58 +00:00
|
|
|
torEnabled = true;
|
2024-01-08 17:33:04 +00:00
|
|
|
} else {
|
2024-01-10 18:00:58 +00:00
|
|
|
torEnabled = false;
|
2024-01-08 17:33:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (settingsStore.torConnectionMode == TorConnectionMode.onionOnly) {
|
2024-01-12 19:53:19 +00:00
|
|
|
if (onionUri == null) {
|
2024-01-08 17:33:04 +00:00
|
|
|
throw Exception("Cannot connect to clearnet");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-12 19:53:19 +00:00
|
|
|
// if tor is enabled, try to connect to the onion url first:
|
|
|
|
if (torEnabled) {
|
|
|
|
if (onionUri != null) {
|
|
|
|
try {
|
|
|
|
return makeGet(
|
|
|
|
client: torClient!,
|
|
|
|
uri: onionUri,
|
|
|
|
headers: headers,
|
|
|
|
);
|
|
|
|
} catch (_) {}
|
|
|
|
}
|
2024-01-08 17:33:04 +00:00
|
|
|
|
2024-01-12 19:53:19 +00:00
|
|
|
if (clearnetUri != null && settingsStore.torConnectionMode != TorConnectionMode.onionOnly) {
|
|
|
|
try {
|
|
|
|
return makeGet(
|
|
|
|
client: torClient!,
|
|
|
|
uri: clearnetUri,
|
|
|
|
headers: headers,
|
|
|
|
);
|
|
|
|
} catch (_) {}
|
2024-01-08 17:33:04 +00:00
|
|
|
}
|
2024-01-12 19:53:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!torOnly && clearnetUri != null) {
|
|
|
|
try {
|
|
|
|
return HttpOverrides.runZoned(
|
|
|
|
() {
|
|
|
|
return makeGet(
|
|
|
|
client: HttpClient(),
|
|
|
|
uri: clearnetUri,
|
|
|
|
headers: headers,
|
|
|
|
);
|
|
|
|
},
|
|
|
|
createHttpClient: NullOverrides().createHttpClient,
|
|
|
|
);
|
|
|
|
} catch (_) {
|
|
|
|
// we weren't able to get a response:
|
|
|
|
rethrow;
|
2024-01-08 17:33:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-12 19:53:19 +00:00
|
|
|
throw Exception("Unable to connect to server");
|
2024-01-08 17:33:04 +00:00
|
|
|
}
|
|
|
|
|
2024-01-12 19:53:19 +00:00
|
|
|
Future<HttpClientResponse> post({
|
2024-01-10 18:00:58 +00:00
|
|
|
Map<String, String>? headers,
|
|
|
|
int? portOverride,
|
|
|
|
bool torOnly = false,
|
|
|
|
Uri? clearnetUri,
|
2024-01-12 19:53:19 +00:00
|
|
|
Uri? onionUri,
|
2024-01-10 18:00:58 +00:00
|
|
|
}) async {
|
2024-01-12 19:53:19 +00:00
|
|
|
HttpClient? torClient;
|
2024-01-10 18:00:58 +00:00
|
|
|
late bool torEnabled;
|
2024-01-08 17:33:04 +00:00
|
|
|
if (settingsStore.torConnectionMode == TorConnectionMode.onionOnly ||
|
|
|
|
settingsStore.torConnectionMode == TorConnectionMode.enabled) {
|
2024-01-12 19:53:19 +00:00
|
|
|
torClient = await getProxyHttpClient(portOverride: portOverride);
|
2024-01-10 18:00:58 +00:00
|
|
|
torEnabled = true;
|
2024-01-08 17:33:04 +00:00
|
|
|
} else {
|
2024-01-10 18:00:58 +00:00
|
|
|
torEnabled = false;
|
2024-01-08 17:33:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (settingsStore.torConnectionMode == TorConnectionMode.onionOnly) {
|
2024-01-12 19:53:19 +00:00
|
|
|
if (onionUri == null) {
|
2024-01-08 17:33:04 +00:00
|
|
|
throw Exception("Cannot connect to clearnet");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-12 19:53:19 +00:00
|
|
|
// if tor is enabled, try to connect to the onion url first:
|
|
|
|
|
|
|
|
if (torEnabled) {
|
|
|
|
if (onionUri != null) {
|
|
|
|
try {
|
|
|
|
return makePost(
|
|
|
|
client: torClient!,
|
|
|
|
uri: onionUri,
|
|
|
|
headers: headers,
|
|
|
|
);
|
|
|
|
} catch (_) {}
|
|
|
|
}
|
2024-01-08 17:33:04 +00:00
|
|
|
|
2024-01-12 19:53:19 +00:00
|
|
|
if (clearnetUri != null && settingsStore.torConnectionMode != TorConnectionMode.onionOnly) {
|
|
|
|
try {
|
|
|
|
return makePost(
|
|
|
|
client: torClient!,
|
|
|
|
uri: clearnetUri,
|
|
|
|
headers: headers,
|
|
|
|
);
|
|
|
|
} catch (_) {}
|
2024-01-08 17:33:04 +00:00
|
|
|
}
|
2024-01-12 19:53:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!torOnly && clearnetUri != null) {
|
|
|
|
try {
|
|
|
|
return HttpOverrides.runZoned(
|
|
|
|
() {
|
|
|
|
return makePost(
|
|
|
|
client: HttpClient(),
|
|
|
|
uri: clearnetUri,
|
|
|
|
headers: headers,
|
|
|
|
);
|
|
|
|
},
|
|
|
|
createHttpClient: NullOverrides().createHttpClient,
|
|
|
|
);
|
|
|
|
} catch (_) {
|
|
|
|
// we weren't able to get a response:
|
|
|
|
rethrow;
|
2024-01-08 17:33:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-12 19:53:19 +00:00
|
|
|
throw Exception("Unable to connect to server");
|
2023-12-07 16:44:07 +00:00
|
|
|
}
|
|
|
|
}
|