2024-04-30 23:38:49 +08:00
|
|
|
import 'dart:async';
|
2024-07-13 16:36:08 +08:00
|
|
|
import 'dart:convert';
|
|
|
|
|
import 'dart:ffi';
|
2024-04-30 23:38:49 +08:00
|
|
|
import 'dart:io';
|
|
|
|
|
import 'dart:isolate';
|
2024-07-13 16:36:08 +08:00
|
|
|
import 'package:fl_clash/clash/clash.dart';
|
|
|
|
|
import 'package:fl_clash/enum/enum.dart';
|
|
|
|
|
import 'package:fl_clash/models/models.dart';
|
2024-04-30 23:38:49 +08:00
|
|
|
import 'package:flutter/services.dart';
|
|
|
|
|
|
2024-08-15 16:18:00 +08:00
|
|
|
class Vpn {
|
|
|
|
|
static Vpn? _instance;
|
2024-04-30 23:38:49 +08:00
|
|
|
late MethodChannel methodChannel;
|
2024-07-13 16:36:08 +08:00
|
|
|
ReceivePort? receiver;
|
|
|
|
|
ServiceMessageListener? _serviceMessageHandler;
|
2024-04-30 23:38:49 +08:00
|
|
|
|
2024-08-15 16:18:00 +08:00
|
|
|
Vpn._internal() {
|
|
|
|
|
methodChannel = const MethodChannel("vpn");
|
2024-04-30 23:38:49 +08:00
|
|
|
methodChannel.setMethodCallHandler((call) async {
|
|
|
|
|
switch (call.method) {
|
2024-07-13 16:36:08 +08:00
|
|
|
case "started":
|
|
|
|
|
final fd = call.arguments;
|
|
|
|
|
onStarted(fd);
|
2024-04-30 23:38:49 +08:00
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
throw MissingPluginException();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-15 16:18:00 +08:00
|
|
|
factory Vpn() {
|
|
|
|
|
_instance ??= Vpn._internal();
|
2024-04-30 23:38:49 +08:00
|
|
|
return _instance!;
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-15 16:18:00 +08:00
|
|
|
Future<bool?> startVpn(port) async {
|
2024-07-26 08:05:22 +08:00
|
|
|
final state = clashCore.getState();
|
2024-08-15 16:18:00 +08:00
|
|
|
return await methodChannel.invokeMethod<bool>("start", {
|
2024-07-26 08:05:22 +08:00
|
|
|
'port': state.mixedPort,
|
|
|
|
|
'args': json.encode(state),
|
2024-07-17 17:02:25 +08:00
|
|
|
});
|
2024-04-30 23:38:49 +08:00
|
|
|
}
|
|
|
|
|
|
2024-08-15 16:18:00 +08:00
|
|
|
Future<bool?> stopVpn() async {
|
|
|
|
|
return await methodChannel.invokeMethod<bool>("stop");
|
2024-04-30 23:38:49 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Future<bool?> setProtect(int fd) async {
|
2024-07-13 16:36:08 +08:00
|
|
|
return await methodChannel.invokeMethod<bool?>("setProtect", {'fd': fd});
|
2024-06-16 19:04:33 +08:00
|
|
|
}
|
|
|
|
|
|
2024-04-30 23:38:49 +08:00
|
|
|
Future<bool?> startForeground({
|
|
|
|
|
required String title,
|
|
|
|
|
required String content,
|
|
|
|
|
}) async {
|
|
|
|
|
return await methodChannel.invokeMethod<bool?>("startForeground", {
|
|
|
|
|
'title': title,
|
|
|
|
|
'content': content,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-13 16:36:08 +08:00
|
|
|
onStarted(int? fd) {
|
|
|
|
|
if (receiver != null) {
|
|
|
|
|
receiver!.close();
|
|
|
|
|
receiver == null;
|
2024-04-30 23:38:49 +08:00
|
|
|
}
|
2024-07-13 16:36:08 +08:00
|
|
|
receiver = ReceivePort();
|
|
|
|
|
receiver!.listen((message) {
|
|
|
|
|
_handleServiceMessage(message);
|
|
|
|
|
});
|
2024-08-15 16:18:00 +08:00
|
|
|
clashCore.startTun(fd ?? 0, receiver!.sendPort.nativePort);
|
2024-07-13 16:36:08 +08:00
|
|
|
}
|
2024-06-16 19:04:33 +08:00
|
|
|
|
2024-07-13 16:36:08 +08:00
|
|
|
setServiceMessageHandler(ServiceMessageListener serviceMessageListener) {
|
|
|
|
|
_serviceMessageHandler = serviceMessageListener;
|
2024-06-16 19:04:33 +08:00
|
|
|
}
|
|
|
|
|
|
2024-07-13 16:36:08 +08:00
|
|
|
_handleServiceMessage(String message) {
|
|
|
|
|
final m = ServiceMessage.fromJson(json.decode(message));
|
|
|
|
|
switch (m.type) {
|
|
|
|
|
case ServiceMessageType.protect:
|
|
|
|
|
_serviceMessageHandler?.onProtect(Fd.fromJson(m.data));
|
|
|
|
|
case ServiceMessageType.process:
|
|
|
|
|
_serviceMessageHandler?.onProcess(Process.fromJson(m.data));
|
|
|
|
|
case ServiceMessageType.started:
|
|
|
|
|
_serviceMessageHandler?.onStarted(m.data);
|
|
|
|
|
case ServiceMessageType.loaded:
|
|
|
|
|
_serviceMessageHandler?.onLoaded(m.data);
|
|
|
|
|
}
|
2024-04-30 23:38:49 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-15 16:18:00 +08:00
|
|
|
final vpn = Platform.isAndroid ? Vpn() : null;
|