Skip to content

Commit

Permalink
add bitcoin onion example
Browse files Browse the repository at this point in the history
- sync lib/socks_socket.dart with cypherstack/tor
- pin socks5_proxy to working version
  • Loading branch information
sneurlax committed Sep 25, 2024
1 parent caf52f3 commit f9d1f89
Show file tree
Hide file tree
Showing 4 changed files with 161 additions and 31 deletions.
113 changes: 112 additions & 1 deletion example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ import 'dart:io';
import 'package:flutter/material.dart';
// Imports needed for tor usage:
import 'package:socks5_proxy/socks_client.dart'; // Just for example; can use any socks5 proxy package, pick your favorite.
import 'package:tor/tor.dart';
import 'package:tor/socks_socket.dart'; // For socket connections
import 'package:tor/tor.dart';

void main() {
runApp(const MyApp());
Expand Down Expand Up @@ -44,6 +44,28 @@ class _MyAppState extends State<Home> {
final hostController = TextEditingController(text: 'https://icanhazip.com/');
// https://check.torproject.org is another good option.

// Set the default text for the onion input field.
final onionController = TextEditingController(
text:
'https://cflarexljc3rw355ysrkrzwapozws6nre6xsy3n4yrj7taye3uiby3ad.onion');
// See https://blog.cloudflare.com/cloudflare-onion-service/ for more options:
// cflarexljc3rw355ysrkrzwapozws6nre6xsy3n4yrj7taye3uiby3ad.onion
// cflarenuttlfuyn7imozr4atzvfbiw3ezgbdjdldmdx7srterayaozid.onion
// cflares35lvdlczhy3r6qbza5jjxbcplzvdveabhf7bsp7y4nzmn67yd.onion
// cflareusni3s7vwhq2f7gc4opsik7aa4t2ajedhzr42ez6uajaywh3qd.onion
// cflareki4v3lh674hq55k3n7xd4ibkwx3pnw67rr3gkpsonjmxbktxyd.onion
// cflarejlah424meosswvaeqzb54rtdetr4xva6mq2bm2hfcx5isaglid.onion
// cflaresuje2rb7w2u3w43pn4luxdi6o7oatv6r2zrfb5xvsugj35d2qd.onion
// cflareer7qekzp3zeyqvcfktxfrmncse4ilc7trbf6bp6yzdabxuload.onion
// cflareub6dtu7nvs3kqmoigcjdwap2azrkx5zohb2yk7gqjkwoyotwqd.onion
// cflare2nge4h4yqr3574crrd7k66lil3torzbisz6uciyuzqc2h2ykyd.onion

final bitcoinOnionController = TextEditingController(
text:
'qly7g5n5t3f3h23xvbp44vs6vpmayurno4basuu5rcvrupli7y2jmgid.onion:50001');
// For more options, see https://bitnodes.io/nodes/addresses/?q=onion and
// https://sethforprivacy.com/about/

Future<void> startTor() async {
await Tor.init();

Expand Down Expand Up @@ -235,6 +257,95 @@ class _MyAppState extends State<Home> {
"Connect to bitcoin.stackwallet.com:50002 (SSL) via socks socket",
),
),
spacerSmall,
Row(
children: [
// Bitcoin onion input field.
Expanded(
child: TextField(
controller: bitcoinOnionController,
decoration: const InputDecoration(
border: OutlineInputBorder(),
hintText: 'Bitcoin onion address to test',
),
),
),
spacerSmall,
TextButton(
onPressed: torStarted
? () async {
// Validate the onion address.www
if (!onionController.text.contains(".onion")) {
print("Invalid onion address");
return;
} else if (!onionController.text.contains(":")) {
print("Invalid onion address (needs port)");
return;
}

String domain =
bitcoinOnionController.text.split(":").first;
int port = int.parse(
bitcoinOnionController.text.split(":").last);

// Instantiate a socks socket at localhost and on the port selected by the tor service.
var socksSocket = await SOCKSSocket.create(
proxyHost: InternetAddress.loopbackIPv4.address,
proxyPort: Tor.instance.port,
sslEnabled: !domain
.endsWith(".onion"), // For SSL connections.
);

// Connect to the socks instantiated above.
await socksSocket.connect();

// Connect to onion node via socks socket.
//
// Note that this is an SSL example.
await socksSocket.connectTo(domain, port);

// Send a server features command to the connected socket, see method for more specific usage example..
await socksSocket.sendServerFeaturesCommand();

// You should see a server response printed to the console.
//
// Example response:
// `flutter: secure responseData: {
// "id": "0",
// "jsonrpc": "2.0",
// "result": {
// "cashtokens": true,
// "dsproof": true,
// "genesis_hash": "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f",
// "hash_function": "sha256",
// "hosts": {
// "bitcoin.stackwallet.com": {
// "ssl_port": 50002,
// "tcp_port": 50001,
// "ws_port": 50003,
// "wss_port": 50004
// }
// },
// "protocol_max": "1.5",
// "protocol_min": "1.4",
// "pruning": null,
// "server_version": "Fulcrum 1.9.1"
// }
// }

// Close the socket.
await socksSocket.close();
}

// A mutex should be added to this example to prevent
// multiple connections from being made at once. TODO
: null,
child: const Text(
"Test Bitcoin onion node connection",
),
),
],
),
],
),
),
Expand Down
28 changes: 14 additions & 14 deletions example/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,10 @@ packages:
dependency: transitive
description:
name: collection
sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a
sha256: a1ace0a119f20aabc852d165077c036cd864315bd99b7eaa10a60100341941bf
url: "https://pub.dev"
source: hosted
version: "1.18.0"
version: "1.19.0"
cupertino_icons:
dependency: "direct main"
description:
Expand Down Expand Up @@ -87,18 +87,18 @@ packages:
dependency: transitive
description:
name: leak_tracker
sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05"
sha256: "7bb2830ebd849694d1ec25bf1f44582d6ac531a57a365a803a6034ff751d2d06"
url: "https://pub.dev"
source: hosted
version: "10.0.5"
version: "10.0.7"
leak_tracker_flutter_testing:
dependency: transitive
description:
name: leak_tracker_flutter_testing
sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806"
sha256: "9491a714cca3667b60b5c420da8217e6de0d1ba7a5ec322fab01758f6998f379"
url: "https://pub.dev"
source: hosted
version: "3.0.5"
version: "3.0.8"
leak_tracker_testing:
dependency: transitive
description:
Expand Down Expand Up @@ -215,15 +215,15 @@ packages:
dependency: transitive
description: flutter
source: sdk
version: "0.0.99"
version: "0.0.0"
socks5_proxy:
dependency: "direct main"
description:
name: socks5_proxy
sha256: "1d21b5606169654bbf4cfb904e8e6ed897e9f763358709f87310c757096d909a"
sha256: e0cba6917cd374de6f6cb0ce081e50e6efc24c61644b8e9f20c8bf8b91bb0b75
url: "https://pub.dev"
source: hosted
version: "1.0.4"
version: "1.0.3+dev.3"
source_span:
dependency: transitive
description:
Expand Down Expand Up @@ -252,10 +252,10 @@ packages:
dependency: transitive
description:
name: string_scanner
sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde"
sha256: "688af5ed3402a4bde5b3a6c15fd768dbf2621a614950b17f04626c431ab3c4c3"
url: "https://pub.dev"
source: hosted
version: "1.2.0"
version: "1.3.0"
term_glyph:
dependency: transitive
description:
Expand All @@ -268,17 +268,17 @@ packages:
dependency: transitive
description:
name: test_api
sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb"
sha256: "664d3a9a64782fcdeb83ce9c6b39e78fd2971d4e37827b9b06c3aa1edc5e760c"
url: "https://pub.dev"
source: hosted
version: "0.7.2"
version: "0.7.3"
tor:
dependency: "direct main"
description:
path: ".."
relative: true
source: path
version: "0.0.7"
version: "0.0.8"
vector_math:
dependency: transitive
description:
Expand Down
2 changes: 1 addition & 1 deletion example/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ dependencies:
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.2
socks5_proxy: ^1.0.3+dev.3
socks5_proxy: 1.0.3+dev.3

