From caef036422e55db09def05e5b97d46649883357e Mon Sep 17 00:00:00 2001 From: Fabian Freund Date: Fri, 29 Aug 2025 13:27:23 +0200 Subject: [PATCH] initial --- .../tor/domain/services/tor_proxy.dart | 7 +- .../features/tor/utils/tor_entrypoint.dart | 40 ++++- app/pubspec.yaml | 6 +- .../pluggable_transports_proxy/.gitignore | 34 +++++ packages/pluggable_transports_proxy/.metadata | 30 ++++ .../pluggable_transports_proxy/CHANGELOG.md | 3 + packages/pluggable_transports_proxy/LICENSE | 1 + packages/pluggable_transports_proxy/README.md | 15 ++ .../analysis_options.yaml | 35 +++++ .../android/.gitignore | 9 ++ .../android/build.gradle | 70 +++++++++ .../android/settings.gradle | 1 + .../android/src/main/AndroidManifest.xml | 3 + .../PluggableTransportsProxyPlugin.kt | 34 +++++ .../pluggable_transports_proxy/ProxyImpl.kt | 33 +++++ .../pigeons/Proxy.g.kt | 137 ++++++++++++++++++ .../lib/pluggable_transports_proxy.dart | 1 + .../lib/src/pigeons/proxy.g.dart | 116 +++++++++++++++ .../pigeons/proxy.dart | 21 +++ .../pluggable_transports_proxy/pubspec.yaml | 71 +++++++++ .../pigeons/Intent.g.kt | 2 +- .../lib/src/pigeons/intent.g.dart | 2 +- pubspec.yaml | 1 + 23 files changed, 665 insertions(+), 7 deletions(-) create mode 100644 packages/pluggable_transports_proxy/.gitignore create mode 100644 packages/pluggable_transports_proxy/.metadata create mode 100644 packages/pluggable_transports_proxy/CHANGELOG.md create mode 100644 packages/pluggable_transports_proxy/LICENSE create mode 100644 packages/pluggable_transports_proxy/README.md create mode 100644 packages/pluggable_transports_proxy/analysis_options.yaml create mode 100644 packages/pluggable_transports_proxy/android/.gitignore create mode 100644 packages/pluggable_transports_proxy/android/build.gradle create mode 100644 packages/pluggable_transports_proxy/android/settings.gradle create mode 100644 packages/pluggable_transports_proxy/android/src/main/AndroidManifest.xml create mode 100644 packages/pluggable_transports_proxy/android/src/main/kotlin/eu/weblibre/pluggable_transports_proxy/PluggableTransportsProxyPlugin.kt create mode 100644 packages/pluggable_transports_proxy/android/src/main/kotlin/eu/weblibre/pluggable_transports_proxy/ProxyImpl.kt create mode 100644 packages/pluggable_transports_proxy/android/src/main/kotlin/eu/weblibre/pluggable_transports_proxy/pigeons/Proxy.g.kt create mode 100644 packages/pluggable_transports_proxy/lib/pluggable_transports_proxy.dart create mode 100644 packages/pluggable_transports_proxy/lib/src/pigeons/proxy.g.dart create mode 100644 packages/pluggable_transports_proxy/pigeons/proxy.dart create mode 100644 packages/pluggable_transports_proxy/pubspec.yaml diff --git a/app/lib/features/tor/domain/services/tor_proxy.dart b/app/lib/features/tor/domain/services/tor_proxy.dart index c5c5aacc..1939b76c 100644 --- a/app/lib/features/tor/domain/services/tor_proxy.dart +++ b/app/lib/features/tor/domain/services/tor_proxy.dart @@ -21,6 +21,7 @@ import 'dart:async'; import 'dart:ui'; import 'package:flutter_background_service/flutter_background_service.dart'; +import 'package:pluggable_transports_proxy/pluggable_transports_proxy.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:rxdart/rxdart.dart'; import 'package:weblibre/core/logger.dart'; @@ -36,8 +37,12 @@ class _TorService { ValueStream get portStream => _portSubject.stream; - Future start() async { + Future start({ProxyType? proxyType, String? bridgeLines}) async { await service.startService(); + service.invoke('start', { + if (proxyType != null) 'proxyType': proxyType.name, + if (bridgeLines != null) 'bridgeLines': bridgeLines, + }); } Future requestSync() async { diff --git a/app/lib/features/tor/utils/tor_entrypoint.dart b/app/lib/features/tor/utils/tor_entrypoint.dart index c6213e4c..d22d282c 100644 --- a/app/lib/features/tor/utils/tor_entrypoint.dart +++ b/app/lib/features/tor/utils/tor_entrypoint.dart @@ -19,12 +19,15 @@ */ import 'dart:async'; +import 'package:collection/collection.dart'; import 'package:flutter_background_service/flutter_background_service.dart'; +import 'package:pluggable_transports_proxy/pluggable_transports_proxy.dart'; import 'package:tor/tor.dart'; @pragma('vm:entry-point') Future onStart(ServiceInstance service) async { Timer? timeout; + final startedProxyType = {}; await Tor.init(); @@ -37,6 +40,12 @@ Future onStart(ServiceInstance service) async { timeout = null; Tor.instance.stop(); + + for (final proxyType in startedProxyType) { + await IPtProxyController().stop(proxyType); + } + startedProxyType.clear(); + service.invoke('portUpdate', {'port': -1}); await portSub.cancel(); @@ -52,7 +61,36 @@ Future onStart(ServiceInstance service) async { }); } - await Tor.instance.start(); + Future startService(ProxyType? proxyType, String? bridgeLines) async { + if (proxyType != null) { + final port = await IPtProxyController().start(proxyType, ""); + startedProxyType.add(proxyType); + + switch (proxyType) { + case ProxyType.obfs4: + await Tor.instance.start(obfs4Port: port, bridgeLines: bridgeLines); + case ProxyType.meekLite: + throw UnimplementedError(); + case ProxyType.webtunnel: + throw UnimplementedError(); + case ProxyType.snowflake: + await Tor.instance.start( + snowflakePort: port, + bridgeLines: bridgeLines, + ); + } + } else { + await Tor.instance.start(); + } + } + + service.on("start").listen((event) async { + final proxyType = ProxyType.values.firstWhereOrNull( + (x) => x.name == event?['proxyType'], + ); + + await startService(proxyType, event?['bridgeLines'] as String?); + }); service.on("heartbeat").listen((event) { // logger.d('Received tor heartbeat'); diff --git a/app/pubspec.yaml b/app/pubspec.yaml index 15f79ec0..4146c778 100644 --- a/app/pubspec.yaml +++ b/app/pubspec.yaml @@ -51,6 +51,8 @@ dependencies: path: ^1.9.1 path_provider: ^2.1.5 permission_handler: ^12.0.1 + pluggable_transports_proxy: + path: ../packages/pluggable_transports_proxy pretty_qr_code: ^3.5.0 riverpod: ^2.6.1 riverpod_annotation: ^2.6.1 @@ -75,9 +77,7 @@ dependencies: text_scroll: ^0.2.0 timeago: ^3.7.1 tor: - git: - url: https://github.com/FaFre/tor.git - ref: update + path: /home/fafre/development/repos/tor universal_io: ^2.2.2 uri_to_file: git: diff --git a/packages/pluggable_transports_proxy/.gitignore b/packages/pluggable_transports_proxy/.gitignore new file mode 100644 index 00000000..60be4cf5 --- /dev/null +++ b/packages/pluggable_transports_proxy/.gitignore @@ -0,0 +1,34 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.build/ +.buildlog/ +.history +.svn/ +.swiftpm/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. +/pubspec.lock +**/doc/api/ +.dart_tool/ +.flutter-plugins-dependencies +/build/ +/coverage/ +.gradle/ diff --git a/packages/pluggable_transports_proxy/.metadata b/packages/pluggable_transports_proxy/.metadata new file mode 100644 index 00000000..d148851c --- /dev/null +++ b/packages/pluggable_transports_proxy/.metadata @@ -0,0 +1,30 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: "20f82749394e68bcfbbeee96bad384abaae09c13" + channel: "stable" + +project_type: plugin + +# Tracks metadata for the flutter migrate command +migration: + platforms: + - platform: root + create_revision: 20f82749394e68bcfbbeee96bad384abaae09c13 + base_revision: 20f82749394e68bcfbbeee96bad384abaae09c13 + - platform: android + create_revision: 20f82749394e68bcfbbeee96bad384abaae09c13 + base_revision: 20f82749394e68bcfbbeee96bad384abaae09c13 + + # User provided section + + # List of Local paths (relative to this file) that should be + # ignored by the migrate tool. + # + # Files that are not part of the templates will be ignored by default. + unmanaged_files: + - 'lib/main.dart' + - 'ios/Runner.xcodeproj/project.pbxproj' diff --git a/packages/pluggable_transports_proxy/CHANGELOG.md b/packages/pluggable_transports_proxy/CHANGELOG.md new file mode 100644 index 00000000..41cc7d81 --- /dev/null +++ b/packages/pluggable_transports_proxy/CHANGELOG.md @@ -0,0 +1,3 @@ +## 0.0.1 + +* TODO: Describe initial release. diff --git a/packages/pluggable_transports_proxy/LICENSE b/packages/pluggable_transports_proxy/LICENSE new file mode 100644 index 00000000..ba75c69f --- /dev/null +++ b/packages/pluggable_transports_proxy/LICENSE @@ -0,0 +1 @@ +TODO: Add your license here. diff --git a/packages/pluggable_transports_proxy/README.md b/packages/pluggable_transports_proxy/README.md new file mode 100644 index 00000000..cd181b79 --- /dev/null +++ b/packages/pluggable_transports_proxy/README.md @@ -0,0 +1,15 @@ +# pluggable_transports_proxy + +A new Flutter plugin project. + +## Getting Started + +This project is a starting point for a Flutter +[plug-in package](https://flutter.dev/to/develop-plugins), +a specialized package that includes platform-specific implementation code for +Android and/or iOS. + +For help getting started with Flutter development, view the +[online documentation](https://docs.flutter.dev), which offers tutorials, +samples, guidance on mobile development, and a full API reference. + diff --git a/packages/pluggable_transports_proxy/analysis_options.yaml b/packages/pluggable_transports_proxy/analysis_options.yaml new file mode 100644 index 00000000..9357549d --- /dev/null +++ b/packages/pluggable_transports_proxy/analysis_options.yaml @@ -0,0 +1,35 @@ +# This file configures the static analysis results for your project (errors, +# warnings, and lints). +# +# 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:lint/package.yaml +# Uncomment the following section to specify additional rules. + +linter: + rules: + unawaited_futures: true + discarded_futures: true + collection_methods_unrelated_type: true + +analyzer: + plugins: + - custom_lint + exclude: + - "**.g.dart" + - "**.swagger.dart" + - "**.freezed.dart" + - "**.chopper.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 diff --git a/packages/pluggable_transports_proxy/android/.gitignore b/packages/pluggable_transports_proxy/android/.gitignore new file mode 100644 index 00000000..161bdcda --- /dev/null +++ b/packages/pluggable_transports_proxy/android/.gitignore @@ -0,0 +1,9 @@ +*.iml +.gradle +/local.properties +/.idea/workspace.xml +/.idea/libraries +.DS_Store +/build +/captures +.cxx diff --git a/packages/pluggable_transports_proxy/android/build.gradle b/packages/pluggable_transports_proxy/android/build.gradle new file mode 100644 index 00000000..98935c2f --- /dev/null +++ b/packages/pluggable_transports_proxy/android/build.gradle @@ -0,0 +1,70 @@ +group = "eu.weblibre.pluggable_transports_proxy" +version = "1.0-SNAPSHOT" + +buildscript { + ext.kotlin_version = "2.1.0" + repositories { + google() + mavenCentral() + } + + dependencies { + classpath("com.android.tools.build:gradle:8.9.1") + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version") + } +} + +allprojects { + repositories { + google() + mavenCentral() + } +} + +apply plugin: "com.android.library" +apply plugin: "kotlin-android" + +android { + namespace = "eu.weblibre.pluggable_transports_proxy" + + compileSdk = 36 + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 + } + + kotlinOptions { + jvmTarget = JavaVersion.VERSION_11 + } + + sourceSets { + main.java.srcDirs += "src/main/kotlin" + test.java.srcDirs += "src/test/kotlin" + } + + defaultConfig { + minSdk = 24 + } + + dependencies { + testImplementation("org.jetbrains.kotlin:kotlin-test") + testImplementation("org.mockito:mockito-core:5.0.0") + } + + testOptions { + unitTests.all { + useJUnitPlatform() + + testLogging { + events "passed", "skipped", "failed", "standardOut", "standardError" + outputs.upToDateWhen {false} + showStandardStreams = true + } + } + } +} + +dependencies { + implementation 'com.netzarchitekten:IPtProxy:4.2.2' +} diff --git a/packages/pluggable_transports_proxy/android/settings.gradle b/packages/pluggable_transports_proxy/android/settings.gradle new file mode 100644 index 00000000..d4df24c1 --- /dev/null +++ b/packages/pluggable_transports_proxy/android/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'pluggable_transports_proxy' diff --git a/packages/pluggable_transports_proxy/android/src/main/AndroidManifest.xml b/packages/pluggable_transports_proxy/android/src/main/AndroidManifest.xml new file mode 100644 index 00000000..3f785482 --- /dev/null +++ b/packages/pluggable_transports_proxy/android/src/main/AndroidManifest.xml @@ -0,0 +1,3 @@ + + diff --git a/packages/pluggable_transports_proxy/android/src/main/kotlin/eu/weblibre/pluggable_transports_proxy/PluggableTransportsProxyPlugin.kt b/packages/pluggable_transports_proxy/android/src/main/kotlin/eu/weblibre/pluggable_transports_proxy/PluggableTransportsProxyPlugin.kt new file mode 100644 index 00000000..25264cea --- /dev/null +++ b/packages/pluggable_transports_proxy/android/src/main/kotlin/eu/weblibre/pluggable_transports_proxy/PluggableTransportsProxyPlugin.kt @@ -0,0 +1,34 @@ +package eu.weblibre.pluggable_transports_proxy + +import IPtProxy.Controller +import eu.weblibre.pluggable_transports_proxy.pigeons.IPtProxyController +import io.flutter.embedding.engine.plugins.FlutterPlugin +import io.flutter.plugin.common.MethodChannel +import java.io.File + +/** PluggableTransportsProxyPlugin */ +class PluggableTransportsProxyPlugin : FlutterPlugin { + private lateinit var channel: MethodChannel + + companion object { + @Volatile + private var INSTANCE: Controller? = null + + fun getController(ptDirPath: String): Controller { + return INSTANCE ?: synchronized(this) { + INSTANCE ?: Controller(ptDirPath, true, false, "INFO", null).also { INSTANCE = it } + } + } + } + + override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { + val ptDir = File(flutterPluginBinding.applicationContext.cacheDir, "pt_state") + val ptc = getController(ptDir.path) + + IPtProxyController.setUp(flutterPluginBinding.binaryMessenger, ProxyImpl(controller = ptc)) + } + + override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) { + // Controller instance remains alive for reuse + } +} diff --git a/packages/pluggable_transports_proxy/android/src/main/kotlin/eu/weblibre/pluggable_transports_proxy/ProxyImpl.kt b/packages/pluggable_transports_proxy/android/src/main/kotlin/eu/weblibre/pluggable_transports_proxy/ProxyImpl.kt new file mode 100644 index 00000000..3f43f65b --- /dev/null +++ b/packages/pluggable_transports_proxy/android/src/main/kotlin/eu/weblibre/pluggable_transports_proxy/ProxyImpl.kt @@ -0,0 +1,33 @@ +package eu.weblibre.pluggable_transports_proxy + +import eu.weblibre.pluggable_transports_proxy.pigeons.IPtProxyController +import eu.weblibre.pluggable_transports_proxy.pigeons.ProxyType + +class ProxyImpl(val controller: IPtProxy.Controller) : IPtProxyController { + override fun start( + proxyType: ProxyType, + proxy: String + ): Long { + val type = when (proxyType) { + ProxyType.OBFS4 -> IPtProxy.IPtProxy.Obfs4 + ProxyType.MEEK_LITE -> IPtProxy.IPtProxy.MeekLite + ProxyType.WEBTUNNEL -> IPtProxy.IPtProxy.Webtunnel + ProxyType.SNOWFLAKE -> IPtProxy.IPtProxy.Snowflake + }; + + controller.start(type, proxy) + + return controller.port(type) + } + + override fun stop(proxyType: ProxyType) { + val type = when (proxyType) { + ProxyType.OBFS4 -> IPtProxy.IPtProxy.Obfs4 + ProxyType.MEEK_LITE -> IPtProxy.IPtProxy.MeekLite + ProxyType.WEBTUNNEL -> IPtProxy.IPtProxy.Webtunnel + ProxyType.SNOWFLAKE -> IPtProxy.IPtProxy.Snowflake + }; + + controller.stop(type) + } +} \ No newline at end of file diff --git a/packages/pluggable_transports_proxy/android/src/main/kotlin/eu/weblibre/pluggable_transports_proxy/pigeons/Proxy.g.kt b/packages/pluggable_transports_proxy/android/src/main/kotlin/eu/weblibre/pluggable_transports_proxy/pigeons/Proxy.g.kt new file mode 100644 index 00000000..e40074f5 --- /dev/null +++ b/packages/pluggable_transports_proxy/android/src/main/kotlin/eu/weblibre/pluggable_transports_proxy/pigeons/Proxy.g.kt @@ -0,0 +1,137 @@ +// Autogenerated from Pigeon (v26.0.1), do not edit directly. +// See also: https://pub.dev/packages/pigeon +@file:Suppress("UNCHECKED_CAST", "ArrayInDataClass") + +package eu.weblibre.pluggable_transports_proxy.pigeons + +import android.util.Log +import io.flutter.plugin.common.BasicMessageChannel +import io.flutter.plugin.common.BinaryMessenger +import io.flutter.plugin.common.EventChannel +import io.flutter.plugin.common.MessageCodec +import io.flutter.plugin.common.StandardMethodCodec +import io.flutter.plugin.common.StandardMessageCodec +import java.io.ByteArrayOutputStream +import java.nio.ByteBuffer +private object ProxyPigeonUtils { + + fun wrapResult(result: Any?): List { + return listOf(result) + } + + fun wrapError(exception: Throwable): List { + return if (exception is FlutterError) { + listOf( + exception.code, + exception.message, + exception.details + ) + } else { + listOf( + exception.javaClass.simpleName, + exception.toString(), + "Cause: " + exception.cause + ", Stacktrace: " + Log.getStackTraceString(exception) + ) + } + } +} + +/** + * Error class for passing custom error details to Flutter via a thrown PlatformException. + * @property code The error code. + * @property message The error message. + * @property details The error details. Must be a datatype supported by the api codec. + */ +class FlutterError ( + val code: String, + override val message: String? = null, + val details: Any? = null +) : Throwable() + +enum class ProxyType(val raw: Int) { + OBFS4(0), + MEEK_LITE(1), + WEBTUNNEL(2), + SNOWFLAKE(3); + + companion object { + fun ofRaw(raw: Int): ProxyType? { + return values().firstOrNull { it.raw == raw } + } + } +} +private open class ProxyPigeonCodec : StandardMessageCodec() { + override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? { + return when (type) { + 129.toByte() -> { + return (readValue(buffer) as Long?)?.let { + ProxyType.ofRaw(it.toInt()) + } + } + else -> super.readValueOfType(type, buffer) + } + } + override fun writeValue(stream: ByteArrayOutputStream, value: Any?) { + when (value) { + is ProxyType -> { + stream.write(129) + writeValue(stream, value.raw) + } + else -> super.writeValue(stream, value) + } + } +} + +/** Generated interface from Pigeon that represents a handler of messages from Flutter. */ +interface IPtProxyController { + fun start(proxyType: ProxyType, proxy: String): Long + fun stop(proxyType: ProxyType) + + companion object { + /** The codec used by IPtProxyController. */ + val codec: MessageCodec by lazy { + ProxyPigeonCodec() + } + /** Sets up an instance of `IPtProxyController` to handle messages through the `binaryMessenger`. */ + @JvmOverloads + fun setUp(binaryMessenger: BinaryMessenger, api: IPtProxyController?, messageChannelSuffix: String = "") { + val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else "" + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.pluggable_transports_proxy.IPtProxyController.start$separatedMessageChannelSuffix", codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val proxyTypeArg = args[0] as ProxyType + val proxyArg = args[1] as String + val wrapped: List = try { + listOf(api.start(proxyTypeArg, proxyArg)) + } catch (exception: Throwable) { + ProxyPigeonUtils.wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.pluggable_transports_proxy.IPtProxyController.stop$separatedMessageChannelSuffix", codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val proxyTypeArg = args[0] as ProxyType + val wrapped: List = try { + api.stop(proxyTypeArg) + listOf(null) + } catch (exception: Throwable) { + ProxyPigeonUtils.wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + } + } +} diff --git a/packages/pluggable_transports_proxy/lib/pluggable_transports_proxy.dart b/packages/pluggable_transports_proxy/lib/pluggable_transports_proxy.dart new file mode 100644 index 00000000..ad52e0a0 --- /dev/null +++ b/packages/pluggable_transports_proxy/lib/pluggable_transports_proxy.dart @@ -0,0 +1 @@ +export 'src/pigeons/proxy.g.dart' show IPtProxyController, ProxyType; diff --git a/packages/pluggable_transports_proxy/lib/src/pigeons/proxy.g.dart b/packages/pluggable_transports_proxy/lib/src/pigeons/proxy.g.dart new file mode 100644 index 00000000..de47c678 --- /dev/null +++ b/packages/pluggable_transports_proxy/lib/src/pigeons/proxy.g.dart @@ -0,0 +1,116 @@ +// Autogenerated from Pigeon (v26.0.1), do not edit directly. +// See also: https://pub.dev/packages/pigeon +// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import, no_leading_underscores_for_local_identifiers + +import 'dart:async'; +import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List; + +import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer; +import 'package:flutter/services.dart'; + +PlatformException _createConnectionError(String channelName) { + return PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel: "$channelName".', + ); +} + +enum ProxyType { + obfs4, + meekLite, + webtunnel, + snowflake, +} + + +class _PigeonCodec extends StandardMessageCodec { + const _PigeonCodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is int) { + buffer.putUint8(4); + buffer.putInt64(value); + } else if (value is ProxyType) { + buffer.putUint8(129); + writeValue(buffer, value.index); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + case 129: + final int? value = readValue(buffer) as int?; + return value == null ? null : ProxyType.values[value]; + default: + return super.readValueOfType(type, buffer); + } + } +} + +class IPtProxyController { + /// Constructor for [IPtProxyController]. The [binaryMessenger] named argument is + /// available for dependency injection. If it is left null, the default + /// BinaryMessenger will be used which routes to the host platform. + IPtProxyController({BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''}) + : pigeonVar_binaryMessenger = binaryMessenger, + pigeonVar_messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; + final BinaryMessenger? pigeonVar_binaryMessenger; + + static const MessageCodec pigeonChannelCodec = _PigeonCodec(); + + final String pigeonVar_messageChannelSuffix; + + Future start(ProxyType proxyType, String proxy) async { + final String pigeonVar_channelName = 'dev.flutter.pigeon.pluggable_transports_proxy.IPtProxyController.start$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = pigeonVar_channel.send([proxyType, proxy]); + final List? pigeonVar_replyList = + await pigeonVar_sendFuture as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else if (pigeonVar_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (pigeonVar_replyList[0] as int?)!; + } + } + + Future stop(ProxyType proxyType) async { + final String pigeonVar_channelName = 'dev.flutter.pigeon.pluggable_transports_proxy.IPtProxyController.stop$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = pigeonVar_channel.send([proxyType]); + final List? pigeonVar_replyList = + await pigeonVar_sendFuture as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else { + return; + } + } +} diff --git a/packages/pluggable_transports_proxy/pigeons/proxy.dart b/packages/pluggable_transports_proxy/pigeons/proxy.dart new file mode 100644 index 00000000..dbd1d1ed --- /dev/null +++ b/packages/pluggable_transports_proxy/pigeons/proxy.dart @@ -0,0 +1,21 @@ +import 'package:pigeon/pigeon.dart'; + +enum ProxyType { obfs4, meekLite, webtunnel, snowflake } + +@ConfigurePigeon( + PigeonOptions( + dartOut: 'lib/src/pigeons/proxy.g.dart', + dartOptions: DartOptions(), + kotlinOut: + 'android/src/main/kotlin/eu/weblibre/pluggable_transports_proxy/pigeons/Proxy.g.kt', + kotlinOptions: KotlinOptions( + package: 'eu.weblibre.pluggable_transports_proxy.pigeons', + ), + dartPackageName: 'pluggable_transports_proxy', + ), +) +@HostApi() +abstract class IPtProxyController { + int start(ProxyType proxyType, String proxy); + void stop(ProxyType proxyType); +} diff --git a/packages/pluggable_transports_proxy/pubspec.yaml b/packages/pluggable_transports_proxy/pubspec.yaml new file mode 100644 index 00000000..482cd436 --- /dev/null +++ b/packages/pluggable_transports_proxy/pubspec.yaml @@ -0,0 +1,71 @@ +name: pluggable_transports_proxy +description: "A new Flutter plugin project." +version: 0.0.1 +publish_to: 'none' +resolution: workspace + +environment: + sdk: ^3.9.0 + flutter: '>=3.3.0' + +dependencies: + flutter: + sdk: flutter + +dev_dependencies: + flutter_test: + sdk: flutter + lint: ^2.8.0 + pigeon: ^26.0.1 + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter packages. +flutter: + # This section identifies this Flutter project as a plugin project. + # The 'pluginClass' specifies the class (in Java, Kotlin, Swift, Objective-C, etc.) + # which should be registered in the plugin registry. This is required for + # using method channels. + # The Android 'package' specifies package in which the registered class is. + # This is required for using method channels on Android. + # The 'ffiPlugin' specifies that native code should be built and bundled. + # This is required for using `dart:ffi`. + # All these are used by the tooling to maintain consistency when + # adding or updating assets for this project. + plugin: + platforms: + android: + package: eu.weblibre.pluggable_transports_proxy + pluginClass: PluggableTransportsProxyPlugin + + # To add assets to your plugin package, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + # + # For details regarding assets in packages, see + # https://flutter.dev/to/asset-from-package + # + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/to/resolution-aware-images + + # To add custom fonts to your plugin package, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts in packages, see + # https://flutter.dev/to/font-from-package diff --git a/packages/simple_intent_receiver/android/src/main/kotlin/eu/weblibre/simple_intent_receiver/pigeons/Intent.g.kt b/packages/simple_intent_receiver/android/src/main/kotlin/eu/weblibre/simple_intent_receiver/pigeons/Intent.g.kt index 78a3ae94..53a7af66 100644 --- a/packages/simple_intent_receiver/android/src/main/kotlin/eu/weblibre/simple_intent_receiver/pigeons/Intent.g.kt +++ b/packages/simple_intent_receiver/android/src/main/kotlin/eu/weblibre/simple_intent_receiver/pigeons/Intent.g.kt @@ -1,4 +1,4 @@ -// Autogenerated from Pigeon (v26.0.0), do not edit directly. +// Autogenerated from Pigeon (v26.0.1), do not edit directly. // See also: https://pub.dev/packages/pigeon @file:Suppress("UNCHECKED_CAST", "ArrayInDataClass") diff --git a/packages/simple_intent_receiver/lib/src/pigeons/intent.g.dart b/packages/simple_intent_receiver/lib/src/pigeons/intent.g.dart index 835377f3..dedd3c77 100644 --- a/packages/simple_intent_receiver/lib/src/pigeons/intent.g.dart +++ b/packages/simple_intent_receiver/lib/src/pigeons/intent.g.dart @@ -1,4 +1,4 @@ -// Autogenerated from Pigeon (v26.0.0), do not edit directly. +// Autogenerated from Pigeon (v26.0.1), do not edit directly. // See also: https://pub.dev/packages/pigeon // ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import, no_leading_underscores_for_local_identifiers diff --git a/pubspec.yaml b/pubspec.yaml index 4591daac..eb2a8941 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -6,6 +6,7 @@ workspace: - app/ - packages/flutter_mozilla_components - packages/flutter_mozilla_components/example + - packages/pluggable_transports_proxy - packages/simple_intent_receiver - packages/simple_intent_receiver/example dev_dependencies: