Underlying VPN Logic
This commit is contained in:
7
Makefile
7
Makefile
@@ -5,7 +5,8 @@ ANDROID_OUT=./android/app/libs
|
|||||||
DESKTOP_OUT=./libcore/bin
|
DESKTOP_OUT=./libcore/bin
|
||||||
GEO_ASSETS_DIR=./assets/core
|
GEO_ASSETS_DIR=./assets/core
|
||||||
|
|
||||||
CORE_NAME=hiddify-libcore
|
CORE_PRODUCT_NAME=libcore
|
||||||
|
CORE_NAME=hiddify-$(CORE_PRODUCT_NAME)
|
||||||
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
|
||||||
@@ -100,7 +101,7 @@ 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)-macos-universal.dylib $(DESKTOP_OUT)/libcore.dylib
|
||||||
|
|
||||||
build-ios-libs: #not tested
|
build-ios-libs: #not tested
|
||||||
make -C libcore -f Makefile ios && mv $(BINDIR)/$(CORE_NAME)-ios.xcframework $(DESKTOP_OUT)/libcore.xcframework
|
make -C libcore -f Makefile ios # && mv $(BINDIR)/$(CORE_PRODUCT_NAME).xcframework $(DESKTOP_OUT)/libcore.xcframework
|
||||||
|
|
||||||
|
|
||||||
release: # Create a new tag for release.
|
release: # Create a new tag for release.
|
||||||
@@ -125,4 +126,4 @@ release: # Create a new tag for release.
|
|||||||
echo "creating git tag : v$${TAG}" && \
|
echo "creating git tag : v$${TAG}" && \
|
||||||
git tag v$${TAG} && \
|
git tag v$${TAG} && \
|
||||||
git push -u origin HEAD --tags && \
|
git push -u origin HEAD --tags && \
|
||||||
echo "Github Actions will detect the new tag and release the new version."'
|
echo "Github Actions will detect the new tag and release the new version."'
|
||||||
|
|||||||
@@ -7,9 +7,18 @@
|
|||||||
objects = {
|
objects = {
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
|
032158B82ADDF8BF008D943B /* VPNManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 032158B72ADDF8BF008D943B /* VPNManager.swift */; };
|
||||||
|
032158BA2ADDFCC9008D943B /* TrafficReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 032158B92ADDFCC9008D943B /* TrafficReader.swift */; };
|
||||||
|
032158BC2ADDFD09008D943B /* SingBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = 032158BB2ADDFD09008D943B /* SingBox.swift */; };
|
||||||
03E392B82ADDA00E000ADF15 /* NetworkExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 03E392B72ADDA00E000ADF15 /* NetworkExtension.framework */; };
|
03E392B82ADDA00E000ADF15 /* NetworkExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 03E392B72ADDA00E000ADF15 /* NetworkExtension.framework */; };
|
||||||
03E392BB2ADDA00F000ADF15 /* PacketTunnelProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03E392BA2ADDA00F000ADF15 /* PacketTunnelProvider.swift */; };
|
03E392BB2ADDA00F000ADF15 /* PacketTunnelProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03E392BA2ADDA00F000ADF15 /* PacketTunnelProvider.swift */; };
|
||||||
03E392C02ADDA00F000ADF15 /* SingBoxPacketTunnel.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 03E392B62ADDA00E000ADF15 /* SingBoxPacketTunnel.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
03E392C02ADDA00F000ADF15 /* SingBoxPacketTunnel.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 03E392B62ADDA00E000ADF15 /* SingBoxPacketTunnel.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||||
|
03E392C92ADDA713000ADF15 /* libcore.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 03E392C82ADDA713000ADF15 /* libcore.xcframework */; };
|
||||||
|
03E392CC2ADDE078000ADF15 /* ExtensionProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03E392CB2ADDE078000ADF15 /* ExtensionProvider.swift */; };
|
||||||
|
03E392CF2ADDEFC8000ADF15 /* FilePath.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03E392CE2ADDEFC8000ADF15 /* FilePath.swift */; };
|
||||||
|
03E392D02ADDF1BD000ADF15 /* FilePath.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03E392CE2ADDEFC8000ADF15 /* FilePath.swift */; };
|
||||||
|
03E392D22ADDF1F4000ADF15 /* ExtensionPlatformInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03E392D12ADDF1F4000ADF15 /* ExtensionPlatformInterface.swift */; };
|
||||||
|
03E392D42ADDF262000ADF15 /* Extension+RunBlocking.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03E392D32ADDF262000ADF15 /* Extension+RunBlocking.swift */; };
|
||||||
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
|
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
|
||||||
331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; };
|
331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; };
|
||||||
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
|
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
|
||||||
@@ -63,6 +72,9 @@
|
|||||||
/* End PBXCopyFilesBuildPhase section */
|
/* End PBXCopyFilesBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
|
032158B72ADDF8BF008D943B /* VPNManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPNManager.swift; sourceTree = "<group>"; };
|
||||||
|
032158B92ADDFCC9008D943B /* TrafficReader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrafficReader.swift; sourceTree = "<group>"; };
|
||||||
|
032158BB2ADDFD09008D943B /* SingBox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SingBox.swift; sourceTree = "<group>"; };
|
||||||
03E392B62ADDA00E000ADF15 /* SingBoxPacketTunnel.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = SingBoxPacketTunnel.appex; sourceTree = BUILT_PRODUCTS_DIR; };
|
03E392B62ADDA00E000ADF15 /* SingBoxPacketTunnel.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = SingBoxPacketTunnel.appex; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
03E392B72ADDA00E000ADF15 /* NetworkExtension.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = NetworkExtension.framework; path = System/Library/Frameworks/NetworkExtension.framework; sourceTree = SDKROOT; };
|
03E392B72ADDA00E000ADF15 /* NetworkExtension.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = NetworkExtension.framework; path = System/Library/Frameworks/NetworkExtension.framework; sourceTree = SDKROOT; };
|
||||||
03E392BA2ADDA00F000ADF15 /* PacketTunnelProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PacketTunnelProvider.swift; sourceTree = "<group>"; };
|
03E392BA2ADDA00F000ADF15 /* PacketTunnelProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PacketTunnelProvider.swift; sourceTree = "<group>"; };
|
||||||
@@ -70,6 +82,11 @@
|
|||||||
03E392BD2ADDA00F000ADF15 /* SingBoxPacketTunnel.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = SingBoxPacketTunnel.entitlements; sourceTree = "<group>"; };
|
03E392BD2ADDA00F000ADF15 /* SingBoxPacketTunnel.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = SingBoxPacketTunnel.entitlements; sourceTree = "<group>"; };
|
||||||
03E392C62ADDA064000ADF15 /* Base.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Base.xcconfig; sourceTree = "<group>"; };
|
03E392C62ADDA064000ADF15 /* Base.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Base.xcconfig; sourceTree = "<group>"; };
|
||||||
03E392C72ADDA26A000ADF15 /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = "<group>"; };
|
03E392C72ADDA26A000ADF15 /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = "<group>"; };
|
||||||
|
03E392C82ADDA713000ADF15 /* libcore.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = libcore.xcframework; path = ../libcore/bin/libcore.xcframework; sourceTree = "<group>"; };
|
||||||
|
03E392CB2ADDE078000ADF15 /* ExtensionProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExtensionProvider.swift; sourceTree = "<group>"; };
|
||||||
|
03E392CE2ADDEFC8000ADF15 /* FilePath.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilePath.swift; sourceTree = "<group>"; };
|
||||||
|
03E392D12ADDF1F4000ADF15 /* ExtensionPlatformInterface.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExtensionPlatformInterface.swift; sourceTree = "<group>"; };
|
||||||
|
03E392D32ADDF262000ADF15 /* Extension+RunBlocking.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Extension+RunBlocking.swift"; sourceTree = "<group>"; };
|
||||||
0F7E04B7207513677AF77112 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = "<group>"; };
|
0F7E04B7207513677AF77112 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = "<group>"; };
|
||||||
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
|
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
|
||||||
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
|
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
|
||||||
@@ -100,6 +117,7 @@
|
|||||||
isa = PBXFrameworksBuildPhase;
|
isa = PBXFrameworksBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
03E392C92ADDA713000ADF15 /* libcore.xcframework in Frameworks */,
|
||||||
03E392B82ADDA00E000ADF15 /* NetworkExtension.framework in Frameworks */,
|
03E392B82ADDA00E000ADF15 /* NetworkExtension.framework in Frameworks */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
@@ -123,16 +141,45 @@
|
|||||||
/* End PBXFrameworksBuildPhase section */
|
/* End PBXFrameworksBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXGroup section */
|
/* Begin PBXGroup section */
|
||||||
|
032158B62ADDF8AF008D943B /* VPN */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
032158B72ADDF8BF008D943B /* VPNManager.swift */,
|
||||||
|
);
|
||||||
|
path = VPN;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
03E392B92ADDA00F000ADF15 /* SingBoxPacketTunnel */ = {
|
03E392B92ADDA00F000ADF15 /* SingBoxPacketTunnel */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
03E392CA2ADDE063000ADF15 /* SingBox */,
|
||||||
03E392BA2ADDA00F000ADF15 /* PacketTunnelProvider.swift */,
|
03E392BA2ADDA00F000ADF15 /* PacketTunnelProvider.swift */,
|
||||||
|
032158B92ADDFCC9008D943B /* TrafficReader.swift */,
|
||||||
03E392BC2ADDA00F000ADF15 /* Info.plist */,
|
03E392BC2ADDA00F000ADF15 /* Info.plist */,
|
||||||
03E392BD2ADDA00F000ADF15 /* SingBoxPacketTunnel.entitlements */,
|
03E392BD2ADDA00F000ADF15 /* SingBoxPacketTunnel.entitlements */,
|
||||||
);
|
);
|
||||||
path = SingBoxPacketTunnel;
|
path = SingBoxPacketTunnel;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
03E392CA2ADDE063000ADF15 /* SingBox */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
03E392CB2ADDE078000ADF15 /* ExtensionProvider.swift */,
|
||||||
|
03E392D12ADDF1F4000ADF15 /* ExtensionPlatformInterface.swift */,
|
||||||
|
03E392D32ADDF262000ADF15 /* Extension+RunBlocking.swift */,
|
||||||
|
032158BB2ADDFD09008D943B /* SingBox.swift */,
|
||||||
|
);
|
||||||
|
path = SingBox;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
03E392CD2ADDE103000ADF15 /* Shared */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
03E392CE2ADDEFC8000ADF15 /* FilePath.swift */,
|
||||||
|
);
|
||||||
|
path = Shared;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
311A4F4314861E02331B8DAC /* Pods */ = {
|
311A4F4314861E02331B8DAC /* Pods */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@@ -169,6 +216,7 @@
|
|||||||
97C146E51CF9000F007C117D = {
|
97C146E51CF9000F007C117D = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
03E392CD2ADDE103000ADF15 /* Shared */,
|
||||||
9740EEB11CF90186004384FC /* Flutter */,
|
9740EEB11CF90186004384FC /* Flutter */,
|
||||||
97C146F01CF9000F007C117D /* Runner */,
|
97C146F01CF9000F007C117D /* Runner */,
|
||||||
03E392B92ADDA00F000ADF15 /* SingBoxPacketTunnel */,
|
03E392B92ADDA00F000ADF15 /* SingBoxPacketTunnel */,
|
||||||
@@ -192,6 +240,7 @@
|
|||||||
97C146F01CF9000F007C117D /* Runner */ = {
|
97C146F01CF9000F007C117D /* Runner */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
032158B62ADDF8AF008D943B /* VPN */,
|
||||||
03E392C72ADDA26A000ADF15 /* Runner.entitlements */,
|
03E392C72ADDA26A000ADF15 /* Runner.entitlements */,
|
||||||
97C146FA1CF9000F007C117D /* Main.storyboard */,
|
97C146FA1CF9000F007C117D /* Main.storyboard */,
|
||||||
97C146FD1CF9000F007C117D /* Assets.xcassets */,
|
97C146FD1CF9000F007C117D /* Assets.xcassets */,
|
||||||
@@ -211,6 +260,7 @@
|
|||||||
60F1D4AAC33ACF5C8307310D /* Pods_Runner.framework */,
|
60F1D4AAC33ACF5C8307310D /* Pods_Runner.framework */,
|
||||||
DDA50BDF2E5E5DDA3995F24D /* Pods_RunnerTests.framework */,
|
DDA50BDF2E5E5DDA3995F24D /* Pods_RunnerTests.framework */,
|
||||||
03E392B72ADDA00E000ADF15 /* NetworkExtension.framework */,
|
03E392B72ADDA00E000ADF15 /* NetworkExtension.framework */,
|
||||||
|
03E392C82ADDA713000ADF15 /* libcore.xcframework */,
|
||||||
);
|
);
|
||||||
name = Frameworks;
|
name = Frameworks;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -449,7 +499,13 @@
|
|||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
032158BA2ADDFCC9008D943B /* TrafficReader.swift in Sources */,
|
||||||
|
032158BC2ADDFD09008D943B /* SingBox.swift in Sources */,
|
||||||
|
03E392D22ADDF1F4000ADF15 /* ExtensionPlatformInterface.swift in Sources */,
|
||||||
|
03E392CC2ADDE078000ADF15 /* ExtensionProvider.swift in Sources */,
|
||||||
03E392BB2ADDA00F000ADF15 /* PacketTunnelProvider.swift in Sources */,
|
03E392BB2ADDA00F000ADF15 /* PacketTunnelProvider.swift in Sources */,
|
||||||
|
03E392CF2ADDEFC8000ADF15 /* FilePath.swift in Sources */,
|
||||||
|
03E392D42ADDF262000ADF15 /* Extension+RunBlocking.swift in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
@@ -467,6 +523,8 @@
|
|||||||
files = (
|
files = (
|
||||||
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
|
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
|
||||||
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
|
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
|
||||||
|
032158B82ADDF8BF008D943B /* VPNManager.swift in Sources */,
|
||||||
|
03E392D02ADDF1BD000ADF15 /* FilePath.swift in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
@@ -525,7 +583,7 @@
|
|||||||
INFOPLIST_FILE = SingBoxPacketTunnel/Info.plist;
|
INFOPLIST_FILE = SingBoxPacketTunnel/Info.plist;
|
||||||
INFOPLIST_KEY_CFBundleDisplayName = SingBoxPacketTunnel;
|
INFOPLIST_KEY_CFBundleDisplayName = SingBoxPacketTunnel;
|
||||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
@@ -566,7 +624,7 @@
|
|||||||
INFOPLIST_FILE = SingBoxPacketTunnel/Info.plist;
|
INFOPLIST_FILE = SingBoxPacketTunnel/Info.plist;
|
||||||
INFOPLIST_KEY_CFBundleDisplayName = SingBoxPacketTunnel;
|
INFOPLIST_KEY_CFBundleDisplayName = SingBoxPacketTunnel;
|
||||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
@@ -604,7 +662,7 @@
|
|||||||
INFOPLIST_FILE = SingBoxPacketTunnel/Info.plist;
|
INFOPLIST_FILE = SingBoxPacketTunnel/Info.plist;
|
||||||
INFOPLIST_KEY_CFBundleDisplayName = SingBoxPacketTunnel;
|
INFOPLIST_KEY_CFBundleDisplayName = SingBoxPacketTunnel;
|
||||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
@@ -663,7 +721,7 @@
|
|||||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
||||||
MTL_ENABLE_DEBUG_INFO = NO;
|
MTL_ENABLE_DEBUG_INFO = NO;
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
SUPPORTED_PLATFORMS = iphoneos;
|
SUPPORTED_PLATFORMS = iphoneos;
|
||||||
@@ -792,7 +850,7 @@
|
|||||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
||||||
MTL_ENABLE_DEBUG_INFO = YES;
|
MTL_ENABLE_DEBUG_INFO = YES;
|
||||||
ONLY_ACTIVE_ARCH = YES;
|
ONLY_ACTIVE_ARCH = YES;
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
@@ -841,7 +899,7 @@
|
|||||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
||||||
MTL_ENABLE_DEBUG_INFO = NO;
|
MTL_ENABLE_DEBUG_INFO = NO;
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
SUPPORTED_PLATFORMS = iphoneos;
|
SUPPORTED_PLATFORMS = iphoneos;
|
||||||
|
|||||||
@@ -2,6 +2,10 @@
|
|||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
|
<key>com.apple.developer.networking.networkextension</key>
|
||||||
|
<array>
|
||||||
|
<string>packet-tunnel-provider</string>
|
||||||
|
</array>
|
||||||
<key>com.apple.security.application-groups</key>
|
<key>com.apple.security.application-groups</key>
|
||||||
<array>
|
<array>
|
||||||
<string>group.$(BASE_BUNDLE_IDENTIFIER)</string>
|
<string>group.$(BASE_BUNDLE_IDENTIFIER)</string>
|
||||||
|
|||||||
177
ios/Runner/VPN/VPNManager.swift
Normal file
177
ios/Runner/VPN/VPNManager.swift
Normal file
@@ -0,0 +1,177 @@
|
|||||||
|
//
|
||||||
|
// VPNManager.swift
|
||||||
|
// Runner
|
||||||
|
//
|
||||||
|
// Created by GFWFighter on 7/25/1402 AP.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import Combine
|
||||||
|
import NetworkExtension
|
||||||
|
|
||||||
|
class VPNManager: ObservableObject {
|
||||||
|
private var cancelBag: Set<AnyCancellable> = []
|
||||||
|
|
||||||
|
private var observer: NSObjectProtocol?
|
||||||
|
private var manager = NEVPNManager.shared()
|
||||||
|
private var loaded: Bool = false
|
||||||
|
private var timer: Timer?
|
||||||
|
|
||||||
|
static let shared: VPNManager = VPNManager()
|
||||||
|
|
||||||
|
@Published private(set) var state: NEVPNStatus = .invalid
|
||||||
|
|
||||||
|
@Published private(set) var upload: Int64 = 0
|
||||||
|
@Published private(set) var download: Int64 = 0
|
||||||
|
@Published private(set) var elapsedTime: TimeInterval = 0
|
||||||
|
|
||||||
|
private var _connectTime: Date?
|
||||||
|
private var connectTime: Date? {
|
||||||
|
set {
|
||||||
|
UserDefaults(suiteName: FilePath.groupName)?.set(newValue?.timeIntervalSince1970, forKey: "SingBoxConnectTime")
|
||||||
|
_connectTime = newValue
|
||||||
|
}
|
||||||
|
get {
|
||||||
|
if let _connectTime {
|
||||||
|
return _connectTime
|
||||||
|
}
|
||||||
|
guard let interval = UserDefaults(suiteName: FilePath.groupName)?.value(forKey: "SingBoxConnectTime") as? TimeInterval else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return Date(timeIntervalSince1970: interval)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private var readingWS: Bool = false
|
||||||
|
|
||||||
|
@Published var isConnectedToAnyVPN: Bool = false
|
||||||
|
|
||||||
|
init() {
|
||||||
|
observer = NotificationCenter.default.addObserver(forName: .NEVPNStatusDidChange, object: nil, queue: nil) { [weak self] notification in
|
||||||
|
guard let connection = notification.object as? NEVPNConnection else { return }
|
||||||
|
self?.state = connection.status
|
||||||
|
}
|
||||||
|
|
||||||
|
timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { [weak self] _ in
|
||||||
|
guard let self else { return }
|
||||||
|
updateStats()
|
||||||
|
elapsedTime = -1 * (connectTime?.timeIntervalSinceNow ?? 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
if let observer {
|
||||||
|
NotificationCenter.default.removeObserver(observer)
|
||||||
|
}
|
||||||
|
timer?.invalidate()
|
||||||
|
}
|
||||||
|
|
||||||
|
func setup() async throws {
|
||||||
|
// guard !loaded else { return }
|
||||||
|
loaded = true
|
||||||
|
try await loadVPNPreference()
|
||||||
|
}
|
||||||
|
|
||||||
|
private func loadVPNPreference() async throws {
|
||||||
|
let managers = try await NETunnelProviderManager.loadAllFromPreferences()
|
||||||
|
if let manager = managers.first {
|
||||||
|
self.manager = manager
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let newManager = NETunnelProviderManager()
|
||||||
|
let `protocol` = NETunnelProviderProtocol()
|
||||||
|
`protocol`.providerBundleIdentifier = "\(FilePath.packageName).SingBoxPacketTunnel"
|
||||||
|
`protocol`.serverAddress = "Hiddify"
|
||||||
|
newManager.protocolConfiguration = `protocol`
|
||||||
|
newManager.localizedDescription = "Hiddify"
|
||||||
|
try await newManager.saveToPreferences()
|
||||||
|
try await newManager.loadFromPreferences()
|
||||||
|
self.manager = newManager
|
||||||
|
}
|
||||||
|
|
||||||
|
private func enableVPNManager() async throws {
|
||||||
|
manager.isEnabled = true
|
||||||
|
try await manager.saveToPreferences()
|
||||||
|
try await manager.loadFromPreferences()
|
||||||
|
}
|
||||||
|
|
||||||
|
@MainActor private func set(upload: Int64, download: Int64) {
|
||||||
|
self.upload = upload
|
||||||
|
self.download = download
|
||||||
|
}
|
||||||
|
|
||||||
|
var isAnyVPNConnected: Bool {
|
||||||
|
let cfDict = CFNetworkCopySystemProxySettings()
|
||||||
|
let nsDict = cfDict!.takeRetainedValue() as NSDictionary
|
||||||
|
guard let keys = nsDict["__SCOPED__"] as? NSDictionary else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for key: String in keys.allKeys as! [String] {
|
||||||
|
if (key == "tap" || key == "tun" || key == "ppp" || key == "ipsec" || key == "ipsec0" || key == "utun1" || key == "utun2") {
|
||||||
|
return true
|
||||||
|
} else if key.starts(with: "utun") {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func reset() {
|
||||||
|
loaded = false
|
||||||
|
if state != .disconnected && state != .invalid {
|
||||||
|
disconnect()
|
||||||
|
}
|
||||||
|
$state.filter { $0 == .disconnected || $0 == .invalid }.first().sink { [weak self] _ in
|
||||||
|
Task { [weak self] () in
|
||||||
|
self?.manager = .shared()
|
||||||
|
let managers = try? await NETunnelProviderManager.loadAllFromPreferences()
|
||||||
|
for manager in managers ?? [] {
|
||||||
|
try? await manager.removeFromPreferences()
|
||||||
|
}
|
||||||
|
try? await self?.loadVPNPreference()
|
||||||
|
}
|
||||||
|
}.store(in: &cancelBag)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private func updateStats() {
|
||||||
|
let isAnyVPNConnected = self.isAnyVPNConnected
|
||||||
|
if isConnectedToAnyVPN != isAnyVPNConnected {
|
||||||
|
isConnectedToAnyVPN = isAnyVPNConnected
|
||||||
|
}
|
||||||
|
guard state == .connected else { return }
|
||||||
|
guard let connection = manager.connection as? NETunnelProviderSession else { return }
|
||||||
|
try? connection.sendProviderMessage("stats".data(using: .utf8)!) { [weak self] response in
|
||||||
|
guard
|
||||||
|
let response,
|
||||||
|
let response = String(data: response, encoding: .utf8)
|
||||||
|
else { return }
|
||||||
|
let responseComponents = response.components(separatedBy: ",")
|
||||||
|
guard
|
||||||
|
responseComponents.count == 2,
|
||||||
|
let upload = Int64(responseComponents[0]),
|
||||||
|
let download = Int64(responseComponents[1])
|
||||||
|
else { return }
|
||||||
|
Task { [upload, download, weak self] () in
|
||||||
|
await self?.set(upload: upload, download: download)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func connect(with config: String, disableMemoryLimit: Bool = false) async throws {
|
||||||
|
await set(upload: 0, download: 0)
|
||||||
|
guard state == .disconnected else { return }
|
||||||
|
try await enableVPNManager()
|
||||||
|
try manager.connection.startVPNTunnel(options: [
|
||||||
|
"Config": config as NSString,
|
||||||
|
"DisableMemoryLimit": (disableMemoryLimit ? "YES" : "NO") as NSString,
|
||||||
|
])
|
||||||
|
connectTime = .now
|
||||||
|
}
|
||||||
|
|
||||||
|
func disconnect() {
|
||||||
|
guard state == .connected else { return }
|
||||||
|
manager.connection.stopVPNTunnel()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
40
ios/Shared/FilePath.swift
Normal file
40
ios/Shared/FilePath.swift
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
//
|
||||||
|
// FilePath.swift
|
||||||
|
// SingBoxPacketTunnel
|
||||||
|
//
|
||||||
|
// Created by GFWFighter on 7/25/1402 AP.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
public enum FilePath {
|
||||||
|
public static let packageName = {
|
||||||
|
Bundle.main.infoDictionary?["BASE_BUNDLE_IDENTIFIER"] as? String ?? "unknown"
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
public extension FilePath {
|
||||||
|
static let groupName = "group.\(packageName)"
|
||||||
|
|
||||||
|
private static let defaultSharedDirectory: URL! = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: FilePath.groupName)
|
||||||
|
|
||||||
|
static let sharedDirectory = defaultSharedDirectory!
|
||||||
|
|
||||||
|
static let cacheDirectory = sharedDirectory
|
||||||
|
.appendingPathComponent("Library", isDirectory: true)
|
||||||
|
.appendingPathComponent("Caches", isDirectory: true)
|
||||||
|
|
||||||
|
static let assetsDirectory = cacheDirectory.appendingPathComponent("Assets", isDirectory: true)
|
||||||
|
|
||||||
|
static let workingDirectory = cacheDirectory.appendingPathComponent("Working", isDirectory: true)
|
||||||
|
}
|
||||||
|
|
||||||
|
public extension URL {
|
||||||
|
var fileName: String {
|
||||||
|
var path = relativePath
|
||||||
|
if let index = path.lastIndex(of: "/") {
|
||||||
|
path = String(path[path.index(index, offsetBy: 1)...])
|
||||||
|
}
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,30 +7,31 @@
|
|||||||
|
|
||||||
import NetworkExtension
|
import NetworkExtension
|
||||||
|
|
||||||
class PacketTunnelProvider: NEPacketTunnelProvider {
|
class PacketTunnelProvider: ExtensionProvider {
|
||||||
|
|
||||||
override func startTunnel(options: [String : NSObject]?, completionHandler: @escaping (Error?) -> Void) {
|
private var upload: Int64 = 0
|
||||||
// Add code here to start the process of connecting the tunnel.
|
private var download: Int64 = 0
|
||||||
}
|
private var trafficLock: NSLock = NSLock()
|
||||||
|
|
||||||
override func stopTunnel(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) {
|
var trafficReader: TrafficReader!
|
||||||
// Add code here to start the process of stopping the tunnel.
|
|
||||||
completionHandler()
|
|
||||||
}
|
|
||||||
|
|
||||||
override func handleAppMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)?) {
|
override func startTunnel(options: [String : NSObject]?) async throws {
|
||||||
// Add code here to handle the message.
|
try await super.startTunnel(options: options)
|
||||||
if let handler = completionHandler {
|
trafficReader = TrafficReader { [unowned self] traffic in
|
||||||
handler(messageData)
|
trafficLock.lock()
|
||||||
|
upload += traffic.up
|
||||||
|
download += traffic.down
|
||||||
|
trafficLock.unlock()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override func sleep(completionHandler: @escaping () -> Void) {
|
override func handleAppMessage(_ messageData: Data) async -> Data? {
|
||||||
// Add code here to get ready to sleep.
|
let message = String(data: messageData, encoding: .utf8)
|
||||||
completionHandler()
|
switch message {
|
||||||
}
|
case "stats":
|
||||||
|
return "\(upload),\(download)".data(using: .utf8)!
|
||||||
override func wake() {
|
default:
|
||||||
// Add code here to wake up.
|
return nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
43
ios/SingBoxPacketTunnel/SingBox/Extension+RunBlocking.swift
Normal file
43
ios/SingBoxPacketTunnel/SingBox/Extension+RunBlocking.swift
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
//
|
||||||
|
// Extension+RunBlocking.swift
|
||||||
|
// SingBoxPacketTunnel
|
||||||
|
//
|
||||||
|
// Created by GFWFighter on 7/25/1402 AP.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import Libcore
|
||||||
|
import NetworkExtension
|
||||||
|
|
||||||
|
func runBlocking<T>(_ block: @escaping () async -> T) -> T {
|
||||||
|
let semaphore = DispatchSemaphore(value: 0)
|
||||||
|
let box = resultBox<T>()
|
||||||
|
Task.detached {
|
||||||
|
let value = await block()
|
||||||
|
box.result0 = value
|
||||||
|
semaphore.signal()
|
||||||
|
}
|
||||||
|
semaphore.wait()
|
||||||
|
return box.result0
|
||||||
|
}
|
||||||
|
|
||||||
|
func runBlocking<T>(_ tBlock: @escaping () async throws -> T) throws -> T {
|
||||||
|
let semaphore = DispatchSemaphore(value: 0)
|
||||||
|
let box = resultBox<T>()
|
||||||
|
Task.detached {
|
||||||
|
do {
|
||||||
|
let value = try await tBlock()
|
||||||
|
box.result = .success(value)
|
||||||
|
} catch {
|
||||||
|
box.result = .failure(error)
|
||||||
|
}
|
||||||
|
semaphore.signal()
|
||||||
|
}
|
||||||
|
semaphore.wait()
|
||||||
|
return try box.result.get()
|
||||||
|
}
|
||||||
|
|
||||||
|
private class resultBox<T> {
|
||||||
|
var result: Result<T, Error>!
|
||||||
|
var result0: T!
|
||||||
|
}
|
||||||
224
ios/SingBoxPacketTunnel/SingBox/ExtensionPlatformInterface.swift
Normal file
224
ios/SingBoxPacketTunnel/SingBox/ExtensionPlatformInterface.swift
Normal file
@@ -0,0 +1,224 @@
|
|||||||
|
//
|
||||||
|
// ExtensionPlatformInterface.swift
|
||||||
|
// SingBoxPacketTunnel
|
||||||
|
//
|
||||||
|
// Created by GFWFighter on 7/25/1402 AP.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import Libcore
|
||||||
|
import NetworkExtension
|
||||||
|
|
||||||
|
public class ExtensionPlatformInterface: NSObject, LibboxPlatformInterfaceProtocol, LibboxCommandServerHandlerProtocol {
|
||||||
|
private let tunnel: ExtensionProvider
|
||||||
|
private var networkSettings: NEPacketTunnelNetworkSettings?
|
||||||
|
|
||||||
|
init(_ tunnel: ExtensionProvider) {
|
||||||
|
self.tunnel = tunnel
|
||||||
|
}
|
||||||
|
|
||||||
|
public func openTun(_ options: LibboxTunOptionsProtocol?, ret0_: UnsafeMutablePointer<Int32>?) throws {
|
||||||
|
try runBlocking { [self] in
|
||||||
|
try await openTun0(options, ret0_)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func openTun0(_ options: LibboxTunOptionsProtocol?, _ ret0_: UnsafeMutablePointer<Int32>?) async throws {
|
||||||
|
guard let options else {
|
||||||
|
throw NSError(domain: "nil options", code: 0)
|
||||||
|
}
|
||||||
|
guard let ret0_ else {
|
||||||
|
throw NSError(domain: "nil return pointer", code: 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
let settings = NEPacketTunnelNetworkSettings(tunnelRemoteAddress: "127.0.0.1")
|
||||||
|
if options.getAutoRoute() {
|
||||||
|
settings.mtu = NSNumber(value: options.getMTU())
|
||||||
|
|
||||||
|
var error: NSError?
|
||||||
|
let dnsServer = options.getDNSServerAddress(&error)
|
||||||
|
if let error {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
settings.dnsSettings = NEDNSSettings(servers: [dnsServer])
|
||||||
|
|
||||||
|
var ipv4Address: [String] = []
|
||||||
|
var ipv4Mask: [String] = []
|
||||||
|
let ipv4AddressIterator = options.getInet4Address()!
|
||||||
|
while ipv4AddressIterator.hasNext() {
|
||||||
|
let ipv4Prefix = ipv4AddressIterator.next()!
|
||||||
|
ipv4Address.append(ipv4Prefix.address)
|
||||||
|
ipv4Mask.append(ipv4Prefix.mask())
|
||||||
|
}
|
||||||
|
let ipv4Settings = NEIPv4Settings(addresses: ipv4Address, subnetMasks: ipv4Mask)
|
||||||
|
var ipv4Routes: [NEIPv4Route] = []
|
||||||
|
let inet4RouteAddressIterator = options.getInet4RouteAddress()!
|
||||||
|
if inet4RouteAddressIterator.hasNext() {
|
||||||
|
while inet4RouteAddressIterator.hasNext() {
|
||||||
|
let ipv4RoutePrefix = inet4RouteAddressIterator.next()!
|
||||||
|
ipv4Routes.append(NEIPv4Route(destinationAddress: ipv4RoutePrefix.address, subnetMask: ipv4RoutePrefix.mask()))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ipv4Routes.append(NEIPv4Route.default())
|
||||||
|
}
|
||||||
|
for (index, address) in ipv4Address.enumerated() {
|
||||||
|
ipv4Routes.append(NEIPv4Route(destinationAddress: address, subnetMask: ipv4Mask[index]))
|
||||||
|
}
|
||||||
|
ipv4Settings.includedRoutes = ipv4Routes
|
||||||
|
settings.ipv4Settings = ipv4Settings
|
||||||
|
|
||||||
|
var ipv6Address: [String] = []
|
||||||
|
var ipv6Prefixes: [NSNumber] = []
|
||||||
|
let ipv6AddressIterator = options.getInet6Address()!
|
||||||
|
while ipv6AddressIterator.hasNext() {
|
||||||
|
let ipv6Prefix = ipv6AddressIterator.next()!
|
||||||
|
ipv6Address.append(ipv6Prefix.address)
|
||||||
|
ipv6Prefixes.append(NSNumber(value: ipv6Prefix.prefix))
|
||||||
|
}
|
||||||
|
let ipv6Settings = NEIPv6Settings(addresses: ipv6Address, networkPrefixLengths: ipv6Prefixes)
|
||||||
|
var ipv6Routes: [NEIPv6Route] = []
|
||||||
|
let inet6RouteAddressIterator = options.getInet6RouteAddress()!
|
||||||
|
if inet6RouteAddressIterator.hasNext() {
|
||||||
|
while inet6RouteAddressIterator.hasNext() {
|
||||||
|
let ipv6RoutePrefix = inet4RouteAddressIterator.next()!
|
||||||
|
ipv6Routes.append(NEIPv6Route(destinationAddress: ipv6RoutePrefix.description, networkPrefixLength: NSNumber(value: ipv6RoutePrefix.prefix)))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ipv6Routes.append(NEIPv6Route.default())
|
||||||
|
}
|
||||||
|
ipv6Settings.includedRoutes = ipv6Routes
|
||||||
|
settings.ipv6Settings = ipv6Settings
|
||||||
|
}
|
||||||
|
|
||||||
|
if options.isHTTPProxyEnabled() {
|
||||||
|
let proxySettings = NEProxySettings()
|
||||||
|
let proxyServer = NEProxyServer(address: options.getHTTPProxyServer(), port: Int(options.getHTTPProxyServerPort()))
|
||||||
|
proxySettings.httpServer = proxyServer
|
||||||
|
proxySettings.httpsServer = proxyServer
|
||||||
|
settings.proxySettings = proxySettings
|
||||||
|
}
|
||||||
|
|
||||||
|
networkSettings = settings
|
||||||
|
try await tunnel.setTunnelNetworkSettings(settings)
|
||||||
|
|
||||||
|
if let tunFd = tunnel.packetFlow.value(forKeyPath: "socket.fileDescriptor") as? Int32 {
|
||||||
|
ret0_.pointee = tunFd
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let tunFdFromLoop = LibboxGetTunnelFileDescriptor()
|
||||||
|
if tunFdFromLoop != -1 {
|
||||||
|
ret0_.pointee = tunFdFromLoop
|
||||||
|
} else {
|
||||||
|
throw NSError(domain: "missing file descriptor", code: 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func usePlatformAutoDetectControl() -> Bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
public func autoDetectControl(_: Int32) throws {}
|
||||||
|
|
||||||
|
public func findConnectionOwner(_: Int32, sourceAddress _: String?, sourcePort _: Int32, destinationAddress _: String?, destinationPort _: Int32, ret0_ _: UnsafeMutablePointer<Int32>?) throws {
|
||||||
|
throw NSError(domain: "not implemented", code: 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func packageName(byUid _: Int32, error _: NSErrorPointer) -> String {
|
||||||
|
""
|
||||||
|
}
|
||||||
|
|
||||||
|
public func uid(byPackageName _: String?, ret0_ _: UnsafeMutablePointer<Int32>?) throws {
|
||||||
|
throw NSError(domain: "not implemented", code: 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func useProcFS() -> Bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
public func writeLog(_ message: String?) {
|
||||||
|
guard let message else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
tunnel.writeMessage(message)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func usePlatformDefaultInterfaceMonitor() -> Bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
public func startDefaultInterfaceMonitor(_: LibboxInterfaceUpdateListenerProtocol?) throws {}
|
||||||
|
|
||||||
|
public func closeDefaultInterfaceMonitor(_: LibboxInterfaceUpdateListenerProtocol?) throws {}
|
||||||
|
|
||||||
|
public func useGetter() -> Bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
public func getInterfaces() throws -> LibboxNetworkInterfaceIteratorProtocol {
|
||||||
|
throw NSError(domain: "not implemented", code: 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func underNetworkExtension() -> Bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
public func clearDNSCache() {
|
||||||
|
guard let networkSettings else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
tunnel.reasserting = true
|
||||||
|
tunnel.setTunnelNetworkSettings(nil) { _ in
|
||||||
|
}
|
||||||
|
tunnel.setTunnelNetworkSettings(networkSettings) { _ in
|
||||||
|
}
|
||||||
|
tunnel.reasserting = false
|
||||||
|
}
|
||||||
|
|
||||||
|
public func serviceReload() throws {
|
||||||
|
runBlocking { [self] in
|
||||||
|
await tunnel.reloadService()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func getSystemProxyStatus() -> LibboxSystemProxyStatus? {
|
||||||
|
let status = LibboxSystemProxyStatus()
|
||||||
|
guard let networkSettings else {
|
||||||
|
return status
|
||||||
|
}
|
||||||
|
guard let proxySettings = networkSettings.proxySettings else {
|
||||||
|
return status
|
||||||
|
}
|
||||||
|
if proxySettings.httpServer == nil {
|
||||||
|
return status
|
||||||
|
}
|
||||||
|
status.available = true
|
||||||
|
status.enabled = proxySettings.httpEnabled
|
||||||
|
return status
|
||||||
|
}
|
||||||
|
|
||||||
|
public func setSystemProxyEnabled(_ isEnabled: Bool) throws {
|
||||||
|
guard let networkSettings else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
guard let proxySettings = networkSettings.proxySettings else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if proxySettings.httpServer == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if proxySettings.httpEnabled == isEnabled {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
proxySettings.httpEnabled = isEnabled
|
||||||
|
proxySettings.httpsEnabled = isEnabled
|
||||||
|
networkSettings.proxySettings = proxySettings
|
||||||
|
try runBlocking {
|
||||||
|
try await self.tunnel.setTunnelNetworkSettings(networkSettings)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func reset() {
|
||||||
|
networkSettings = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
160
ios/SingBoxPacketTunnel/SingBox/ExtensionProvider.swift
Normal file
160
ios/SingBoxPacketTunnel/SingBox/ExtensionProvider.swift
Normal file
@@ -0,0 +1,160 @@
|
|||||||
|
//
|
||||||
|
// ExtensionProvider.swift
|
||||||
|
// SingBoxPacketTunnel
|
||||||
|
//
|
||||||
|
// Created by GFWFighter on 7/25/1402 AP.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import Libcore
|
||||||
|
import NetworkExtension
|
||||||
|
|
||||||
|
open class ExtensionProvider: NEPacketTunnelProvider {
|
||||||
|
public static let errorFile = FilePath.workingDirectory.appendingPathComponent("network_extension_error")
|
||||||
|
|
||||||
|
private var commandServer: LibboxCommandServer!
|
||||||
|
private var boxService: LibboxBoxService!
|
||||||
|
private var systemProxyAvailable = false
|
||||||
|
private var systemProxyEnabled = false
|
||||||
|
private var platformInterface: ExtensionPlatformInterface!
|
||||||
|
private var config: String!
|
||||||
|
|
||||||
|
override open func startTunnel(options: [String: NSObject]?) async throws {
|
||||||
|
let disableMemoryLimit = (options?["DisableMemoryLimit"] as? NSString as? String ?? "NO") == "YES"
|
||||||
|
|
||||||
|
guard let config = options?["Config"] as? NSString as? String else {
|
||||||
|
writeFatalError("(packet-tunnel) error: config not provided")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
guard let config = SingBox.setupConfig(config: config) else {
|
||||||
|
writeFatalError("(packet-tunnel) error: config is invalid")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.config = config
|
||||||
|
|
||||||
|
try? FileManager.default.removeItem(at: ExtensionProvider.errorFile)
|
||||||
|
|
||||||
|
do {
|
||||||
|
try FileManager.default.createDirectory(at: FilePath.workingDirectory, withIntermediateDirectories: true)
|
||||||
|
} catch {
|
||||||
|
writeFatalError("(packet-tunnel) error: create working directory: \(error.localizedDescription)")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
LibboxSetup(FilePath.sharedDirectory.relativePath, FilePath.workingDirectory.relativePath, FilePath.cacheDirectory.relativePath, false)
|
||||||
|
|
||||||
|
var error: NSError?
|
||||||
|
LibboxRedirectStderr(FilePath.cacheDirectory.appendingPathComponent("stderr.log").relativePath, &error)
|
||||||
|
if let error {
|
||||||
|
writeError("(packet-tunnel) redirect stderr error: \(error.localizedDescription)")
|
||||||
|
}
|
||||||
|
|
||||||
|
LibboxSetMemoryLimit(!disableMemoryLimit)
|
||||||
|
|
||||||
|
if platformInterface == nil {
|
||||||
|
platformInterface = ExtensionPlatformInterface(self)
|
||||||
|
}
|
||||||
|
commandServer = LibboxNewCommandServer(platformInterface, Int32(30))
|
||||||
|
do {
|
||||||
|
try commandServer.start()
|
||||||
|
} catch {
|
||||||
|
writeFatalError("(packet-tunnel): log server start error: \(error.localizedDescription)")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
writeMessage("(packet-tunnel) log server started")
|
||||||
|
await startService()
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeMessage(_ message: String) {
|
||||||
|
if let commandServer {
|
||||||
|
commandServer.writeMessage(message)
|
||||||
|
} else {
|
||||||
|
NSLog(message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeError(_ message: String) {
|
||||||
|
writeMessage(message)
|
||||||
|
try? message.write(to: ExtensionProvider.errorFile, atomically: true, encoding: .utf8)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func writeFatalError(_ message: String) {
|
||||||
|
#if DEBUG
|
||||||
|
NSLog(message)
|
||||||
|
#endif
|
||||||
|
writeError(message)
|
||||||
|
cancelTunnelWithError(NSError(domain: message, code: 0))
|
||||||
|
}
|
||||||
|
|
||||||
|
private func startService() async {
|
||||||
|
let configContent = config
|
||||||
|
var error: NSError?
|
||||||
|
let service = LibboxNewService(configContent, platformInterface, &error)
|
||||||
|
if let error {
|
||||||
|
writeError("(packet-tunnel) error: create service: \(error.localizedDescription)")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
guard let service else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
do {
|
||||||
|
try service.start()
|
||||||
|
} catch {
|
||||||
|
writeError("(packet-tunnel) error: start service: \(error.localizedDescription)")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
boxService = service
|
||||||
|
commandServer.setService(service)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func stopService() {
|
||||||
|
if let service = boxService {
|
||||||
|
do {
|
||||||
|
try service.close()
|
||||||
|
} catch {
|
||||||
|
writeError("(packet-tunnel) error: stop service: \(error.localizedDescription)")
|
||||||
|
}
|
||||||
|
boxService = nil
|
||||||
|
commandServer.setService(nil)
|
||||||
|
}
|
||||||
|
if let platformInterface {
|
||||||
|
platformInterface.reset()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func reloadService() async {
|
||||||
|
writeMessage("(packet-tunnel) reloading service")
|
||||||
|
reasserting = true
|
||||||
|
defer {
|
||||||
|
reasserting = false
|
||||||
|
}
|
||||||
|
stopService()
|
||||||
|
await startService()
|
||||||
|
}
|
||||||
|
|
||||||
|
override open func stopTunnel(with reason: NEProviderStopReason) async {
|
||||||
|
writeMessage("(packet-tunnel) stopping, reason: \(reason)")
|
||||||
|
stopService()
|
||||||
|
if let server = commandServer {
|
||||||
|
try? await Task.sleep(nanoseconds: 100 * NSEC_PER_MSEC)
|
||||||
|
try? server.close()
|
||||||
|
commandServer = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override open func handleAppMessage(_ messageData: Data) async -> Data? {
|
||||||
|
messageData
|
||||||
|
}
|
||||||
|
|
||||||
|
override open func sleep() async {
|
||||||
|
if let boxService {
|
||||||
|
boxService.sleep()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override open func wake() {
|
||||||
|
if let boxService {
|
||||||
|
boxService.wake()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
59
ios/SingBoxPacketTunnel/SingBox/SingBox.swift
Normal file
59
ios/SingBoxPacketTunnel/SingBox/SingBox.swift
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
//
|
||||||
|
// SingBox.swift
|
||||||
|
// SingBoxPacketTunnel
|
||||||
|
//
|
||||||
|
// Created by GFWFighter on 7/25/1402 AP.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
class SingBox {
|
||||||
|
static func setupConfig(config: String, mtu: Int = 9000) -> String? {
|
||||||
|
guard
|
||||||
|
let config = config.data(using: .utf8),
|
||||||
|
var json = try? JSONSerialization
|
||||||
|
.jsonObject(
|
||||||
|
with: config,
|
||||||
|
options: [.mutableLeaves, .mutableContainers]
|
||||||
|
) as? [String:Any]
|
||||||
|
else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
json["log"] = [
|
||||||
|
"disabled": false,
|
||||||
|
"level": "info",
|
||||||
|
"output": "log",
|
||||||
|
"timestamp": true
|
||||||
|
] as [String:Any]
|
||||||
|
json["experimental"] = [
|
||||||
|
"clash_api": [
|
||||||
|
"external_controller": "127.0.0.1:10864"
|
||||||
|
]
|
||||||
|
]
|
||||||
|
json["inbounds"] = [
|
||||||
|
[
|
||||||
|
"type": "tun",
|
||||||
|
"inet4_address": "172.19.0.1/30",
|
||||||
|
"auto_route": true,
|
||||||
|
"mtu": mtu,
|
||||||
|
"sniff": true
|
||||||
|
] as [String:Any]
|
||||||
|
]
|
||||||
|
var routing = (json["route"] as? [String:Any]) ?? [
|
||||||
|
"rules": [Any](),
|
||||||
|
"auto_detect_interface": true,
|
||||||
|
"final": (json["inbounds"] as? [[String:Any]])?.first?["tag"] ?? "proxy"
|
||||||
|
]
|
||||||
|
routing["geoip"] = [
|
||||||
|
"path": FilePath.assetsDirectory.appendingPathComponent("geoip.db"),
|
||||||
|
]
|
||||||
|
routing["geosite"] = [
|
||||||
|
"path": FilePath.assetsDirectory.appendingPathComponent("geosite.db"),
|
||||||
|
]
|
||||||
|
json["route"] = routing
|
||||||
|
guard let data = try? JSONSerialization.data(withJSONObject: json) else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return String(data: data, encoding: .utf8)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,10 @@
|
|||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
|
<key>com.apple.developer.networking.networkextension</key>
|
||||||
|
<array>
|
||||||
|
<string>packet-tunnel-provider</string>
|
||||||
|
</array>
|
||||||
<key>com.apple.security.application-groups</key>
|
<key>com.apple.security.application-groups</key>
|
||||||
<array>
|
<array>
|
||||||
<string>group.$(BASE_BUNDLE_IDENTIFIER)</string>
|
<string>group.$(BASE_BUNDLE_IDENTIFIER)</string>
|
||||||
|
|||||||
70
ios/SingBoxPacketTunnel/TrafficReader.swift
Normal file
70
ios/SingBoxPacketTunnel/TrafficReader.swift
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
//
|
||||||
|
// TrafficReader.swift
|
||||||
|
// SingBoxPacketTunnel
|
||||||
|
//
|
||||||
|
// Created by GFWFighter on 7/25/1402 AP.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
struct TrafficReaderUpdate: Codable {
|
||||||
|
let up: Int64
|
||||||
|
let down: Int64
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class TrafficReader {
|
||||||
|
private var task: URLSessionWebSocketTask!
|
||||||
|
private let callback: (TrafficReaderUpdate) -> ()
|
||||||
|
|
||||||
|
init(onUpdate: @escaping (TrafficReaderUpdate) -> ()) {
|
||||||
|
self.callback = onUpdate
|
||||||
|
Task(priority: .background) { [weak self] () in
|
||||||
|
await self?.setup()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func setup() async {
|
||||||
|
try? await Task.sleep(nanoseconds: 5_000_000_000)
|
||||||
|
//return
|
||||||
|
while true {
|
||||||
|
do {
|
||||||
|
let (_, response) = try await URLSession.shared.data(from: URL(string: "http://127.0.0.1:10864")!)
|
||||||
|
let code = (response as? HTTPURLResponse)?.statusCode ?? -1
|
||||||
|
if code >= 200 && code < 300 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// pass
|
||||||
|
}
|
||||||
|
try? await Task.sleep(nanoseconds: 5_000_000)
|
||||||
|
}
|
||||||
|
let task = URLSession.shared.webSocketTask(with: URL(string: "ws://127.0.0.1:10864/traffic")!)
|
||||||
|
self.task = task
|
||||||
|
read()
|
||||||
|
task.resume()
|
||||||
|
}
|
||||||
|
|
||||||
|
private func read() {
|
||||||
|
task.receive { [weak self] result in
|
||||||
|
switch result {
|
||||||
|
case .failure(_):
|
||||||
|
break
|
||||||
|
case .success(let message):
|
||||||
|
switch message {
|
||||||
|
case .string(let message):
|
||||||
|
guard let data = message.data(using: .utf8) else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
guard let response = try? JSONDecoder().decode(TrafficReaderUpdate.self, from: data) else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
self?.callback(response)
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
self?.read()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user