dev_dependencies:
flutter_test:
Expand Down
49 changes: 34 additions & 15 deletions lib/socks_socket.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
// SPDX-FileCopyrightText: 2024 Foundation Devices Inc.
//
// SPDX-License-Identifier: MIT

import 'dart:async';
import 'dart:convert';
import 'dart:io';
Expand All @@ -10,8 +6,7 @@ import 'package:flutter/foundation.dart';

/// A SOCKS5 socket.
///
/// This class is a wrapper around the Socket class that implements the
/// SOCKS5 protocol. It supports SSL and non-SSL connections.
/// A Dart 3 Socket wrapper that implements the SOCKS5 protocol. Now with SSL!
///
/// Properties:
/// - [proxyHost]: The host of the SOCKS5 proxy server.
Expand Down Expand Up @@ -99,6 +94,26 @@ class SOCKSSocket {
/// Private constructor.
SOCKSSocket._(this.proxyHost, this.proxyPort, this.sslEnabled);

/// Provides a stream of data as List<int>.
Stream<List<int>> get inputStream => sslEnabled
? _secureResponseController.stream
: _responseController.stream;

/// Provides a StreamSink compatible with List<int> for sending data.
StreamSink<List<int>> get outputStream {
// Create a simple StreamSink wrapper for _socksSocket and
// _secureSocksSocket that accepts List<int> and forwards it to write method.
var sink = StreamController<List<int>>();
sink.stream.listen((data) {
if (sslEnabled) {
_secureSocksSocket.add(data);
} else {
_socksSocket.add(data);
}
});
return sink.sink;
}

/// Creates a SOCKS5 socket to the specified [proxyHost] and [proxyPort].
///
/// This method is a factory constructor that returns a Future that resolves
Expand Down Expand Up @@ -163,7 +178,7 @@ class SOCKSSocket {
},
onDone: () {
// Close the response controller when the socket is closed.
_responseController.close();
// _responseController.close();
},
);
}
Expand Down Expand Up @@ -221,7 +236,7 @@ class SOCKSSocket {
'socks_socket.connectTo(): Failed to connect to target through SOCKS5 proxy.');
}

// Upgrade to SSL if needed
// Upgrade to SSL if needed.
if (sslEnabled) {
// Upgrade to SSL.
_secureSocksSocket = await SecureSocket.secure(
Expand Down Expand Up @@ -283,15 +298,19 @@ class SOCKSSocket {
/// A Future that resolves to void.
Future<void> close() async {
// Ensure all data is sent before closing.
//
// TODO test this.
if (sslEnabled) {
try {
if (sslEnabled) {
await _secureSocksSocket.flush();
}
await _socksSocket.flush();
await _secureResponseController.close();
} finally {
await _subscription?.cancel();
await _socksSocket.close();
_responseController.close();
if (sslEnabled) {
_secureResponseController.close();
}
}
await _socksSocket.flush();
await _responseController.close();
return await _socksSocket.close();
}

StreamSubscription<List<int>> listen(
Expand Down

0 comments on commit f9d1f89

Please sign in to comment.