123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369 |
- import 'dart:convert';
- import 'dart:io';
- import 'dart:async';
- import 'package:get/get.dart';
- import 'package:yaml_edit/yaml_edit.dart';
- import 'package:path/path.dart' as path;
- import '../../common/constants.dart';
- import '../../const/const.dart';
- import '../../controller/controllers.dart';
- import '../../data/model/NodeMode.dart';
- import '../../utils/shell.dart';
- import '../../utils/utils.dart';
- import '../mode/clash_config.dart';
- class ClashService extends GetxController {
- Process? clashCoreProcess;
- final coreStatus = RunningState.stoped.obs;
- final serviceMode = false.obs;
- bool get clashServiceIsRuning => coreStatus.value == RunningState.running;
- Future<void> updatePorts() async {
- // 检查端口占用
- int newPort = await findAvailablePort(controllers.config.mixedPort.value+1);
- controllers.config.mixedPort.value = newPort;
- controllers.global.updateMsg("混合端口已更新为: $newPort");
- // 检查API端口占用
- int newApiPort = await findAvailablePort(controllers.config.apiAddressPort.value);
- controllers.config.apiAddressPort.value = newApiPort;
- controllers.global.updateMsg("API端口已更新为: $newApiPort");
- int newDnsPort = await findAvailablePort(int.parse(controllers.config.dnsPort.value.split(':').last));
- controllers.config.dnsPort.value = '${controllers.config.dnsPort.value.split(':').first}:$newDnsPort';
- await controllers.config.saveConfig();
- //await controllers.config.readClashCoreApi();
- }
- Future<void> makeInitConfig() async {
- //await updatePorts();
- //await controllers.config.readClashCoreApi();
- var mode = controllers.global.modesSelect.value;
- var clashConfig = ClashConfig(
- mixedPort: controllers.config.mixedPort.value,
- allowLan: true,
- bindAddress: '*',
- mode: mode,
- logLevel: 'debug',
- externalController: '127.0.0.1:${controllers.config.apiAddressPort.value}',
- unifiedDelay: false,
- geodataMode: true,
- tcpConcurrent: false,
- findProcessMode: 'strict',
- globalClientFingerprint: 'chrome',
- dns: DNS(
- enable: false,
- listen: controllers.config.dnsPort.value,
- ipv6: false,
- enhancedMode: kRedirHostMode,
- fakeIpFilter: null,
- nameserver: kDomesticDNS,
- proxyServerNameserver: kDomesticDNS,
- nameserverPolicy: {
- 'geosite:cn,private': kDomesticDNS,
- 'geosite:geolocation-!cn': kForeignDNS,
- },
- ),
- tun: Tun(
- enable: false,
- stack: 'system',
- autoRoute: true,
- autoRedirect: true,
- autoDetectInterface: true,
- dnsHijack: ['any:53'],
- ),
- proxies: [],
- rules: [
- 'GEOIP,CN,DIRECT',
- 'MATCH,DIRECT'
- ]
- );
- try {
- final file = File(path.join(Paths.config.path, Files.makeInitProxyConfig.path ));
- await file.writeAsString(clashConfig.toYaml());
- print('配置文件已成功保存到: ${file.path}');
- } catch (e) {
- print('保存配置文件时发生错误: $e');
- throw Exception('无法保存配置文件');
- }
- }
- Future<bool> startClashCore() async {
- final timeout = const Duration(seconds: 30);
- final checkInterval = const Duration(milliseconds: 200);
- var startTime = DateTime.now();
- await controllers.config.readClashCoreApi();
- try {
- controllers.global.updateMsg("启动内核---${controllers.config.config.value.selected}");
- if (controllers.config.config.value.selected == controllers.config.config.value.selected) {
- controllers.global.updateMsg("启动内核初始化");
- } else {
- controllers.global.updateMsg("启动内核");
- }
- coreStatus.value = RunningState.starting;
- int? exitCode;
- clashCoreProcess = await Process.start(
- Files.assetsCCore.path,
- ['-d', Paths.config.path, '-f', path.join(Paths.config.path, controllers.config.config.value.selected)],
- mode: ProcessStartMode.inheritStdio
- );
- clashCoreProcess!.exitCode.then((code) => exitCode = code);
- if (exitCode != null && exitCode != 0) {
- controllers.global.updateMsg("启动内核错误,请重启点电脑测试");
- return false;
- }
- controllers.core.setApi(controllers.config.clashCoreApiAddress.value, controllers.config.clashCoreApiSecret.value);
- while (DateTime.now().difference(startTime) < timeout) {
- try {
- controllers.global.updateMsg("等待内核启动..");
- await controllers.core.fetchHello();
- break;
- } catch (_) {
- await Future.delayed(checkInterval);
- }
- }
- if (DateTime.now().difference(startTime) >= timeout) {
- coreStatus.value = RunningState.error;
- controllers.global.updateMsg("内核启动超时,重新点击加速后尝试。");
- return false;
- }
-
- await controllers.core.updateConfig();
- coreStatus.value = RunningState.running;
- controllers.global.updateMsg("点击连接 ");
- return true;
- } catch (e) {
- controllers.global.updateMsg("启动内核错误");
- coreStatus.value = RunningState.error;
- return false;
- }
- }
- Future<void> stopClashCore() async {
- coreStatus.value = RunningState.stopping;
- await controllers.global.closeProxy();
- clashCoreProcess?.kill();
- killProcess(ClashName.name);
- coreStatus.value = RunningState.stoped;
- }
- Future<bool> initClashCoreConfig() async {
- controllers.config.config.value.selected = Files.makeProxyConfig.path;
- //await makeInitConfig();
- await startClashCore();
- if (coreStatus.value == RunningState.error) {
- controllers.global.updateMsg("启动内核失败...");
- return false;
- } else {
- await controllers.core.updateVersion();
- controllers.global.updateMsg("启动内核成功...");
- return true;
- }
- }
- Future<void> stopClash() async {
- controllers.config.config.value.selected = Files.makeInitProxyConfig.path;
- if (coreStatus.value == RunningState.running) {
- await controllers.config.readClashCoreApi();
- controllers.core.setApi(controllers.config.clashCoreApiAddress.value, controllers.config.clashCoreApiSecret.value);
- await controllers.core.changeConfig(path.join(Paths.config.path, controllers.config.config.value.selected));
- }
- }
- Future<void> reloadClashCore() async {
- try {
- // if(coreStatus.value == RunningState.stoped){
- // await updatePorts();
- // }
- controllers.config.config.value.selected = Files.makeProxyConfig.path;
- if (coreStatus.value == RunningState.running) {
- controllers.global.updateMsg("切换配置...");
- await controllers.config.readClashCoreApi();
- controllers.core.setApi(controllers.config.clashCoreApiAddress.value, controllers.config.clashCoreApiSecret.value);
- await controllers.core.changeConfig(path.join(Paths.config.path, controllers.config.config.value.selected));
- controllers.global.updateMsg("fetchReloadConfig${controllers.config.clashCoreApiAddress.value}...");
- }
- } catch (e) {
- // if(coreStatus.value == RunningState.stoped){
- // await updatePorts();
- // }
- controllers.global.updateMsg("重新配置...");
- await controllers.config.readClashCoreApi();
- controllers.core.setApi(controllers.config.clashCoreApiAddress.value, controllers.config.clashCoreApiSecret.value);
- await controllers.core.changeConfig(path.join(Paths.config.path, controllers.config.config.value.selected));
- }
- }
- Future<String> generateYamlConfig(List<NodeMode> nodes) async {
- // await updatePorts();
- final config = ClashConfig(
- mixedPort: controllers.config.mixedPort.value,
- allowLan: true,
- bindAddress: '*',
- mode: controllers.global.modesSelect.value == "global" ? "global" : "rule",
- logLevel: 'info',
- externalController: '127.0.0.1:${controllers.config.apiAddressPort.value}',
- unifiedDelay: false,
- geodataMode: true,
- tcpConcurrent: false,
- findProcessMode: 'strict',
- globalClientFingerprint: 'chrome',
- profile: {
- 'store-selected': true,
- 'store-fake-ip': true,
- },
- sniffer: Sniffer(
- enable: true,
- sniff: {
- 'HTTP': {
- 'ports': [80, '8080-8880'],
- 'override-destination': true,
- },
- 'TLS': {
- 'ports': [443, 8443],
- },
- // 'QUIC': {
- // 'ports': [443, 8443],
- // },
- },
- skipDomain: ['www.baidu.com'],
- ),
- dns: DNS(
- enable: true,
- listen: kDnsListenPort,
- ipv6: false,
- enhancedMode: kRedirHostMode,
- fakeIpFilter: null,
- nameserver: kDomesticDNS,
- proxyServerNameserver: kDomesticDNS,
- nameserverPolicy: {
- 'geosite:cn,private': kDomesticDNS,
- 'geosite:geolocation-!cn': kForeignDNS,
- },
- ),
- tun: Tun(
- enable: controllers.global.routeModesSelect.value == "tun" ? true:false,
- stack: 'gvisor',
- autoRoute: true,
- autoRedirect: false,
- autoDetectInterface: true,
- dnsHijack: ['any:53'],
- ),
- proxies: [],
- proxyGroups: [
- ProxyGroup(
- name: 'proxy',
- type: 'select',
- proxies: [],
- ),
- ],
- rules: [
- 'GEOIP,CN,DIRECT',
- 'MATCH,proxy',
- ],
- );
- for (final node in nodes) {
- BaseProxy proxy;
- switch (node.type) {
- case 'trojan':
- proxy = TrojanProxy(
- name: node.name ?? '',
- server: node.host ?? '',
- port: node.port ?? 0,
- password: node.passwd ?? '',
- udp: node.udp == 1,
- sni: node.sni,
- );
- break;
- case 'shadowsocks':
- proxy = SSProxy(
- name: node.name ?? '',
- type: 'ss',
- server: node.host ?? '',
- port: node.port ?? 0,
- password: node.passwd ?? '',
- cipher: node.method ?? '',
- udp: node.udp == 1,
- );
- break;
- case 'v2ray':
- final type = (node.vless == 1) ? 'vless' : 'vmess';
- if (type == 'vless') {
- proxy = VlessProxy(
- name: node.name ?? '',
- uuid: node.uuid ?? '',
- server: node.host ?? '',
- port: node.port ?? 0,
- udp: node.udp == 1,
- flow: '',
- tls: true,
- servername: node.v2Sni,
- realityOpts: {'public-key': node.vlessPulkey},
- network: 'tcp',
- );
- } else {
- proxy = VmessProxy(
- name: node.name ?? '',
- uuid: node.uuid ?? '',
- server: node.host ?? '',
- port: node.port ?? 0,
- alterId: node.v2AlterId,
- cipher: node.method,
- udp: node.udp == 1,
- );
- }
- break;
- default:
- continue;
- }
- config.proxies?.add(proxy.toJson());
- config.proxyGroups?[0].proxies.add(node.name ?? '');
- }
- return config.toYaml();
- }
- Future<void> saveConfigToFile(String filePath, List<NodeMode> nodes) async {
- try {
- final configYaml = await generateYamlConfig(nodes);
- final file = File(filePath);
- await file.writeAsString(configYaml);
- print('配置文件已成功保存到: $filePath');
- } catch (e) {
- print('保存配置文件时发生错误: $e');
- throw Exception('无法保存配置文件');
- }
- }
- }
- extension ConfigToYaml on ClashConfig {
- String toYaml() {
- final yamlMap = toJson();
- final yamlEditor = YamlEditor('');
- yamlEditor.update([], yamlMap);
- return yamlEditor.toString();
- }
- }
|