mirror of
https://github.com/cypherstack/stack_wallet.git
synced 2024-12-23 11:59:30 +00:00
WIP SOCKSSocket
This commit is contained in:
parent
29341fc0b0
commit
1eeed74cf6
2 changed files with 100 additions and 70 deletions
|
@ -14,6 +14,7 @@ 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/socks5.dart';
|
||||||
import 'package:stackwallet/networking/tor_service.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';
|
||||||
|
@ -35,7 +36,8 @@ class JsonRPC {
|
||||||
|
|
||||||
final _requestMutex = Mutex();
|
final _requestMutex = Mutex();
|
||||||
final _JsonRPCRequestQueue _requestQueue = _JsonRPCRequestQueue();
|
final _JsonRPCRequestQueue _requestQueue = _JsonRPCRequestQueue();
|
||||||
Socket? _socket; // TODO make a SocksSocket extension/wrapper or similar
|
Socket? _socket;
|
||||||
|
SOCKSSocket? _socksSocket;
|
||||||
StreamSubscription<Uint8List>? _subscription;
|
StreamSubscription<Uint8List>? _subscription;
|
||||||
|
|
||||||
void _dataHandler(List<int> data) {
|
void _dataHandler(List<int> data) {
|
||||||
|
@ -79,7 +81,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
|
||||||
|
if (_socket != null) {
|
||||||
_socket!.write('${req.jsonRequest}\r\n');
|
_socket!.write('${req.jsonRequest}\r\n');
|
||||||
|
}
|
||||||
|
if (_socksSocket != null) {
|
||||||
|
_socksSocket!.write('${req.jsonRequest}\r\n');
|
||||||
|
}
|
||||||
|
|
||||||
// TODO different timeout length?
|
// TODO different timeout length?
|
||||||
req.initiateTimeout(
|
req.initiateTimeout(
|
||||||
|
@ -96,6 +103,7 @@ class JsonRPC {
|
||||||
Duration requestTimeout,
|
Duration requestTimeout,
|
||||||
) async {
|
) async {
|
||||||
await _requestMutex.protect(() async {
|
await _requestMutex.protect(() async {
|
||||||
|
if (!Prefs.instance.useTor) {
|
||||||
if (_socket == null) {
|
if (_socket == null) {
|
||||||
Logging.instance.log(
|
Logging.instance.log(
|
||||||
"JsonRPC request: opening socket $host:$port",
|
"JsonRPC request: opening socket $host:$port",
|
||||||
|
@ -103,6 +111,15 @@ class JsonRPC {
|
||||||
);
|
);
|
||||||
await connect();
|
await connect();
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
if (_socksSocket == null) {
|
||||||
|
Logging.instance.log(
|
||||||
|
"JsonRPC request: opening SOCKS socket $host:$port",
|
||||||
|
level: LogLevel.Info,
|
||||||
|
);
|
||||||
|
await connect();
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
final req = _JsonRPCRequest(
|
final req = _JsonRPCRequest(
|
||||||
|
@ -141,6 +158,9 @@ class JsonRPC {
|
||||||
_subscription = null;
|
_subscription = null;
|
||||||
_socket?.destroy();
|
_socket?.destroy();
|
||||||
_socket = null;
|
_socket = null;
|
||||||
|
unawaited(_socksSocket?.close(keepOpen: false));
|
||||||
|
// TODO check that it's ok to not await this
|
||||||
|
_socksSocket = null;
|
||||||
|
|
||||||
// clean up remaining queue
|
// clean up remaining queue
|
||||||
await _requestQueue.completeRemainingWithError(
|
await _requestQueue.completeRemainingWithError(
|
||||||
|
@ -150,68 +170,7 @@ class JsonRPC {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> connect() async {
|
Future<void> connect() async {
|
||||||
if (_socket != null) {
|
if (!Prefs.instance.useTor) {
|
||||||
throw Exception(
|
|
||||||
"JsonRPC attempted to connect to an already existing socket!",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
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,
|
|
||||||
// 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) {
|
if (useSSL) {
|
||||||
_socket = await SecureSocket.connect(
|
_socket = await SecureSocket.connect(
|
||||||
host,
|
host,
|
||||||
|
@ -226,6 +185,61 @@ class JsonRPC {
|
||||||
timeout: connectionTimeout,
|
timeout: connectionTimeout,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
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...
|
||||||
|
|
||||||
|
// 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 {
|
||||||
|
final sock = await RawSocket.connect(
|
||||||
|
InternetAddress.loopbackIPv4, proxyInfo!.port);
|
||||||
|
|
||||||
|
_socksSocket = SOCKSSocket(sock);
|
||||||
|
if (_socksSocket == null) {
|
||||||
|
Logging.instance.log(
|
||||||
|
"JsonRPC.connect(): failed to create SOCKS socket at $proxyInfo",
|
||||||
|
level: LogLevel.Error);
|
||||||
|
throw Exception(
|
||||||
|
"JsonRPC.connect(): failed to create SOCKS socket at $proxyInfo");
|
||||||
|
} else {
|
||||||
|
Logging.instance.log(
|
||||||
|
"JsonRPC.connect(): created SOCKS socket at $proxyInfo",
|
||||||
|
level: LogLevel.Info);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (!isIpAddress(host)) {
|
||||||
|
await _socksSocket!.connect("$host:$port");
|
||||||
|
} else {
|
||||||
|
await _socksSocket!.connectIp(InternetAddress(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 = _socket!.listen(
|
_subscription = _socket!.listen(
|
||||||
|
@ -356,3 +370,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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -377,4 +377,9 @@ class SOCKSSocket {
|
||||||
throw "Must be in RequestReady state, current state $_state";
|
throw "Must be in RequestReady state, current state $_state";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void write(Object? object) {
|
||||||
|
_sock.write(utf8.encode(object.toString()));
|
||||||
|
// TODO make sure the is correct; see _writeRequest above, may need to construct a SOCKSRequest from the data coming in
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue