new: add tunnel service for windows and linux

This commit is contained in:
Hiddify
2024-02-02 17:35:32 +01:00
parent c344191293
commit e56e67bd44
6 changed files with 188 additions and 22 deletions

View File

@@ -40,19 +40,19 @@ jobs:
filename: hiddify-windows-x64 filename: hiddify-windows-x64
- platform: linux-appimage - platform: linux-appimage
os: ubuntu-latest os: ubuntu-20.04
aarch: amd64 aarch: amd64
targets: AppImage targets: AppImage
filename: hiddify-linux-x64 filename: hiddify-linux-x64
- platform: linux-deb - platform: linux-deb
os: ubuntu-latest os: ubuntu-20.04
aarch: amd64 aarch: amd64
targets: deb targets: deb
filename: hiddify-debian-x64 filename: hiddify-debian-x64
- platform: linux-rpm - platform: linux-rpm
os: ubuntu-latest os: ubuntu-20.04
aarch: amd64 aarch: amd64
targets: rpm targets: rpm
filename: hiddify-rpm-x64 filename: hiddify-rpm-x64

View File

@@ -6,8 +6,9 @@ IOS_OUT=./ios/Frameworks
DESKTOP_OUT=./libcore/bin DESKTOP_OUT=./libcore/bin
GEO_ASSETS_DIR=./assets/core GEO_ASSETS_DIR=./assets/core
CORE_PRODUCT_NAME=libcore CORE_PRODUCT_NAME=hiddify-core
CORE_NAME=hiddify-$(CORE_PRODUCT_NAME) CORE_NAME=$(CORE_PRODUCT_NAME)
SRV_NAME=hiddify-service
ifeq ($(CHANNEL),prod) ifeq ($(CHANNEL),prod)
CORE_URL=https://github.com/hiddify/hiddify-next-core/releases/download/v$(core.version) CORE_URL=https://github.com/hiddify/hiddify-next-core/releases/download/v$(core.version)
else else
@@ -81,18 +82,18 @@ ios-release: #not tested
android-libs: android-libs:
mkdir -p $(ANDROID_OUT) mkdir -p $(ANDROID_OUT)
curl -L $(CORE_URL)/$(CORE_NAME)-android.aar.gz | gunzip > $(ANDROID_OUT)/libcore.aar curl -L $(CORE_URL)/$(CORE_NAME)-android.tar.gz | tar xz -C $(ANDROID_OUT)/
android-apk-libs: android-libs android-apk-libs: android-libs
android-aab-libs: android-libs android-aab-libs: android-libs
windows-libs: windows-libs:
mkdir -p $(DESKTOP_OUT) mkdir -p $(DESKTOP_OUT)
curl -L $(CORE_URL)/$(CORE_NAME)-windows-amd64.dll.gz | gunzip > $(DESKTOP_OUT)/libcore.dll curl -L $(CORE_URL)/$(CORE_NAME)-windows-amd64.tar.gz | tar xz -C $(DESKTOP_OUT)/
linux-libs: linux-libs:
mkdir -p $(DESKTOP_OUT) mkdir -p $(DESKTOP_OUT)
curl -L $(CORE_URL)/$(CORE_NAME)-linux-amd64.so.gz | gunzip > $(DESKTOP_OUT)/libcore.so curl -L $(CORE_URL)/$(CORE_NAME)-linux-amd64.tar.gz | tar xz -C $(DESKTOP_OUT)/
linux-deb-libs:linux-libs linux-deb-libs:linux-libs
@@ -101,13 +102,12 @@ linux-appimage-libs:linux-libs
macos-libs: macos-libs:
mkdir -p $(DESKTOP_OUT)/ &&\ mkdir -p $(DESKTOP_OUT)/ &&\
curl -L $(CORE_URL)/$(CORE_NAME)-macos-universal.dylib.gz | gunzip > $(DESKTOP_OUT)/libcore.dylib curl -L $(CORE_URL)/$(CORE_NAME)-macos-universal.tar.gz | tar xz -C $(DESKTOP_OUT)/
ios-libs: #not tested ios-libs: #not tested
mkdir -p $(DESKTOP_OUT)/ && \ mkdir -p $(DESKTOP_OUT)/ && \
rm -rf $(IOS_OUT)/Libcore.xcframework && \ rm -rf $(IOS_OUT)/Libcore.xcframework && \
curl -L $(CORE_URL)/$(CORE_NAME)-ios.xcframework.tar.gz | tar xz -C "$(IOS_OUT)" && \ curl -L $(CORE_URL)/$(CORE_NAME)-ios.tar.gz | tar xz -C "$(IOS_OUT)"
mv $(IOS_OUT)/$(CORE_NAME)-ios.xcframework $(IOS_OUT)/Libcore.xcframework
get-geo-assets: get-geo-assets:
curl -L https://github.com/SagerNet/sing-geoip/releases/latest/download/geoip.db -o $(GEO_ASSETS_DIR)/geoip.db curl -L https://github.com/SagerNet/sing-geoip/releases/latest/download/geoip.db -o $(GEO_ASSETS_DIR)/geoip.db
@@ -117,16 +117,23 @@ build-headers:
make -C libcore -f Makefile headers && mv $(BINDIR)/$(CORE_NAME)-headers.h $(BINDIR)/libcore.h make -C libcore -f Makefile headers && mv $(BINDIR)/$(CORE_NAME)-headers.h $(BINDIR)/libcore.h
build-android-libs: build-android-libs:
make -C libcore -f Makefile android && mv $(BINDIR)/$(CORE_NAME)-android.aar $(ANDROID_OUT)/libcore.aar make -C libcore -f Makefile android
mv $(BINDIR)/$(CORE_NAME).aar $(ANDROID_OUT)/
build-windows-libs: build-windows-libs:
make -C libcore -f Makefile windows-amd64 && mv $(BINDIR)/$(CORE_NAME)-windows-amd64.dll $(DESKTOP_OUT)/libcore.dll make -C libcore -f Makefile windows-amd64
mv $(BINDIR)/$(CORE_NAME).dll $(DESKTOP_OUT)/
mv $(BINDIR)/$(SRV_NAME) $(DESKTOP_OUT)/
build-linux-libs: build-linux-libs:
make -C libcore -f Makefile linux-amd64 && mv $(BINDIR)/$(CORE_NAME)-linux-amd64.so $(DESKTOP_OUT)/libcore.so make -C libcore -f Makefile linux-amd64
mv $(BINDIR)/$(CORE_NAME).so $(DESKTOP_OUT)/
mv $(BINDIR)/$(SRV_NAME) $(DESKTOP_OUT)/
build-macos-libs: build-macos-libs:
make -C libcore -f Makefile macos-universal && mv $(BINDIR)/$(CORE_NAME)-macos-universal.dylib $(DESKTOP_OUT)/libcore.dylib make -C libcore -f Makefile macos-universal
mv $(BINDIR)/$(CORE_NAME).dylib $(DESKTOP_OUT)/
mv $(BINDIR)/$(SRV_NAME) $(DESKTOP_OUT)/
build-ios-libs: build-ios-libs:
rm -rf $(IOS_OUT)/Libcore.xcframework && \ rm -rf $(IOS_OUT)/Libcore.xcframework && \

