fix lints

This commit is contained in:
Fabian Freund
2025-09-15 07:23:31 +02:00
parent 8e9b2e839d
commit 8b7780642f
8 changed files with 152 additions and 100 deletions
@@ -258,8 +258,8 @@ class TorProxyScreen extends HookConsumerWidget {
left: 56, left: 56,
right: 24, right: 24,
), ),
title: Text('Direct Connection'), title: const Text('Direct Connection'),
subtitle: Text( subtitle: const Text(
'The best way to connect to Tor if Tor is not blocked', 'The best way to connect to Tor if Tor is not blocked',
), ),
), ),
@@ -285,8 +285,8 @@ class TorProxyScreen extends HookConsumerWidget {
left: 56, left: 56,
right: 24, right: 24,
), ),
title: Text('obfs4'), title: const Text('obfs4'),
subtitle: Text( subtitle: const Text(
'Suitable for light censorship and high bandwidth needs', 'Suitable for light censorship and high bandwidth needs',
), ),
), ),
@@ -312,8 +312,8 @@ class TorProxyScreen extends HookConsumerWidget {
left: 56, left: 56,
right: 24, right: 24,
), ),
title: Text('Snowflake'), title: const Text('Snowflake'),
subtitle: Text('Suitable for heavy censorship'), subtitle: const Text('Suitable for heavy censorship'),
), ),
), ),
CheckboxListTile.adaptive( CheckboxListTile.adaptive(
+1 -1
View File
@@ -74,7 +74,7 @@ dependencies:
text_scroll: ^0.2.1 text_scroll: ^0.2.1
timeago: ^3.7.1 timeago: ^3.7.1
tor: tor:
path: ./packages/tor path: ../packages/tor
universal_io: ^2.2.2 universal_io: ^2.2.2
uri_to_file: uri_to_file:
git: git:
@@ -31,14 +31,20 @@ class MoatApi {
MoatApi(this._client); MoatApi(this._client);
Future<SettingsResponse> settings([SettingsRequest? request]) async { Future<SettingsResponse> settings([SettingsRequest? request]) async {
final response = await _post('settings', request ?? SettingsRequest()); final response = await _post(
'settings',
request ?? const SettingsRequest(),
);
return SettingsResponse.fromJson( return SettingsResponse.fromJson(
jsonDecode(response.body) as Map<String, dynamic>, jsonDecode(response.body) as Map<String, dynamic>,
); );
} }
Future<SettingsResponse> defaults([SettingsRequest? request]) async { Future<SettingsResponse> defaults([SettingsRequest? request]) async {
final response = await _post('defaults', request ?? SettingsRequest()); final response = await _post(
'defaults',
request ?? const SettingsRequest(),
);
return SettingsResponse.fromJson( return SettingsResponse.fromJson(
jsonDecode(response.body) as Map<String, dynamic>, jsonDecode(response.body) as Map<String, dynamic>,
); );
@@ -21,7 +21,6 @@ import 'dart:io';
import 'package:http/io_client.dart'; import 'package:http/io_client.dart';
import 'package:pluggable_transports_proxy/pluggable_transports_proxy.dart'; import 'package:pluggable_transports_proxy/pluggable_transports_proxy.dart';
import 'package:pluggable_transports_proxy/src/data/models/moat.dart';
import 'package:pluggable_transports_proxy/src/data/service/moat_api.dart'; import 'package:pluggable_transports_proxy/src/data/service/moat_api.dart';
import 'package:socks5_proxy/socks_client.dart'; import 'package:socks5_proxy/socks_client.dart';
+34 -6
View File
@@ -1,11 +1,39 @@
# SPDX-FileCopyrightText: 2022 Foundation Devices Inc. # This file configures the static analysis results for your project (errors,
# SPDX-FileCopyrightText: 2024 Foundation Devices Inc. # warnings, and lints).
# #
# SPDX-License-Identifier: MIT # This enables the 'recommended' set of lints from `package:lints`.
# This set helps identify many issues that may lead to problems when running
# or consuming Dart code, and enforces writing Dart using a single, idiomatic
# style and format.
#
# If you want a smaller set of lints you can change this to specify
# 'package:lints/core.yaml'. These are just the most critical lints
# (the recommended set includes the core lints).
# The core lints are also what is used by pub.dev for scoring packages.
include: package:flutter_lints/flutter.yaml include: package:lint/strict.yaml
# Uncomment the following section to specify additional rules.
linter:
rules:
unawaited_futures: true
discarded_futures: true
collection_methods_unrelated_type: true
analyzer: analyzer:
errors:
overridden_fields: ignore
plugins:
- custom_lint
exclude: exclude:
- 'lib/generated*' - "**.drift"
- 'cargokit' - "**.g.dart"
- "**.swagger.dart"
- "**.freezed.dart"
- "**.chopper.dart"
- "lib/generated_bindings.dart"
# For more information about the core and recommended set of lints, see
# https://dart.dev/go/core-lints
# For additional information about configuring this file, see
# https://dart.dev/guides/language/analysis-options
+33 -32
View File
@@ -95,19 +95,28 @@ class SOCKSSocket {
/// Is SSL enabled? /// Is SSL enabled?
final bool sslEnabled; final bool sslEnabled;
/// Constructor.
SOCKSSocket({
required this.proxyHost,
required this.proxyPort,
required this.sslEnabled,
}) {
unawaited(_init());
}
/// Private constructor. /// Private constructor.
SOCKSSocket._(this.proxyHost, this.proxyPort, this.sslEnabled); SOCKSSocket._(this.proxyHost, this.proxyPort, this.sslEnabled);
/// Provides a stream of data as List<int>. /// Provides a stream of data as List`<int>`.
Stream<List<int>> get inputStream => sslEnabled Stream<List<int>> get inputStream => sslEnabled
? _secureResponseController.stream ? _secureResponseController.stream
: _responseController.stream; : _responseController.stream;
/// Provides a StreamSink compatible with List<int> for sending data. /// Provides a StreamSink compatible with List`<int>` for sending data.
StreamSink<List<int>> get outputStream { StreamSink<List<int>> get outputStream {
// Create a simple StreamSink wrapper for _socksSocket and // Create a simple StreamSink wrapper for _socksSocket and
// _secureSocksSocket that accepts List<int> and forwards it to write method. // _secureSocksSocket that accepts List<int> and forwards it to write method.
var sink = StreamController<List<int>>(); final sink = StreamController<List<int>>();
sink.stream.listen((data) { sink.stream.listen((data) {
if (sslEnabled) { if (sslEnabled) {
_secureSocksSocket.add(data); _secureSocksSocket.add(data);
@@ -129,12 +138,13 @@ class SOCKSSocket {
/// ///
/// Returns: /// Returns:
/// A Future that resolves to a SOCKSSocket instance. /// A Future that resolves to a SOCKSSocket instance.
static Future<SOCKSSocket> create( static Future<SOCKSSocket> create({
{required String proxyHost, required String proxyHost,
required int proxyPort, required int proxyPort,
bool sslEnabled = false}) async { bool sslEnabled = false,
}) async {
// Create a SOCKS socket instance. // Create a SOCKS socket instance.
var instance = SOCKSSocket._(proxyHost, proxyPort, sslEnabled); final instance = SOCKSSocket._(proxyHost, proxyPort, sslEnabled);
// Initialize the SOCKS socket. // Initialize the SOCKS socket.
await instance._init(); await instance._init();
@@ -143,14 +153,6 @@ class SOCKSSocket {
return instance; return instance;
} }
/// Constructor.
SOCKSSocket(
{required this.proxyHost,
required this.proxyPort,
required this.sslEnabled}) {
_init();
}
/// Initializes the SOCKS socket. /// Initializes the SOCKS socket.
/// ///
/// This method is a private method that is called by the constructor. /// This method is a private method that is called by the constructor.
@@ -159,10 +161,7 @@ class SOCKSSocket {
/// A Future that resolves to void. /// A Future that resolves to void.
Future<void> _init() async { Future<void> _init() async {
// Connect to the SOCKS proxy server. // Connect to the SOCKS proxy server.
_socksSocket = await Socket.connect( _socksSocket = await Socket.connect(proxyHost, proxyPort);
proxyHost,
proxyPort,
);
// Listen to the socket. // Listen to the socket.
_subscription = _socksSocket.listen( _subscription = _socksSocket.listen(
@@ -196,12 +195,13 @@ class SOCKSSocket {
_socksSocket.add([0x05, 0x01, 0x00]); _socksSocket.add([0x05, 0x01, 0x00]);
// Wait for server response. // Wait for server response.
var response = await _responseController.stream.first; final response = await _responseController.stream.first;
// Check if the connection was successful. // Check if the connection was successful.
if (response[1] != 0x00) { if (response[1] != 0x00) {
throw Exception( throw Exception(
'socks_socket.connect(): Failed to connect to SOCKS5 proxy.'); 'socks_socket.connect(): Failed to connect to SOCKS5 proxy.',
);
} }
return; return;
@@ -217,7 +217,7 @@ class SOCKSSocket {
/// A Future that resolves to void. /// A Future that resolves to void.
Future<void> connectTo(String domain, int port) async { Future<void> connectTo(String domain, int port) async {
// Connect command. // Connect command.
var request = [ final request = [
0x05, // SOCKS version. 0x05, // SOCKS version.
0x01, // Connect command. 0x01, // Connect command.
0x00, // Reserved. 0x00, // Reserved.
@@ -225,19 +225,20 @@ class SOCKSSocket {
domain.length, domain.length,
...domain.codeUnits, ...domain.codeUnits,
(port >> 8) & 0xFF, (port >> 8) & 0xFF,
port & 0xFF port & 0xFF,
]; ];
// Send the connect command to the SOCKS proxy server. // Send the connect command to the SOCKS proxy server.
_socksSocket.add(request); _socksSocket.add(request);
// Wait for server response. // Wait for server response.
var response = await _responseController.stream.first; final response = await _responseController.stream.first;
// Check if the connection was successful. // Check if the connection was successful.
if (response[1] != 0x00) { if (response[1] != 0x00) {
throw Exception( throw Exception(
'socks_socket.connectTo(): Failed to connect to target through SOCKS5 proxy.'); 'socks_socket.connectTo(): Failed to connect to target through SOCKS5 proxy.',
);
} }
// Upgrade to SSL if needed. // Upgrade to SSL if needed.
@@ -265,9 +266,9 @@ class SOCKSSocket {
_secureResponseController.addError("$e"); _secureResponseController.addError("$e");
// TODO make sure sending error as string is acceptable. // TODO make sure sending error as string is acceptable.
}, },
onDone: () { onDone: () async {
// Close the response controller when the socket is closed. // Close the response controller when the socket is closed.
_secureResponseController.close(); await _secureResponseController.close();
}, },
); );
} }
@@ -288,7 +289,7 @@ class SOCKSSocket {
if (object == null) return; if (object == null) return;
// Write the data to the socket. // Write the data to the socket.
List<int> data = utf8.encode(object.toString()); final List<int> data = utf8.encode(object.toString());
if (sslEnabled) { if (sslEnabled) {
_secureSocksSocket.add(data); _secureSocksSocket.add(data);
} else { } else {
@@ -310,9 +311,9 @@ class SOCKSSocket {
} finally { } finally {
await _subscription?.cancel(); await _subscription?.cancel();
await _socksSocket.close(); await _socksSocket.close();
_responseController.close(); await _responseController.close();
if (sslEnabled) { if (sslEnabled) {
_secureResponseController.close(); await _secureResponseController.close();
} }
} }
} }
@@ -355,7 +356,7 @@ class SOCKSSocket {
_socksSocket.writeln(command); _socksSocket.writeln(command);
// Wait for the response from the proxy server. // Wait for the response from the proxy server.
var responseData = await _responseController.stream.first; final responseData = await _responseController.stream.first;
if (kDebugMode) { if (kDebugMode) {
print("responseData: ${utf8.decode(responseData)}"); print("responseData: ${utf8.decode(responseData)}");
} }
@@ -364,7 +365,7 @@ class SOCKSSocket {
_secureSocksSocket.writeln(command); _secureSocksSocket.writeln(command);
// Wait for the response from the proxy server. // Wait for the response from the proxy server.
var responseData = await _secureResponseController.stream.first; final responseData = await _secureResponseController.stream.first;
if (kDebugMode) { if (kDebugMode) {
print("secure responseData: ${utf8.decode(responseData)}"); print("secure responseData: ${utf8.decode(responseData)}");
} }
+45 -27
View File
@@ -14,7 +14,7 @@ import 'package:flutter/foundation.dart';
import 'package:path_provider/path_provider.dart'; import 'package:path_provider/path_provider.dart';
import 'package:tor/generated_bindings.dart' as rust; import 'package:tor/generated_bindings.dart' as rust;
DynamicLibrary load(name) { DynamicLibrary load(String name) {
if (Platform.isAndroid || Platform.isLinux) { if (Platform.isAndroid || Platform.isLinux) {
return DynamicLibrary.open('lib$name.so'); return DynamicLibrary.open('lib$name.so');
} else if (Platform.isIOS || Platform.isMacOS) { } else if (Platform.isIOS || Platform.isMacOS) {
@@ -33,7 +33,9 @@ class CouldntBootstrapDirectory implements Exception {
} }
class NotSupportedPlatform implements Exception { class NotSupportedPlatform implements Exception {
NotSupportedPlatform(String s); String reason;
NotSupportedPlatform(this.reason);
} }
class ClientNotActive implements Exception {} class ClientNotActive implements Exception {}
@@ -97,8 +99,8 @@ class Tor {
/// Returns a Future that completes when the Tor service has started. /// Returns a Future that completes when the Tor service has started.
/// ///
/// Throws an exception if the Tor service fails to start. /// Throws an exception if the Tor service fails to start.
static Future<Tor> init({enabled = true}) async { static Future<Tor> init({bool enabled = true}) async {
var singleton = Tor._instance; final singleton = Tor._instance;
singleton._enabled = enabled; singleton._enabled = enabled;
return singleton; return singleton;
} }
@@ -117,15 +119,15 @@ class Tor {
} }
Future<int> _getRandomUnusedPort({List<int> excluded = const []}) async { Future<int> _getRandomUnusedPort({List<int> excluded = const []}) async {
var random = Random.secure(); final random = Random.secure();
int potentialPort = 0; int potentialPort = 0;
retry: retry:
while (potentialPort <= 0 || excluded.contains(potentialPort)) { while (potentialPort <= 0 || excluded.contains(potentialPort)) {
potentialPort = random.nextInt(65535); potentialPort = random.nextInt(65535);
try { try {
var socket = await ServerSocket.bind("0.0.0.0", potentialPort); final socket = await ServerSocket.bind("0.0.0.0", potentialPort);
socket.close(); await socket.close();
return potentialPort; return potentialPort;
} catch (_) { } catch (_) {
continue retry; continue retry;
@@ -142,24 +144,29 @@ class Tor {
/// Throws an exception if the Tor service fails to start. /// Throws an exception if the Tor service fails to start.
/// ///
/// Returns a Future that completes when the Tor service has started. /// Returns a Future that completes when the Tor service has started.
Future<void> start( Future<void> start({
{int? obfs4Port, int? snowflakePort, String? bridgeLines}) async { int? obfs4Port,
int? snowflakePort,
String? bridgeLines,
}) async {
broadcastState(); broadcastState();
// Set the state and cache directories. // Set the state and cache directories.
final Directory appSupportDir = await getApplicationSupportDirectory(); final Directory appSupportDir = await getApplicationSupportDirectory();
final stateDir = final stateDir = await Directory(
await Directory('${appSupportDir.path}/tor_state').create(); '${appSupportDir.path}/tor_state',
final cacheDir = ).create();
await Directory('${appSupportDir.path}/tor_cache').create(); final cacheDir = await Directory(
'${appSupportDir.path}/tor_cache',
).create();
// Generate a random port. // Generate a random port.
int newPort = await _getRandomUnusedPort(); final newPort = await _getRandomUnusedPort();
// Start the Tor service in an isolate. // Start the Tor service in an isolate.
final tor = await Isolate.run(() async { final tor = await Isolate.run(() {
// Load the Tor library. // Load the Tor library.
var lib = rust.NativeLibrary(load(libName)); final lib = rust.NativeLibrary(load(libName));
// Start the Tor service. // Start the Tor service.
final tor = lib.tor_start( final tor = lib.tor_start(
@@ -168,7 +175,8 @@ class Tor {
cacheDir.path.toNativeUtf8() as Pointer<Char>, cacheDir.path.toNativeUtf8() as Pointer<Char>,
obfs4Port ?? -1, obfs4Port ?? -1,
snowflakePort ?? -1, snowflakePort ?? -1,
bridgeLines?.toNativeUtf8() as Pointer<Char>? ?? nullptr); bridgeLines?.toNativeUtf8() as Pointer<Char>? ?? nullptr,
);
// Throw an exception if the Tor service fails to start. // Throw an exception if the Tor service fails to start.
if (tor.client == nullptr) { if (tor.client == nullptr) {
@@ -190,16 +198,21 @@ class Tor {
broadcastState(); broadcastState();
} }
Future<void> reconfigure( Future<void> reconfigure({
{int? obfs4Port, int? snowflakePort, String? bridgeLines}) async { int? obfs4Port,
int? snowflakePort,
String? bridgeLines,
}) async {
final lib = rust.NativeLibrary(_lib); final lib = rust.NativeLibrary(_lib);
// Set the state and cache directories. // Set the state and cache directories.
final Directory appSupportDir = await getApplicationSupportDirectory(); final Directory appSupportDir = await getApplicationSupportDirectory();
final stateDir = final stateDir = await Directory(
await Directory('${appSupportDir.path}/tor_state').create(); '${appSupportDir.path}/tor_state',
final cacheDir = ).create();
await Directory('${appSupportDir.path}/tor_cache').create(); final cacheDir = await Directory(
'${appSupportDir.path}/tor_cache',
).create();
final reconfigured = lib.tor_reconfigure( final reconfigured = lib.tor_reconfigure(
_clientPtr, _clientPtr,
@@ -207,7 +220,8 @@ class Tor {
cacheDir.path.toNativeUtf8() as Pointer<Char>, cacheDir.path.toNativeUtf8() as Pointer<Char>,
obfs4Port ?? -1, obfs4Port ?? -1,
snowflakePort ?? -1, snowflakePort ?? -1,
bridgeLines?.toNativeUtf8() as Pointer<Char>? ?? nullptr); bridgeLines?.toNativeUtf8() as Pointer<Char>? ?? nullptr,
);
if (!reconfigured) { if (!reconfigured) {
throwRustException(lib); throwRustException(lib);
@@ -284,11 +298,15 @@ class Tor {
// This way we avoid making clearnet req's while Tor is initialising // This way we avoid making clearnet req's while Tor is initialising
return true; return true;
})); }),
);
} }
static throwRustException(rust.NativeLibrary lib) { static void throwRustException(rust.NativeLibrary lib) {
String rustError = lib.tor_last_error_message().cast<Utf8>().toDartString(); final String rustError = lib
.tor_last_error_message()
.cast<Utf8>()
.toDartString();
throw _getRustException(rustError); throw _getRustException(rustError);
} }
+2 -2
View File
@@ -20,17 +20,17 @@ environment:
flutter: '>=3.3.0' flutter: '>=3.3.0'
dependencies: dependencies:
ffi: ^2.0.1
flutter: flutter:
sdk: flutter sdk: flutter
path_provider: ^2.1.4 path_provider: ^2.1.4
ffi: ^2.0.1
plugin_platform_interface: ^2.0.2 plugin_platform_interface: ^2.0.2
dev_dependencies: dev_dependencies:
ffigen: ^19.1.0 ffigen: ^19.1.0
flutter_test: flutter_test:
sdk: flutter sdk: flutter
flutter_lints: ^6.0.0 lint: ^2.8.0
ffigen: ffigen:
output: 'lib/generated_bindings.dart' output: 'lib/generated_bindings.dart'