Added fastlane scripts, old ids cleaned up
1
.gitignore
vendored
|
@ -40,6 +40,7 @@ client/qrc_*.cpp
|
|||
client/ui_*.h
|
||||
client/ui_*.cpp
|
||||
client/Makefile*
|
||||
client/fastlane/build/
|
||||
client/*build-*
|
||||
client/AmneziaVPN.xcodeproj
|
||||
client/Debug-iphonesimulator/
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "icon_40x40.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "2x",
|
||||
"size" : "20x20"
|
||||
},
|
||||
{
|
||||
"filename" : "icon_60x60.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "3x",
|
||||
"size" : "20x20"
|
||||
},
|
||||
{
|
||||
"filename" : "icon_58x58.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "2x",
|
||||
"size" : "29x29"
|
||||
},
|
||||
{
|
||||
"filename" : "icon_87x87.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "3x",
|
||||
"size" : "29x29"
|
||||
},
|
||||
{
|
||||
"filename" : "icon_80x80.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "2x",
|
||||
"size" : "40x40"
|
||||
},
|
||||
{
|
||||
"filename" : "icon_120x120.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "3x",
|
||||
"size" : "40x40"
|
||||
},
|
||||
{
|
||||
"filename" : "icon_120x120-1.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "2x",
|
||||
"size" : "60x60"
|
||||
},
|
||||
{
|
||||
"filename" : "icon_180x180.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "3x",
|
||||
"size" : "60x60"
|
||||
},
|
||||
{
|
||||
"filename" : "icon_1024x1024.png",
|
||||
"idiom" : "ios-marketing",
|
||||
"scale" : "1x",
|
||||
"size" : "1024x1024"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 499 KiB |
After Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 27 KiB |
After Width: | Height: | Size: 3.5 KiB |
After Width: | Height: | Size: 5.9 KiB |
After Width: | Height: | Size: 6.1 KiB |
After Width: | Height: | Size: 9 KiB |
After Width: | Height: | Size: 10 KiB |
6
client/AmneziaVPN/Images.xcassets/Contents.json
Normal file
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
7
client/fastlane/Appfile
Normal file
|
@ -0,0 +1,7 @@
|
|||
app_identifier(["org.amnezia.AmneziaVPN", "org.amnezia.AmneziaVPN.network-extension"]) # The bundle identifier of your app
|
||||
apple_id("dartsyms@gmail.com") # Your Apple email address
|
||||
|
||||
itc_team_id("122591025") # App Store Connect Team ID
|
||||
team_id("X7UJ388FXK") # Developer Portal Team ID
|
||||
#itc_team_id("119381903")
|
||||
#team_id("7VAGZXD78P")
|
197
client/fastlane/Fastfile
Normal file
|
@ -0,0 +1,197 @@
|
|||
# Uncomment the line if you want fastlane to automatically update itself
|
||||
# update_fastlane
|
||||
|
||||
project_name = "AmneziaVPN"
|
||||
project_scheme = "AmneziaVPN"
|
||||
project_identifier = "org.amnezia.AmneziaVPN"
|
||||
itunes_username = "dartsyms@gmail.com"
|
||||
|
||||
before_all do
|
||||
ENV['GYM_SCHEME'] = project_scheme
|
||||
end
|
||||
|
||||
def tag_name(version_number, build_number)
|
||||
"#{version_number}_#{build_number}"
|
||||
end
|
||||
|
||||
def incrementBuild
|
||||
increment_build_number(xcodeproj: "./#{project_name}.xcodeproj")
|
||||
end
|
||||
|
||||
default_platform :ios
|
||||
|
||||
# Fastfile actions accept additional configuration, but
|
||||
# don't worry, fastlane will prompt you for required
|
||||
# info which you can add here later
|
||||
lane :beta do
|
||||
# build your iOS app
|
||||
build_app(
|
||||
scheme: "AmneziaVPN",
|
||||
export_method: "ad-hoc"
|
||||
)
|
||||
end
|
||||
|
||||
lane :incrementVersion do
|
||||
increment_build_number(
|
||||
xcodeproj: "./#{project_name}.xcodeproj"
|
||||
)
|
||||
|
||||
version_number = get_version_number(
|
||||
xcodeproj: "./#{project_name}.xcodeproj",
|
||||
target: project_name
|
||||
)
|
||||
|
||||
build_number = get_build_number(
|
||||
xcodeproj: "./#{project_name}.xcodeproj"
|
||||
)
|
||||
|
||||
puts "Version = #{version_number} Build = #{build_number}"
|
||||
end
|
||||
|
||||
desc "Update Certificates, Run All tests, Build app"
|
||||
desc "Add tag to git, send to Testflight, slack notification"
|
||||
lane :addToTestFlight do
|
||||
certificates
|
||||
clean_build_artifacts
|
||||
|
||||
build_app(
|
||||
scheme: project_scheme,
|
||||
configuration: "Release", # Debug / Release
|
||||
export_method: "app-store", # Valid values are: app-store, ad-hoc, package, enterprise, development, developer-id
|
||||
clean: true,
|
||||
include_bitcode: false,
|
||||
include_symbols: true,
|
||||
export_options: {
|
||||
provisioningProfiles: {
|
||||
"org.amnezia.AmneziaVPN"=>"match AppStore org.amnezia.AmneziaVPN",
|
||||
"org.amnezia.AmneziaVPN.network-extension"=>"match development AmneziaVPN networkextension",
|
||||
}
|
||||
},
|
||||
output_directory: "fastlane/build/", # Destination directory. Defaults to current directory.
|
||||
output_name: "#{project_name}.ipa"
|
||||
)
|
||||
|
||||
testflight(
|
||||
username: itunes_username,
|
||||
app_identifier: project_identifier,
|
||||
ipa: "fastlane/build/#{project_name}.ipa",
|
||||
skip_waiting_for_build_processing: true
|
||||
)
|
||||
|
||||
# Section for Slack
|
||||
version_number = get_version_number(
|
||||
xcodeproj: "./#{project_name}.xcodeproj",
|
||||
target: project_name
|
||||
)
|
||||
|
||||
build_number = get_build_number(
|
||||
xcodeproj: "./#{project_name}.xcodeproj"
|
||||
)
|
||||
|
||||
msg = "Build v.#{version_number} (#{build_number}) was Created for TestFlight"
|
||||
puts msg
|
||||
# msgToSlack(msg, true)
|
||||
increment_build_number
|
||||
end
|
||||
|
||||
lane :certificates do
|
||||
match(
|
||||
username: itunes_username,
|
||||
type: "appstore",
|
||||
app_identifier: [project_identifier],
|
||||
readonly: true,
|
||||
git_url: "https://github.com/amnezia-vpn/amnezia-ios-certificates.git"
|
||||
)
|
||||
notification(subtitle: "Finished", message: "Certificates done")
|
||||
end
|
||||
|
||||
lane :adhoc_certificates do
|
||||
match(
|
||||
username: itunes_username,
|
||||
type: "adhoc",
|
||||
app_identifier: [project_identifier],
|
||||
readonly: true,
|
||||
git_url: "https://github.com/amnezia-vpn/amnezia-ios-certificates.git"
|
||||
)
|
||||
notification(subtitle: "Finished", message: "Certificates done")
|
||||
end
|
||||
|
||||
lane :createAPNS do
|
||||
get_push_certificate(
|
||||
force: true, # create a new profile, even if the old one is still valid
|
||||
app_identifier: project_identifier, # optional app identifier,
|
||||
username: itunes_username,
|
||||
p12_password: apns_pass,
|
||||
pem_name: apns_pem_name,
|
||||
output_path: "fastlane/APNS",
|
||||
save_private_key: true,
|
||||
new_profile: proc do |profile_path| # this block gets called when a new profile was generated
|
||||
puts profile_path # the absolute path to the new PEM file
|
||||
# insert the code to upload the PEM file to the server
|
||||
end
|
||||
)
|
||||
|
||||
# msgToSlack("Successfully APNS Created", true)
|
||||
end
|
||||
|
||||
desc "Distribute app via Firebase for testers"
|
||||
lane :firebase_test do
|
||||
adhoc_certificates
|
||||
build_ios_app(
|
||||
scheme: project_scheme,
|
||||
output_directory: "./fastlane/builds/#{project_scheme}",
|
||||
output_name: "#{project_scheme}.ipa",
|
||||
export_options: {
|
||||
method: "ad-hoc",
|
||||
compileBitcode: false
|
||||
},
|
||||
clean: true,
|
||||
include_bitcode: true,
|
||||
include_symbols: true,
|
||||
skip_package_ipa: false
|
||||
)
|
||||
|
||||
# firebase_app_distribution(
|
||||
# app: "1:880592554695:ios:af464a9ed02207f6284ce2",
|
||||
# testers: "dartsyms@gmail.com, shogun14@yandex.ru, qa@wevied.com",
|
||||
# release_notes: "Another build: ready to test."
|
||||
# )
|
||||
|
||||
clean_build_artifacts
|
||||
increment_build_number
|
||||
end
|
||||
|
||||
desc "Distribute app via Firebase for testers"
|
||||
lane :distribute_firebase do
|
||||
adhoc_certificates
|
||||
clean_build_artifacts
|
||||
|
||||
build_ios_app(
|
||||
scheme: project_scheme,
|
||||
output_directory: "./fastlane/builds/#{project_scheme}",
|
||||
output_name: "#{project_scheme}.ipa",
|
||||
export_options: {
|
||||
method: "ad-hoc",
|
||||
compileBitcode: false
|
||||
},
|
||||
clean: true,
|
||||
include_bitcode: true,
|
||||
include_symbols: true,
|
||||
skip_package_ipa: false
|
||||
)
|
||||
|
||||
# firebase_app_distribution(
|
||||
# app: "1:880592554695:ios:af464a9ed02207f6284ce2",
|
||||
# testers: "dartsyms@gmail.com, shogun14@yandex.ru, qa@wevied.com",
|
||||
# release_notes: "Another build: ready to test."
|
||||
# )
|
||||
end
|
||||
|
||||
def msgToSlack(msg, result)
|
||||
slack(
|
||||
message: msg,
|
||||
success: result,
|
||||
default_payloads: [:lane, :test_result, :git_branch, :git_author] # Optional, lets you specify a whitelist of default payloads to include. Pass an empty array to suppress all the default payloads.
|
||||
# Don't add this key, or pass nil, if you want all the default payloads. The available default payloads are: `lane`, `test_result`, `git_branch`, `git_author`, `last_git_commit_message`, `last_git_commit_hash`.
|
||||
)
|
||||
end
|
13
client/fastlane/Matchfile
Normal file
|
@ -0,0 +1,13 @@
|
|||
git_url("https://github.com/amnezia-vpn/amnezia-ios-certificates")
|
||||
|
||||
storage_mode("git")
|
||||
|
||||
type("appstore") # The default type, can be: appstore, adhoc, enterprise or development
|
||||
|
||||
# app_identifier(["tools.fastlane.app", "tools.fastlane.app2"])
|
||||
# username("user@fastlane.tools") # Your Apple Developer Portal username
|
||||
|
||||
# For all available options run `fastlane match --help`
|
||||
# Remove the # in the beginning of the line to enable the other options
|
||||
|
||||
# The docs are available on https://docs.fastlane.tools/actions/match
|
6
client/fastlane/Pluginfile
Normal file
|
@ -0,0 +1,6 @@
|
|||
# Autogenerated by fastlane
|
||||
#
|
||||
# Ensure this file is checked in to source control!
|
||||
|
||||
#gem 'fastlane-plugin-run_tests_firebase_testlab'
|
||||
#gem 'fastlane-plugin-firebase_app_distribution'
|
88
client/fastlane/README.md
Normal file
|
@ -0,0 +1,88 @@
|
|||
fastlane documentation
|
||||
----
|
||||
|
||||
# Installation
|
||||
|
||||
Make sure you have the latest version of the Xcode command line tools installed:
|
||||
|
||||
```sh
|
||||
xcode-select --install
|
||||
```
|
||||
|
||||
For _fastlane_ installation instructions, see [Installing _fastlane_](https://docs.fastlane.tools/#installing-fastlane)
|
||||
|
||||
# Available Actions
|
||||
|
||||
### beta
|
||||
|
||||
```sh
|
||||
[bundle exec] fastlane beta
|
||||
```
|
||||
|
||||
|
||||
|
||||
### incrementVersion
|
||||
|
||||
```sh
|
||||
[bundle exec] fastlane incrementVersion
|
||||
```
|
||||
|
||||
|
||||
|
||||
### addToTestFlight
|
||||
|
||||
```sh
|
||||
[bundle exec] fastlane addToTestFlight
|
||||
```
|
||||
|
||||
Update Certificates, Run All tests, Build app
|
||||
|
||||
Add tag to git, send to Testflight, slack notification
|
||||
|
||||
### certificates
|
||||
|
||||
```sh
|
||||
[bundle exec] fastlane certificates
|
||||
```
|
||||
|
||||
|
||||
|
||||
### adhoc_certificates
|
||||
|
||||
```sh
|
||||
[bundle exec] fastlane adhoc_certificates
|
||||
```
|
||||
|
||||
|
||||
|
||||
### createAPNS
|
||||
|
||||
```sh
|
||||
[bundle exec] fastlane createAPNS
|
||||
```
|
||||
|
||||
|
||||
|
||||
### firebase_test
|
||||
|
||||
```sh
|
||||
[bundle exec] fastlane firebase_test
|
||||
```
|
||||
|
||||
Distribute app via Firebase for testers
|
||||
|
||||
### distribute_firebase
|
||||
|
||||
```sh
|
||||
[bundle exec] fastlane distribute_firebase
|
||||
```
|
||||
|
||||
Distribute app via Firebase for testers
|
||||
|
||||
----
|
||||
|
||||
This README.md is auto-generated and will be re-generated every time [_fastlane_](https://fastlane.tools) is run.
|
||||
|
||||
More information about _fastlane_ can be found on [fastlane.tools](https://fastlane.tools).
|
||||
|
||||
The documentation of _fastlane_ can be found on [docs.fastlane.tools](https://docs.fastlane.tools).
|
58
client/fastlane/report.xml
Normal file
|
@ -0,0 +1,58 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<testsuites>
|
||||
<testsuite name="fastlane.lanes">
|
||||
|
||||
|
||||
|
||||
|
||||
<testcase classname="fastlane.lanes" name="0: default_platform" time="0.000294">
|
||||
|
||||
</testcase>
|
||||
|
||||
|
||||
<testcase classname="fastlane.lanes" name="1: Switch to certificates lane" time="0.000162">
|
||||
|
||||
</testcase>
|
||||
|
||||
|
||||
<testcase classname="fastlane.lanes" name="2: match" time="16.218694">
|
||||
|
||||
</testcase>
|
||||
|
||||
|
||||
<testcase classname="fastlane.lanes" name="3: notification" time="0.234117">
|
||||
|
||||
</testcase>
|
||||
|
||||
|
||||
<testcase classname="fastlane.lanes" name="4: clean_build_artifacts" time="0.000678">
|
||||
|
||||
</testcase>
|
||||
|
||||
|
||||
<testcase classname="fastlane.lanes" name="5: build_app" time="245.559392">
|
||||
|
||||
</testcase>
|
||||
|
||||
|
||||
<testcase classname="fastlane.lanes" name="6: testflight" time="66.240632">
|
||||
|
||||
</testcase>
|
||||
|
||||
|
||||
<testcase classname="fastlane.lanes" name="7: get_version_number" time="0.122953">
|
||||
|
||||
</testcase>
|
||||
|
||||
|
||||
<testcase classname="fastlane.lanes" name="8: get_build_number" time="1.192314">
|
||||
|
||||
</testcase>
|
||||
|
||||
|
||||
<testcase classname="fastlane.lanes" name="9: increment_build_number" time="2.5453">
|
||||
|
||||
</testcase>
|
||||
|
||||
</testsuite>
|
||||
</testsuites>
|
|
@ -25,13 +25,15 @@
|
|||
<key>CFBundleShortVersionString</key>
|
||||
<string>$(MARKETING_VERSION)</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||
<string>3</string>
|
||||
<key>ITSAppUsesNonExemptEncryption</key>
|
||||
<false/>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>LSSupportsOpeningDocumentsInPlace</key>
|
||||
<false/>
|
||||
<key>NSCameraUsageDescription</key>
|
||||
<string>Amnezia VPN needs access to the camera for reading QR-codes.</string>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>AmneziaVPNLaunchScreen</string>
|
||||
<key>UIRequiresFullScreen</key>
|
||||
|
|
|
@ -8,13 +8,13 @@
|
|||
</array>
|
||||
<key>com.apple.security.application-groups</key>
|
||||
<array>
|
||||
<string>group.ru.kotit.AmneziaVPN.udev</string>
|
||||
<string>group.org.amnezia.AmneziaVPN</string>
|
||||
</array>
|
||||
<key>com.apple.security.files.user-selected.read-write</key>
|
||||
<true/>
|
||||
<key>keychain-access-groups</key>
|
||||
<array>
|
||||
<string>$(AppIdentifierPrefix)group.ru.kotit.AmneziaVPN.udev</string>
|
||||
<string>$(AppIdentifierPrefix)group.org.amnezia.AmneziaVPN</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
|
@ -8,11 +8,17 @@
|
|||
</array>
|
||||
<key>com.apple.security.application-groups</key>
|
||||
<array>
|
||||
<string>group.ru.kotit.AmneziaVPN.udev</string>
|
||||
<string>group.org.amnezia.AmneziaVPN</string>
|
||||
</array>
|
||||
<key>keychain-access-groups</key>
|
||||
<array>
|
||||
<string>$(AppIdentifierPrefix)group.ru.kotit.AmneziaVPN.udev</string>
|
||||
<string>$(AppIdentifierPrefix)group.org.amnezia.AmneziaVPN</string>
|
||||
</array>
|
||||
<!--<key>com.apple.security.network.client</key>-->
|
||||
<!--<true/>-->
|
||||
<!--<key>com.apple.security.network.server</key>-->
|
||||
<!--<true/>-->
|
||||
<!--<key>com.apple.security.app-sandbox</key>-->
|
||||
<!--<true/>-->
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
<key>CFBundleShortVersionString</key>
|
||||
<string>$(MARKETING_VERSION)</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||
<string>3</string>
|
||||
<key>ITSAppUsesNonExemptEncryption</key>
|
||||
<false/>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
#include "macos/gobridge/wireguard.h"
|
||||
#include "wireguard-go-version.h"
|
||||
#include "3rd/wireguard-apple/Sources/WireGuardKitC/WireGuardKitC.h"
|
||||
//#include "../../platforms/ios/Shadowsocks.h"
|
||||
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
|
65
client/platforms/ios/Shadowsocks.h
Normal file
|
@ -0,0 +1,65 @@
|
|||
// Copyright 2018 The Outline Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef Shadowsocks_h
|
||||
#define Shadowsocks_h
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
/**
|
||||
* Manages the lifecycle and configuration of ss-local, the Shadowsocks client library.
|
||||
*/
|
||||
@interface Shadowsocks : NSObject
|
||||
|
||||
extern const int kShadowsocksLocalPort;
|
||||
|
||||
typedef NS_ENUM(NSInteger, ErrorCode) {
|
||||
noError = 0,
|
||||
undefinedError = 1,
|
||||
vpnPermissionNotGranted = 2,
|
||||
invalidServerCredentials = 3,
|
||||
udpRelayNotEnabled = 4,
|
||||
serverUnreachable = 5,
|
||||
vpnStartFailure = 6,
|
||||
illegalServerConfiguration = 7,
|
||||
shadowsocksStartFailure = 8,
|
||||
configureSystemProxyFailure = 9,
|
||||
noAdminPermissions = 10,
|
||||
unsupportedRoutingTable = 11,
|
||||
systemMisconfigured = 12
|
||||
};
|
||||
|
||||
@property (nonatomic) NSDictionary *config;
|
||||
|
||||
/**
|
||||
* Initializes the object with a Shadowsocks server configuration, |config|.
|
||||
*/
|
||||
- (id)init:(NSDictionary *)config;
|
||||
|
||||
/**
|
||||
* Starts ss-local on a separate thread with the configuration supplied in the constructor.
|
||||
* If |checkConnectivity| is true, verifies that the server credentials are valid and that
|
||||
* the remote supports UDP forwarding, calling |completion| with the result.
|
||||
*/
|
||||
- (void)startWithConnectivityChecks:(bool)checkConnectivity
|
||||
completion:(void (^)(ErrorCode))completion;
|
||||
|
||||
/**
|
||||
* Stops the thread running ss-local. Calls |completion| with the success of the operation.
|
||||
*/
|
||||
- (void)stop:(void (^)(ErrorCode))completion;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* Shadowsocks_h */
|
202
client/platforms/ios/Shadowsocks.m
Normal file
|
@ -0,0 +1,202 @@
|
|||
// Copyright 2018 The Outline Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#import "Shadowsocks.h"
|
||||
#include <limits.h>
|
||||
#include <pthread.h>
|
||||
#import "ShadowsocksConnectivity.h"
|
||||
#import <Shadowsocks/shadowsocks.h>
|
||||
|
||||
const int kShadowsocksLocalPort = 9999;
|
||||
static const int kShadowsocksTimeoutSecs = INT_MAX;
|
||||
static const int kShadowsocksTcpAndUdpMode =
|
||||
1; // See https://github.com/shadowsocks/shadowsocks-libev/blob/4ea517/src/jconf.h#L44
|
||||
static char *const kShadowsocksLocalAddress = "127.0.0.1";
|
||||
|
||||
@interface Shadowsocks ()
|
||||
@property (nonatomic) pthread_t ssLocalThreadId;
|
||||
@property (nonatomic, copy) void (^startCompletion)(ErrorCode);
|
||||
@property (nonatomic, copy) void (^stopCompletion)(ErrorCode);
|
||||
@property (nonatomic) dispatch_queue_t dispatchQueue;
|
||||
@property (nonatomic) dispatch_group_t dispatchGroup;
|
||||
@property(nonatomic) bool checkConnectivity;
|
||||
@property(nonatomic) ShadowsocksConnectivity *ssConnectivity;
|
||||
@end
|
||||
|
||||
@implementation Shadowsocks
|
||||
|
||||
- (id) init:(NSDictionary *)config {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_config = config;
|
||||
_dispatchQueue = dispatch_queue_create("Shadowsocks", DISPATCH_QUEUE_SERIAL);
|
||||
_dispatchGroup = dispatch_group_create();
|
||||
_ssConnectivity = [[ShadowsocksConnectivity alloc] initWithPort:kShadowsocksLocalPort];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)startWithConnectivityChecks:(bool)checkConnectivity
|
||||
completion:(void (^)(ErrorCode))completion {
|
||||
if (self.ssLocalThreadId != 0) {
|
||||
return completion(shadowsocksStartFailure);
|
||||
}
|
||||
self.checkConnectivity = checkConnectivity;
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
// Start ss-local from the main application thread.
|
||||
self.startCompletion = completion;
|
||||
[self startShadowsocksThread];
|
||||
});
|
||||
}
|
||||
|
||||
-(void)stop:(void (^)(ErrorCode))completion {
|
||||
if (self.ssLocalThreadId == 0) {
|
||||
return;
|
||||
}
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
// The ev_loop in the ss-local thread will not break unless it is signaled to stop from the main
|
||||
// application thread.
|
||||
self.stopCompletion = completion;
|
||||
pthread_kill(self.ssLocalThreadId, SIGUSR1);
|
||||
self.ssLocalThreadId = 0;
|
||||
});
|
||||
}
|
||||
|
||||
#pragma mark - Lifecycle
|
||||
|
||||
void shadowsocksCallback(int socks_fd, int udp_fd, void *udata) {
|
||||
if (socks_fd <= 0 || udp_fd <= 0) {
|
||||
return;
|
||||
}
|
||||
Shadowsocks* ss = (__bridge Shadowsocks *)udata;
|
||||
[ss checkServerConnectivity];
|
||||
}
|
||||
|
||||
- (void)startShadowsocks {
|
||||
if (self.config == nil) {
|
||||
self.startCompletion(illegalServerConfiguration);
|
||||
return;
|
||||
}
|
||||
int port = [self.config[@"port"] intValue];
|
||||
char *host = (char *)[self.config[@"host"] UTF8String];
|
||||
char *password = (char *)[self.config[@"password"] UTF8String];
|
||||
char *method = (char *)[self.config[@"method"] UTF8String];
|
||||
const profile_t profile = {.remote_host = host,
|
||||
.local_addr = kShadowsocksLocalAddress,
|
||||
.method = method,
|
||||
.password = password,
|
||||
.remote_port = port,
|
||||
.local_port = kShadowsocksLocalPort,
|
||||
.timeout = kShadowsocksTimeoutSecs,
|
||||
.acl = NULL,
|
||||
.log = NULL,
|
||||
.fast_open = 0,
|
||||
.mode = kShadowsocksTcpAndUdpMode,
|
||||
.verbose = 0};
|
||||
int success = start_ss_local_server_with_callback(profile, shadowsocksCallback,
|
||||
(__bridge void *)self);
|
||||
if (success < 0) {
|
||||
self.startCompletion(shadowsocksStartFailure);
|
||||
return;
|
||||
}
|
||||
|
||||
if (self.stopCompletion) {
|
||||
self.stopCompletion(noError);
|
||||
self.stopCompletion = nil;
|
||||
}
|
||||
}
|
||||
|
||||
// Entry point for the Shadowsocks POSIX thread.
|
||||
void *startShadowsocks(void *udata) {
|
||||
Shadowsocks* ss = (__bridge Shadowsocks *)udata;
|
||||
[ss startShadowsocks];
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Starts a POSIX thread that runs ss-local.
|
||||
- (void)startShadowsocksThread {
|
||||
pthread_attr_t attr;
|
||||
int err = pthread_attr_init(&attr);
|
||||
if (err) {
|
||||
self.startCompletion(shadowsocksStartFailure);
|
||||
return;
|
||||
}
|
||||
err = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
|
||||
if (err) {
|
||||
self.startCompletion(shadowsocksStartFailure);
|
||||
return;
|
||||
}
|
||||
err = pthread_create(&_ssLocalThreadId, &attr, &startShadowsocks, (__bridge void *)self);
|
||||
if (err) {
|
||||
self.startCompletion(shadowsocksStartFailure);
|
||||
}
|
||||
err = pthread_attr_destroy(&attr);
|
||||
if (err) {
|
||||
self.startCompletion(shadowsocksStartFailure);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Connectivity
|
||||
|
||||
/**
|
||||
* Checks that the remote server is reachable, allows UDP forwarding, and the credentials are valid.
|
||||
* Synchronizes and parallelizes the execution of the connectivity checks and calls
|
||||
* |startCompletion| with the combined outcome.
|
||||
* Only performs the tests if |checkConnectivity| is true; otherwise calls |startCompletion|
|
||||
* with success.
|
||||
*/
|
||||
- (void) checkServerConnectivity {
|
||||
if (!self.checkConnectivity) {
|
||||
self.startCompletion(noError);
|
||||
return;
|
||||
}
|
||||
__block BOOL isRemoteUdpForwardingEnabled = false;
|
||||
__block BOOL serverCredentialsAreValid = false;
|
||||
__block BOOL isServerReachable = false;
|
||||
|
||||
// Enter the group once for each check
|
||||
dispatch_group_enter(self.dispatchGroup);
|
||||
dispatch_group_enter(self.dispatchGroup);
|
||||
dispatch_group_enter(self.dispatchGroup);
|
||||
|
||||
[self.ssConnectivity isUdpForwardingEnabled:^(BOOL enabled) {
|
||||
isRemoteUdpForwardingEnabled = enabled;
|
||||
dispatch_group_leave(self.dispatchGroup);
|
||||
}];
|
||||
[self.ssConnectivity isReachable:self.config[@"host"]
|
||||
port:[self.config[@"port"] intValue]
|
||||
completion:^(BOOL isReachable) {
|
||||
isServerReachable = isReachable;
|
||||
dispatch_group_leave(self.dispatchGroup);
|
||||
}];
|
||||
[self.ssConnectivity checkServerCredentials:^(BOOL valid) {
|
||||
serverCredentialsAreValid = valid;
|
||||
dispatch_group_leave(self.dispatchGroup);
|
||||
}];
|
||||
|
||||
dispatch_group_notify(self.dispatchGroup, self.dispatchQueue, ^{
|
||||
if (isRemoteUdpForwardingEnabled) {
|
||||
self.startCompletion(noError);
|
||||
} else if (serverCredentialsAreValid) {
|
||||
self.startCompletion(udpRelayNotEnabled);
|
||||
} else if (isServerReachable) {
|
||||
self.startCompletion(invalidServerCredentials);
|
||||
} else {
|
||||
self.startCompletion(serverUnreachable);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@end
|
50
client/platforms/ios/ShadowsocksConnectivity.h
Normal file
|
@ -0,0 +1,50 @@
|
|||
// Copyright 2018 The Outline Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef ShadowsocksConnectivity_h
|
||||
#define ShadowsocksConnectivity_h
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
/**
|
||||
* Non-thread-safe class to perform Shadowsocks connectivity checks.
|
||||
*/
|
||||
@interface ShadowsocksConnectivity : NSObject
|
||||
|
||||
/**
|
||||
* Initializes the object with a local Shadowsocks port, |shadowsocksPort|.
|
||||
*/
|
||||
- (id)initWithPort:(uint16_t)shadowsocksPort;
|
||||
|
||||
/**
|
||||
* Verifies that the server has enabled UDP forwarding. Performs an end-to-end test by sending
|
||||
* a DNS request through the proxy. This method is a superset of |checkServerCredentials|, as its
|
||||
* success implies that the server credentials are valid.
|
||||
*/
|
||||
- (void)isUdpForwardingEnabled:(void (^)(BOOL))completion;
|
||||
|
||||
/**
|
||||
* Verifies that the server credentials are valid. Performs an end-to-end authentication test
|
||||
* by issuing an HTTP HEAD request to a target domain through the proxy.
|
||||
*/
|
||||
- (void)checkServerCredentials:(void (^)(BOOL))completion;
|
||||
|
||||
/**
|
||||
* Checks that the server is reachable on |host| and |port|.
|
||||
*/
|
||||
- (void)isReachable:(NSString *)host port:(uint16_t)port completion:(void (^)(BOOL))completion;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* ShadowsocksConnectivity_h */
|
333
client/platforms/ios/ShadowsocksConnectivity.m
Normal file
|
@ -0,0 +1,333 @@
|
|||
// Copyright 2018 The Outline Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#import "ShadowsocksConnectivity.h"
|
||||
#include <arpa/inet.h>
|
||||
#import <ShadowSocks/shadowsocks.h>
|
||||
#import <CocoaAsyncSocket/CocoaAsyncSocket.h>
|
||||
|
||||
//@import CocoaAsyncSocket;
|
||||
|
||||
static char *const kShadowsocksLocalAddress = "127.0.0.1";
|
||||
|
||||
static char *const kDnsResolverAddress = "208.67.222.222"; // OpenDNS
|
||||
static const uint16_t kDnsResolverPort = 53;
|
||||
static const size_t kDnsRequestNumBytes = 28;
|
||||
|
||||
static const size_t kSocksHeaderNumBytes = 10;
|
||||
static const uint8_t kSocksMethodsResponseNumBytes = 2;
|
||||
static const size_t kSocksConnectResponseNumBytes = 10;
|
||||
static const uint8_t kSocksVersion = 0x5;
|
||||
static const uint8_t kSocksMethodNoAuth = 0x0;
|
||||
static const uint8_t kSocksCmdConnect = 0x1;
|
||||
static const uint8_t kSocksAtypIpv4 = 0x1;
|
||||
static const uint8_t kSocksAtypDomainname = 0x3;
|
||||
|
||||
static const NSTimeInterval kTcpSocketTimeoutSecs = 10.0;
|
||||
static const NSTimeInterval kUdpSocketTimeoutSecs = 1.0;
|
||||
static const long kSocketTagHttpRequest = 100;
|
||||
static const int kUdpForwardingMaxChecks = 5;
|
||||
static const uint16_t kHttpPort = 80;
|
||||
|
||||
@interface ShadowsocksConnectivity ()<GCDAsyncSocketDelegate, GCDAsyncUdpSocketDelegate>
|
||||
|
||||
@property(nonatomic) uint16_t shadowsocksPort;
|
||||
|
||||
@property(nonatomic, copy) void (^udpForwardingCompletion)(BOOL);
|
||||
@property(nonatomic, copy) void (^reachabilityCompletion)(BOOL);
|
||||
@property(nonatomic, copy) void (^credentialsCompletion)(BOOL);
|
||||
|
||||
@property(nonatomic) dispatch_queue_t dispatchQueue;
|
||||
@property(nonatomic) GCDAsyncUdpSocket *udpSocket;
|
||||
@property(nonatomic) GCDAsyncSocket *credentialsSocket;
|
||||
@property(nonatomic) GCDAsyncSocket *reachabilitySocket;
|
||||
|
||||
@property(nonatomic) bool isRemoteUdpForwardingEnabled;
|
||||
@property(nonatomic) bool areServerCredentialsValid;
|
||||
@property(nonatomic) bool isServerReachable;
|
||||
@property(nonatomic) int udpForwardingNumChecks;
|
||||
@end
|
||||
|
||||
@implementation ShadowsocksConnectivity
|
||||
|
||||
- (id)initWithPort:(uint16_t)shadowsocksPort {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_shadowsocksPort = shadowsocksPort;
|
||||
_dispatchQueue = dispatch_queue_create("ShadowsocksConnectivity", DISPATCH_QUEUE_SERIAL);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark - UDP Forwarding
|
||||
|
||||
struct socks_udp_header {
|
||||
uint16_t rsv;
|
||||
uint8_t frag;
|
||||
uint8_t atyp;
|
||||
uint32_t addr;
|
||||
uint16_t port;
|
||||
};
|
||||
|
||||
- (void)isUdpForwardingEnabled:(void (^)(BOOL))completion {
|
||||
self.isRemoteUdpForwardingEnabled = false;
|
||||
self.udpForwardingNumChecks = 0;
|
||||
self.udpForwardingCompletion = completion;
|
||||
self.udpSocket =
|
||||
[[GCDAsyncUdpSocket alloc] initWithDelegate:self delegateQueue:self.dispatchQueue];
|
||||
struct in_addr dnsResolverAddress;
|
||||
if (!inet_aton(kDnsResolverAddress, &dnsResolverAddress)) {
|
||||
[self udpForwardingCheckDone:false];
|
||||
return;
|
||||
}
|
||||
struct socks_udp_header socksHeader = {
|
||||
.atyp = kSocksAtypIpv4,
|
||||
.addr = dnsResolverAddress.s_addr, // Already in network order
|
||||
.port = htons(kDnsResolverPort)};
|
||||
uint8_t *dnsRequest = [self getDnsRequest];
|
||||
size_t packetNumBytes = kSocksHeaderNumBytes + kDnsRequestNumBytes;
|
||||
uint8_t socksPacket[packetNumBytes];
|
||||
memset(socksPacket, 0, packetNumBytes);
|
||||
memcpy(socksPacket, &socksHeader, kSocksHeaderNumBytes);
|
||||
memcpy(socksPacket + kSocksHeaderNumBytes, dnsRequest, kDnsRequestNumBytes);
|
||||
|
||||
NSData *packetData = [[NSData alloc] initWithBytes:socksPacket length:packetNumBytes];
|
||||
|
||||
dispatch_source_t timer =
|
||||
dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, self.dispatchQueue);
|
||||
if (!timer) {
|
||||
[self udpForwardingCheckDone:false];
|
||||
return;
|
||||
}
|
||||
dispatch_source_set_timer(timer, dispatch_time(DISPATCH_TIME_NOW, 0),
|
||||
kUdpSocketTimeoutSecs * NSEC_PER_SEC, 0);
|
||||
__weak ShadowsocksConnectivity *weakSelf = self;
|
||||
dispatch_source_set_event_handler(timer, ^{
|
||||
if (++weakSelf.udpForwardingNumChecks > kUdpForwardingMaxChecks ||
|
||||
weakSelf.isRemoteUdpForwardingEnabled) {
|
||||
dispatch_source_cancel(timer);
|
||||
if (!weakSelf.isRemoteUdpForwardingEnabled) {
|
||||
[weakSelf udpForwardingCheckDone:false];
|
||||
}
|
||||
[weakSelf.udpSocket close];
|
||||
return;
|
||||
}
|
||||
[weakSelf.udpSocket sendData:packetData
|
||||
toHost:[[NSString alloc] initWithUTF8String:kShadowsocksLocalAddress]
|
||||
port:self.shadowsocksPort
|
||||
withTimeout:kUdpSocketTimeoutSecs
|
||||
tag:0];
|
||||
if (![weakSelf.udpSocket receiveOnce:nil]) {
|
||||
}
|
||||
});
|
||||
dispatch_resume(timer);
|
||||
}
|
||||
|
||||
// Returns a byte representation of a DNS request for "google.com".
|
||||
- (uint8_t *)getDnsRequest {
|
||||
static uint8_t kDnsRequest[] = {
|
||||
0, 0, // [0-1] query ID
|
||||
1, 0, // [2-3] flags; byte[2] = 1 for recursion desired (RD).
|
||||
0, 1, // [4-5] QDCOUNT (number of queries)
|
||||
0, 0, // [6-7] ANCOUNT (number of answers)
|
||||
0, 0, // [8-9] NSCOUNT (number of name server records)
|
||||
0, 0, // [10-11] ARCOUNT (number of additional records)
|
||||
6, 'g', 'o', 'o', 'g', 'l', 'e', 3, 'c', 'o', 'm',
|
||||
0, // null terminator of FQDN (root TLD)
|
||||
0, 1, // QTYPE, set to A
|
||||
0, 1 // QCLASS, set to 1 = IN (Internet)
|
||||
};
|
||||
return kDnsRequest;
|
||||
}
|
||||
|
||||
#pragma mark - GCDAsyncUdpSocketDelegate
|
||||
|
||||
- (void)udpSocket:(GCDAsyncUdpSocket *)sock
|
||||
didNotSendDataWithTag:(long)tag
|
||||
dueToError:(NSError *)error {
|
||||
}
|
||||
|
||||
- (void)udpSocket:(GCDAsyncUdpSocket *)sock
|
||||
didReceiveData:(NSData *)data
|
||||
fromAddress:(NSData *)address
|
||||
withFilterContext:(id)filterContext {
|
||||
if (!self.isRemoteUdpForwardingEnabled) {
|
||||
// Only report success if it hasn't been done so already.
|
||||
[self udpForwardingCheckDone:true];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)udpForwardingCheckDone:(BOOL)enabled {
|
||||
self.isRemoteUdpForwardingEnabled = enabled;
|
||||
if (self.udpForwardingCompletion != NULL) {
|
||||
self.udpForwardingCompletion(self.isRemoteUdpForwardingEnabled);
|
||||
self.udpForwardingCompletion = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Credentials
|
||||
|
||||
struct socks_methods_request {
|
||||
uint8_t ver;
|
||||
uint8_t nmethods;
|
||||
uint8_t method;
|
||||
};
|
||||
|
||||
struct socks_request_header {
|
||||
uint8_t ver;
|
||||
uint8_t cmd;
|
||||
uint8_t rsv;
|
||||
uint8_t atyp;
|
||||
};
|
||||
|
||||
- (void)checkServerCredentials:(void (^)(BOOL))completion {
|
||||
self.areServerCredentialsValid = false;
|
||||
self.credentialsCompletion = completion;
|
||||
self.credentialsSocket =
|
||||
[[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:self.dispatchQueue];
|
||||
NSError *error;
|
||||
[self.credentialsSocket
|
||||
connectToHost:[[NSString alloc] initWithUTF8String:kShadowsocksLocalAddress]
|
||||
onPort:self.shadowsocksPort
|
||||
withTimeout:kTcpSocketTimeoutSecs
|
||||
error:&error];
|
||||
if (error) {
|
||||
[self serverCredentialsCheckDone];
|
||||
return;
|
||||
}
|
||||
|
||||
struct socks_methods_request methodsRequest = {
|
||||
.ver = kSocksVersion, .nmethods = 0x1, .method = kSocksMethodNoAuth};
|
||||
NSData *methodsRequestData =
|
||||
[[NSData alloc] initWithBytes:&methodsRequest length:sizeof(struct socks_methods_request)];
|
||||
[self.credentialsSocket writeData:methodsRequestData withTimeout:kTcpSocketTimeoutSecs tag:0];
|
||||
[self.credentialsSocket readDataToLength:kSocksMethodsResponseNumBytes
|
||||
withTimeout:kTcpSocketTimeoutSecs
|
||||
tag:0];
|
||||
|
||||
size_t socksRequestHeaderNumBytes = sizeof(struct socks_request_header);
|
||||
NSString *domain = [self chooseRandomDomain];
|
||||
uint8_t domainNameNumBytes = domain.length;
|
||||
size_t socksRequestNumBytes = socksRequestHeaderNumBytes + domainNameNumBytes +
|
||||
sizeof(uint16_t) /* port */ +
|
||||
sizeof(uint8_t) /* domain name length */;
|
||||
|
||||
struct socks_request_header socksRequestHeader = {
|
||||
.ver = kSocksVersion, .cmd = kSocksCmdConnect, .atyp = kSocksAtypDomainname};
|
||||
uint8_t socksRequest[socksRequestNumBytes];
|
||||
memset(socksRequest, 0x0, socksRequestNumBytes);
|
||||
memcpy(socksRequest, &socksRequestHeader, socksRequestHeaderNumBytes);
|
||||
socksRequest[socksRequestHeaderNumBytes] = domainNameNumBytes;
|
||||
memcpy(socksRequest + socksRequestHeaderNumBytes + sizeof(uint8_t), [domain UTF8String],
|
||||
domainNameNumBytes);
|
||||
uint16_t httpPort = htons(kHttpPort);
|
||||
memcpy(socksRequest + socksRequestHeaderNumBytes + sizeof(uint8_t) + domainNameNumBytes,
|
||||
&httpPort, sizeof(uint16_t));
|
||||
|
||||
NSData *socksRequestData =
|
||||
[[NSData alloc] initWithBytes:socksRequest length:socksRequestNumBytes];
|
||||
[self.credentialsSocket writeData:socksRequestData withTimeout:kTcpSocketTimeoutSecs tag:0];
|
||||
[self.credentialsSocket readDataToLength:kSocksConnectResponseNumBytes
|
||||
withTimeout:kTcpSocketTimeoutSecs
|
||||
tag:0];
|
||||
|
||||
NSString *httpRequest =
|
||||
[[NSString alloc] initWithFormat:@"HEAD / HTTP/1.1\r\nHost: %@\r\n\r\n", domain];
|
||||
[self.credentialsSocket
|
||||
writeData:[NSData dataWithBytes:[httpRequest UTF8String] length:httpRequest.length]
|
||||
withTimeout:kTcpSocketTimeoutSecs
|
||||
tag:kSocketTagHttpRequest];
|
||||
[self.credentialsSocket readDataWithTimeout:kTcpSocketTimeoutSecs tag:kSocketTagHttpRequest];
|
||||
[self.credentialsSocket disconnectAfterReading];
|
||||
}
|
||||
|
||||
// Returns a statically defined array containing domain names for validating server credentials.
|
||||
+ (const NSArray *)getCredentialsValidationDomains {
|
||||
static const NSArray *kCredentialsValidationDomains;
|
||||
static dispatch_once_t kDispatchOnceToken;
|
||||
dispatch_once(&kDispatchOnceToken, ^{
|
||||
// We have chosen these domains due to their neutrality.
|
||||
kCredentialsValidationDomains =
|
||||
@[ @"eff.org", @"ietf.org", @"w3.org", @"wikipedia.org", @"example.com" ];
|
||||
});
|
||||
return kCredentialsValidationDomains;
|
||||
}
|
||||
|
||||
// Returns a random domain from |kCredentialsValidationDomains|.
|
||||
- (NSString *)chooseRandomDomain {
|
||||
const NSArray *domains = [ShadowsocksConnectivity getCredentialsValidationDomains];
|
||||
int index = arc4random_uniform((uint32_t)domains.count);
|
||||
return domains[index];
|
||||
}
|
||||
|
||||
// Calls |credentialsCompletion| once with |areServerCredentialsValid|.
|
||||
- (void)serverCredentialsCheckDone {
|
||||
if (self.credentialsCompletion != NULL) {
|
||||
self.credentialsCompletion(self.areServerCredentialsValid);
|
||||
self.credentialsCompletion = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Reachability
|
||||
|
||||
- (void)isReachable:(NSString *)host port:(uint16_t)port completion:(void (^)(BOOL))completion {
|
||||
self.isServerReachable = false;
|
||||
self.reachabilityCompletion = completion;
|
||||
self.reachabilitySocket =
|
||||
[[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:self.dispatchQueue];
|
||||
NSError *error;
|
||||
[self.reachabilitySocket connectToHost:host
|
||||
onPort:port
|
||||
withTimeout:kTcpSocketTimeoutSecs
|
||||
error:&error];
|
||||
if (error) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Calls |reachabilityCompletion| once with |isServerReachable|.
|
||||
- (void)reachabilityCheckDone {
|
||||
if (self.reachabilityCompletion != NULL) {
|
||||
self.reachabilityCompletion(self.isServerReachable);
|
||||
self.reachabilityCompletion = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - GCDAsyncSocketDelegate
|
||||
|
||||
- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag {
|
||||
// We don't need to inspect any of the data, as the SOCKS responses are hardcoded in ss-local and
|
||||
// the fact that we have read the HTTP response indicates that the server credentials are valid.
|
||||
if (tag == kSocketTagHttpRequest && data != nil) {
|
||||
NSString *httpResponse = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
|
||||
self.areServerCredentialsValid = httpResponse != nil && [httpResponse hasPrefix:@"HTTP/1.1"];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(uint16_t)port {
|
||||
if ([self.reachabilitySocket isEqual:sock]) {
|
||||
self.isServerReachable = true;
|
||||
[self.reachabilitySocket disconnect];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)error {
|
||||
if ([self.reachabilitySocket isEqual:sock]) {
|
||||
[self reachabilityCheckDone];
|
||||
} else {
|
||||
[self serverCredentialsCheckDone];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
86
client/platforms/ios/Subnet.swift
Normal file
|
@ -0,0 +1,86 @@
|
|||
// Copyright 2018 The Outline Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import Foundation
|
||||
|
||||
// Represents an IP subnetwork.
|
||||
@objcMembers
|
||||
class Subnet: NSObject {
|
||||
static let kReservedSubnets = [
|
||||
"10.0.0.0/8",
|
||||
"100.64.0.0/10",
|
||||
"169.254.0.0/16",
|
||||
"172.16.0.0/12",
|
||||
"192.0.0.0/24",
|
||||
"192.0.2.0/24",
|
||||
"192.31.196.0/24",
|
||||
"192.52.193.0/24",
|
||||
"192.88.99.0/24",
|
||||
"192.168.0.0/16",
|
||||
"192.175.48.0/24",
|
||||
"198.18.0.0/15",
|
||||
"198.51.100.0/24",
|
||||
"203.0.113.0/24",
|
||||
"240.0.0.0/4"
|
||||
]
|
||||
|
||||
// Parses a CIDR subnet into a Subnet object. Returns nil on failure.
|
||||
static func parse(_ cidrSubnet: String) -> Subnet? {
|
||||
let components = cidrSubnet.components(separatedBy: "/")
|
||||
guard components.count == 2 else {
|
||||
NSLog("Malformed CIDR subnet")
|
||||
return nil
|
||||
}
|
||||
guard let prefix = UInt16(components[1]) else {
|
||||
NSLog("Invalid subnet prefix")
|
||||
return nil
|
||||
}
|
||||
return Subnet(address: components[0], prefix: prefix)
|
||||
}
|
||||
|
||||
// Returns a list of reserved Subnets.
|
||||
static func getReservedSubnets() -> [Subnet] {
|
||||
var subnets: [Subnet] = []
|
||||
for cidrSubnet in kReservedSubnets {
|
||||
if let subnet = self.parse(cidrSubnet) {
|
||||
subnets.append(subnet)
|
||||
}
|
||||
}
|
||||
return subnets
|
||||
}
|
||||
|
||||
public var address: String
|
||||
public var prefix: UInt16
|
||||
public var mask: String
|
||||
|
||||
init(address: String, prefix: UInt16) {
|
||||
self.address = address
|
||||
self.prefix = prefix
|
||||
let mask = (0xffffffff as UInt32) << (32 - prefix);
|
||||
self.mask = mask.IPv4String()
|
||||
}
|
||||
}
|
||||
|
||||
extension UInt32 {
|
||||
// Returns string representation of the integer as an IP address.
|
||||
public func IPv4String() -> String {
|
||||
let ip = self
|
||||
let a = UInt8((ip>>24) & 0xff)
|
||||
let b = UInt8((ip>>16) & 0xff)
|
||||
let c = UInt8((ip>>8) & 0xff)
|
||||
let d = UInt8(ip & 0xff)
|
||||
return "\(a).\(b).\(c).\(d)"
|
||||
}
|
||||
}
|
||||
|
|
@ -5,8 +5,8 @@ import Foundation
|
|||
import NetworkExtension
|
||||
import os
|
||||
import OpenVPNAdapter
|
||||
import ShadowSocks
|
||||
//import Tun2Socks
|
||||
//import ShadowSocks
|
||||
//import Tun2socks
|
||||
|
||||
enum TunnelProtoType: String {
|
||||
case wireguard, openvpn, shadowsocks, none
|
||||
|
@ -28,11 +28,12 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
|
|||
return adapter
|
||||
}()
|
||||
|
||||
private var shadowSocksPort: Int32 = 0
|
||||
private var isShadowsocksRunning: Bool = false
|
||||
var ssCompletion: ShadowsocksProxyCompletion = nil
|
||||
private let ssQueue = DispatchQueue(label: "org.amnezia.shadowsocks")
|
||||
private var shadowSocksConfig: Data? = nil
|
||||
var ssCompletion: ShadowsocksProxyCompletion = nil
|
||||
|
||||
// private var shadowSocksPort: Int32 = 0
|
||||
// private var isShadowsocksRunning: Bool = false
|
||||
// private let ssQueue = DispatchQueue(label: "org.amnezia.shadowsocks")
|
||||
|
||||
// private var tun2socksWriter: AmneziaTun2SocksWriter? = nil
|
||||
// private var tun2socksTunnel: Tun2socksOutlineTunnelProtocol? = nil
|
||||
|
@ -67,9 +68,7 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
|
|||
case .openvpn:
|
||||
startOpenVPN(completionHandler: completionHandler)
|
||||
case .shadowsocks:
|
||||
startShadowSocks { error in
|
||||
|
||||
}
|
||||
startShadowSocks(completionHandler: completionHandler)
|
||||
case .none:
|
||||
break
|
||||
}
|
||||
|
@ -207,8 +206,8 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
|
|||
wg_log(.error, message: "Cannot start startShadowSocks()")
|
||||
return
|
||||
}
|
||||
// self.shadowSocksConfig = ssConfiguration
|
||||
//
|
||||
self.shadowSocksConfig = ssConfiguration
|
||||
|
||||
// guard let config = self.shadowSocksConfig else { return }
|
||||
// guard let ssConfig = try? JSONSerialization.jsonObject(with: config, options: []) as? [String: Any] else {
|
||||
// self.ssCompletion?(0, NSError(domain: Bundle.main.bundleIdentifier ?? "unknown",
|
||||
|
@ -216,36 +215,50 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
|
|||
// userInfo: [NSLocalizedDescriptionKey: "Cannot parse json for ss in tunnel"]))
|
||||
// return
|
||||
// }
|
||||
|
||||
// let sshost = ssConfig["local_addr"] as? String
|
||||
// let ssport = ssConfig["local_port"] as? Int ?? Int(self.shadowSocksPort)
|
||||
//
|
||||
// let sshost = ssConfig["local_addr"] as? String
|
||||
// let ssport = ssConfig["local_port"] as? Int ?? 8585
|
||||
// let method = ssConfig["method"] as? String
|
||||
// let password = ssConfig["password"] as? String
|
||||
//
|
||||
// var errorCode: Int = 0
|
||||
// ShadowsocksCheckConnectivity(sshost, ssport, password, method, &errorCode, nil)
|
||||
// if (errorCode != 0) {
|
||||
// self.ssCompletion?(0, NSError(domain: Bundle.main.bundleIdentifier ?? "unknown",
|
||||
// code: 100,
|
||||
// userInfo: [NSLocalizedDescriptionKey: "Error checking ss connectivity: \(errorCode)"]))
|
||||
// return
|
||||
// }
|
||||
|
||||
|
||||
// Thread.detachNewThread { [weak self] in
|
||||
setupAndLaunchShadowSocksProxy(withConfig: ssConfiguration, ssHandler: { [weak self] port, error in
|
||||
wg_log(.info,
|
||||
message: "Prepare to start openvpn, self is \(self == nil ? "null" : "not null")")
|
||||
self.setupAndlaunchOpenVPN(withConfig: ovpnConfiguration) { error in
|
||||
guard error == nil else {
|
||||
wg_log(.error, message: "Stopping tunnel: \(error?.localizedDescription ?? "none")")
|
||||
wg_log(.error, message: "Start OpenVPN tunnel error : \(error?.localizedDescription ?? "none")")
|
||||
completionHandler(error!)
|
||||
return
|
||||
}
|
||||
|
||||
self?.setupAndlaunchOpenVPN(withConfig: ovpnConfiguration) { error in
|
||||
guard error == nil else {
|
||||
wg_log(.error, message: "Start OpenVPN tunnel error : \(error?.localizedDescription ?? "none")")
|
||||
completionHandler(error!)
|
||||
return
|
||||
}
|
||||
wg_log(.error, message: "OpenVPN tunnel connected.")
|
||||
}
|
||||
|
||||
// self?.startTun2Socks(host: sshost, port: ssport, password: password, cipher: method, isUDPEnabled: false, error: nil)
|
||||
})
|
||||
// }
|
||||
wg_log(.error, message: "OpenVPN tunnel connected.")
|
||||
// self.startTun2Socks(host: sshost, port: ssport, password: password, cipher: method, isUDPEnabled: false, error: nil)
|
||||
}
|
||||
//// Thread.detachNewThread { [weak self] in
|
||||
//// setupAndLaunchShadowSocksProxy(withConfig: ssConfiguration, ssHandler: { [weak self] port, error in
|
||||
//// wg_log(.info,
|
||||
//// message: "Prepare to start openvpn, self is \(self == nil ? "null" : "not null")")
|
||||
//// guard error == nil else {
|
||||
//// wg_log(.error, message: "Stopping tunnel: \(error?.localizedDescription ?? "none")")
|
||||
//// completionHandler(error!)
|
||||
//// return
|
||||
//// }
|
||||
////
|
||||
//// self?.setupAndlaunchOpenVPN(withConfig: ovpnConfiguration) { error in
|
||||
//// guard error == nil else {
|
||||
//// wg_log(.error, message: "Start OpenVPN tunnel error : \(error?.localizedDescription ?? "none")")
|
||||
//// completionHandler(error!)
|
||||
//// return
|
||||
//// }
|
||||
//// wg_log(.error, message: "OpenVPN tunnel connected.")
|
||||
//// }
|
||||
//// })
|
||||
//// }
|
||||
}
|
||||
|
||||
private func stopWireguard(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) {
|
||||
|
@ -294,7 +307,7 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
|
|||
// self.processQueue.sync { self.processPackets() }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
|
||||
// private func processPackets() {
|
||||
// wg_log(.info, message: "Inside startTun2SocksPacketForwarder")
|
||||
// packetFlow.readPacketObjects { [weak self] packets in
|
||||
|
@ -341,100 +354,100 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
|
|||
ovpnAdapter.connect(using: packetFlow)
|
||||
}
|
||||
|
||||
private func setupAndLaunchShadowSocksProxy(withConfig config: Data, ssHandler: ShadowsocksProxyCompletion) {
|
||||
let str = String(decoding: config, as: UTF8.self)
|
||||
wg_log(.info, message: "config: \(str)")
|
||||
ssCompletion = ssHandler
|
||||
guard let ssConfig = try? JSONSerialization.jsonObject(with: config, options: []) as? [String: Any] else {
|
||||
ssHandler?(0, NSError(domain: Bundle.main.bundleIdentifier ?? "unknown",
|
||||
code: 100,
|
||||
userInfo: [NSLocalizedDescriptionKey: "Cannot parse json for ss in tunnel"]))
|
||||
return
|
||||
}
|
||||
|
||||
wg_log(.info, message: "SS Config: \(ssConfig)")
|
||||
|
||||
guard let remoteHost = ssConfig["server"] as? String, // UnsafeMutablePointer<CChar>,
|
||||
let remotePort = ssConfig["server_port"] as? Int32,
|
||||
let localAddress = ssConfig["local_addr"] as? String, //UnsafeMutablePointer<CChar>,
|
||||
let localPort = ssConfig["local_port"] as? Int32,
|
||||
let method = ssConfig["method"] as? String, //UnsafeMutablePointer<CChar>,
|
||||
let password = ssConfig["password"] as? String,//UnsafeMutablePointer<CChar>,
|
||||
let timeout = ssConfig["timeout"] as? Int32
|
||||
else {
|
||||
ssHandler?(0, NSError(domain: Bundle.main.bundleIdentifier ?? "unknown",
|
||||
code: 100,
|
||||
userInfo: [NSLocalizedDescriptionKey: "Cannot assing profile params for ss in tunnel"]))
|
||||
return
|
||||
}
|
||||
|
||||
/* An example profile
|
||||
*
|
||||
* const profile_t EXAMPLE_PROFILE = {
|
||||
* .remote_host = "example.com",
|
||||
* .local_addr = "127.0.0.1",
|
||||
* .method = "bf-cfb",
|
||||
* .password = "barfoo!",
|
||||
* .remote_port = 8338,
|
||||
* .local_port = 1080,
|
||||
* .timeout = 600;
|
||||
* .acl = NULL,
|
||||
* .log = NULL,
|
||||
* .fast_open = 0,
|
||||
* .mode = 0,
|
||||
* .verbose = 0
|
||||
* };
|
||||
*/
|
||||
|
||||
var profile: profile_t = .init()
|
||||
memset(&profile, 0, MemoryLayout<profile_t>.size)
|
||||
profile.remote_host = strdup(remoteHost)
|
||||
profile.remote_port = remotePort
|
||||
profile.local_addr = strdup(localAddress)
|
||||
profile.local_port = localPort
|
||||
profile.method = strdup(method)
|
||||
profile.password = strdup(password)
|
||||
profile.timeout = timeout
|
||||
profile.acl = nil
|
||||
profile.log = nil
|
||||
profile.mtu = 1600
|
||||
profile.fast_open = 1
|
||||
profile.mode = 0
|
||||
profile.verbose = 1
|
||||
|
||||
wg_log(.debug, message: "Prepare to start shadowsocks proxy server...")
|
||||
let observer = UnsafeMutableRawPointer(Unmanaged.passUnretained(self).toOpaque())
|
||||
ssQueue.sync { [weak self] in
|
||||
let success = start_ss_local_server_with_callback(profile, { socks_fd, udp_fd, data in
|
||||
wg_log(.debug, message: "Inside cb callback")
|
||||
wg_log(.debug, message: "Params: socks_fd -> \(socks_fd), udp_fd -> \(udp_fd)")
|
||||
if let obs = data {
|
||||
wg_log(.debug, message: "Prepare to call onShadowsocksCallback() with socks port \(socks_fd) and udp port \(udp_fd)")
|
||||
let mySelf = Unmanaged<PacketTunnelProvider>.fromOpaque(obs).takeUnretainedValue()
|
||||
mySelf.onShadowsocksCallback(fd: socks_fd)
|
||||
}
|
||||
}, observer)
|
||||
if success != -1 {
|
||||
wg_log(.error, message: "ss proxy started on port \(localPort)")
|
||||
self?.shadowSocksPort = localPort
|
||||
self?.isShadowsocksRunning = true
|
||||
} else {
|
||||
wg_log(.error, message: "Failed to start ss proxy")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func onShadowsocksCallback(fd: Int32) {
|
||||
wg_log(.debug, message: "Inside onShadowsocksCallback() with port \(fd)")
|
||||
var error: NSError? = nil
|
||||
if fd > 0 {
|
||||
// shadowSocksPort = getSockPort(for: fd)
|
||||
isShadowsocksRunning = true
|
||||
} else {
|
||||
error = NSError(domain: Bundle.main.bundleIdentifier ?? "unknown", code: 100, userInfo: [NSLocalizedDescriptionKey : "Failed to start shadowsocks proxy"])
|
||||
}
|
||||
ssCompletion?(shadowSocksPort, error)
|
||||
}
|
||||
// private func setupAndLaunchShadowSocksProxy(withConfig config: Data, ssHandler: ShadowsocksProxyCompletion) {
|
||||
// let str = String(decoding: config, as: UTF8.self)
|
||||
// wg_log(.info, message: "config: \(str)")
|
||||
// ssCompletion = ssHandler
|
||||
// guard let ssConfig = try? JSONSerialization.jsonObject(with: config, options: []) as? [String: Any] else {
|
||||
// ssHandler?(0, NSError(domain: Bundle.main.bundleIdentifier ?? "unknown",
|
||||
// code: 100,
|
||||
// userInfo: [NSLocalizedDescriptionKey: "Cannot parse json for ss in tunnel"]))
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// wg_log(.info, message: "SS Config: \(ssConfig)")
|
||||
//
|
||||
// guard let remoteHost = ssConfig["server"] as? String, // UnsafeMutablePointer<CChar>,
|
||||
// let remotePort = ssConfig["server_port"] as? Int32,
|
||||
// let localAddress = ssConfig["local_addr"] as? String, //UnsafeMutablePointer<CChar>,
|
||||
// let localPort = ssConfig["local_port"] as? Int32,
|
||||
// let method = ssConfig["method"] as? String, //UnsafeMutablePointer<CChar>,
|
||||
// let password = ssConfig["password"] as? String,//UnsafeMutablePointer<CChar>,
|
||||
// let timeout = ssConfig["timeout"] as? Int32
|
||||
// else {
|
||||
// ssHandler?(0, NSError(domain: Bundle.main.bundleIdentifier ?? "unknown",
|
||||
// code: 100,
|
||||
// userInfo: [NSLocalizedDescriptionKey: "Cannot assing profile params for ss in tunnel"]))
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// /* An example profile
|
||||
// *
|
||||
// * const profile_t EXAMPLE_PROFILE = {
|
||||
// * .remote_host = "example.com",
|
||||
// * .local_addr = "127.0.0.1",
|
||||
// * .method = "bf-cfb",
|
||||
// * .password = "barfoo!",
|
||||
// * .remote_port = 8338,
|
||||
// * .local_port = 1080,
|
||||
// * .timeout = 600;
|
||||
// * .acl = NULL,
|
||||
// * .log = NULL,
|
||||
// * .fast_open = 0,
|
||||
// * .mode = 0,
|
||||
// * .verbose = 0
|
||||
// * };
|
||||
// */
|
||||
//
|
||||
// var profile: profile_t = .init()
|
||||
// memset(&profile, 0, MemoryLayout<profile_t>.size)
|
||||
// profile.remote_host = strdup(remoteHost)
|
||||
// profile.remote_port = remotePort
|
||||
// profile.local_addr = strdup(localAddress)
|
||||
// profile.local_port = localPort
|
||||
// profile.method = strdup(method)
|
||||
// profile.password = strdup(password)
|
||||
// profile.timeout = timeout
|
||||
// profile.acl = nil
|
||||
// profile.log = nil
|
||||
// profile.mtu = 1600
|
||||
// profile.fast_open = 1
|
||||
// profile.mode = 0
|
||||
// profile.verbose = 1
|
||||
//
|
||||
// wg_log(.debug, message: "Prepare to start shadowsocks proxy server...")
|
||||
// let observer = UnsafeMutableRawPointer(Unmanaged.passUnretained(self).toOpaque())
|
||||
// ssQueue.sync { [weak self] in
|
||||
// let success = start_ss_local_server_with_callback(profile, { socks_fd, udp_fd, data in
|
||||
// wg_log(.debug, message: "Inside cb callback")
|
||||
// wg_log(.debug, message: "Params: socks_fd -> \(socks_fd), udp_fd -> \(udp_fd)")
|
||||
// if let obs = data {
|
||||
// wg_log(.debug, message: "Prepare to call onShadowsocksCallback() with socks port \(socks_fd) and udp port \(udp_fd)")
|
||||
// let mySelf = Unmanaged<PacketTunnelProvider>.fromOpaque(obs).takeUnretainedValue()
|
||||
// mySelf.onShadowsocksCallback(fd: socks_fd)
|
||||
// }
|
||||
// }, observer)
|
||||
// if success != -1 {
|
||||
// wg_log(.error, message: "ss proxy started on port \(localPort)")
|
||||
// self?.shadowSocksPort = localPort
|
||||
// self?.isShadowsocksRunning = true
|
||||
// } else {
|
||||
// wg_log(.error, message: "Failed to start ss proxy")
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private func onShadowsocksCallback(fd: Int32) {
|
||||
// wg_log(.debug, message: "Inside onShadowsocksCallback() with port \(fd)")
|
||||
// var error: NSError? = nil
|
||||
// if fd > 0 {
|
||||
//// shadowSocksPort = getSockPort(for: fd)
|
||||
// isShadowsocksRunning = true
|
||||
// } else {
|
||||
// error = NSError(domain: Bundle.main.bundleIdentifier ?? "unknown", code: 100, userInfo: [NSLocalizedDescriptionKey : "Failed to start shadowsocks proxy"])
|
||||
// }
|
||||
// ssCompletion?(shadowSocksPort, error)
|
||||
// }
|
||||
|
||||
private func getSockPort(for fd: Int32) -> Int32 {
|
||||
var addr_in = sockaddr_in();
|
||||
|
|
|
@ -112,4 +112,3 @@ die() {
|
|||
|
||||
exit 1
|
||||
}
|
||||
|
||||
|
|
|
@ -17,6 +17,6 @@ SDKROOT[arch=armv7s] = iphoneos
|
|||
|
||||
VALID_ARCHS[sdk=iphoneos*] = arm64
|
||||
|
||||
PROJECT_TEMP_DIR = /Users/sanchez/work/vied/ios/vpn/desktop-client-bkp/client/3rd/PacketProcessor/build/PacketProcessor.build
|
||||
CONFIGURATION_BUILD_DIR = /Users/sanchez/work/vied/ios/vpn/desktop-client-bkp/client/3rd/PacketProcessor/build/Release-iphoneos
|
||||
BUILT_PRODUCTS_DIR = /Users/sanchez/work/vied/ios/vpn/desktop-client-bkp/client/3rd/PacketProcessor/build/Release-iphoneos
|
||||
PROJECT_TEMP_DIR = $(SRCROOT)/client/3rd/PacketProcessor/build/PacketProcessor.build
|
||||
CONFIGURATION_BUILD_DIR = $(SRCROOT)/client/3rd/PacketProcessor/build/Release-iphoneos
|
||||
BUILT_PRODUCTS_DIR = $(SRCROOT)/client/3rd/PacketProcessor/build/Release-iphoneos
|
|
@ -19,6 +19,6 @@ SDKROOT[arch=armv7s] = iphoneos
|
|||
|
||||
VALID_ARCHS[sdk=iphoneos*] = arm64
|
||||
|
||||
PROJECT_TEMP_DIR = /Users/sanchez/work/vied/ios/vpn/desktop-client-bkp/client/3rd/ShadowSocks/build/ShadowSocks.build
|
||||
CONFIGURATION_BUILD_DIR = /Users/sanchez/work/vied/ios/vpn/desktop-client-bkp/client/3rd/ShadowSocks/build/Release-iphoneos
|
||||
BUILT_PRODUCTS_DIR = /Users/sanchez/work/vied/ios/vpn/desktop-client-bkp/client/3rd/ShadowSocks/build/Release-iphoneos
|
||||
PROJECT_TEMP_DIR = $(SRCROOT)/client/3rd/ShadowSocks/build/ShadowSocks.build
|
||||
CONFIGURATION_BUILD_DIR = $(SRCROOT)/client/3rd/ShadowSocks/build/Release-iphoneos
|
||||
BUILT_PRODUCTS_DIR = $(SRCROOT)/client/3rd/ShadowSocks/build/Release-iphoneos
|
|
@ -110,6 +110,9 @@ class XCodeprojPatcher
|
|||
config.build_settings['SWIFT_OPTIMIZATION_LEVEL'] ||= '-Onone'
|
||||
end
|
||||
end
|
||||
|
||||
# images_ref = @target_main.new_reference('ios/app/Images.xcassets')
|
||||
# @target_main.add_resources([images_ref])
|
||||
|
||||
if networkExtension
|
||||
# WireGuard group
|
||||
|
@ -252,10 +255,14 @@ class XCodeprojPatcher
|
|||
config.build_settings['SWIFT_OBJC_BRIDGING_HEADER'] ||= 'macos/networkextension/WireGuardNetworkExtension-Bridging-Header.h'
|
||||
config.build_settings['SWIFT_PRECOMPILE_BRIDGING_HEADER'] = 'NO'
|
||||
config.build_settings['APPLICATION_EXTENSION_API_ONLY'] = 'YES'
|
||||
# config.build_settings['STRIP_BITCODE_FROM_COPIED_FILES'] = 'NO'
|
||||
config.build_settings['FRAMEWORK_SEARCH_PATHS'] ||= [
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/3rd",
|
||||
"$(PROJECT_DIR)/3rd/OpenVPNAdapter/build/Debug-iphoneos"
|
||||
"$(PROJECT_DIR)/3rd/OpenVPNAdapter/build/Release-iphoneos",
|
||||
# "$(PROJECT_DIR)/3rd/ShadowSocks/build/Release-iphoneos/",
|
||||
# "$(PROJECT_DIR)/3rd/PacketProcessor/build/Release-iphoneos/",
|
||||
# "$(PROJECT_DIR)/3rd/outline-go-tun2socks/build/ios/"
|
||||
]
|
||||
|
||||
# Versions and names
|
||||
|
@ -341,6 +348,11 @@ class XCodeprojPatcher
|
|||
'platforms/ios/iostunnel.swift',
|
||||
'platforms/ios/iosglue.mm',
|
||||
'platforms/ios/ioslogger.swift',
|
||||
# 'platforms/ios/Shadowsocks.h',
|
||||
# 'platforms/ios/Shadowsocks.m',
|
||||
# 'platforms/ios/ShadowsocksConnectivity.h',
|
||||
# 'platforms/ios/ShadowsocksConnectivity.m',
|
||||
# 'platforms/ios/Subnet.swift',
|
||||
].each { |filename|
|
||||
file = group.new_file(filename)
|
||||
@target_extension.add_file_references([file])
|
||||
|
@ -369,14 +381,14 @@ class XCodeprojPatcher
|
|||
framework_ref = frameworks_group.new_file('3rd/OpenVPNAdapter/build/Release-iphoneos/OpenVPNAdapter.framework')
|
||||
frameworks_build_phase.add_file_reference(framework_ref)
|
||||
|
||||
framework_ref = frameworks_group.new_file('3rd/ShadowSocks/build/Release-iphoneos/ShadowSocks.framework')
|
||||
frameworks_build_phase.add_file_reference(framework_ref)
|
||||
|
||||
# framework_ref = frameworks_group.new_file('3rd/PacketProcessor/build/Release-iphoneos/PacketProcessor.framework')
|
||||
# framework_ref = frameworks_group.new_file('3rd/ShadowSocks/build/Release-iphoneos/ShadowSocks.framework')
|
||||
# frameworks_build_phase.add_file_reference(framework_ref)
|
||||
#
|
||||
# framework_ref = frameworks_group.new_file('3rd/PacketProcessor/PacketProcessor/CocoaAsyncSocket/CocoaAsyncSocket.framework')
|
||||
# frameworks_build_phase.add_file_reference(framework_ref)
|
||||
#
|
||||
# framework_ref = frameworks_group.new_file('3rd/outline-go-tun2socks/build/ios/Tun2socks.framework')
|
||||
# frameworks_build_phase.add_file_reference(framework_ref)
|
||||
|
||||
framework_ref = frameworks_group.new_file('3rd/outline-go-tun2socks/build/ios/Tun2Socks.xcframework')
|
||||
frameworks_build_phase.add_file_reference(framework_ref)
|
||||
|
||||
|
||||
# This fails: @target_main.add_dependency @target_extension
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
DEVELOPMENT_TEAM = 7VAGZXD78P
|
||||
DEVELOPMENT_TEAM = X7UJ388FXK
|
||||
|
||||
GROUP_ID_IOS = group.ru.kotit.AmneziaVPN.udev
|
||||
APP_ID_IOS = ru.kotit.AmneziaVPN
|
||||
NETEXT_ID_IOS = ru.kotit.AmneziaVPN.network-extension
|
||||
GROUP_ID_IOS = group.org.amnezia.AmneziaVPN
|
||||
APP_ID_IOS = org.amnezia.AmneziaVPN
|
||||
NETEXT_ID_IOS = org.amnezia.AmneziaVPN.network-extension
|
||||
|
|