WIP proxied sockets

This commit is contained in:
sneurlax 2023-08-08 16:01:41 -05:00
parent ec5ae60a61
commit 281cd98390
3 changed files with 199 additions and 40 deletions

View file

@ -15,6 +15,7 @@ import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:decimal/decimal.dart';
import 'package:stackwallet/electrumx_rpc/rpc.dart';
import 'package:stackwallet/exceptions/electrumx/no_such_transaction.dart';
import 'package:stackwallet/networking/tor_service.dart';
import 'package:stackwallet/utilities/logger.dart';
import 'package:stackwallet/utilities/prefs.dart';
import 'package:uuid/uuid.dart';
@ -71,6 +72,8 @@ class ElectrumX {
final Duration connectionTimeoutForSpecialCaseJsonRPCClients;
({String host, int port})? proxyInfo;
ElectrumX({
required String host,
required int port,
@ -80,6 +83,7 @@ class ElectrumX {
JsonRPC? client,
this.connectionTimeoutForSpecialCaseJsonRPCClients =
const Duration(seconds: 60),
({String host, int port})? proxyInfo,
}) {
_prefs = prefs;
_host = host;
@ -92,14 +96,38 @@ class ElectrumX {
required ElectrumXNode node,
required Prefs prefs,
required List<ElectrumXNode> failovers,
}) =>
ElectrumX(
({String host, int port})? proxyInfo,
}) {
if (Prefs.instance.useTor) {
if (proxyInfo == null) {
// TODO await tor / make sure it's running
proxyInfo = (
host: InternetAddress.loopbackIPv4.address,
port: TorService.sharedInstance.port
);
Logging.instance.log(
"ElectrumX.batchRequest(): no tor proxy info, read $proxyInfo",
level: LogLevel.Warning);
}
return ElectrumX(
host: node.address,
port: node.port,
useSSL: node.useSSL,
prefs: prefs,
failovers: failovers,
proxyInfo: proxyInfo,
);
} else {
return ElectrumX(
host: node.address,
port: node.port,
useSSL: node.useSSL,
prefs: prefs,
failovers: failovers,
proxyInfo: null,
);
}
}
Future<bool> _allow() async {
if (_prefs.wifiOnly) {
@ -121,20 +149,52 @@ class ElectrumX {
throw WifiOnlyException();
}
if (currentFailoverIndex == -1) {
_rpcClient ??= JsonRPC(
host: host,
port: port,
useSSL: useSSL,
connectionTimeout: connectionTimeoutForSpecialCaseJsonRPCClients,
);
if (Prefs.instance.useTor) {
if (proxyInfo == null) {
// TODO await tor / make sure Tor is running
proxyInfo = (
host: InternetAddress.loopbackIPv4.address,
port: TorService.sharedInstance.port
);
Logging.instance.log(
"ElectrumX.batchRequest(): no tor proxy info, read $proxyInfo",
level: LogLevel.Warning);
}
if (currentFailoverIndex == -1) {
_rpcClient ??= JsonRPC(
host: host,
port: port,
useSSL: useSSL,
connectionTimeout: connectionTimeoutForSpecialCaseJsonRPCClients,
proxyInfo: proxyInfo,
);
} else {
_rpcClient = JsonRPC(
host: failovers![currentFailoverIndex].address,
port: failovers![currentFailoverIndex].port,
useSSL: failovers![currentFailoverIndex].useSSL,
connectionTimeout: connectionTimeoutForSpecialCaseJsonRPCClients,
proxyInfo: proxyInfo,
);
}
} else {
_rpcClient = JsonRPC(
host: failovers![currentFailoverIndex].address,
port: failovers![currentFailoverIndex].port,
useSSL: failovers![currentFailoverIndex].useSSL,
connectionTimeout: connectionTimeoutForSpecialCaseJsonRPCClients,
);
if (currentFailoverIndex == -1) {
_rpcClient ??= JsonRPC(
host: host,
port: port,
useSSL: useSSL,
connectionTimeout: connectionTimeoutForSpecialCaseJsonRPCClients,
proxyInfo: null,
);
} else {
_rpcClient = JsonRPC(
host: failovers![currentFailoverIndex].address,
port: failovers![currentFailoverIndex].port,
useSSL: failovers![currentFailoverIndex].useSSL,
connectionTimeout: connectionTimeoutForSpecialCaseJsonRPCClients,
proxyInfo: null,
);
}
}
try {
@ -221,20 +281,53 @@ class ElectrumX {
throw WifiOnlyException();
}
if (currentFailoverIndex == -1) {
_rpcClient ??= JsonRPC(
host: host,
port: port,
useSSL: useSSL,
connectionTimeout: connectionTimeoutForSpecialCaseJsonRPCClients,
);
if (Prefs.instance.useTor) {
// TODO await tor / make sure Tor is initialized
if (proxyInfo == null) {
proxyInfo = (
host: InternetAddress.loopbackIPv4.address,
port: TorService.sharedInstance.port
);
Logging.instance.log(
"ElectrumX.batchRequest(): no tor proxy info, read $proxyInfo",
level: LogLevel.Warning);
}
if (currentFailoverIndex == -1) {
_rpcClient ??= JsonRPC(
host: host,
port: port,
useSSL: useSSL,
connectionTimeout: connectionTimeoutForSpecialCaseJsonRPCClients,
proxyInfo: proxyInfo,
);
} else {
_rpcClient = JsonRPC(
host: failovers![currentFailoverIndex].address,
port: failovers![currentFailoverIndex].port,
useSSL: failovers![currentFailoverIndex].useSSL,
connectionTimeout: connectionTimeoutForSpecialCaseJsonRPCClients,
proxyInfo: proxyInfo,
);
}
} else {
_rpcClient = JsonRPC(
host: failovers![currentFailoverIndex].address,
port: failovers![currentFailoverIndex].port,
useSSL: failovers![currentFailoverIndex].useSSL,
connectionTimeout: connectionTimeoutForSpecialCaseJsonRPCClients,
);
if (currentFailoverIndex == -1) {
_rpcClient ??= JsonRPC(
host: host,
port: port,
useSSL: useSSL,
connectionTimeout: connectionTimeoutForSpecialCaseJsonRPCClients,
proxyInfo: null,
);
} else {
_rpcClient = JsonRPC(
host: failovers![currentFailoverIndex].address,
port: failovers![currentFailoverIndex].port,
useSSL: failovers![currentFailoverIndex].useSSL,
connectionTimeout: connectionTimeoutForSpecialCaseJsonRPCClients,
proxyInfo: null,
);
}
}
try {

View file

@ -14,7 +14,11 @@ import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:mutex/mutex.dart';
import 'package:socks5_proxy/socks.dart';
import 'package:socks5_proxy/src/client/socks_client.dart';
import 'package:stackwallet/networking/tor_service.dart';
import 'package:stackwallet/utilities/logger.dart';
import 'package:stackwallet/utilities/prefs.dart';
// Json RPC class to handle connecting to electrumx servers
class JsonRPC {
@ -23,11 +27,13 @@ class JsonRPC {
required this.port,
this.useSSL = false,
this.connectionTimeout = const Duration(seconds: 60),
required ({String host, int port})? proxyInfo,
});
final bool useSSL;
final String host;
final int port;
final Duration connectionTimeout;
({String host, int port})? proxyInfo;
final _requestMutex = Mutex();
final _JsonRPCRequestQueue _requestQueue = _JsonRPCRequestQueue();
@ -152,19 +158,76 @@ class JsonRPC {
);
}
if (useSSL) {
_socket = await SecureSocket.connect(
host,
if (Prefs.instance.useTor) {
if (proxyInfo == null) {
// TODO await tor / make sure it's running
proxyInfo = (
host: InternetAddress.loopbackIPv4.address,
port: TorService.sharedInstance.port
);
Logging.instance.log(
"ElectrumX.connect(): no tor proxy info, read $proxyInfo",
level: LogLevel.Warning);
}
// TODO connect to proxy socket...
// https://github.com/LacticWhale/socks_dart/blob/master/lib/src/client/socks_client.dart#L50C46-L50C56
// TODO implement ssl over tor
// if (useSSL) {
// _socket = await SecureSocket.connect(
// host,
// port,
// timeout: connectionTimeout,
// onBadCertificate: (_) => true,
// ); // TODO do not automatically trust bad certificates
// final _client = SocksSocket.protected(_socket, type);
// } else {
// _socket = await Socket.connect(
// proxyInfo!.host,
// proxyInfo!.port,
// timeout: connectionTimeout,
// );
// final _client = SocksSocket.protected(
// _socket!, SocksConnectionType.connect
// );
final InternetAddress _host =
await InternetAddress.lookup(host).then((value) => value.first);
var _socket = await SocksSocket.initialize(
[
ProxySettings(
InternetAddress.loopbackIPv4,
proxyInfo!.port,
)
],
_host,
port,
timeout: connectionTimeout,
onBadCertificate: (_) => true,
); // TODO do not automatically trust bad certificates
} else {
_socket = await Socket.connect(
host,
port,
timeout: connectionTimeout,
SocksConnectionType.connect,
);
if (_socket == null) {
Logging.instance.log(
"JsonRPC.connect(): failed to connect to $host over tor proxy at $proxyInfo",
level: LogLevel.Error);
throw Exception("JsonRPC.connect(): failed to connect to tor proxy");
} else {
Logging.instance.log(
"JsonRPC.connect(): connected to $host over tor proxy at $proxyInfo",
level: LogLevel.Info);
}
} else {
if (useSSL) {
_socket = await SecureSocket.connect(
host,
port,
timeout: connectionTimeout,
onBadCertificate: (_) => true,
); // TODO do not automatically trust bad certificates
} else {
_socket = await Socket.connect(
host,
port,
timeout: connectionTimeout,
);
}
}
_subscription = _socket!.listen(

View file

@ -11,6 +11,7 @@ void main() {
port: DefaultNodes.bitcoin.port,
useSSL: true,
connectionTimeout: const Duration(seconds: 40),
proxyInfo: null, // TODO test for proxyInfo
);
const jsonRequestString =
@ -27,7 +28,8 @@ void main() {
final jsonRPC = JsonRPC(
host: "some.bad.address.thingdsfsdfsdaf",
port: 3000,
connectionTimeout: Duration(seconds: 10),
connectionTimeout: const Duration(seconds: 10),
proxyInfo: null,
);
const jsonRequestString =
@ -47,6 +49,7 @@ void main() {
port: 3000,
useSSL: false,
connectionTimeout: const Duration(seconds: 1),
proxyInfo: null,
);
const jsonRequestString =