From b1fa8ca505e7e488edb4c2859f0218d48b15dead Mon Sep 17 00:00:00 2001 From: sneurlax Date: Fri, 19 Apr 2024 14:24:37 -0500 Subject: [PATCH] 1.0 release: add example, finish README.md, add LICENSE & CHANGELOG.md and remove tests to remove dart pub publish warnings --- .idea/libraries/Dart_Packages.xml | 582 ++++++++++++++++++ .idea/libraries/Dart_SDK.xml | 29 + CHANGELOG.md | 8 +- LICENSE | 22 + README.md | 55 +- example/README.md | 10 +- example/lib/main.dart | 308 +++++---- example/linux/flutter/generated_plugins.cmake | 1 + .../Flutter/GeneratedPluginRegistrant.swift | 2 + example/pubspec.yaml | 17 +- example/test/widget_test.dart | 30 - .../windows/flutter/generated_plugins.cmake | 1 + pubspec.yaml | 6 +- test/socks_socket_test.dart | 16 - 14 files changed, 887 insertions(+), 200 deletions(-) create mode 100644 .idea/libraries/Dart_Packages.xml create mode 100644 .idea/libraries/Dart_SDK.xml create mode 100755 LICENSE delete mode 100644 example/test/widget_test.dart delete mode 100644 test/socks_socket_test.dart diff --git a/.idea/libraries/Dart_Packages.xml b/.idea/libraries/Dart_Packages.xml new file mode 100644 index 0000000..ee95adb --- /dev/null +++ b/.idea/libraries/Dart_Packages.xml @@ -0,0 +1,582 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Dart_SDK.xml b/.idea/libraries/Dart_SDK.xml new file mode 100644 index 0000000..fcbce96 --- /dev/null +++ b/.idea/libraries/Dart_SDK.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index effe43c..1e56d2a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ ## 1.0.0 -- Initial version. +- Dart & Flutter 3. +- Supports SOCKS version 5 protocol. +- SSL support. +- Supports ElectrumX and Fulcrum servers via socket(s). +- Async support for non-blocking network communication. +- Lightweight and minimal dependencies. +- Working example. diff --git a/LICENSE b/LICENSE new file mode 100755 index 0000000..4a63907 --- /dev/null +++ b/LICENSE @@ -0,0 +1,22 @@ + +The MIT License (MIT) + +Copyright (c) 2024 sneurlax + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index 8b55e73..9a5b748 100644 --- a/README.md +++ b/README.md @@ -1,39 +1,40 @@ - - -TODO: Put a short description of the package here that helps potential users -know whether this package might be useful for them. +SOCKS version 5 sockets for Dart and Flutter, *eg.* ElectrumX and/or Fulcrum over Tor via socket(s). ## Features -TODO: List what your package can do. Maybe include images, gifs, or videos. +- Dart & Flutter 3. +- Support for SOCKS version 5 protocol. +- Supports ElectrumX and Fulcrum servers via socket(s). +- Async support for non-blocking network communication. +- Lightweight and minimal dependencies. -## Getting started +## Getting Started -TODO: List prerequisites and provide or point to information on how to -start using the package. +See `socks_socket.dart` itself for properties and methods and the example for reference. -## Usage +```dart +import 'package:socks_socket/socks_socket.dart'; -TODO: Include short and useful examples for package users. Add longer examples -to `/example` folder. +// 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: true, // For SSL connections. +); -```dart -const like = 'sample'; -``` +// Connect to the socks instantiated above. +await socksSocket.connect(); -## Additional information +// Connect to bitcoin.stackwallet.com on port 50002 via socks socket. +// +// Note that this is an SSL example. +await socksSocket.connectTo('bitcoin.stackwallet.com', 50002); -TODO: Tell users more about the package: where to find more information, how to -contribute to the package, how to file issues, what response they can expect -from the package authors, and more. +// Send a server features command to the socket, see method for more specific usage example. +await socksSocket.sendServerFeaturesCommand(); +``` diff --git a/example/README.md b/example/README.md index 2b3fce4..5a75e39 100644 --- a/example/README.md +++ b/example/README.md @@ -1,9 +1,15 @@ -# example +# socks_socket_example -A new Flutter project. +Demonstrates how to use the socks_socket package. Uses cypherstack/tor for the tor service. + +## TODO + +- Show how/why the other packages available are not suitable for this use case. ## Getting Started +Run as in `flutter run`. + This project is a starting point for a Flutter application. A few resources to get you started if this is your first Flutter project: diff --git a/example/lib/main.dart b/example/lib/main.dart index 13e1bef..1a18b3e 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -1,32 +1,14 @@ -/// TODO make a good example. Here's one which is incomplete because it doesn't -/// include tor. After Foundation-Devices/tor publishes their tor package, then -/// we can use that to make a good example. -/// -/// See cypherstack/tor's example for a good example of using this SOCKSSocket -/// class/package. -/* - // 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.port, - // sslEnabled: true, // For SSL connections. - ); - - // Connect to the socks instantiated above. - await socksSocket.connect(); - - // Connect to bitcoincash.stackwallet.com on port 50001 via socks socket. - await socksSocket.connectTo('bitcoincash.stackwallet.com', 50001); - - // Send a server features command to the connected socket, see method for - // more specific usage example.. - await socksSocket.sendServerFeaturesCommand(); - await socksSocket.close(); - */ +// Example app deps, not necessarily needed for tor usage. +import 'dart:async'; +import 'dart:convert'; +import 'dart:io'; import 'package:flutter/material.dart'; -import 'package:socks_socket/socks_socket.dart'; +import 'package:path_provider/path_provider.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_ffi_plugin/tor_ffi_plugin.dart'; +import 'package:tor_ffi_plugin/socks_socket.dart'; // For socket connections void main() { runApp(const MyApp()); @@ -35,119 +17,209 @@ void main() { class MyApp extends StatelessWidget { const MyApp({super.key}); - // This widget is the root of your application. @override Widget build(BuildContext context) { - return MaterialApp( - title: 'Flutter Demo', - theme: ThemeData( - // This is the theme of your application. - // - // TRY THIS: Try running your application with "flutter run". You'll see - // the application has a blue toolbar. Then, without quitting the app, - // try changing the seedColor in the colorScheme below to Colors.green - // and then invoke "hot reload" (save your changes or press the "hot - // reload" button in a Flutter-supported IDE, or press "r" if you used - // the command line to start the app). - // - // Notice that the counter didn't reset back to zero; the application - // state is not lost during the reload. To reset the state, use hot - // restart instead. - // - // This works for code too, not just values: Most code changes can be - // tested with just a hot reload. - colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), - useMaterial3: true, - ), - home: const MyHomePage(title: 'Flutter Demo Home Page'), + return const MaterialApp( + home: Home(), ); } } -class MyHomePage extends StatefulWidget { - const MyHomePage({super.key, required this.title}); - - // This widget is the home page of your application. It is stateful, meaning - // that it has a State object (defined below) that contains fields that affect - // how it looks. - - // This class is the configuration for the state. It holds the values (in this - // case the title) provided by the parent (in this case the App widget) and - // used by the build method of the State. Fields in a Widget subclass are - // always marked "final". - - final String title; +class Home extends StatefulWidget { + const Home({super.key}); @override - State createState() => _MyHomePageState(); + State createState() => _MyAppState(); } -class _MyHomePageState extends State { - int _counter = 0; +class _MyAppState extends State { + // Flag to track if tor has started. + bool torIsRunning = false; + + // Set the default text for the host input field. + final hostController = TextEditingController(text: 'https://icanhazip.com/'); + // https://check.torproject.org is another good option. + + Future startTor() async { + // Start the Tor daemon. + await Tor.instance.start( + torDataDirPath: (await getApplicationSupportDirectory()).path, + ); - void _incrementCounter() { + // Toggle started flag. setState(() { - // This call to setState tells the Flutter framework that something has - // changed in this State, which causes it to rerun the build method below - // so that the display can reflect the updated values. If we changed - // _counter without calling setState(), then the build method would not be - // called again, and so nothing would appear to happen. - _counter++; + torIsRunning = Tor.instance.status == TorStatus.on; // Update flag }); + + print('Done awaiting; tor should be running'); + } + + @override + void dispose() { + // Clean up the controller when the widget is disposed. + hostController.dispose(); + super.dispose(); } @override Widget build(BuildContext context) { - // This method is rerun every time setState is called, for instance as done - // by the _incrementCounter method above. - // - // The Flutter framework has been optimized to make rerunning build methods - // fast, so that you can just rebuild anything that needs updating rather - // than having to individually change instances of widgets. + const spacerSmall = SizedBox(height: 10); return Scaffold( appBar: AppBar( - // TRY THIS: Try changing the color here to a specific color (to - // Colors.amber, perhaps?) and trigger a hot reload to see the AppBar - // change color while the other colors stay the same. - backgroundColor: Theme.of(context).colorScheme.inversePrimary, - // Here we take the value from the MyHomePage object that was created by - // the App.build method, and use it to set our appbar title. - title: Text(widget.title), + title: const Text('Tor example'), ), - body: Center( - // Center is a layout widget. It takes a single child and positions it - // in the middle of the parent. - child: Column( - // Column is also a layout widget. It takes a list of children and - // arranges them vertically. By default, it sizes itself to fit its - // children horizontally, and tries to be as tall as its parent. - // - // Column has various properties to control how it sizes itself and - // how it positions its children. Here we use mainAxisAlignment to - // center the children vertically; the main axis here is the vertical - // axis because Columns are vertical (the cross axis would be - // horizontal). - // - // TRY THIS: Invoke "debug painting" (choose the "Toggle Debug Paint" - // action in the IDE, or press "p" in the console), to see the - // wireframe for each widget. - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Text( - 'You have pushed the button this many times:', - ), - Text( - '$_counter', - style: Theme.of(context).textTheme.headlineMedium, - ), - ], + body: SingleChildScrollView( + child: Container( + padding: const EdgeInsets.all(10), + child: Column( + children: [ + TextButton( + onPressed: torIsRunning + ? null + : () async { + unawaited( + showDialog( + barrierDismissible: false, + context: context, + builder: (_) => const Dialog( + child: Padding( + padding: EdgeInsets.all(20.0), + child: Text("Starting tor..."), + ), + ), + ), + ); + + final time = DateTime.now(); + + print("NOW: $time"); + + await startTor(); + + print("Start tor took " + "${DateTime.now().difference(time).inSeconds} " + "seconds"); + + if (mounted) { + Navigator.of(context).pop(); + } + }, + child: const Text("Start tor"), + ), + Row( + children: [ + // Host input field. + Expanded( + child: TextField( + controller: hostController, + decoration: const InputDecoration( + border: OutlineInputBorder(), + hintText: 'Host to request', + ), + ), + ), + spacerSmall, + TextButton( + onPressed: torIsRunning + ? () async { + // `socks5_proxy` package example, use another socks5 + // connection of your choice. + + // Create HttpClient object + final client = HttpClient(); + + // Assign connection factory. + SocksTCPClient.assignToHttpClient(client, [ + ProxySettings(InternetAddress.loopbackIPv4, + Tor.instance.port, + password: + null), // TODO Need to get from tor config file. + ]); + + // GET request. + final request = await client + .getUrl(Uri.parse(hostController.text)); + final response = await request.close(); + + // Print response. + var responseString = + await utf8.decodeStream(response); + print(responseString); + // If host input left to default icanhazip.com, a Tor + // exit node IP should be printed to the console. + // + // https://check.torproject.org is also good for + // doublechecking torability. + + // Close client + client.close(); + } + : null, + child: const Text("Make proxied request"), + ), + ], + ), + spacerSmall, + TextButton( + onPressed: torIsRunning + ? () async { + // 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: true, // For SSL connections. + ); + + // Connect to the socks instantiated above. + await socksSocket.connect(); + + // Connect to bitcoin.stackwallet.com on port 50002 via socks socket. + // + // Note that this is an SSL example. + await socksSocket.connectTo( + 'bitcoin.stackwallet.com', 50002); + + // 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(); + } + : null, + child: const Text( + "Connect to bitcoin.stackwallet.com:50002 (SSL) via socks socket", + ), + ), + ], + ), ), ), - floatingActionButton: FloatingActionButton( - onPressed: _incrementCounter, - tooltip: 'Increment', - child: const Icon(Icons.add), - ), // This trailing comma makes auto-formatting nicer for build methods. ); } } diff --git a/example/linux/flutter/generated_plugins.cmake b/example/linux/flutter/generated_plugins.cmake index 2e1de87..52b84f4 100644 --- a/example/linux/flutter/generated_plugins.cmake +++ b/example/linux/flutter/generated_plugins.cmake @@ -6,6 +6,7 @@ list(APPEND FLUTTER_PLUGIN_LIST ) list(APPEND FLUTTER_FFI_PLUGIN_LIST + tor_ffi_plugin ) set(PLUGIN_BUNDLED_LIBRARIES) diff --git a/example/macos/Flutter/GeneratedPluginRegistrant.swift b/example/macos/Flutter/GeneratedPluginRegistrant.swift index cccf817..e777c67 100644 --- a/example/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/example/macos/Flutter/GeneratedPluginRegistrant.swift @@ -5,6 +5,8 @@ import FlutterMacOS import Foundation +import path_provider_foundation func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) } diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 75b9149..c5c90b7 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -1,5 +1,5 @@ -name: example -description: A new Flutter project. +name: socks_socket_example +description: Demonstrates how to use the socks_socket package. # The following line prevents the package from being accidentally published to # pub.dev using `flutter pub publish`. This is preferred for private packages. publish_to: 'none' # Remove this line if you wish to publish to pub.dev @@ -19,7 +19,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev version: 1.0.0+1 environment: - sdk: '>=3.0.6 <4.0.0' + sdk: '>=3.0.0 <4.0.0' # Dependencies specify other packages that your package needs in order to work. # To automatically upgrade your package dependencies to the latest versions @@ -31,10 +31,21 @@ dependencies: flutter: sdk: flutter + socks_socket: + path: .. + tor_ffi_plugin: + # When depending on this package from a real application you should use: + # tor_ffi_plugin: ^x.y.z + # See https://dart.dev/tools/pub/dependencies#version-constraints + # The example app is bundled with the plugin so we use a path dependency on + # the parent directory to use the current plugin's version. + git: https://github.com/cypherstack/tor # TODO: rework example app to use user-provided Tor proxy. # 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 + path_provider: ^2.1.1 dev_dependencies: flutter_test: diff --git a/example/test/widget_test.dart b/example/test/widget_test.dart deleted file mode 100644 index 092d222..0000000 --- a/example/test/widget_test.dart +++ /dev/null @@ -1,30 +0,0 @@ -// This is a basic Flutter widget test. -// -// To perform an interaction with a widget in your test, use the WidgetTester -// utility in the flutter_test package. For example, you can send tap and scroll -// gestures. You can also use WidgetTester to find child widgets in the widget -// tree, read text, and verify that the values of widget properties are correct. - -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; - -import 'package:example/main.dart'; - -void main() { - testWidgets('Counter increments smoke test', (WidgetTester tester) async { - // Build our app and trigger a frame. - await tester.pumpWidget(const MyApp()); - - // Verify that our counter starts at 0. - expect(find.text('0'), findsOneWidget); - expect(find.text('1'), findsNothing); - - // Tap the '+' icon and trigger a frame. - await tester.tap(find.byIcon(Icons.add)); - await tester.pump(); - - // Verify that our counter has incremented. - expect(find.text('0'), findsNothing); - expect(find.text('1'), findsOneWidget); - }); -} diff --git a/example/windows/flutter/generated_plugins.cmake b/example/windows/flutter/generated_plugins.cmake index b93c4c3..46a2ee5 100644 --- a/example/windows/flutter/generated_plugins.cmake +++ b/example/windows/flutter/generated_plugins.cmake @@ -6,6 +6,7 @@ list(APPEND FLUTTER_PLUGIN_LIST ) list(APPEND FLUTTER_FFI_PLUGIN_LIST + tor_ffi_plugin ) set(PLUGIN_BUNDLED_LIBRARIES) diff --git a/pubspec.yaml b/pubspec.yaml index 5c1d7c1..3acd02f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: socks_socket -description: SOCKS5 client implementation for Dart and Flutter. Now with SSL! -version: 0.1.0 -repository: https://github.com/cypherstack/socks_socket +description: SOCKS version 5 sockets for Dart and Flutter, *eg.* ElectrumX and/or Fulcrum over Tor via socket(s). +version: 1.0.0 +repository: https://github.com/sneurlax/socks_socket environment: sdk: ^3.0.6 diff --git a/test/socks_socket_test.dart b/test/socks_socket_test.dart deleted file mode 100644 index ce54ede..0000000 --- a/test/socks_socket_test.dart +++ /dev/null @@ -1,16 +0,0 @@ -import 'package:socks_socket/socks_socket.dart'; -import 'package:test/test.dart'; - -void main() { - group('A group of tests', () { - final awesome = Awesome(); - - setUp(() { - // Additional setup goes here. - }); - - test('First Test', () { - expect(awesome.isAwesome, isTrue); - }); - }); -}