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
|
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
|
||||||
|
|||||||
31
Makefile
31
Makefile
@@ -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 && \
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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}"
|
||||||
|
|||||||
@@ -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}"
|
||||||
|
|||||||
Reference in New Issue
Block a user