Initial commit (Security Hardened Slate)
This commit is contained in:
commit
157f60aad2
65 files changed
+3735
No files matched your search
@@ -0,0 +1,117 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:process_run/shell.dart';
|
||||
|
||||
import '../wireguard_flutter_platform_interface.dart';
|
||||
|
||||
class WireGuardFlutterLinux extends WireGuardFlutterInterface {
|
||||
String? name;
|
||||
File? configFile;
|
||||
|
||||
VpnStage _stage = VpnStage.noConnection;
|
||||
final _stageController = StreamController<VpnStage>.broadcast();
|
||||
void _setStage(VpnStage stage) {
|
||||
_stage = stage;
|
||||
_stageController.add(stage);
|
||||
}
|
||||
|
||||
final shell = Shell(runInShell: true, verbose: kDebugMode);
|
||||
|
||||
@override
|
||||
Future<void> initialize({required String interfaceName}) async {
|
||||
name = interfaceName.replaceAll(' ', '_');
|
||||
await refreshStage();
|
||||
}
|
||||
|
||||
Future<String> get filePath async {
|
||||
final tempDir = await getTemporaryDirectory();
|
||||
return '${tempDir.path}${Platform.pathSeparator}$name.conf';
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> startVpn({
|
||||
required String serverAddress,
|
||||
required String wgQuickConfig,
|
||||
required String providerBundleIdentifier,
|
||||
}) async {
|
||||
final isAlreadyConnected = await isConnected();
|
||||
if (!isAlreadyConnected) {
|
||||
_setStage(VpnStage.preparing);
|
||||
} else {
|
||||
debugPrint('Already connected');
|
||||
}
|
||||
|
||||
try {
|
||||
configFile = await File(await filePath).create();
|
||||
await configFile!.writeAsString(wgQuickConfig);
|
||||
} on PathAccessException {
|
||||
debugPrint('Denied to write file. Trying to start interface');
|
||||
if (isAlreadyConnected) {
|
||||
return _setStage(VpnStage.connected);
|
||||
}
|
||||
|
||||
try {
|
||||
await shell.run('sudo wg-quick up $name');
|
||||
} catch (_) {
|
||||
} finally {
|
||||
_setStage(VpnStage.denied);
|
||||
}
|
||||
}
|
||||
|
||||
if (!isAlreadyConnected) {
|
||||
_setStage(VpnStage.connecting);
|
||||
await shell.run('sudo wg-quick up ${configFile?.path ?? await filePath}');
|
||||
_setStage(VpnStage.connected);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> stopVpn() async {
|
||||
assert(
|
||||
(await isConnected()),
|
||||
'Bad state: vpn has not been started. Call startVpn',
|
||||
);
|
||||
_setStage(VpnStage.disconnecting);
|
||||
try {
|
||||
await shell
|
||||
.run('sudo wg-quick down ${configFile?.path ?? (await filePath)}');
|
||||
} catch (e) {
|
||||
await refreshStage();
|
||||
rethrow;
|
||||
}
|
||||
await refreshStage();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<VpnStage> stage() async => _stage;
|
||||
|
||||
@override
|
||||
Stream<VpnStage> get vpnStageSnapshot => _stageController.stream;
|
||||
|
||||
@override
|
||||
Future<void> refreshStage() async {
|
||||
if (await isConnected()) {
|
||||
_setStage(VpnStage.connected);
|
||||
} else if (name == null) {
|
||||
_setStage(VpnStage.waitingConnection);
|
||||
} else if (configFile == null) {
|
||||
_setStage(VpnStage.noConnection);
|
||||
} else {
|
||||
_setStage(VpnStage.disconnected);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> isConnected() async {
|
||||
assert(
|
||||
name != null,
|
||||
'Bad state: not initialized. Call "initialize" before calling this command',
|
||||
);
|
||||
final processResultList = await shell.run('sudo wg');
|
||||
final process = processResultList.first;
|
||||
return process.outLines.any((line) => line.trim() == 'interface: $name');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:wireguard_flutter/linux/wireguard_flutter_linux.dart';
|
||||
import 'package:wireguard_flutter/wireguard_flutter_method_channel.dart';
|
||||
|
||||
import 'wireguard_flutter_platform_interface.dart';
|
||||
|
||||
export 'wireguard_flutter_platform_interface.dart' show VpnStage;
|
||||
|
||||
class WireGuardFlutter extends WireGuardFlutterInterface {
|
||||
static WireGuardFlutterInterface? __instance;
|
||||
static WireGuardFlutterInterface get _instance => __instance!;
|
||||
static WireGuardFlutterInterface get instance {
|
||||
registerWith();
|
||||
return _instance;
|
||||
}
|
||||
|
||||
static void registerWith() {
|
||||
if (__instance == null) {
|
||||
if (kIsWeb) {
|
||||
throw UnsupportedError('The web platform is not supported');
|
||||
} else if (Platform.isLinux) {
|
||||
__instance = WireGuardFlutterLinux();
|
||||
} else {
|
||||
__instance = WireGuardFlutterMethodChannel();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WireGuardFlutter._();
|
||||
|
||||
@override
|
||||
Stream<VpnStage> get vpnStageSnapshot => _instance.vpnStageSnapshot;
|
||||
|
||||
@override
|
||||
Future<void> initialize({required String interfaceName}) {
|
||||
return _instance.initialize(interfaceName: interfaceName);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> startVpn({
|
||||
required String serverAddress,
|
||||
required String wgQuickConfig,
|
||||
required String providerBundleIdentifier,
|
||||
}) async {
|
||||
return _instance.startVpn(
|
||||
serverAddress: serverAddress,
|
||||
wgQuickConfig: wgQuickConfig,
|
||||
providerBundleIdentifier: providerBundleIdentifier,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> stopVpn() => _instance.stopVpn();
|
||||
|
||||
@override
|
||||
Future<void> refreshStage() => _instance.refreshStage();
|
||||
|
||||
@override
|
||||
Future<VpnStage> stage() => _instance.stage();
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
import 'wireguard_flutter_platform_interface.dart';
|
||||
|
||||
class WireGuardFlutterMethodChannel extends WireGuardFlutterInterface {
|
||||
static const _methodChannelVpnControl =
|
||||
"billion.group.wireguard_flutter/wgcontrol";
|
||||
static const _methodChannel = MethodChannel(_methodChannelVpnControl);
|
||||
static const _eventChannelVpnStage =
|
||||
'billion.group.wireguard_flutter/wgstage';
|
||||
static const _eventChannel = EventChannel(_eventChannelVpnStage);
|
||||
|
||||
@override
|
||||
Stream<VpnStage> get vpnStageSnapshot =>
|
||||
_eventChannel.receiveBroadcastStream().map(
|
||||
(event) => event == VpnStage.denied.code
|
||||
? VpnStage.disconnected
|
||||
: VpnStage.values.firstWhere(
|
||||
(stage) => stage.code == event,
|
||||
orElse: () => VpnStage.noConnection,
|
||||
),
|
||||
);
|
||||
|
||||
@override
|
||||
Future<void> initialize({required String interfaceName}) {
|
||||
return _methodChannel.invokeMethod("initialize", {
|
||||
"localizedDescription": interfaceName,
|
||||
"win32ServiceName": interfaceName,
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> startVpn({
|
||||
required String serverAddress,
|
||||
required String wgQuickConfig,
|
||||
required String providerBundleIdentifier,
|
||||
}) async {
|
||||
return _methodChannel.invokeMethod("start", {
|
||||
"serverAddress": serverAddress,
|
||||
"wgQuickConfig": wgQuickConfig,
|
||||
"providerBundleIdentifier": providerBundleIdentifier,
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> stopVpn() => _methodChannel.invokeMethod('stop');
|
||||
|
||||
@override
|
||||
Future<void> refreshStage() => _methodChannel.invokeMethod("refresh");
|
||||
|
||||
@override
|
||||
Future<VpnStage> stage() => _methodChannel.invokeMethod("stage").then(
|
||||
(value) => value != null
|
||||
? VpnStage.values.firstWhere(
|
||||
(stage) => stage.code == value.toString(),
|
||||
orElse: () => VpnStage.disconnected,
|
||||
)
|
||||
: VpnStage.disconnected,
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
abstract class WireGuardFlutterInterface {
|
||||
Stream<VpnStage> get vpnStageSnapshot;
|
||||
|
||||
Future<void> initialize({required String interfaceName});
|
||||
|
||||
Future<void> startVpn({
|
||||
required String serverAddress,
|
||||
required String wgQuickConfig,
|
||||
required String providerBundleIdentifier,
|
||||
});
|
||||
|
||||
Future<void> stopVpn();
|
||||
|
||||
Future<void> refreshStage();
|
||||
Future<VpnStage> stage();
|
||||
Future<bool> isConnected() =>
|
||||
stage().then((stage) => stage == VpnStage.connected);
|
||||
}
|
||||
|
||||
enum VpnStage {
|
||||
connected('connected'),
|
||||
connecting('connecting'),
|
||||
disconnecting('disconnecting'),
|
||||
disconnected('disconnected'),
|
||||
waitingConnection('wait_connection'),
|
||||
authenticating('authenticating'),
|
||||
reconnect('reconnect'),
|
||||
noConnection('no_connection'),
|
||||
preparing('prepare'),
|
||||
denied('denied'),
|
||||
exiting('exiting');
|
||||
|
||||
final String code;
|
||||
|
||||
const VpnStage(this.code);
|
||||
}
|
||||
Reference in new issue
Block a user