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
@@ -31,14 +31,20 @@ class MoatApi {
MoatApi(this._client);
Future<SettingsResponse> settings([SettingsRequest? request]) async {
final response = await _post('settings', request ?? SettingsRequest());
final response = await _post(
'settings',
request ?? const SettingsRequest(),
);
return SettingsResponse.fromJson(
jsonDecode(response.body) as Map<String, dynamic>,
);
}
Future<SettingsResponse> defaults([SettingsRequest? request]) async {
final response = await _post('defaults', request ?? SettingsRequest());
final response = await _post(
'defaults',
request ?? const SettingsRequest(),
);
return SettingsResponse.fromJson(
jsonDecode(response.body) as Map<String, dynamic>,
);
@@ -21,7 +21,6 @@ import 'dart:io';
import 'package:http/io_client.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:socks5_proxy/socks_client.dart';
+34 -6
View File
@@ -1,11 +1,39 @@
# SPDX-FileCopyrightText: 2022 Foundation Devices Inc.
# SPDX-FileCopyrightText: 2024 Foundation Devices Inc.
# This file configures the static analysis results for your project (errors,
# 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:
errors:
overridden_fields: ignore
plugins:
- custom_lint
exclude:
- 'lib/generated*'
- 'cargokit'
- "**.drift"
- "**.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
+34 -33
View File
@@ -95,19 +95,28 @@ class SOCKSSocket {
/// Is SSL enabled?
final bool sslEnabled;
/// Constructor.
SOCKSSocket({
required this.proxyHost,
required this.proxyPort,
required this.sslEnabled,
}) {
unawaited(_init());
}
/// Private constructor.
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
? _secureResponseController.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 {
// Create a simple StreamSink wrapper for _socksSocket and
// _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) {
if (sslEnabled) {
_secureSocksSocket.add(data);
@@ -129,12 +138,13 @@ class SOCKSSocket {
///
/// Returns:
/// A Future that resolves to a SOCKSSocket instance.
static Future<SOCKSSocket> create(
{required String proxyHost,
required int proxyPort,
bool sslEnabled = false}) async {
static Future<SOCKSSocket> create({
required String proxyHost,
required int proxyPort,
bool sslEnabled = false,
}) async {
// Create a SOCKS socket instance.
var instance = SOCKSSocket._(proxyHost, proxyPort, sslEnabled);
final instance = SOCKSSocket._(proxyHost, proxyPort, sslEnabled);
// Initialize the SOCKS socket.
await instance._init();
@@ -143,14 +153,6 @@ class SOCKSSocket {
return instance;
}
/// Constructor.
SOCKSSocket(
{required this.proxyHost,
required this.proxyPort,
required this.sslEnabled}) {
_init();
}
/// Initializes the SOCKS socket.
///
/// This method is a private method that is called by the constructor.
@@ -159,10 +161,7 @@ class SOCKSSocket {
/// A Future that resolves to void.
Future<void> _init() async {
// Connect to the SOCKS proxy server.
_socksSocket = await Socket.connect(
proxyHost,
proxyPort,
);
_socksSocket = await Socket.connect(proxyHost, proxyPort);
// Listen to the socket.
_subscription = _socksSocket.listen(
@@ -196,12 +195,13 @@ class SOCKSSocket {
_socksSocket.add([0x05, 0x01, 0x00]);
// Wait for server response.
var response = await _responseController.stream.first;
final response = await _responseController.stream.first;
// Check if the connection was successful.
if (response[1] != 0x00) {
throw Exception(
'socks_socket.connect(): Failed to connect to SOCKS5 proxy.');
'socks_socket.connect(): Failed to connect to SOCKS5 proxy.',
);
}
return;
@@ -217,7 +217,7 @@ class SOCKSSocket {
/// A Future that resolves to void.
Future<void> connectTo(String domain, int port) async {
// Connect command.
var request = [
final request = [
0x05, // SOCKS version.
0x01, // Connect command.
0x00, // Reserved.
@@ -225,19 +225,20 @@ class SOCKSSocket {
domain.length,
...domain.codeUnits,
(port >> 8) & 0xFF,
port & 0xFF
port & 0xFF,
];
// Send the connect command to the SOCKS proxy server.
_socksSocket.add(request);
// Wait for server response.
var response = await _responseController.stream.first;
final response = await _responseController.stream.first;
// Check if the connection was successful.
if (response[1] != 0x00) {
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.
@@ -265,9 +266,9 @@ class SOCKSSocket {
_secureResponseController.addError("$e");
// TODO make sure sending error as string is acceptable.
},
onDone: () {
onDone: () async {
// Close the response controller when the socket is closed.
_secureResponseController.close();
await _secureResponseController.close();
},
);
}
@@ -288,7 +289,7 @@ class SOCKSSocket {
if (object == null) return;
// Write the data to the socket.
List<int> data = utf8.encode(object.toString());
final List<int> data = utf8.encode(object.toString());
if (sslEnabled) {
_secureSocksSocket.add(data);
} else {
@@ -310,9 +311,9 @@ class SOCKSSocket {
} finally {
await _subscription?.cancel();
await _socksSocket.close();
_responseController.close();
await _responseController.close();
if (sslEnabled) {
_secureResponseController.close();
await _secureResponseController.close();
}
}
}
@@ -355,7 +356,7 @@ class SOCKSSocket {
_socksSocket.writeln(command);
// Wait for the response from the proxy server.
var responseData = await _responseController.stream.first;
final responseData = await _responseController.stream.first;
if (kDebugMode) {
print("responseData: ${utf8.decode(responseData)}");
}
@@ -364,7 +365,7 @@ class SOCKSSocket {
_secureSocksSocket.writeln(command);
// Wait for the response from the proxy server.
var responseData = await _secureResponseController.stream.first;
final responseData = await _secureResponseController.stream.first;
if (kDebugMode) {
print("secure responseData: ${utf8.decode(responseData)}");
}
+67 -49
View File
@@ -14,7 +14,7 @@ import 'package:flutter/foundation.dart';
import 'package:path_provider/path_provider.dart';
import 'package:tor/generated_bindings.dart' as rust;
DynamicLibrary load(name) {
DynamicLibrary load(String name) {
if (Platform.isAndroid || Platform.isLinux) {
return DynamicLibrary.open('lib$name.so');
} else if (Platform.isIOS || Platform.isMacOS) {
@@ -33,7 +33,9 @@ class CouldntBootstrapDirectory implements Exception {
}
class NotSupportedPlatform implements Exception {
NotSupportedPlatform(String s);
String reason;
NotSupportedPlatform(this.reason);
}
class ClientNotActive implements Exception {}
@@ -97,8 +99,8 @@ class Tor {
/// Returns a Future that completes when the Tor service has started.
///
/// Throws an exception if the Tor service fails to start.
static Future<Tor> init({enabled = true}) async {
var singleton = Tor._instance;
static Future<Tor> init({bool enabled = true}) async {
final singleton = Tor._instance;
singleton._enabled = enabled;
return singleton;
}
@@ -117,15 +119,15 @@ class Tor {
}
Future<int> _getRandomUnusedPort({List<int> excluded = const []}) async {
var random = Random.secure();
final random = Random.secure();
int potentialPort = 0;
retry:
while (potentialPort <= 0 || excluded.contains(potentialPort)) {
potentialPort = random.nextInt(65535);
try {
var socket = await ServerSocket.bind("0.0.0.0", potentialPort);
socket.close();
final socket = await ServerSocket.bind("0.0.0.0", potentialPort);
await socket.close();
return potentialPort;
} catch (_) {
continue retry;
@@ -142,33 +144,39 @@ class Tor {
/// Throws an exception if the Tor service fails to start.
///
/// Returns a Future that completes when the Tor service has started.
Future<void> start(
{int? obfs4Port, int? snowflakePort, String? bridgeLines}) async {
Future<void> start({
int? obfs4Port,
int? snowflakePort,
String? bridgeLines,
}) async {
broadcastState();
// Set the state and cache directories.
final Directory appSupportDir = await getApplicationSupportDirectory();
final stateDir =
await Directory('${appSupportDir.path}/tor_state').create();
final cacheDir =
await Directory('${appSupportDir.path}/tor_cache').create();
final stateDir = await Directory(
'${appSupportDir.path}/tor_state',
).create();
final cacheDir = await Directory(
'${appSupportDir.path}/tor_cache',
).create();
// Generate a random port.
int newPort = await _getRandomUnusedPort();
final newPort = await _getRandomUnusedPort();
// Start the Tor service in an isolate.
final tor = await Isolate.run(() async {
final tor = await Isolate.run(() {
// Load the Tor library.
var lib = rust.NativeLibrary(load(libName));
final lib = rust.NativeLibrary(load(libName));
// Start the Tor service.
final tor = lib.tor_start(
newPort,
stateDir.path.toNativeUtf8() as Pointer<Char>,
cacheDir.path.toNativeUtf8() as Pointer<Char>,
obfs4Port ?? -1,
snowflakePort ?? -1,
bridgeLines?.toNativeUtf8() as Pointer<Char>? ?? nullptr);
newPort,
stateDir.path.toNativeUtf8() as Pointer<Char>,
cacheDir.path.toNativeUtf8() as Pointer<Char>,
obfs4Port ?? -1,
snowflakePort ?? -1,
bridgeLines?.toNativeUtf8() as Pointer<Char>? ?? nullptr,
);
// Throw an exception if the Tor service fails to start.
if (tor.client == nullptr) {
@@ -190,24 +198,30 @@ class Tor {
broadcastState();
}
Future<void> reconfigure(
{int? obfs4Port, int? snowflakePort, String? bridgeLines}) async {
Future<void> reconfigure({
int? obfs4Port,
int? snowflakePort,
String? bridgeLines,
}) async {
final lib = rust.NativeLibrary(_lib);
// Set the state and cache directories.
final Directory appSupportDir = await getApplicationSupportDirectory();
final stateDir =
await Directory('${appSupportDir.path}/tor_state').create();
final cacheDir =
await Directory('${appSupportDir.path}/tor_cache').create();
final stateDir = await Directory(
'${appSupportDir.path}/tor_state',
).create();
final cacheDir = await Directory(
'${appSupportDir.path}/tor_cache',
).create();
final reconfigured = lib.tor_reconfigure(
_clientPtr,
stateDir.path.toNativeUtf8() as Pointer<Char>,
cacheDir.path.toNativeUtf8() as Pointer<Char>,
obfs4Port ?? -1,
snowflakePort ?? -1,
bridgeLines?.toNativeUtf8() as Pointer<Char>? ?? nullptr);
_clientPtr,
stateDir.path.toNativeUtf8() as Pointer<Char>,
cacheDir.path.toNativeUtf8() as Pointer<Char>,
obfs4Port ?? -1,
snowflakePort ?? -1,
bridgeLines?.toNativeUtf8() as Pointer<Char>? ?? nullptr,
);
if (!reconfigured) {
throwRustException(lib);
@@ -270,25 +284,29 @@ class Tor {
Future<void> isReady() async {
return await Future.doWhile(
() => Future.delayed(const Duration(seconds: 1)).then((_) {
// We are waiting and making absolutely no request unless:
// Tor is disabled
if (!enabled) {
return false;
}
() => Future.delayed(const Duration(seconds: 1)).then((_) {
// We are waiting and making absolutely no request unless:
// Tor is disabled
if (!enabled) {
return false;
}
// ...or Tor circuit is established
if (bootstrapped) {
return false;
}
// ...or Tor circuit is established
if (bootstrapped) {
return false;
}
// This way we avoid making clearnet req's while Tor is initialising
return true;
}));
// This way we avoid making clearnet req's while Tor is initialising
return true;
}),
);
}
static throwRustException(rust.NativeLibrary lib) {
String rustError = lib.tor_last_error_message().cast<Utf8>().toDartString();
static void throwRustException(rust.NativeLibrary lib) {
final String rustError = lib
.tor_last_error_message()
.cast<Utf8>()
.toDartString();
throw _getRustException(rustError);
}
+2 -2
View File
@@ -20,17 +20,17 @@ environment:
flutter: '>=3.3.0'
dependencies:
ffi: ^2.0.1
flutter:
sdk: flutter
path_provider: ^2.1.4
ffi: ^2.0.1
plugin_platform_interface: ^2.0.2
dev_dependencies:
ffigen: ^19.1.0
flutter_test:
sdk: flutter
flutter_lints: ^6.0.0
lint: ^2.8.0
ffigen:
output: 'lib/generated_bindings.dart'