new: add tunnel service for windows and linux
This commit is contained in:
6
.github/workflows/build.yml
vendored
6
.github/workflows/build.yml
vendored
@@ -40,19 +40,19 @@ jobs:
|
||||
filename: hiddify-windows-x64
|
||||
|
||||
- platform: linux-appimage
|
||||
os: ubuntu-latest
|
||||
os: ubuntu-20.04
|
||||
aarch: amd64
|
||||
targets: AppImage
|
||||
filename: hiddify-linux-x64
|
||||
|
||||
- platform: linux-deb
|
||||
os: ubuntu-latest
|
||||
os: ubuntu-20.04
|
||||
aarch: amd64
|
||||
targets: deb
|
||||
filename: hiddify-debian-x64
|
||||
|
||||
- platform: linux-rpm
|
||||
os: ubuntu-latest
|
||||
os: ubuntu-20.04
|
||||
aarch: amd64
|
||||
targets: rpm
|
||||
filename: hiddify-rpm-x64
|
||||
|
||||
31
Makefile
31
Makefile
@@ -6,8 +6,9 @@ IOS_OUT=./ios/Frameworks
|
||||
DESKTOP_OUT=./libcore/bin
|
||||
GEO_ASSETS_DIR=./assets/core
|
||||
|
||||
CORE_PRODUCT_NAME=libcore
|
||||
CORE_NAME=hiddify-$(CORE_PRODUCT_NAME)
|
||||
CORE_PRODUCT_NAME=hiddify-core
|
||||
CORE_NAME=$(CORE_PRODUCT_NAME)
|
||||
SRV_NAME=hiddify-service
|
||||
ifeq ($(CHANNEL),prod)
|
||||
CORE_URL=https://github.com/hiddify/hiddify-next-core/releases/download/v$(core.version)
|
||||
else
|
||||
@@ -81,18 +82,18 @@ ios-release: #not tested
|
||||
|
||||
android-libs:
|
||||
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-aab-libs: android-libs
|
||||
|
||||
windows-libs:
|
||||
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:
|
||||
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
|
||||
@@ -101,13 +102,12 @@ linux-appimage-libs:linux-libs
|
||||
|
||||
macos-libs:
|
||||
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
|
||||
mkdir -p $(DESKTOP_OUT)/ && \
|
||||
rm -rf $(IOS_OUT)/Libcore.xcframework && \
|
||||
curl -L $(CORE_URL)/$(CORE_NAME)-ios.xcframework.tar.gz | tar xz -C "$(IOS_OUT)" && \
|
||||
mv $(IOS_OUT)/$(CORE_NAME)-ios.xcframework $(IOS_OUT)/Libcore.xcframework
|
||||
curl -L $(CORE_URL)/$(CORE_NAME)-ios.tar.gz | tar xz -C "$(IOS_OUT)"
|
||||
|
||||
get-geo-assets:
|
||||
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
|
||||
|
||||
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:
|
||||
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:
|
||||
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:
|
||||
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:
|
||||
rm -rf $(IOS_OUT)/Libcore.xcframework && \
|
||||
|
||||
@@ -1,13 +1,19 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:ffi';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:hiddify/core/utils/ffi_utils.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:win32/win32.dart';
|
||||
|
||||
abstract interface class ConnectionPlatformSource {
|
||||
Future<bool> checkPrivilege();
|
||||
Future<bool> activateTunnel();
|
||||
Future<bool> deactivateTunnel();
|
||||
}
|
||||
|
||||
class ConnectionPlatformSourceImpl
|
||||
@@ -57,6 +63,122 @@ class ConnectionPlatformSourceImpl
|
||||
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 {
|
||||
|
||||
@@ -159,11 +159,16 @@ class ConnectionRepositoryImpl
|
||||
await $(
|
||||
TaskEither(() async {
|
||||
if (options.enableTun) {
|
||||
final hasPrivilege = await platformSource.checkPrivilege();
|
||||
if (!hasPrivilege) {
|
||||
loggy.warning("missing privileges for tun mode");
|
||||
final active = await platformSource.activateTunnel();
|
||||
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);
|
||||
}),
|
||||
@@ -185,10 +190,35 @@ class ConnectionRepositoryImpl
|
||||
|
||||
@override
|
||||
TaskEither<ConnectionFailure, Unit> disconnect() {
|
||||
return exceptionHandler(
|
||||
() => singbox.stop().mapLeft(UnexpectedConnectionFailure.new).run(),
|
||||
UnexpectedConnectionFailure.new,
|
||||
return TaskEither<ConnectionFailure, Unit>.Do(
|
||||
($) async {
|
||||
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
|
||||
|
||||
@@ -120,6 +120,9 @@ install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
|
||||
install(FILES "../libcore/bin/libcore.so" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
|
||||
COMPONENT Runtime)
|
||||
|
||||
install(FILES "../libcore/bin/hiddify-service" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
|
||||
COMPONENT Runtime)
|
||||
|
||||
foreach(bundled_library ${PLUGIN_BUNDLED_LIBRARIES})
|
||||
install(FILES "${bundled_library}"
|
||||
DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
|
||||
|
||||
@@ -87,6 +87,10 @@ set(HIDDIFY_NEXT_LIB "../libcore/bin/libcore.dll")
|
||||
install(FILES "${HIDDIFY_NEXT_LIB}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
|
||||
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)
|
||||
install(FILES "${PLUGIN_BUNDLED_LIBRARIES}"
|
||||
|
||||
Reference in New Issue
Block a user