├── android
├── settings.gradle
├── .gitignore
├── src
│ └── main
│ │ ├── AndroidManifest.xml
│ │ └── kotlin
│ │ └── app
│ │ └── wachu
│ │ └── wireguard_vpn
│ │ └── WireguardVpnPlugin.kt
├── proguard-android-optimize.txt
└── build.gradle
├── example
├── android
│ ├── app
│ │ ├── proguard-rules.pro
│ │ ├── src
│ │ │ ├── main
│ │ │ │ ├── res
│ │ │ │ │ ├── mipmap-hdpi
│ │ │ │ │ │ └── ic_launcher.png
│ │ │ │ │ ├── mipmap-mdpi
│ │ │ │ │ │ └── ic_launcher.png
│ │ │ │ │ ├── mipmap-xhdpi
│ │ │ │ │ │ └── ic_launcher.png
│ │ │ │ │ ├── mipmap-xxhdpi
│ │ │ │ │ │ └── ic_launcher.png
│ │ │ │ │ ├── mipmap-xxxhdpi
│ │ │ │ │ │ └── ic_launcher.png
│ │ │ │ │ ├── drawable
│ │ │ │ │ │ └── launch_background.xml
│ │ │ │ │ ├── drawable-v21
│ │ │ │ │ │ └── launch_background.xml
│ │ │ │ │ ├── values
│ │ │ │ │ │ └── styles.xml
│ │ │ │ │ └── values-night
│ │ │ │ │ │ └── styles.xml
│ │ │ │ ├── kotlin
│ │ │ │ │ └── app
│ │ │ │ │ │ └── wachu
│ │ │ │ │ │ └── wireguard_vpn_example
│ │ │ │ │ │ └── MainActivity.kt
│ │ │ │ └── AndroidManifest.xml
│ │ │ ├── debug
│ │ │ │ └── AndroidManifest.xml
│ │ │ └── profile
│ │ │ │ └── AndroidManifest.xml
│ │ ├── proguard-android-optimize.txt
│ │ └── build.gradle
│ ├── gradle.properties
│ ├── gradle
│ │ └── wrapper
│ │ │ └── gradle-wrapper.properties
│ ├── .gitignore
│ ├── settings.gradle
│ └── build.gradle
├── README.md
├── .gitignore
├── analysis_options.yaml
├── lib
│ └── main.dart
├── pubspec.yaml
└── pubspec.lock
├── lib
├── src
│ ├── models
│ │ ├── models.dart
│ │ ├── set_state_params.dart
│ │ ├── stats.dart
│ │ ├── state_change_data.dart
│ │ └── tunnel.dart
│ └── errors
│ │ └── exceptions.dart
├── wireguard_vpn.dart
├── wireguard_vpn_platform_interface.dart
└── wireguard_vpn_method_channel.dart
├── analysis_options.yaml
├── .gitignore
├── CHANGELOG.md
├── .metadata
├── LICENSE
├── pubspec.yaml
└── README.md
/android/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name = 'wireguard_vpn'
2 |
--------------------------------------------------------------------------------
/example/android/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | -keep class app.wachu.wireguard_vpn.** {*;}
2 | -keep class com.beust.klaxon.** {*;}
--------------------------------------------------------------------------------
/example/android/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Xmx1536M
2 | android.useAndroidX=true
3 | android.enableJetifier=true
4 |
--------------------------------------------------------------------------------
/lib/src/models/models.dart:
--------------------------------------------------------------------------------
1 | export 'set_state_params.dart';
2 | export 'state_change_data.dart';
3 | export 'stats.dart';
4 | export 'tunnel.dart';
5 |
--------------------------------------------------------------------------------
/android/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/workspace.xml
5 | /.idea/libraries
6 | .DS_Store
7 | /build
8 | /captures
9 | .cxx
10 |
--------------------------------------------------------------------------------
/android/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wachu985/flutter_wireguard_vpn/HEAD/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wachu985/flutter_wireguard_vpn/HEAD/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wachu985/flutter_wireguard_vpn/HEAD/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/analysis_options.yaml:
--------------------------------------------------------------------------------
1 | include: package:flutter_lints/flutter.yaml
2 |
3 | # Additional information about this file can be found at
4 | # https://dart.dev/guides/language/analysis-options
5 |
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wachu985/flutter_wireguard_vpn/HEAD/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wachu985/flutter_wireguard_vpn/HEAD/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | zipStoreBase=GRADLE_USER_HOME
4 | zipStorePath=wrapper/dists
5 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip
6 |
--------------------------------------------------------------------------------
/lib/src/errors/exceptions.dart:
--------------------------------------------------------------------------------
1 | class ConnectionException implements Exception {
2 | /// Constructor of the [ConnectionException] class that allows storing the error [message].
3 | const ConnectionException({required this.message});
4 |
5 | /// It stores the error [message].
6 | final String message;
7 | }
8 |
--------------------------------------------------------------------------------
/example/android/.gitignore:
--------------------------------------------------------------------------------
1 | gradle-wrapper.jar
2 | /.gradle
3 | /captures/
4 | /gradlew
5 | /gradlew.bat
6 | /local.properties
7 | GeneratedPluginRegistrant.java
8 |
9 | # Remember to never publicly share your keystore.
10 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
11 | key.properties
12 | **/*.keystore
13 | **/*.jks
14 |
--------------------------------------------------------------------------------
/example/android/app/src/main/kotlin/app/wachu/wireguard_vpn_example/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package app.wachu.wireguard_vpn_example
2 |
3 | import io.flutter.embedding.android.FlutterActivity
4 |
5 | import android.content.Intent
6 | import android.app.Activity
7 | import android.os.Bundle
8 | import android.content.Context
9 | //import com.wireguard.android.backend.*
10 |
11 | class MainActivity: FlutterActivity() {
12 | }
13 |
--------------------------------------------------------------------------------
/example/android/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/example/android/app/src/profile/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/example/android/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
3 | def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
4 | def properties = new Properties()
5 |
6 | assert localPropertiesFile.exists()
7 | localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
8 |
9 | def flutterSdkPath = properties.getProperty("flutter.sdk")
10 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
11 | apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
12 |
--------------------------------------------------------------------------------
/example/android/app/src/main/res/drawable/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/example/android/app/src/main/res/drawable-v21/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/example/README.md:
--------------------------------------------------------------------------------
1 | # wireguard_vpn_example
2 |
3 | Demonstrates how to use the wireguard_vpn plugin.
4 |
5 | ## Getting Started
6 |
7 | This project is a starting point for a Flutter application.
8 |
9 | A few resources to get you started if this is your first Flutter project:
10 |
11 | - [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab)
12 | - [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook)
13 |
14 | For help getting started with Flutter development, view the
15 | [online documentation](https://docs.flutter.dev/), which offers tutorials,
16 | samples, guidance on mobile development, and a full API reference.
17 |
--------------------------------------------------------------------------------
/lib/src/models/set_state_params.dart:
--------------------------------------------------------------------------------
1 | import 'tunnel.dart';
2 |
3 | class SetStateParams {
4 | final bool state;
5 | final Tunnel tunnel;
6 |
7 | /// Constructor of the [SetStateParams] class that receives [state], a boolean that defines the
8 | /// value that the tunnel is going to receive, [tunnel], an object of the [Tunnel] class that is the
9 | /// tunnel to be modified.
10 | SetStateParams({
11 | required this.state,
12 | required this.tunnel,
13 | });
14 |
15 | /// Method [toJson] to convert the class to JSON.
16 | Map toJson() => {
17 | 'state': state,
18 | 'tunnel': tunnel.toJson(),
19 | };
20 | }
21 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Miscellaneous
2 | *.class
3 | *.log
4 | *.pyc
5 | *.swp
6 | .DS_Store
7 | .atom/
8 | .buildlog/
9 | .history
10 | .svn/
11 | migrate_working_dir/
12 | .fvm/
13 |
14 | # IntelliJ related
15 | *.iml
16 | *.ipr
17 | *.iws
18 | .idea/
19 |
20 | # The .vscode folder contains launch configuration and tasks you configure in
21 | # VS Code which you may wish to be included in version control, so this line
22 | # is commented out by default.
23 | #.vscode/
24 |
25 | # Flutter/Dart/Pub related
26 | # Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
27 | /pubspec.lock
28 | **/doc/api/
29 | .dart_tool/
30 | .packages
31 | build/
32 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## 0.0.2 - Fixed the execution error in release mode.
2 |
3 | * Updated README.md
4 | * Update proguard rules.
5 | ## 0.0.1+3 - Update minSdkVersion
6 |
7 | * Updated README.md
8 | * The public API has Dartdoc comments added.
9 | ## 0.0.1+2 - Updated Readme
10 |
11 | * Updated README.md
12 | ## 0.0.1+1 - Minor Changes
13 |
14 | * Updated README.md
15 | * Updated LICENSE
16 | ## 0.0.1 - Initial Commit
17 |
18 | * Added three new methods to allow management of VPN connection state:
19 | * changeStateParams: allows enabling and disabling of VPN connection.
20 | * runningTunnelNames: returns the name of the currently active tunnel.
21 | * tunnelGetStats: returns statistics for the requested tunnel.
22 | .
23 |
--------------------------------------------------------------------------------
/example/android/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | ext.kotlin_version = '1.7.10'
3 | repositories {
4 | google()
5 | mavenCentral()
6 | }
7 |
8 | dependencies {
9 | classpath 'com.android.tools.build:gradle:7.2.0'
10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
11 | }
12 | }
13 |
14 | allprojects {
15 | repositories {
16 | google()
17 | mavenCentral()
18 | }
19 | }
20 |
21 | rootProject.buildDir = '../build'
22 | subprojects {
23 | project.buildDir = "${rootProject.buildDir}/${project.name}"
24 | }
25 | subprojects {
26 | project.evaluationDependsOn(':app')
27 | }
28 |
29 | tasks.register("clean", Delete) {
30 | delete rootProject.buildDir
31 | }
32 |
--------------------------------------------------------------------------------
/lib/src/models/stats.dart:
--------------------------------------------------------------------------------
1 | class Stats {
2 | final num totalDownload;
3 | final num totalUpload;
4 |
5 | /// Constructor of the [Stats] class that receives [totalDownload] where total downloaded data is stored,
6 | /// [totalUpload] where uploaded data is stored.
7 | Stats({
8 | required this.totalDownload,
9 | required this.totalUpload,
10 | });
11 |
12 | /// Method [toJson] to convert the class to JSON.
13 | Map toJson() => {
14 | 'totalDownload': totalDownload,
15 | 'totalUpload': totalUpload,
16 | };
17 |
18 | /// Method [Stats.fromJson] to convert the JSON to class.
19 | factory Stats.fromJson(Map json) {
20 | return Stats(
21 | totalDownload: json['totalDownload'] as num,
22 | totalUpload: json['totalUpload'] as num,
23 | );
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/example/.gitignore:
--------------------------------------------------------------------------------
1 | # Miscellaneous
2 | *.class
3 | *.log
4 | *.pyc
5 | *.swp
6 | .DS_Store
7 | .atom/
8 | .buildlog/
9 | .history
10 | .svn/
11 | migrate_working_dir/
12 |
13 | # IntelliJ related
14 | *.iml
15 | *.ipr
16 | *.iws
17 | .idea/
18 |
19 | # The .vscode folder contains launch configuration and tasks you configure in
20 | # VS Code which you may wish to be included in version control, so this line
21 | # is commented out by default.
22 | #.vscode/
23 |
24 | # Flutter/Dart/Pub related
25 | **/doc/api/
26 | **/ios/Flutter/.last_build_id
27 | .dart_tool/
28 | .flutter-plugins
29 | .flutter-plugins-dependencies
30 | .packages
31 | .pub-cache/
32 | .pub/
33 | /build/
34 |
35 | # Symbolication related
36 | app.*.symbols
37 |
38 | # Obfuscation related
39 | app.*.map.json
40 |
41 | # Android Studio will place build artifacts here
42 | /android/app/debug
43 | /android/app/profile
44 | /android/app/release
45 |
--------------------------------------------------------------------------------
/lib/src/models/state_change_data.dart:
--------------------------------------------------------------------------------
1 | class StateChangeData {
2 | final String tunnelName;
3 | final bool tunnelState;
4 |
5 | /// Constructor of the [StateChangeData] class that receives [tunnelName] which is the name
6 | /// of the tunnel, and [tunnelState] which is a boolean that can be true or false.
7 | StateChangeData({
8 | required this.tunnelName,
9 | required this.tunnelState,
10 | });
11 |
12 | /// Method [toJson] to convert the class to JSON.
13 | Map toJson() => {
14 | 'tunnelName': tunnelName,
15 | 'tunnel': tunnelState,
16 | };
17 |
18 | /// Method [StateChangeData.fromJson] to convert the JSON to class.
19 | factory StateChangeData.fromJson(Map json) {
20 | return StateChangeData(
21 | tunnelName: json['tunnelName'] as String,
22 | tunnelState: (json['tunnelState'] as bool?) ?? false,
23 | );
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/android/proguard-android-optimize.txt:
--------------------------------------------------------------------------------
1 | -allowaccessmodification
2 | -dontusemixedcaseclassnames
3 | -dontobfuscate
4 | -verbose
5 |
6 | -keepattributes *Annotation*
7 |
8 | -keepclasseswithmembernames class * {
9 | native ;
10 | }
11 |
12 | -keepclassmembers enum * {
13 | public static **[] values();
14 | public static ** valueOf(java.lang.String);
15 | }
16 |
17 | -keepclassmembers class * implements android.os.Parcelable {
18 | public static final ** CREATOR;
19 | }
20 |
21 | -keep class androidx.annotation.Keep
22 |
23 | -keep @androidx.annotation.Keep class * {*;}
24 |
25 | -keepclasseswithmembers class * {
26 | @androidx.annotation.Keep ;
27 | }
28 |
29 | -keepclasseswithmembers class * {
30 | @androidx.annotation.Keep ;
31 | }
32 |
33 | -keepclasseswithmembers class * {
34 | @androidx.annotation.Keep (...);
35 | }
36 |
37 |
38 | -keep class app.wachu.wireguard_vpn.** {*;}
39 | -keep class com.beust.klaxon.** {*;}
40 |
--------------------------------------------------------------------------------
/example/android/app/proguard-android-optimize.txt:
--------------------------------------------------------------------------------
1 | -allowaccessmodification
2 | -dontusemixedcaseclassnames
3 | -dontobfuscate
4 | -verbose
5 |
6 | -keepattributes *Annotation*
7 |
8 | -keepclasseswithmembernames class * {
9 | native ;
10 | }
11 |
12 | -keepclassmembers enum * {
13 | public static **[] values();
14 | public static ** valueOf(java.lang.String);
15 | }
16 |
17 | -keepclassmembers class * implements android.os.Parcelable {
18 | public static final ** CREATOR;
19 | }
20 |
21 | -keep class androidx.annotation.Keep
22 |
23 | -keep @androidx.annotation.Keep class * {*;}
24 |
25 | -keepclasseswithmembers class * {
26 | @androidx.annotation.Keep ;
27 | }
28 |
29 | -keepclasseswithmembers class * {
30 | @androidx.annotation.Keep ;
31 | }
32 |
33 | -keepclasseswithmembers class * {
34 | @androidx.annotation.Keep (...);
35 | }
36 |
37 | -keep class app.wachu.wireguard_vpn.** {*;}
38 | -keep class com.beust.klaxon.** {*;}
39 |
--------------------------------------------------------------------------------
/.metadata:
--------------------------------------------------------------------------------
1 | # This file tracks properties of this Flutter project.
2 | # Used by Flutter tool to assess capabilities and perform upgrades etc.
3 | #
4 | # This file should be version controlled.
5 |
6 | version:
7 | revision: 90c64ed42ba53a52d18f0cb3b17666c8662ed2a0
8 | channel: stable
9 |
10 | project_type: plugin
11 |
12 | # Tracks metadata for the flutter migrate command
13 | migration:
14 | platforms:
15 | - platform: root
16 | create_revision: 90c64ed42ba53a52d18f0cb3b17666c8662ed2a0
17 | base_revision: 90c64ed42ba53a52d18f0cb3b17666c8662ed2a0
18 | - platform: android
19 | create_revision: 90c64ed42ba53a52d18f0cb3b17666c8662ed2a0
20 | base_revision: 90c64ed42ba53a52d18f0cb3b17666c8662ed2a0
21 |
22 | # User provided section
23 |
24 | # List of Local paths (relative to this file) that should be
25 | # ignored by the migrate tool.
26 | #
27 | # Files that are not part of the templates will be ignored by default.
28 | unmanaged_files:
29 | - 'lib/main.dart'
30 | - 'ios/Runner.xcodeproj/project.pbxproj'
31 |
--------------------------------------------------------------------------------
/lib/wireguard_vpn.dart:
--------------------------------------------------------------------------------
1 | import 'src/models/models.dart';
2 | import 'wireguard_vpn_platform_interface.dart';
3 |
4 | export 'src/models/models.dart';
5 | export 'src/errors/exceptions.dart';
6 |
7 | /// Main Class
8 | class WireguardVpn {
9 | /// Method [changeStateParams] that receives [params] of the [SetStateParams] class and returns true or false
10 | /// when activating or deactivating the tunnel state.
11 | Future changeStateParams(SetStateParams params) {
12 | return WireguardVpnPlatform.instance.changeStateParams(params);
13 | }
14 |
15 | /// Method [runningTunnelNames] returns the name of the running tunnel.
16 | Future runningTunnelNames() {
17 | return WireguardVpnPlatform.instance.runningTunnelNames();
18 | }
19 |
20 | /// Method [tunnelGetStats] receives the [name] of the tunnel that will take the Stats
21 | /// and returns an object of the [Stats] class with the values of the tunnel.
22 | Future tunnelGetStats(String name) {
23 | return WireguardVpnPlatform.instance.tunnelGetStats(name);
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/example/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
15 |
18 |
19 |
--------------------------------------------------------------------------------
/example/android/app/src/main/res/values-night/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
15 |
18 |
19 |
--------------------------------------------------------------------------------
/lib/src/models/tunnel.dart:
--------------------------------------------------------------------------------
1 | class Tunnel {
2 | final String name;
3 | final String address;
4 | final String listenPort;
5 | final String dnsServer;
6 | final String privateKey;
7 | final String peerAllowedIp;
8 | final String peerPublicKey;
9 | final String peerEndpoint;
10 | final String peerPresharedKey;
11 |
12 | /// Constructor of the [Tunnel] class that receives the [name] of the tunnel,
13 | /// the [address] IP address of the local, [listenPort] listen port,
14 | /// [dnsServer] DNS servers, [privateKey] server private key,
15 | /// [peerAllowedIp] allowed IP addresses, [peerEndpoint] server IP address,
16 | /// [peerPresharedKey] server preshared key.
17 | Tunnel(
18 | {required this.name,
19 | required this.address,
20 | required this.listenPort,
21 | required this.dnsServer,
22 | required this.privateKey,
23 | required this.peerAllowedIp,
24 | required this.peerPublicKey,
25 | required this.peerEndpoint,
26 | required this.peerPresharedKey});
27 |
28 | /// Method [toJson] to convert the class to JSON.
29 | Map toJson() => {
30 | 'name': name,
31 | 'address': address,
32 | 'listenPort': listenPort,
33 | 'dnsServer': dnsServer,
34 | 'privateKey': privateKey,
35 | 'peerAllowedIp': peerAllowedIp,
36 | 'peerPublicKey': peerPublicKey,
37 | 'peerEndpoint': peerEndpoint,
38 | 'peerPresharedKey': peerPresharedKey
39 | };
40 | }
41 |
--------------------------------------------------------------------------------
/example/analysis_options.yaml:
--------------------------------------------------------------------------------
1 | # This file configures the analyzer, which statically analyzes Dart code to
2 | # check for errors, warnings, and lints.
3 | #
4 | # The issues identified by the analyzer are surfaced in the UI of Dart-enabled
5 | # IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
6 | # invoked from the command line by running `flutter analyze`.
7 |
8 | # The following line activates a set of recommended lints for Flutter apps,
9 | # packages, and plugins designed to encourage good coding practices.
10 | include: package:flutter_lints/flutter.yaml
11 |
12 | linter:
13 | # The lint rules applied to this project can be customized in the
14 | # section below to disable rules from the `package:flutter_lints/flutter.yaml`
15 | # included above or to enable additional rules. A list of all available lints
16 | # and their documentation is published at
17 | # https://dart-lang.github.io/linter/lints/index.html.
18 | #
19 | # Instead of disabling a lint rule for the entire project in the
20 | # section below, it can also be suppressed for a single line of code
21 | # or a specific dart file by using the `// ignore: name_of_lint` and
22 | # `// ignore_for_file: name_of_lint` syntax on the line or in the file
23 | # producing the lint.
24 | rules:
25 | # avoid_print: false # Uncomment to disable the `avoid_print` rule
26 | # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
27 |
28 | # Additional information about this file can be found at
29 | # https://dart.dev/guides/language/analysis-options
30 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright 2023 Pedro Dominguez Bonilla. All rights reserved.
2 |
3 | Redistribution and use in source and binary forms, with or without modification,
4 | are permitted provided that the following conditions are met:
5 |
6 | * Redistributions of source code must retain the above copyright
7 | notice, this list of conditions and the following disclaimer.
8 | * Redistributions in binary form must reproduce the above
9 | copyright notice, this list of conditions and the following
10 | disclaimer in the documentation and/or other materials provided
11 | with the distribution.
12 | * Neither the name of Google Inc. nor the names of its
13 | contributors may be used to endorse or promote products derived
14 | from this software without specific prior written permission.
15 |
16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
20 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
23 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 |
--------------------------------------------------------------------------------
/lib/wireguard_vpn_platform_interface.dart:
--------------------------------------------------------------------------------
1 | import 'package:plugin_platform_interface/plugin_platform_interface.dart';
2 |
3 | import 'src/models/models.dart';
4 | import 'wireguard_vpn_method_channel.dart';
5 |
6 | abstract class WireguardVpnPlatform extends PlatformInterface {
7 | /// Constructs a WireguardVpnPlatform.
8 | WireguardVpnPlatform() : super(token: _token);
9 |
10 | static final Object _token = Object();
11 |
12 | static WireguardVpnPlatform _instance = MethodChannelWireguardVpn();
13 |
14 | /// The default instance of [WireguardVpnPlatform] to use.
15 | ///
16 | /// Defaults to [MethodChannelWireguardVpn].
17 | static WireguardVpnPlatform get instance => _instance;
18 |
19 | /// Platform-specific implementations should set this with their own
20 | /// platform-specific class that extends [WireguardVpnPlatform] when
21 | /// they register themselves.
22 | static set instance(WireguardVpnPlatform instance) {
23 | PlatformInterface.verifyToken(instance, _token);
24 | _instance = instance;
25 | }
26 |
27 | /// Declaration of the method [changeStateParams] that receives an object of [SetStateParams].
28 | Future changeStateParams(SetStateParams params) {
29 | throw UnimplementedError('changeStateParams() has not been implemented.');
30 | }
31 |
32 | /// Declaration of the method [runningTunnelNames] this is responsible for obtaining the active tunnels.
33 | Future runningTunnelNames() {
34 | throw UnimplementedError('runningTunnelNames() has not been implemented.');
35 | }
36 |
37 | /// Declaration of the method [tunnelGetStats] this is responsible for obtaining the stats of the tunnels.
38 | Future tunnelGetStats(String name) {
39 | throw UnimplementedError('runningTunnelNames() has not been implemented.');
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/lib/wireguard_vpn_method_channel.dart:
--------------------------------------------------------------------------------
1 | import 'dart:convert';
2 |
3 | // import 'package:flutter/foundation.dart';
4 | import 'package:flutter/services.dart';
5 |
6 | import 'src/errors/exceptions.dart';
7 | import 'src/models/models.dart';
8 | import 'wireguard_vpn_platform_interface.dart';
9 |
10 | /// An implementation of [WireguardVpnPlatform] that uses method channels.
11 | class MethodChannelWireguardVpn extends WireguardVpnPlatform {
12 | /// The method channel used to interact with the native platform.
13 | final methodChannel = const MethodChannel('wachu985/wireguard-flutter');
14 |
15 | /// Implementation of the method [changeStateParams] using the PlatformChannel.
16 | @override
17 | Future changeStateParams(SetStateParams params) async {
18 | try {
19 | final state = await methodChannel.invokeMethod(
20 | 'setState', jsonEncode(params.toJson()));
21 |
22 | return state;
23 | } on Exception catch (e) {
24 | throw ConnectionException(message: e.toString());
25 | }
26 | }
27 |
28 | /// Implementation of the method [runningTunnelNames] using the PlatformChannel.
29 | @override
30 | Future runningTunnelNames() async {
31 | try {
32 | final result = await methodChannel.invokeMethod('getTunnelNames');
33 | return result;
34 | } on PlatformException catch (e) {
35 | throw ConnectionException(message: e.message ?? '');
36 | }
37 | }
38 |
39 | /// Implementation of the method [tunnelGetStats] using the PlatformChannel.
40 | @override
41 | Future tunnelGetStats(String name) async {
42 | try {
43 | final result = await methodChannel.invokeMethod('getStats', name);
44 | final stats = Stats.fromJson(jsonDecode(result));
45 | return stats;
46 | } on Exception catch (e) {
47 | throw ConnectionException(message: e.toString());
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/example/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
9 |
17 |
21 |
25 |
26 |
27 |
28 |
29 |
30 |
32 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/android/build.gradle:
--------------------------------------------------------------------------------
1 | group 'app.wachu.wireguard_vpn'
2 | version '1.0-SNAPSHOT'
3 |
4 | buildscript {
5 | ext.kotlin_version = '1.7.10'
6 | repositories {
7 | google()
8 | mavenCentral()
9 | }
10 |
11 | dependencies {
12 | classpath 'com.android.tools.build:gradle:7.2.0'
13 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
14 | }
15 | }
16 |
17 | allprojects {
18 | repositories {
19 | google()
20 | mavenCentral()
21 | }
22 | }
23 |
24 | apply plugin: 'com.android.library'
25 | apply plugin: 'kotlin-android'
26 |
27 | android {
28 | compileSdkVersion 31
29 |
30 | compileOptions {
31 | sourceCompatibility JavaVersion.VERSION_1_8
32 | targetCompatibility JavaVersion.VERSION_1_8
33 | coreLibraryDesugaringEnabled = true
34 | }
35 | buildTypes {
36 | release {
37 | proguardFiles("proguard-android-optimize.txt")
38 | packagingOptions {
39 | exclude "DebugProbesKt.bin"
40 | exclude "kotlin-tooling-metadata.json"
41 | }
42 | }
43 | }
44 |
45 | kotlinOptions {
46 | jvmTarget = '1.8'
47 | }
48 |
49 | sourceSets {
50 | main.java.srcDirs += 'src/main/kotlin'
51 | }
52 |
53 | defaultConfig {
54 | minSdkVersion 21
55 | }
56 | }
57 |
58 | dependencies {
59 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
60 | implementation 'com.wireguard.android:tunnel:1.0.20230321'
61 | implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.1'
62 | implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.1"
63 | implementation 'com.beust:klaxon:5.5'
64 | implementation 'androidx.appcompat:appcompat:1.4.0'
65 |
66 | /// to run wireguard on Android 25 or lower
67 | coreLibraryDesugaring "com.android.tools:desugar_jdk_libs:1.1.5"
68 | }
--------------------------------------------------------------------------------
/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: wireguard_vpn
2 | description: A Flutter package that allows connection through a VPN using WireGuard.
3 | version: 0.0.2
4 | homepage: https://github.com/Wachu985/flutter_wireguard_vpn
5 |
6 | environment:
7 | sdk: '>=2.19.5 <4.0.0'
8 | flutter: ">=2.5.0"
9 |
10 | dependencies:
11 | flutter:
12 | sdk: flutter
13 | plugin_platform_interface: ^2.0.2
14 |
15 | dev_dependencies:
16 | flutter_test:
17 | sdk: flutter
18 | flutter_lints: ^2.0.0
19 |
20 | # For information on the generic Dart part of this file, see the
21 | # following page: https://dart.dev/tools/pub/pubspec
22 |
23 | # The following section is specific to Flutter packages.
24 | flutter:
25 | # This section identifies this Flutter project as a plugin project.
26 | # The 'pluginClass' specifies the class (in Java, Kotlin, Swift, Objective-C, etc.)
27 | # which should be registered in the plugin registry. This is required for
28 | # using method channels.
29 | # The Android 'package' specifies package in which the registered class is.
30 | # This is required for using method channels on Android.
31 | # The 'ffiPlugin' specifies that native code should be built and bundled.
32 | # This is required for using `dart:ffi`.
33 | # All these are used by the tooling to maintain consistency when
34 | # adding or updating assets for this project.
35 | plugin:
36 | platforms:
37 | android:
38 | package: app.wachu.wireguard_vpn
39 | pluginClass: WireguardVpnPlugin
40 |
41 | # To add assets to your plugin package, add an assets section, like this:
42 | # assets:
43 | # - images/a_dot_burr.jpeg
44 | # - images/a_dot_ham.jpeg
45 | #
46 | # For details regarding assets in packages, see
47 | # https://flutter.dev/assets-and-images/#from-packages
48 | #
49 | # An image asset can refer to one or more resolution-specific "variants", see
50 | # https://flutter.dev/assets-and-images/#resolution-aware
51 |
52 | # To add custom fonts to your plugin package, add a fonts section here,
53 | # in this "flutter" section. Each entry in this list should have a
54 | # "family" key with the font family name, and a "fonts" key with a
55 | # list giving the asset and other descriptors for the font. For
56 | # example:
57 | # fonts:
58 | # - family: Schyler
59 | # fonts:
60 | # - asset: fonts/Schyler-Regular.ttf
61 | # - asset: fonts/Schyler-Italic.ttf
62 | # style: italic
63 | # - family: Trajan Pro
64 | # fonts:
65 | # - asset: fonts/TrajanPro.ttf
66 | # - asset: fonts/TrajanPro_Bold.ttf
67 | # weight: 700
68 | #
69 | # For details regarding fonts in packages, see
70 | # https://flutter.dev/custom-fonts/#from-packages
71 |
--------------------------------------------------------------------------------
/example/lib/main.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 |
3 | import 'package:flutter/material.dart';
4 |
5 | import 'package:wireguard_vpn/wireguard_vpn.dart';
6 |
7 | void main() {
8 | runApp(const MyApp());
9 | }
10 |
11 | class MyApp extends StatefulWidget {
12 | const MyApp({super.key});
13 |
14 | @override
15 | State createState() => _MyAppState();
16 | }
17 |
18 | class _MyAppState extends State {
19 | final _wireguardFlutterPlugin = WireguardVpn();
20 | bool vpnActivate = false;
21 | Stats stats = Stats(totalDownload: 0, totalUpload: 0);
22 | final String initName = 'MyWireguardVPN';
23 | final String initAddress = "10.7.0.2/24";
24 | final String initPort = "51820";
25 | final String initDnsServer = "8.8.8.8, 8.8.4.4";
26 | final String initPrivateKey = "PRIVATE_KEY";
27 | final String initAllowedIp = "0.0.0.0/0, ::/0";
28 | final String initPublicKey = "PUBLIC_KEY";
29 | final String initEndpoint = "vpn.example.com:51820";
30 | final String presharedKey = 'PRESHARED_KEY';
31 |
32 | @override
33 | void initState() {
34 | super.initState();
35 | vpnActivate ? _obtainStats() : null;
36 | }
37 |
38 | @override
39 | Widget build(BuildContext context) {
40 | return MaterialApp(
41 | home: Scaffold(
42 | appBar: AppBar(
43 | title: const Text('Wireguard-VPN Example'),
44 | ),
45 | body: Column(
46 | children: [
47 | Text(
48 | 'Active VPN: ${stats.totalDownload} D -- ${stats.totalUpload} U'),
49 | SwitchListTile(
50 | value: vpnActivate,
51 | onChanged: _activateVpn,
52 | title: Text(initName),
53 | subtitle: Text(initEndpoint),
54 | ),
55 | ],
56 | )),
57 | );
58 | }
59 |
60 | void _obtainStats() {
61 | Timer.periodic(const Duration(seconds: 1), (timer) async {
62 | final results = await _wireguardFlutterPlugin.tunnelGetStats(initName);
63 | setState(() {
64 | stats = results ?? Stats(totalDownload: 0, totalUpload: 0);
65 | });
66 | });
67 | }
68 |
69 | void _activateVpn(bool value) async {
70 | final results =
71 | await _wireguardFlutterPlugin.changeStateParams(SetStateParams(
72 | state: !vpnActivate,
73 | tunnel: Tunnel(
74 | name: initName,
75 | address: initAddress,
76 | dnsServer: initDnsServer,
77 | listenPort: initPort,
78 | peerAllowedIp: initAllowedIp,
79 | peerEndpoint: initEndpoint,
80 | peerPublicKey: initPublicKey,
81 | privateKey: initPrivateKey,
82 | peerPresharedKey: presharedKey),
83 | ));
84 | setState(() {
85 | vpnActivate = results ?? false;
86 | vpnActivate ? _obtainStats() : null;
87 | });
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/example/android/app/build.gradle:
--------------------------------------------------------------------------------
1 | def localProperties = new Properties()
2 | def localPropertiesFile = rootProject.file('local.properties')
3 | if (localPropertiesFile.exists()) {
4 | localPropertiesFile.withReader('UTF-8') { reader ->
5 | localProperties.load(reader)
6 | }
7 | }
8 |
9 | def flutterRoot = localProperties.getProperty('flutter.sdk')
10 | if (flutterRoot == null) {
11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
12 | }
13 |
14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
15 | if (flutterVersionCode == null) {
16 | flutterVersionCode = '1'
17 | }
18 |
19 | def flutterVersionName = localProperties.getProperty('flutter.versionName')
20 | if (flutterVersionName == null) {
21 | flutterVersionName = '1.0'
22 | }
23 |
24 | apply plugin: 'com.android.application'
25 | apply plugin: 'kotlin-android'
26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
27 |
28 |
29 | def keystoreProperties = new Properties()
30 | def keystorePropertiesFile = rootProject.file('key.properties')
31 | if (keystorePropertiesFile.exists()) {
32 | keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
33 | }
34 |
35 |
36 | android {
37 | compileSdkVersion 31
38 | ndkVersion flutter.ndkVersion
39 |
40 | compileOptions {
41 | sourceCompatibility JavaVersion.VERSION_1_8
42 | targetCompatibility JavaVersion.VERSION_1_8
43 | }
44 |
45 | kotlinOptions {
46 | jvmTarget = '1.8'
47 | }
48 |
49 | sourceSets {
50 | main.java.srcDirs += 'src/main/kotlin'
51 | }
52 |
53 | defaultConfig {
54 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
55 | applicationId "app.wachu.wireguard_vpn_example"
56 | // You can update the following values to match your application needs.
57 | // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
58 | minSdkVersion 21
59 | targetSdkVersion flutter.targetSdkVersion
60 | versionCode flutterVersionCode.toInteger()
61 | versionName flutterVersionName
62 | }
63 | signingConfigs {
64 | release {
65 | keyAlias keystoreProperties['keyAlias']
66 | keyPassword keystoreProperties['keyPassword']
67 | storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null
68 | storePassword keystoreProperties['storePassword']
69 | }
70 | }
71 | buildTypes {
72 | release {
73 | minifyEnabled = true
74 | shrinkResources = true
75 | proguardFiles("proguard-android-optimize.txt")
76 | signingConfig signingConfigs.release
77 | }
78 | }
79 | }
80 |
81 | flutter {
82 | source '../..'
83 | }
84 |
85 | dependencies {
86 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
87 | }
88 |
--------------------------------------------------------------------------------
/example/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: wireguard_vpn_example
2 | description: Demonstrates how to use the wireguard_vpn plugin.
3 | # The following line prevents the package from being accidentally published to
4 | # pub.dev using `flutter pub publish`. This is preferred for private packages.
5 | publish_to: 'none' # Remove this line if you wish to publish to pub.dev
6 |
7 | environment:
8 | sdk: '>=2.19.5 <3.0.0'
9 |
10 | # Dependencies specify other packages that your package needs in order to work.
11 | # To automatically upgrade your package dependencies to the latest versions
12 | # consider running `flutter pub upgrade --major-versions`. Alternatively,
13 | # dependencies can be manually updated by changing the version numbers below to
14 | # the latest version available on pub.dev. To see which dependencies have newer
15 | # versions available, run `flutter pub outdated`.
16 | dependencies:
17 | flutter:
18 | sdk: flutter
19 |
20 | wireguard_vpn:
21 | # When depending on this package from a real application you should use:
22 | # wireguard_vpn: ^x.y.z
23 | # See https://dart.dev/tools/pub/dependencies#version-constraints
24 | # The example app is bundled with the plugin so we use a path dependency on
25 | # the parent directory to use the current plugin's version.
26 | path: ../
27 |
28 | # The following adds the Cupertino Icons font to your application.
29 | # Use with the CupertinoIcons class for iOS style icons.
30 | cupertino_icons: ^1.0.2
31 |
32 | dev_dependencies:
33 | flutter_test:
34 | sdk: flutter
35 |
36 | # The "flutter_lints" package below contains a set of recommended lints to
37 | # encourage good coding practices. The lint set provided by the package is
38 | # activated in the `analysis_options.yaml` file located at the root of your
39 | # package. See that file for information about deactivating specific lint
40 | # rules and activating additional ones.
41 | flutter_lints: ^2.0.0
42 |
43 | # For information on the generic Dart part of this file, see the
44 | # following page: https://dart.dev/tools/pub/pubspec
45 |
46 | # The following section is specific to Flutter packages.
47 | flutter:
48 |
49 | # The following line ensures that the Material Icons font is
50 | # included with your application, so that you can use the icons in
51 | # the material Icons class.
52 | uses-material-design: true
53 |
54 | # To add assets to your application, add an assets section, like this:
55 | # assets:
56 | # - images/a_dot_burr.jpeg
57 | # - images/a_dot_ham.jpeg
58 |
59 | # An image asset can refer to one or more resolution-specific "variants", see
60 | # https://flutter.dev/assets-and-images/#resolution-aware
61 |
62 | # For details regarding adding assets from package dependencies, see
63 | # https://flutter.dev/assets-and-images/#from-packages
64 |
65 | # To add custom fonts to your application, add a fonts section here,
66 | # in this "flutter" section. Each entry in this list should have a
67 | # "family" key with the font family name, and a "fonts" key with a
68 | # list giving the asset and other descriptors for the font. For
69 | # example:
70 | # fonts:
71 | # - family: Schyler
72 | # fonts:
73 | # - asset: fonts/Schyler-Regular.ttf
74 | # - asset: fonts/Schyler-Italic.ttf
75 | # style: italic
76 | # - family: Trajan Pro
77 | # fonts:
78 | # - asset: fonts/TrajanPro.ttf
79 | # - asset: fonts/TrajanPro_Bold.ttf
80 | # weight: 700
81 | #
82 | # For details regarding fonts from package dependencies,
83 | # see https://flutter.dev/custom-fonts/#from-packages
84 |
--------------------------------------------------------------------------------
/example/pubspec.lock:
--------------------------------------------------------------------------------
1 | # Generated by pub
2 | # See https://dart.dev/tools/pub/glossary#lockfile
3 | packages:
4 | async:
5 | dependency: transitive
6 | description:
7 | name: async
8 | sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c"
9 | url: "https://pub.dev"
10 | source: hosted
11 | version: "2.11.0"
12 | boolean_selector:
13 | dependency: transitive
14 | description:
15 | name: boolean_selector
16 | sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66"
17 | url: "https://pub.dev"
18 | source: hosted
19 | version: "2.1.1"
20 | characters:
21 | dependency: transitive
22 | description:
23 | name: characters
24 | sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605"
25 | url: "https://pub.dev"
26 | source: hosted
27 | version: "1.3.0"
28 | clock:
29 | dependency: transitive
30 | description:
31 | name: clock
32 | sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf
33 | url: "https://pub.dev"
34 | source: hosted
35 | version: "1.1.1"
36 | collection:
37 | dependency: transitive
38 | description:
39 | name: collection
40 | sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c"
41 | url: "https://pub.dev"
42 | source: hosted
43 | version: "1.17.1"
44 | cupertino_icons:
45 | dependency: "direct main"
46 | description:
47 | name: cupertino_icons
48 | sha256: e35129dc44c9118cee2a5603506d823bab99c68393879edb440e0090d07586be
49 | url: "https://pub.dev"
50 | source: hosted
51 | version: "1.0.5"
52 | fake_async:
53 | dependency: transitive
54 | description:
55 | name: fake_async
56 | sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78"
57 | url: "https://pub.dev"
58 | source: hosted
59 | version: "1.3.1"
60 | flutter:
61 | dependency: "direct main"
62 | description: flutter
63 | source: sdk
64 | version: "0.0.0"
65 | flutter_lints:
66 | dependency: "direct dev"
67 | description:
68 | name: flutter_lints
69 | sha256: "2118df84ef0c3ca93f96123a616ae8540879991b8b57af2f81b76a7ada49b2a4"
70 | url: "https://pub.dev"
71 | source: hosted
72 | version: "2.0.2"
73 | flutter_test:
74 | dependency: "direct dev"
75 | description: flutter
76 | source: sdk
77 | version: "0.0.0"
78 | js:
79 | dependency: transitive
80 | description:
81 | name: js
82 | sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3
83 | url: "https://pub.dev"
84 | source: hosted
85 | version: "0.6.7"
86 | lints:
87 | dependency: transitive
88 | description:
89 | name: lints
90 | sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452"
91 | url: "https://pub.dev"
92 | source: hosted
93 | version: "2.1.1"
94 | matcher:
95 | dependency: transitive
96 | description:
97 | name: matcher
98 | sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb"
99 | url: "https://pub.dev"
100 | source: hosted
101 | version: "0.12.15"
102 | material_color_utilities:
103 | dependency: transitive
104 | description:
105 | name: material_color_utilities
106 | sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724
107 | url: "https://pub.dev"
108 | source: hosted
109 | version: "0.2.0"
110 | meta:
111 | dependency: transitive
112 | description:
113 | name: meta
114 | sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3"
115 | url: "https://pub.dev"
116 | source: hosted
117 | version: "1.9.1"
118 | path:
119 | dependency: transitive
120 | description:
121 | name: path
122 | sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917"
123 | url: "https://pub.dev"
124 | source: hosted
125 | version: "1.8.3"
126 | plugin_platform_interface:
127 | dependency: transitive
128 | description:
129 | name: plugin_platform_interface
130 | sha256: "43798d895c929056255600343db8f049921cbec94d31ec87f1dc5c16c01935dd"
131 | url: "https://pub.dev"
132 | source: hosted
133 | version: "2.1.5"
134 | sky_engine:
135 | dependency: transitive
136 | description: flutter
137 | source: sdk
138 | version: "0.0.99"
139 | source_span:
140 | dependency: transitive
141 | description:
142 | name: source_span
143 | sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250
144 | url: "https://pub.dev"
145 | source: hosted
146 | version: "1.9.1"
147 | stack_trace:
148 | dependency: transitive
149 | description:
150 | name: stack_trace
151 | sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5
152 | url: "https://pub.dev"
153 | source: hosted
154 | version: "1.11.0"
155 | stream_channel:
156 | dependency: transitive
157 | description:
158 | name: stream_channel
159 | sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8"
160 | url: "https://pub.dev"
161 | source: hosted
162 | version: "2.1.1"
163 | string_scanner:
164 | dependency: transitive
165 | description:
166 | name: string_scanner
167 | sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde"
168 | url: "https://pub.dev"
169 | source: hosted
170 | version: "1.2.0"
171 | term_glyph:
172 | dependency: transitive
173 | description:
174 | name: term_glyph
175 | sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84
176 | url: "https://pub.dev"
177 | source: hosted
178 | version: "1.2.1"
179 | test_api:
180 | dependency: transitive
181 | description:
182 | name: test_api
183 | sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb
184 | url: "https://pub.dev"
185 | source: hosted
186 | version: "0.5.1"
187 | vector_math:
188 | dependency: transitive
189 | description:
190 | name: vector_math
191 | sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
192 | url: "https://pub.dev"
193 | source: hosted
194 | version: "2.1.4"
195 | wireguard_vpn:
196 | dependency: "direct main"
197 | description:
198 | path: ".."
199 | relative: true
200 | source: path
201 | version: "0.0.2"
202 | sdks:
203 | dart: ">=3.0.0 <4.0.0"
204 | flutter: ">=2.5.0"
205 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # wireguard_vpn
2 |
3 | A Flutter plugin that enables the activation and deactivation of VPN connections using [WireGuard](https://www.wireguard.com/).
4 |
5 | ## Setup
6 | - Modify the file /android/app/build.gradle and set the minSdkVersion to 21:
7 | ``` gradle
8 | android {
9 | defaultConfig {
10 | minSdkVersion 21
11 | }
12 | }
13 | ```
14 | - To run the application in release mode, you must add a file named ```proguard-rules.pro``` with the following content to the ```./android/app/``` directory:
15 | ```
16 | -keep class app.wachu.wireguard_vpn.** {*;}
17 | -keep class com.beust.klaxon.** {*;}
18 | ```
19 | - Another option is to add the following to the ```./android/app/build.gradle``` file under the ```buildtypes release```:
20 | ```
21 | shrinkResources false
22 | minifyEnabled false
23 | ```
24 | ### I'd like to thank the user [ByteSizedMarius](https://github.com/ByteSizedMarius) for their contribution regarding the execution in release mode of the package. Thank you.
25 | ## Usage
26 |
27 | To use this plugin, you must first add it to your pubspec.yaml file:
28 |
29 | ``` yaml
30 | dependencies:
31 | wireguard_vpn: ^0.0.2
32 | ```
33 |
34 | Then, import the package in your .dart file:
35 | ``` dart
36 | import 'package:wireguard_vpn/wireguard_vpn.dart';
37 | ```
38 |
39 | ## Activate and Deactivate VPN
40 |
41 | To activate or deactivate the VPN connection, use the changeStateParams method of the WireguardFlutterPlugin class. This method takes a SetStateParams object as a parameter, which includes information about the VPN tunnel.
42 |
43 | ``` dart
44 | bool vpnActivate = false;
45 | String initName = "MyWireguardVPN";
46 | String initAddress = "192.168.1.1/24";
47 | String initDnsServer = "8.8.8.8";
48 | String initPort = "51820";
49 | String initAllowedIp = "0.0.0.0/0";
50 | String initEndpoint = "vpn.example.com:51820";
51 | String initPublicKey = "PUBLIC_KEY";
52 | String initPrivateKey = "PRIVATE_KEY";
53 | String presharedKey = "PRESHARED_KEY";
54 |
55 | final _wireguardFlutterPlugin = WireguardFlutterPlugin();
56 |
57 | void _activateVpn(bool value) async {
58 | final results = await _wireguardFlutterPlugin.changeStateParams(SetStateParams(
59 | state: !vpnActivate,
60 | tunnel: Tunnel(
61 | name: initName,
62 | address: initAddress,
63 | dnsServer: initDnsServer,
64 | listenPort: initPort,
65 | peerAllowedIp: initAllowedIp,
66 | peerEndpoint: initEndpoint,
67 | peerPublicKey: initPublicKey,
68 | privateKey: initPrivateKey,
69 | peerPresharedKey: presharedKey),
70 | ));
71 | setState(() {
72 | vpnActivate = results ?? false;
73 | });
74 | }
75 | ```
76 |
77 | ## Obtain connection statistics
78 |
79 | To obtain statistics of the VPN connection, use the tunnelGetStats method of the WireguardFlutterPlugin class. This method takes the name of the VPN tunnel as a parameter.
80 |
81 | ``` dart
82 | String initName = "MyWireguardVPN";
83 |
84 | final _wireguardFlutterPlugin = WireguardFlutterPlugin();
85 |
86 | void _obtainStats() {
87 | Timer.periodic(const Duration(seconds: 1), (timer) async {
88 | final results = await _wireguardFlutterPlugin.tunnelGetStats(initName);
89 | setState(() {
90 | stats = results ?? Stats(totalDownload: 0, totalUpload: 0);
91 | });
92 | });
93 | }
94 | ```
95 | ## Complete example
96 |
97 | Here's an example of how to use this plugin to activate and deactivate the VPN connection and obtain connection statistics:
98 |
99 | ``` dart
100 | import 'dart:async';
101 |
102 | import 'package:flutter/material.dart';
103 |
104 | import 'package:wireguard_vpn/wireguard_vpn.dart';
105 |
106 | void main() {
107 | runApp(const MyApp());
108 | }
109 |
110 | class MyApp extends StatefulWidget {
111 | const MyApp({super.key});
112 |
113 | @override
114 | State createState() => _MyAppState();
115 | }
116 |
117 | class _MyAppState extends State {
118 | final _wireguardFlutterPlugin = WireguardVpn();
119 | bool vpnActivate = false;
120 | Stats stats = Stats(totalDownload: 0, totalUpload: 0);
121 | final String initName = 'MyWireguardVPN';
122 | final String initAddress = "10.7.0.2/24";
123 | final String initPort = "51820";
124 | final String initDnsServer = "8.8.8.8, 8.8.4.4";
125 | final String initPrivateKey = "PRIVATE_KEY";
126 | final String initAllowedIp = "0.0.0.0/0, ::/0";
127 | final String initPublicKey = "PUBLIC_KEY";
128 | final String initEndpoint = "vpn.example.com:51820";
129 | final String presharedKey = 'PRESHARED_KEY';
130 |
131 | @override
132 | void initState() {
133 | super.initState();
134 | vpnActivate ? _obtainStats() : null;
135 | }
136 |
137 | @override
138 | Widget build(BuildContext context) {
139 | return MaterialApp(
140 | home: Scaffold(
141 | appBar: AppBar(
142 | title: const Text('Wireguard-VPN Example'),
143 | ),
144 | body: Column(
145 | children: [
146 | Text(
147 | 'Active VPN: ${stats.totalDownload} D -- ${stats.totalUpload} U'),
148 | SwitchListTile(
149 | value: vpnActivate,
150 | onChanged: _activateVpn,
151 | title: Text(initName),
152 | subtitle: Text(initEndpoint),
153 | ),
154 | ],
155 | )),
156 | );
157 | }
158 |
159 | void _obtainStats() {
160 | Timer.periodic(const Duration(seconds: 1), (timer) async {
161 | final results = await _wireguardFlutterPlugin.tunnelGetStats(initName);
162 | setState(() {
163 | stats = results ?? Stats(totalDownload: 0, totalUpload: 0);
164 | });
165 | });
166 | }
167 |
168 | void _activateVpn(bool value) async {
169 | final results =
170 | await _wireguardFlutterPlugin.changeStateParams(SetStateParams(
171 | state: !vpnActivate,
172 | tunnel: Tunnel(
173 | name: initName,
174 | address: initAddress,
175 | dnsServer: initDnsServer,
176 | listenPort: initPort,
177 | peerAllowedIp: initAllowedIp,
178 | peerEndpoint: initEndpoint,
179 | peerPublicKey: initPublicKey,
180 | privateKey: initPrivateKey,
181 | peerPresharedKey: presharedKey),
182 | ));
183 | setState(() {
184 | vpnActivate = results ?? false;
185 | vpnActivate ? _obtainStats() : null;
186 | });
187 | }
188 | }
189 | ```
190 | For more information, you can check the [example tab](https://pub.dev/packages/wireguard_vpn/example) or the [GitHub repository](https://github.com/Wachu985/flutter_wireguard_vpn).
191 |
192 | ## Generate WireGuard VPN configurations
193 | **To obtain WireGuard VPN configurations for testing, you can visit the [PotonVPN](https://account.protonvpn.com/login) website, register, and generate a configuration under the downloads section. You can also follow the guide on the official [WireGuard VPN](https://www.wireguard.com/) website.**
194 |
195 | ## Contributions
196 |
197 | Contributions are welcome. If you find a bug or want to add a new feature, please open a new [issue](https://github.com/Wachu985/flutter_wireguard_vpn/issues) or send a [pull request](https://github.com/Wachu985/flutter_wireguard_vpn/pulls).
198 |
199 | ## License
200 |
201 | This package is available under the terms of the [BSD 3-clause license](https://opensource.org/license/bsd-3-clause/). Please refer to the [LICENSE](https://pub.dev/packages/wireguard_vpn/license) file for more information.
202 |
--------------------------------------------------------------------------------
/android/src/main/kotlin/app/wachu/wireguard_vpn/WireguardVpnPlugin.kt:
--------------------------------------------------------------------------------
1 | package app.wachu.wireguard_vpn
2 |
3 | import androidx.annotation.NonNull
4 |
5 | import io.flutter.embedding.engine.plugins.FlutterPlugin
6 | import io.flutter.plugin.common.MethodCall
7 | import io.flutter.plugin.common.MethodChannel
8 | import io.flutter.plugin.common.MethodChannel.MethodCallHandler
9 | import io.flutter.plugin.common.MethodChannel.Result
10 | import io.flutter.embedding.engine.plugins.activity.ActivityAware;
11 | import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding;
12 | import io.flutter.plugin.common.PluginRegistry
13 | import io.flutter.plugin.common.PluginRegistry.Registrar
14 |
15 | import android.app.Activity
16 | import io.flutter.embedding.android.FlutterActivity
17 | import android.app.Application
18 | import android.content.Intent
19 | import android.os.Build
20 | import android.os.Bundle
21 | import android.content.Context
22 | import android.util.Log
23 | import com.beust.klaxon.Klaxon
24 | import com.wireguard.android.backend.*
25 | //import com.wireguard.android.util.ModuleLoader
26 | import com.wireguard.android.util.RootShell
27 | import com.wireguard.android.util.ToolsInstaller
28 | import com.wireguard.config.Config
29 | import com.wireguard.config.Interface
30 | import com.wireguard.config.Peer
31 | import kotlinx.coroutines.*
32 | import java.util.*
33 |
34 |
35 | import kotlinx.coroutines.launch
36 | import java.lang.ref.WeakReference
37 |
38 | /** WireguardVpnPlugin */
39 | class WireguardVpnPlugin: FlutterPlugin, MethodCallHandler ,ActivityAware,PluginRegistry.ActivityResultListener {
40 | /// The MethodChannel that will the communication between Flutter and native Android
41 | ///
42 | /// This local reference serves to register the plugin with the Flutter Engine and unregister it
43 | /// when the Flutter Engine is detached from the Activity
44 | private lateinit var channel : MethodChannel
45 | private val permissionRequestCode = 10014
46 | private val channelName = "wachu985/wireguard-flutter"
47 | private val futureBackend = CompletableDeferred()
48 | private val scope = CoroutineScope(Job() + Dispatchers.Main.immediate)
49 | private var backend: Backend? = null
50 | private lateinit var rootShell: RootShell
51 | private lateinit var toolsInstaller: ToolsInstaller
52 | private var havePermission = false
53 | private lateinit var context:Context
54 | private var activity:Activity? = null
55 |
56 | // Have to keep tunnels, because WireGuard requires to use the _same_
57 | // instance of a tunnel every time you change the state.
58 | private var tunnels = HashMap()
59 |
60 | companion object {
61 | const val TAG = "MainActivity"
62 | }
63 | override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?): Boolean{
64 | //super.onActivityResult(requestCode, resultCode, data)
65 | havePermission = (requestCode == permissionRequestCode) && (resultCode == Activity.RESULT_OK)
66 | return havePermission
67 |
68 | }
69 |
70 | override fun onAttachedToActivity(activityPluginBinding: ActivityPluginBinding) {
71 | Log.d(TAG, "Entre 1")
72 | this.activity = activityPluginBinding.activity as FlutterActivity
73 | // TODO: your plugin is now attached to an Activity
74 | }
75 |
76 | override fun onDetachedFromActivityForConfigChanges() {
77 | // TODO: the Activity your plugin was attached to was
78 | // destroyed to change configuration.
79 | // This call will be followed by onReattachedToActivityForConfigChanges().
80 | this.activity = null;
81 | }
82 |
83 | override fun onReattachedToActivityForConfigChanges(activityPluginBinding: ActivityPluginBinding) {
84 | // TODO: your plugin is now attached to a new Activity
85 | // after a configuration change.
86 | this.activity = activityPluginBinding.activity as FlutterActivity
87 | }
88 |
89 | override fun onDetachedFromActivity() {
90 | // TODO: your plugin is no longer associated with an Activity.
91 | // Clean up references.
92 | this.activity = null;
93 | }
94 |
95 |
96 | override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
97 | channel = MethodChannel(flutterPluginBinding.binaryMessenger, channelName)
98 | context = flutterPluginBinding.applicationContext
99 | rootShell = RootShell(context)
100 | toolsInstaller = ToolsInstaller(context, rootShell)
101 |
102 | scope.launch(Dispatchers.IO) {
103 | try {
104 | backend = createBackend()
105 | Log.e(TAG, "Entre 1")
106 | futureBackend.complete(backend!!)
107 | checkPermission()
108 | } catch (e: Throwable) {
109 | Log.e(TAG, Log.getStackTraceString(e))
110 | }
111 | }
112 | channel.setMethodCallHandler(this)
113 | }
114 |
115 |
116 | override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) {
117 |
118 | when (call.method) {
119 | "getTunnelNames" -> handleGetNames(result)
120 | "setState" -> handleSetState(call.arguments, result)
121 | "getStats" -> handleGetStats(call.arguments, result)
122 | else -> flutterNotImplemented(result)
123 | }
124 | }
125 |
126 | override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) {
127 | channel.setMethodCallHandler(null)
128 | }
129 |
130 | private fun checkPermission() {
131 | val intent = GoBackend.VpnService.prepare(this.activity)
132 | if (intent != null) {
133 | havePermission = false
134 | this.activity?.startActivityForResult(intent, permissionRequestCode)
135 | } else {
136 | havePermission = true
137 | }
138 | }
139 |
140 | private fun createBackend(): Backend {
141 | var backend: Backend? = null
142 | var didStartRootShell = false
143 | try {
144 | if (!didStartRootShell) {
145 | rootShell.start()
146 | }
147 | val wgQuickBackend = WgQuickBackend(context, rootShell, toolsInstaller)
148 | //wgQuickBackend.setMultipleTunnels(UserKnobs.multipleTunnels.first())
149 | backend = wgQuickBackend
150 | // what is that? I totally did not understand
151 | /*UserKnobs.multipleTunnels.onEach {
152 | wgQuickBackend.setMultipleTunnels(it)
153 | }.launchIn(coroutineScope)*/
154 | } catch (ignored: Exception) {
155 | Log.e(TAG, Log.getStackTraceString(ignored))
156 | }
157 | if (backend == null) {
158 | backend = GoBackend(context)
159 | }
160 | return backend
161 | }
162 |
163 | private fun flutterSuccess(result: MethodChannel.Result, o: Any) {
164 | scope.launch(Dispatchers.Main) {
165 | result.success(o)
166 | }
167 | }
168 |
169 | private fun flutterError(result: MethodChannel.Result, error: String) {
170 | scope.launch(Dispatchers.Main) {
171 | result.error(error, null, null)
172 | }
173 | }
174 |
175 | private fun flutterNotImplemented(result: MethodChannel.Result) {
176 | scope.launch(Dispatchers.Main) {
177 | result.notImplemented()
178 | }
179 | }
180 |
181 | private fun handleSetState(arguments: Any, result: MethodChannel.Result) {
182 |
183 | scope.launch(Dispatchers.IO) {
184 | try {
185 | val params = Klaxon().parse(arguments.toString())
186 | if (params == null) {
187 | flutterError(result, "Set state params is missing")
188 | return@launch
189 | }
190 |
191 | val config = Config.Builder()
192 | .setInterface(
193 | Interface.Builder()
194 | .parseAddresses(params.tunnel.address)
195 | .parseListenPort(params.tunnel.listenPort)
196 | .parseDnsServers(params.tunnel.dnsServer)
197 | .parsePrivateKey(params.tunnel.privateKey)
198 | .build(),
199 | )
200 | .addPeer(
201 | Peer.Builder()
202 | .parseAllowedIPs(params.tunnel.peerAllowedIp)
203 | .parsePublicKey(params.tunnel.peerPublicKey)
204 | .parseEndpoint(params.tunnel.peerEndpoint)
205 | .parsePreSharedKey(params.tunnel.peerPresharedKey)
206 | .parsePersistentKeepalive("25")
207 | .build()
208 | )
209 | .build()
210 | //futureBackend.await().setState(MyTunnel(params.tunnel.name), params.tuTunnel.State.UP, config)
211 | futureBackend.await().setState(
212 | tunnel(params.tunnel.name) { state ->
213 | scope.launch(Dispatchers.Main) {
214 | Log.i(TAG, "onStateChange - $state")
215 | channel?.invokeMethod(
216 | "onStateChange",
217 | Klaxon().toJsonString(
218 | StateChangeData(params.tunnel.name, state == Tunnel.State.UP)
219 | )
220 | )
221 | }
222 | },
223 | if (params.state) Tunnel.State.UP else Tunnel.State.DOWN,
224 | config
225 | )
226 | Log.i(TAG, "handleSetState - success!")
227 | flutterSuccess(result, params.state)
228 | } catch (e: BackendException) {
229 | Log.e(TAG, "handleSetState - BackendException - ERROR - ${e.reason}")
230 | flutterError(result, e.reason.toString())
231 | } catch (e: Throwable) {
232 | Log.e(TAG, "handleSetState - Can't set tunnel state: $e, ${Log.getStackTraceString(e)}")
233 | flutterError(result, e.message.toString())
234 | }
235 | }
236 | }
237 |
238 | private fun handleGetNames(result: MethodChannel.Result) {
239 | scope.launch(Dispatchers.IO) {
240 | try {
241 | val names = futureBackend.await().runningTunnelNames
242 | Log.i(TAG, "Success $names")
243 | flutterSuccess(result, names.toString())
244 | } catch (e: Throwable) {
245 | Log.e(TAG, "Can't get tunnel names: " + e.message + " " + e.stackTrace)
246 | flutterError(result, "Can't get tunnel names")
247 | }
248 | }
249 | }
250 |
251 | private fun handleGetStats(arguments: Any?, result: MethodChannel.Result) {
252 | val tunnelName = arguments?.toString()
253 | if (tunnelName == null || tunnelName.isEmpty()) {
254 | flutterError(result, "Provide tunnel name to get statistics")
255 | return
256 | }
257 |
258 | scope.launch(Dispatchers.IO) {
259 |
260 | try {
261 | val stats = futureBackend.await().getStatistics(tunnel(tunnelName))
262 |
263 | flutterSuccess(result, Klaxon().toJsonString(
264 | Stats(stats.totalRx(), stats.totalTx())
265 | ))
266 |
267 | } catch (e: BackendException) {
268 | Log.e(TAG, "handleGetStats - BackendException - ERROR - ${e.reason}")
269 | flutterError(result, e.reason.toString())
270 | } catch (e: Throwable) {
271 | Log.e(TAG, "handleGetStats - Can't get stats: $e")
272 | flutterError(result, e.message.toString())
273 | }
274 | }
275 | }
276 |
277 | /**
278 | * Return existing [MyTunnel] from the [tunnels], or create new, add to the list and return it
279 | */
280 | private fun tunnel(name: String, callback: StateChangeCallback? = null): Tunnel {
281 | return tunnels.getOrPut(name, { MyTunnel(name, callback) })
282 | }
283 | }
284 |
285 |
286 | typealias StateChangeCallback = (Tunnel.State) -> Unit
287 |
288 | class MyTunnel(private val name: String,
289 | private val onStateChanged: StateChangeCallback? = null) : Tunnel {
290 |
291 | override fun getName() = name
292 |
293 | override fun onStateChange(newState: Tunnel.State) {
294 | onStateChanged?.invoke(newState)
295 | }
296 |
297 | }
298 |
299 | class SetStateParams(
300 | val state: Boolean,
301 | val tunnel: TunnelData
302 | )
303 |
304 | class TunnelData(
305 | val name: String,
306 | val address: String,
307 | val listenPort: String,
308 | val dnsServer: String,
309 | val privateKey: String,
310 | val peerAllowedIp: String,
311 | val peerPublicKey: String,
312 | val peerEndpoint: String,
313 | val peerPresharedKey:String
314 | )
315 |
316 | class StateChangeData(
317 | val tunnelName: String,
318 | val tunnelState: Boolean,
319 | )
320 |
321 | class Stats(
322 | val totalDownload: Long,
323 | val totalUpload: Long,
324 | )
325 |
--------------------------------------------------------------------------------