View File

@@ -1,13 +1,19 @@
import 'dart:async';
import 'dart:convert';
import 'dart:ffi'; import 'dart:ffi';
import 'dart:io'; import 'dart:io';
import 'package:hiddify/core/utils/ffi_utils.dart'; import 'package:hiddify/core/utils/ffi_utils.dart';
import 'package:hiddify/utils/custom_loggers.dart'; import 'package:hiddify/utils/custom_loggers.dart';
import 'package:hiddify/utils/utils.dart';
import 'package:path/path.dart' as p;
import 'package:posix/posix.dart'; import 'package:posix/posix.dart';
import 'package:win32/win32.dart'; import 'package:win32/win32.dart';
abstract interface class ConnectionPlatformSource { abstract interface class ConnectionPlatformSource {
Future<bool> checkPrivilege(); Future<bool> checkPrivilege();
Future<bool> activateTunnel();
Future<bool> deactivateTunnel();
} }
class ConnectionPlatformSourceImpl class ConnectionPlatformSourceImpl
@@ -57,6 +63,122 @@ class ConnectionPlatformSourceImpl
return true; // return true so core handles it return true; // return true so core handles it
} }
} }
@override
Future<bool> activateTunnel() async {
if (!Platform.isWindows && !Platform.isLinux && !Platform.isMacOS) {
return true;
}
try {
final socket = await Socket.connect('127.0.0.1', 18020,
timeout: Duration(seconds: 1));
await socket.close();
return await startTunnelRequest();
} catch (error) {
loggy.warning(
'Tunnel Service is not running. Error: $error.--> Running...');
return await runTunnelService();
}
}
@override
Future<bool> deactivateTunnel() async {
if (!Platform.isWindows && !Platform.isLinux && !Platform.isMacOS) {
return true;
}
try {
return await stopTunnelRequest();
} catch (error) {
loggy.error('Tunnel Service Stop Error: $error.');
return false;
}
}
Future<bool> startTunnelRequest() async {
final params = {
"Ipv6": false,
"ServerPort": "2334",
"StrictRoute": false,
"EndpointIndependentNat": false,
"Stack": "gvisor",
};
final query = mapToQueryString(params);
try {
final request =
await HttpClient().get('localhost', 18020, "/start?$query");
final response = await request.close();
final body = await response.transform(utf8.decoder).join();
loggy.debug(
'Status Code: ${response.statusCode} ${response.reasonPhrase}');
loggy.debug('Response Body: ${body}');
return true;
} catch (error) {
loggy.error('HTTP Request Error: $error');
return false;
}
}
Future<bool> stopTunnelRequest() async {
try {
final request = await HttpClient().get('localhost', 18020, "/stop");
final response = await request.close();
final body = await response.transform(utf8.decoder).join();
loggy.debug(
'Status Code: ${response.statusCode} ${response.reasonPhrase}');
loggy.debug('Response Body: ${body}');
return true;
} catch (error) {
loggy.error('HTTP Request Error: $error');
return false;
}
}
String mapToQueryString(Map<String, dynamic> params) {
return params.entries.map((entry) {
final key = Uri.encodeQueryComponent(entry.key);
final value = Uri.encodeQueryComponent(entry.value.toString());
return '$key=$value';
}).join('&');
}
Future<bool> runTunnelService() async {
final executablePath = getTunnelServicePath();
var command = [executablePath, "install"];
if (Platform.isLinux) {
command.insert(0, 'pkexec');
}
try {
final result =
await Process.run(command[0], command.sublist(1), runInShell: true);
loggy.debug('Shell command executed: ${result.stdout} ${result.stderr}');
return await startTunnelRequest();
} catch (error) {
loggy.error('Error executing shell command: $error');
return false;
}
}
static String getTunnelServicePath() {
String fullPath = "";
final binFolder =
Directory(Platform.resolvedExecutable).parent.absolute.path;
if (Platform.environment.containsKey('FLUTTER_TEST')) {
fullPath = "libcore";
}
if (Platform.isWindows) {
fullPath = p.join(fullPath, "hiddify-service.exe");
} else if (Platform.isMacOS) {
fullPath = p.join(fullPath, "hiddify-service");
} else {
fullPath = p.join(fullPath, "hiddify-service");
}
return "$binFolder/$fullPath";
}
} }
sealed class _TokenElevation extends Struct { sealed class _TokenElevation extends Struct {

View File

@@ -159,11 +159,16 @@ class ConnectionRepositoryImpl
await $( await $(
TaskEither(() async { TaskEither(() async {
if (options.enableTun) { if (options.enableTun) {
final hasPrivilege = await platformSource.checkPrivilege(); final active = await platformSource.activateTunnel();
if (!hasPrivilege) { if (!active) {
loggy.warning("missing privileges for tun mode"); loggy.warning("Possiblity missing privileges for tun mode");
return left(const MissingPrivilege()); return left(const MissingPrivilege());
} }
// final hasPrivilege = await platformSource.checkPrivilege();
// if (!hasPrivilege) {
// loggy.warning("missing privileges for tun mode");
// return left(const MissingPrivilege());
// }
} }
return right(unit); return right(unit);
}), }),
@@ -185,10 +190,35 @@ class ConnectionRepositoryImpl
@override @override
TaskEither<ConnectionFailure, Unit> disconnect() { TaskEither<ConnectionFailure, Unit> disconnect() {
return exceptionHandler( return TaskEither<ConnectionFailure, Unit>.Do(
() => singbox.stop().mapLeft(UnexpectedConnectionFailure.new).run(), ($) async {
UnexpectedConnectionFailure.new, final options = await $(getConfigOption());
);
await $(
TaskEither(() async {
if (options.enableTun) {
final active = await platformSource.deactivateTunnel();
if (!active) {
loggy.warning("Possiblity missing privileges for tun mode");
return left(const MissingPrivilege());
}
// final hasPrivilege = await platformSource.checkPrivilege();
// if (!hasPrivilege) {
// loggy.warning("missing privileges for tun mode");
// return left(const MissingPrivilege());
// }
}
return right(unit);
}),
);
return await $(
singbox.stop()
.mapLeft(UnexpectedConnectionFailure.new),
);
},
).handleExceptions(UnexpectedConnectionFailure.new);
} }
@override @override

View File

@@ -120,6 +120,9 @@ install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
install(FILES "../libcore/bin/libcore.so" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" install(FILES "../libcore/bin/libcore.so" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
COMPONENT Runtime) COMPONENT Runtime)
install(FILES "../libcore/bin/hiddify-service" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
COMPONENT Runtime)
foreach(bundled_library ${PLUGIN_BUNDLED_LIBRARIES}) foreach(bundled_library ${PLUGIN_BUNDLED_LIBRARIES})
install(FILES "${bundled_library}" install(FILES "${bundled_library}"
DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"

View File

@@ -87,6 +87,10 @@ set(HIDDIFY_NEXT_LIB "../libcore/bin/libcore.dll")
install(FILES "${HIDDIFY_NEXT_LIB}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" install(FILES "${HIDDIFY_NEXT_LIB}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
COMPONENT Runtime RENAME libcore.dll) COMPONENT Runtime RENAME libcore.dll)
set(HIDDIFY_NEXT_LIB "../libcore/bin/hiddify-service.exe")
install(FILES "${HIDDIFY_NEXT_LIB}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
COMPONENT Runtime RENAME hiddify-service.exe)
if(PLUGIN_BUNDLED_LIBRARIES) if(PLUGIN_BUNDLED_LIBRARIES)
install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" install(FILES "${PLUGIN_BUNDLED_LIBRARIES}"