mirror of
https://github.com/cypherstack/stack_wallet.git
synced 2024-11-16 17:27:39 +00:00
use socks socket if useTor in ElectrumX and JsonRPC,
This commit is contained in:
parent
80fcdef330
commit
256db85472
8 changed files with 382 additions and 65 deletions
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -58,3 +58,7 @@ libmobileliblelantus.dll
|
||||||
libtor_ffi.dll
|
libtor_ffi.dll
|
||||||
/libisar.so
|
/libisar.so
|
||||||
libtor_ffi.so
|
libtor_ffi.so
|
||||||
|
|
||||||
|
tor_logs.txt
|
||||||
|
|
||||||
|
torrc
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit 6aca8f78a10972ce07ee1ededcce02e97cc46834
|
Subproject commit fc91c3f421467545f198d95558848e94de2fa6d9
|
|
@ -15,6 +15,7 @@ import 'package:connectivity_plus/connectivity_plus.dart';
|
||||||
import 'package:decimal/decimal.dart';
|
import 'package:decimal/decimal.dart';
|
||||||
import 'package:stackwallet/electrumx_rpc/rpc.dart';
|
import 'package:stackwallet/electrumx_rpc/rpc.dart';
|
||||||
import 'package:stackwallet/exceptions/electrumx/no_such_transaction.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/logger.dart';
|
||||||
import 'package:stackwallet/utilities/prefs.dart';
|
import 'package:stackwallet/utilities/prefs.dart';
|
||||||
import 'package:uuid/uuid.dart';
|
import 'package:uuid/uuid.dart';
|
||||||
|
@ -71,6 +72,8 @@ class ElectrumX {
|
||||||
|
|
||||||
final Duration connectionTimeoutForSpecialCaseJsonRPCClients;
|
final Duration connectionTimeoutForSpecialCaseJsonRPCClients;
|
||||||
|
|
||||||
|
({String host, int port})? proxyInfo;
|
||||||
|
|
||||||
ElectrumX({
|
ElectrumX({
|
||||||
required String host,
|
required String host,
|
||||||
required int port,
|
required int port,
|
||||||
|
@ -80,6 +83,7 @@ class ElectrumX {
|
||||||
JsonRPC? client,
|
JsonRPC? client,
|
||||||
this.connectionTimeoutForSpecialCaseJsonRPCClients =
|
this.connectionTimeoutForSpecialCaseJsonRPCClients =
|
||||||
const Duration(seconds: 60),
|
const Duration(seconds: 60),
|
||||||
|
({String host, int port})? proxyInfo,
|
||||||
}) {
|
}) {
|
||||||
_prefs = prefs;
|
_prefs = prefs;
|
||||||
_host = host;
|
_host = host;
|
||||||
|
@ -92,14 +96,38 @@ class ElectrumX {
|
||||||
required ElectrumXNode node,
|
required ElectrumXNode node,
|
||||||
required Prefs prefs,
|
required Prefs prefs,
|
||||||
required List<ElectrumXNode> failovers,
|
required List<ElectrumXNode> failovers,
|
||||||
}) =>
|
({String host, int port})? proxyInfo,
|
||||||
ElectrumX(
|
}) {
|
||||||
|
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.from(): no tor proxy info, read $proxyInfo",
|
||||||
|
level: LogLevel.Warning);
|
||||||
|
}
|
||||||
|
return ElectrumX(
|
||||||
host: node.address,
|
host: node.address,
|
||||||
port: node.port,
|
port: node.port,
|
||||||
useSSL: node.useSSL,
|
useSSL: node.useSSL,
|
||||||
prefs: prefs,
|
prefs: prefs,
|
||||||
failovers: failovers,
|
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 {
|
Future<bool> _allow() async {
|
||||||
if (_prefs.wifiOnly) {
|
if (_prefs.wifiOnly) {
|
||||||
|
@ -121,20 +149,52 @@ class ElectrumX {
|
||||||
throw WifiOnlyException();
|
throw WifiOnlyException();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentFailoverIndex == -1) {
|
if (Prefs.instance.useTor) {
|
||||||
_rpcClient ??= JsonRPC(
|
if (proxyInfo == null) {
|
||||||
host: host,
|
// TODO await tor / make sure Tor is running
|
||||||
port: port,
|
proxyInfo = (
|
||||||
useSSL: useSSL,
|
host: InternetAddress.loopbackIPv4.address,
|
||||||
connectionTimeout: connectionTimeoutForSpecialCaseJsonRPCClients,
|
port: TorService.sharedInstance.port
|
||||||
);
|
);
|
||||||
|
Logging.instance.log(
|
||||||
|
"ElectrumX.request(): 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 {
|
} else {
|
||||||
_rpcClient = JsonRPC(
|
if (currentFailoverIndex == -1) {
|
||||||
host: failovers![currentFailoverIndex].address,
|
_rpcClient ??= JsonRPC(
|
||||||
port: failovers![currentFailoverIndex].port,
|
host: host,
|
||||||
useSSL: failovers![currentFailoverIndex].useSSL,
|
port: port,
|
||||||
connectionTimeout: connectionTimeoutForSpecialCaseJsonRPCClients,
|
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 {
|
try {
|
||||||
|
@ -153,7 +213,8 @@ class ElectrumX {
|
||||||
);
|
);
|
||||||
|
|
||||||
if (response.exception != null) {
|
if (response.exception != null) {
|
||||||
throw response.exception!;
|
throw response.exception!
|
||||||
|
as Object; // TODO properly check that .exception is an Object
|
||||||
}
|
}
|
||||||
|
|
||||||
if (response.data is Map && response.data["error"] != null) {
|
if (response.data is Map && response.data["error"] != null) {
|
||||||
|
@ -221,20 +282,53 @@ class ElectrumX {
|
||||||
throw WifiOnlyException();
|
throw WifiOnlyException();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentFailoverIndex == -1) {
|
if (Prefs.instance.useTor) {
|
||||||
_rpcClient ??= JsonRPC(
|
// TODO await tor / make sure Tor is initialized
|
||||||
host: host,
|
if (proxyInfo == null) {
|
||||||
port: port,
|
proxyInfo = (
|
||||||
useSSL: useSSL,
|
host: InternetAddress.loopbackIPv4.address,
|
||||||
connectionTimeout: connectionTimeoutForSpecialCaseJsonRPCClients,
|
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 {
|
} else {
|
||||||
_rpcClient = JsonRPC(
|
if (currentFailoverIndex == -1) {
|
||||||
host: failovers![currentFailoverIndex].address,
|
_rpcClient ??= JsonRPC(
|
||||||
port: failovers![currentFailoverIndex].port,
|
host: host,
|
||||||
useSSL: failovers![currentFailoverIndex].useSSL,
|
port: port,
|
||||||
connectionTimeout: connectionTimeoutForSpecialCaseJsonRPCClients,
|
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 {
|
try {
|
||||||
|
@ -260,7 +354,8 @@ class ElectrumX {
|
||||||
(await _rpcClient!.request(request, requestTimeout));
|
(await _rpcClient!.request(request, requestTimeout));
|
||||||
|
|
||||||
if (jsonRpcResponse.exception != null) {
|
if (jsonRpcResponse.exception != null) {
|
||||||
throw jsonRpcResponse.exception!;
|
throw jsonRpcResponse.exception!
|
||||||
|
as Object; // TODO properly check that .exception is an Object
|
||||||
}
|
}
|
||||||
|
|
||||||
final response = jsonRpcResponse.data as List;
|
final response = jsonRpcResponse.data as List;
|
||||||
|
|
|
@ -14,7 +14,10 @@ import 'dart:io';
|
||||||
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:mutex/mutex.dart';
|
import 'package:mutex/mutex.dart';
|
||||||
|
import 'package:stackwallet/networking/socks_socket.dart';
|
||||||
|
import 'package:stackwallet/networking/tor_service.dart';
|
||||||
import 'package:stackwallet/utilities/logger.dart';
|
import 'package:stackwallet/utilities/logger.dart';
|
||||||
|
import 'package:stackwallet/utilities/prefs.dart';
|
||||||
|
|
||||||
// Json RPC class to handle connecting to electrumx servers
|
// Json RPC class to handle connecting to electrumx servers
|
||||||
class JsonRPC {
|
class JsonRPC {
|
||||||
|
@ -23,16 +26,20 @@ class JsonRPC {
|
||||||
required this.port,
|
required this.port,
|
||||||
this.useSSL = false,
|
this.useSSL = false,
|
||||||
this.connectionTimeout = const Duration(seconds: 60),
|
this.connectionTimeout = const Duration(seconds: 60),
|
||||||
|
required ({String host, int port})? proxyInfo,
|
||||||
});
|
});
|
||||||
final bool useSSL;
|
final bool useSSL;
|
||||||
final String host;
|
final String host;
|
||||||
final int port;
|
final int port;
|
||||||
final Duration connectionTimeout;
|
final Duration connectionTimeout;
|
||||||
|
({String host, int port})? proxyInfo;
|
||||||
|
|
||||||
final _requestMutex = Mutex();
|
final _requestMutex = Mutex();
|
||||||
final _JsonRPCRequestQueue _requestQueue = _JsonRPCRequestQueue();
|
final _JsonRPCRequestQueue _requestQueue = _JsonRPCRequestQueue();
|
||||||
Socket? _socket;
|
Socket? _socket;
|
||||||
|
SOCKSSocket? _socksSocket;
|
||||||
StreamSubscription<Uint8List>? _subscription;
|
StreamSubscription<Uint8List>? _subscription;
|
||||||
|
StreamSubscription<Uint8List>? get subscription => _subscription;
|
||||||
|
|
||||||
void _dataHandler(List<int> data) {
|
void _dataHandler(List<int> data) {
|
||||||
_requestQueue.nextIncompleteReq.then((req) {
|
_requestQueue.nextIncompleteReq.then((req) {
|
||||||
|
@ -75,7 +82,12 @@ class JsonRPC {
|
||||||
_requestQueue.nextIncompleteReq.then((req) {
|
_requestQueue.nextIncompleteReq.then((req) {
|
||||||
if (req != null) {
|
if (req != null) {
|
||||||
// \r\n required by electrumx server
|
// \r\n required by electrumx server
|
||||||
_socket!.write('${req.jsonRequest}\r\n');
|
if (_socket != null) {
|
||||||
|
_socket!.write('${req.jsonRequest}\r\n');
|
||||||
|
}
|
||||||
|
if (_socksSocket != null) {
|
||||||
|
_socksSocket!.socket.writeln('${req.jsonRequest}\r\n');
|
||||||
|
}
|
||||||
|
|
||||||
// TODO different timeout length?
|
// TODO different timeout length?
|
||||||
req.initiateTimeout(
|
req.initiateTimeout(
|
||||||
|
@ -92,12 +104,22 @@ class JsonRPC {
|
||||||
Duration requestTimeout,
|
Duration requestTimeout,
|
||||||
) async {
|
) async {
|
||||||
await _requestMutex.protect(() async {
|
await _requestMutex.protect(() async {
|
||||||
if (_socket == null) {
|
if (!Prefs.instance.useTor) {
|
||||||
Logging.instance.log(
|
if (_socket == null) {
|
||||||
"JsonRPC request: opening socket $host:$port",
|
Logging.instance.log(
|
||||||
level: LogLevel.Info,
|
"JsonRPC request: opening socket $host:$port",
|
||||||
);
|
level: LogLevel.Info,
|
||||||
await connect();
|
);
|
||||||
|
await connect();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (_socksSocket == null) {
|
||||||
|
Logging.instance.log(
|
||||||
|
"JsonRPC request: opening SOCKS socket to $host:$port",
|
||||||
|
level: LogLevel.Info,
|
||||||
|
);
|
||||||
|
await connect();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -137,6 +159,8 @@ class JsonRPC {
|
||||||
_subscription = null;
|
_subscription = null;
|
||||||
_socket?.destroy();
|
_socket?.destroy();
|
||||||
_socket = null;
|
_socket = null;
|
||||||
|
await _socksSocket?.close();
|
||||||
|
_socksSocket = null;
|
||||||
|
|
||||||
// clean up remaining queue
|
// clean up remaining queue
|
||||||
await _requestQueue.completeRemainingWithError(
|
await _requestQueue.completeRemainingWithError(
|
||||||
|
@ -146,33 +170,100 @@ class JsonRPC {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> connect() async {
|
Future<void> connect() async {
|
||||||
if (_socket != null) {
|
if (!Prefs.instance.useTor) {
|
||||||
throw Exception(
|
if (useSSL) {
|
||||||
"JsonRPC attempted to connect to an already existing socket!",
|
_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,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (useSSL) {
|
_subscription = _socket!.listen(
|
||||||
_socket = await SecureSocket.connect(
|
_dataHandler,
|
||||||
host,
|
onError: _errorHandler,
|
||||||
port,
|
onDone: _doneHandler,
|
||||||
timeout: connectionTimeout,
|
cancelOnError: true,
|
||||||
onBadCertificate: (_) => true,
|
);
|
||||||
); // TODO do not automatically trust bad certificates
|
|
||||||
} else {
|
} else {
|
||||||
_socket = await Socket.connect(
|
if (proxyInfo == null) {
|
||||||
host,
|
// TODO await tor / make sure it's running
|
||||||
port,
|
proxyInfo = (
|
||||||
timeout: connectionTimeout,
|
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...
|
||||||
|
|
||||||
_subscription = _socket!.listen(
|
// TODO implement ssl over tor
|
||||||
_dataHandler,
|
// if (useSSL) {
|
||||||
onError: _errorHandler,
|
// _socket = await SecureSocket.connect(
|
||||||
onDone: _doneHandler,
|
// host,
|
||||||
cancelOnError: true,
|
// port,
|
||||||
);
|
// timeout: connectionTimeout,
|
||||||
|
// onBadCertificate: (_) => true,
|
||||||
|
// ); // TODO do not automatically trust bad certificates
|
||||||
|
// final _client = SocksSocket.protected(_socket, type);
|
||||||
|
// } else {
|
||||||
|
// instantiate a socks socket at localhost and on the port selected by the tor service
|
||||||
|
_socksSocket = await SOCKSSocket.create(
|
||||||
|
proxyHost: InternetAddress.loopbackIPv4.address,
|
||||||
|
proxyPort: TorService.sharedInstance.port,
|
||||||
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
Logging.instance.log(
|
||||||
|
"JsonRPC.connect(): connecting to SOCKS socket at $proxyInfo...",
|
||||||
|
level: LogLevel.Info);
|
||||||
|
|
||||||
|
await _socksSocket?.connect();
|
||||||
|
|
||||||
|
Logging.instance.log(
|
||||||
|
"JsonRPC.connect(): connected to SOCKS socket at $proxyInfo...",
|
||||||
|
level: LogLevel.Info);
|
||||||
|
} catch (e) {
|
||||||
|
Logging.instance.log(
|
||||||
|
"JsonRPC.connect(): failed to connect to SOCKS socket at $proxyInfo, $e",
|
||||||
|
level: LogLevel.Error);
|
||||||
|
throw Exception(
|
||||||
|
"JsonRPC.connect(): failed to connect to SOCKS socket at $proxyInfo, $e");
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
Logging.instance.log(
|
||||||
|
"JsonRPC.connect(): connecting to $host:$port over SOCKS socket at $proxyInfo...",
|
||||||
|
level: LogLevel.Info);
|
||||||
|
|
||||||
|
await _socksSocket?.connectTo(host, port);
|
||||||
|
|
||||||
|
Logging.instance.log(
|
||||||
|
"JsonRPC.connect(): connected to $host:$port over SOCKS socket at $proxyInfo",
|
||||||
|
level: LogLevel.Info);
|
||||||
|
} catch (e) {
|
||||||
|
Logging.instance.log(
|
||||||
|
"JsonRPC.connect(): failed to connect to $host over tor proxy at $proxyInfo, $e",
|
||||||
|
level: LogLevel.Error);
|
||||||
|
throw Exception(
|
||||||
|
"JsonRPC.connect(): failed to connect to tor proxy, $e");
|
||||||
|
}
|
||||||
|
|
||||||
|
// _subscription = _socksSocket!.socket.listen(
|
||||||
|
// _dataHandler,
|
||||||
|
// onError: _errorHandler,
|
||||||
|
// onDone: _doneHandler,
|
||||||
|
// cancelOnError: true,
|
||||||
|
// ) as StreamSubscription<Uint8List>?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -295,3 +386,14 @@ class JsonRPCResponse {
|
||||||
|
|
||||||
JsonRPCResponse({this.data, this.exception});
|
JsonRPCResponse({this.data, this.exception});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isIpAddress(String host) {
|
||||||
|
try {
|
||||||
|
// if the string can be parsed into an InternetAddress, it's an IP.
|
||||||
|
InternetAddress(host);
|
||||||
|
return true;
|
||||||
|
} catch (e) {
|
||||||
|
// if parsing fails, it's not an IP.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
112
lib/networking/socks_socket.dart
Normal file
112
lib/networking/socks_socket.dart
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
import 'dart:async';
|
||||||
|
import 'dart:convert';
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
class SOCKSSocket {
|
||||||
|
final String proxyHost;
|
||||||
|
final int proxyPort;
|
||||||
|
|
||||||
|
late final Socket _socksSocket;
|
||||||
|
Socket get socket => _socksSocket;
|
||||||
|
|
||||||
|
final StreamController<List<int>> _responseController =
|
||||||
|
StreamController.broadcast();
|
||||||
|
|
||||||
|
// Private constructor
|
||||||
|
SOCKSSocket._(this.proxyHost, this.proxyPort);
|
||||||
|
|
||||||
|
static Future<SOCKSSocket> create(
|
||||||
|
{required String proxyHost, required int proxyPort}) async {
|
||||||
|
var instance = SOCKSSocket._(proxyHost, proxyPort);
|
||||||
|
await instance._init();
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
SOCKSSocket({required this.proxyHost, required this.proxyPort}) {
|
||||||
|
_init();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Initializes the SOCKS socket.
|
||||||
|
|
||||||
|
Future<void> _init() async {
|
||||||
|
_socksSocket = await Socket.connect(
|
||||||
|
proxyHost,
|
||||||
|
proxyPort,
|
||||||
|
);
|
||||||
|
|
||||||
|
_socksSocket.listen(
|
||||||
|
(data) {
|
||||||
|
_responseController.add(data);
|
||||||
|
},
|
||||||
|
onError: (dynamic e) {
|
||||||
|
if (e is Object) {
|
||||||
|
_responseController.addError(e);
|
||||||
|
}
|
||||||
|
_responseController.addError("$e");
|
||||||
|
// TODO make sure sending error as string is acceptable
|
||||||
|
},
|
||||||
|
onDone: () {
|
||||||
|
_responseController.close();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Connects to the SOCKS socket.
|
||||||
|
Future<void> connect() async {
|
||||||
|
// Greeting and method selection
|
||||||
|
_socksSocket.add([0x05, 0x01, 0x00]);
|
||||||
|
|
||||||
|
// Wait for server response
|
||||||
|
var response = await _responseController.stream.first;
|
||||||
|
if (response[1] != 0x00) {
|
||||||
|
throw Exception('Failed to connect to SOCKS5 socket.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Connects to the specified [domain] and [port] through the SOCKS socket.
|
||||||
|
Future<void> connectTo(String domain, int port) async {
|
||||||
|
var request = [
|
||||||
|
0x05,
|
||||||
|
0x01,
|
||||||
|
0x00,
|
||||||
|
0x03,
|
||||||
|
domain.length,
|
||||||
|
...domain.codeUnits,
|
||||||
|
(port >> 8) & 0xFF,
|
||||||
|
port & 0xFF
|
||||||
|
];
|
||||||
|
|
||||||
|
_socksSocket.add(request);
|
||||||
|
|
||||||
|
var response = await _responseController.stream.first;
|
||||||
|
if (response[1] != 0x00) {
|
||||||
|
throw Exception('Failed to connect to target through SOCKS5 proxy.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Converts [object] to a String by invoking [Object.toString] and
|
||||||
|
/// sends the encoding of the result to the socket.
|
||||||
|
void write(Object? object) {
|
||||||
|
if (object == null) return;
|
||||||
|
|
||||||
|
List<int> data = utf8.encode(object.toString());
|
||||||
|
_socksSocket.add(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sends the server.features command to the proxy server.
|
||||||
|
Future<void> sendServerFeaturesCommand() async {
|
||||||
|
const String command =
|
||||||
|
'{"jsonrpc":"2.0","id":"0","method":"server.features","params":[]}';
|
||||||
|
_socksSocket.writeln(command);
|
||||||
|
|
||||||
|
var responseData = await _responseController.stream.first;
|
||||||
|
print("responseData: ${utf8.decode(responseData)}");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Closes the connection to the Tor proxy.
|
||||||
|
Future<void> close() async {
|
||||||
|
await _socksSocket.flush(); // Ensure all data is sent before closing
|
||||||
|
await _responseController.close();
|
||||||
|
return await _socksSocket.close();
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,7 +12,8 @@ class TorService {
|
||||||
|
|
||||||
Future<void> start() async {
|
Future<void> start() async {
|
||||||
final dir = await StackFileSystem.applicationTorDirectory();
|
final dir = await StackFileSystem.applicationTorDirectory();
|
||||||
return await _tor.start(torDir: dir);
|
await _tor.start(torDir: dir);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> stop() async {
|
Future<void> stop() async {
|
||||||
|
|
|
@ -9,7 +9,7 @@ mkdir -p build
|
||||||
(cd ../../crypto_plugins/flutter_liblelantus/scripts/linux && ./build_all.sh ) &
|
(cd ../../crypto_plugins/flutter_liblelantus/scripts/linux && ./build_all.sh ) &
|
||||||
(cd ../../crypto_plugins/flutter_libepiccash/scripts/linux && ./build_all.sh ) &
|
(cd ../../crypto_plugins/flutter_libepiccash/scripts/linux && ./build_all.sh ) &
|
||||||
(cd ../../crypto_plugins/flutter_libmonero/scripts/linux && ./build_monero_all.sh && ./build_sharedfile.sh ) &
|
(cd ../../crypto_plugins/flutter_libmonero/scripts/linux && ./build_monero_all.sh && ./build_sharedfile.sh ) &
|
||||||
(cd ../../crypto_plugins/tor/scripts/linux && ./build_all.sh ) &
|
(cd ../../crypto_plugins/flutter_libtor/scripts/linux && ./build_all.sh ) &
|
||||||
|
|
||||||
wait
|
wait
|
||||||
echo "Done building"
|
echo "Done building"
|
||||||
|
|
|
@ -11,6 +11,7 @@ void main() {
|
||||||
port: DefaultNodes.bitcoin.port,
|
port: DefaultNodes.bitcoin.port,
|
||||||
useSSL: true,
|
useSSL: true,
|
||||||
connectionTimeout: const Duration(seconds: 40),
|
connectionTimeout: const Duration(seconds: 40),
|
||||||
|
proxyInfo: null, // TODO test for proxyInfo
|
||||||
);
|
);
|
||||||
|
|
||||||
const jsonRequestString =
|
const jsonRequestString =
|
||||||
|
@ -27,7 +28,8 @@ void main() {
|
||||||
final jsonRPC = JsonRPC(
|
final jsonRPC = JsonRPC(
|
||||||
host: "some.bad.address.thingdsfsdfsdaf",
|
host: "some.bad.address.thingdsfsdfsdaf",
|
||||||
port: 3000,
|
port: 3000,
|
||||||
connectionTimeout: Duration(seconds: 10),
|
connectionTimeout: const Duration(seconds: 10),
|
||||||
|
proxyInfo: null,
|
||||||
);
|
);
|
||||||
|
|
||||||
const jsonRequestString =
|
const jsonRequestString =
|
||||||
|
@ -47,6 +49,7 @@ void main() {
|
||||||
port: 3000,
|
port: 3000,
|
||||||
useSSL: false,
|
useSSL: false,
|
||||||
connectionTimeout: const Duration(seconds: 1),
|
connectionTimeout: const Duration(seconds: 1),
|
||||||
|
proxyInfo: null,
|
||||||
);
|
);
|
||||||
|
|
||||||
const jsonRequestString =
|
const jsonRequestString =
|
||||||
|
|
Loading…
Reference in a new issue