Compare commits
273 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5d7446ca80 | ||
|
|
866aa6bd90 | ||
|
|
5ab07664d3 | ||
|
|
38f3de8942 | ||
|
|
32433ed572 | ||
|
|
e28aa08cd7 | ||
|
|
57d1ac26cb | ||
|
|
656c1cdf55 | ||
|
|
a95157e7da | ||
|
|
aeaa69df28 | ||
|
|
6bd8827961 | ||
|
|
959492944b | ||
|
|
27a2c48bf2 | ||
|
|
37a53acf0e | ||
|
|
0a2c6d4531 | ||
|
|
bcda3a46e1 | ||
|
|
0c1ca9b3d8 | ||
|
|
e2c37d9417 | ||
|
|
e6af1fa26f | ||
|
|
7ddca0724b | ||
|
|
7c419fcccf | ||
|
|
29adf98043 | ||
|
|
c8015a381d | ||
|
|
07f5247e5b | ||
|
|
2c5fac8da4 | ||
|
|
153e51bf66 | ||
|
|
d0e1425f42 | ||
|
|
b4414f4845 | ||
|
|
0ea4f2743e | ||
|
|
54860148b4 | ||
|
|
65bc5ba39a | ||
|
|
6a19c36e83 | ||
|
|
c9f1bb633a | ||
|
|
032ec025ac | ||
|
|
0bb8fdad23 | ||
|
|
b79afcfd3e | ||
|
|
e3878d4d3a | ||
|
|
f1fe1c06f8 | ||
|
|
9fe66c693a | ||
|
|
f41b114632 | ||
|
|
e4775b3d44 | ||
|
|
21cb297b3b | ||
|
|
87a00a8c3e | ||
|
|
e8426f1465 | ||
|
|
fbc9514922 | ||
|
|
f587bdf722 | ||
|
|
92090b4fcd | ||
|
|
e102e47f50 | ||
|
|
08101f9766 | ||
|
|
e5c25ad31d | ||
|
|
87c6069581 | ||
|
|
5516a3f1e0 | ||
|
|
8d6c8d6b5e | ||
|
|
7e4e5e6f37 | ||
|
|
8e47f93b16 | ||
|
|
4f34aad38a | ||
|
|
cbff3bcedb | ||
|
|
f3f050d1c6 | ||
|
|
b29d266a37 | ||
|
|
5110ae80dd | ||
|
|
f6dd718180 | ||
|
|
f63745cb4a | ||
|
|
e5bcbd1ad2 | ||
|
|
b9ac25eb4c | ||
|
|
bfa62b8f88 | ||
|
|
f3feb893e0 | ||
|
|
a8d2123154 | ||
|
|
e963a83588 | ||
|
|
71617ff998 | ||
|
|
47c4f34d32 | ||
|
|
b941334934 | ||
|
|
994f7dcaa5 | ||
|
|
4eca25530c | ||
|
|
055e40d2cb | ||
|
|
bf98ce1860 | ||
|
|
3d5397fa7e | ||
|
|
031f9408a0 | ||
|
|
832c3b8405 | ||
|
|
d5534d6e34 | ||
|
|
91b4c755f2 | ||
|
|
dd5eda3793 | ||
|
|
0f72faaede | ||
|
|
8dd0f2d50e | ||
|
|
b0715bc56f | ||
|
|
9033c43d84 | ||
|
|
a5cb503490 | ||
|
|
b047b9042c | ||
|
|
17ce2d56a0 | ||
|
|
fcec5ef24f | ||
|
|
3313b1d9dc | ||
|
|
e42f7dd7fa | ||
|
|
219df8ccc1 | ||
|
|
e7710e9928 | ||
|
|
547b548525 | ||
|
|
3591b9840a | ||
|
|
06c5a5fc65 | ||
|
|
f6aaedfdb8 | ||
|
|
5c96c63613 | ||
|
|
caf33b9ce1 | ||
|
|
ba48c966a1 | ||
|
|
103dc02919 | ||
|
|
3d2c9d6019 | ||
|
|
0cec7bbf9c | ||
|
|
67a8db045d | ||
|
|
7433d55450 | ||
|
|
17f950ecba | ||
|
|
2c3f8a4b2f | ||
|
|
6faca0f72b | ||
|
|
55b541403e | ||
|
|
febbf73029 | ||
|
|
f2db5aa19b | ||
|
|
4bbda1c4cc | ||
|
|
01d5a48a63 | ||
|
|
13fbfc849a | ||
|
|
2b1277347d | ||
|
|
4bea9abec9 | ||
|
|
41dfa8b900 | ||
|
|
60f2036303 | ||
|
|
9a555a97a5 | ||
|
|
cdd65efb28 | ||
|
|
1302d2b374 | ||
|
|
f282f8088a | ||
|
|
e0db86ee54 | ||
|
|
307843e01a | ||
|
|
e1a4acc360 | ||
|
|
302d034d53 | ||
|
|
f3725d67f3 | ||
|
|
5c7051fec3 | ||
|
|
7e0d0b858d | ||
|
|
1d4cbbeb73 | ||
|
|
d2502718db | ||
|
|
1adf591e1c | ||
|
|
89c35e840e | ||
|
|
3b29799e70 | ||
|
|
9997724409 | ||
|
|
4579a7cb75 | ||
|
|
e2994b78c4 | ||
|
|
cab0456355 | ||
|
|
48c8612f2c | ||
|
|
d92b2d1999 | ||
|
|
dcb6962205 | ||
|
|
83533600ad | ||
|
|
5faedd45d9 | ||
|
|
3e97eec20b | ||
|
|
8f7ba01404 | ||
|
|
76de00f801 | ||
|
|
c2f2953b4e | ||
|
|
56cd5b15ed | ||
|
|
ebb9cf90a7 | ||
|
|
5d42bc0833 | ||
|
|
8ef17080a8 | ||
|
|
03f8802e3b | ||
|
|
e32b754cc5 | ||
|
|
cfdb06a06f | ||
|
|
53e26ff328 | ||
|
|
f82bc5acc1 | ||
|
|
851d565f76 | ||
|
|
eac8267d4e | ||
|
|
2a5c61fb6b | ||
|
|
b18c5b60dc | ||
|
|
9e4a505697 | ||
|
|
853fb0df13 | ||
|
|
143b2845b6 | ||
|
|
7f97b6cf48 | ||
|
|
a68dccf9d1 | ||
|
|
1da5ecef36 | ||
|
|
0ad8d115bc | ||
|
|
d873148619 | ||
|
|
fcc41a2bcd | ||
|
|
bbab4c95b7 | ||
|
|
b3e49d5b36 | ||
|
|
716cd5653d | ||
|
|
e4753bce4f | ||
|
|
c0d42ee1c5 | ||
|
|
fe7cfcfcea | ||
|
|
de988fb3b9 | ||
|
|
3d54ac1ae8 | ||
|
|
0df3ebc797 | ||
|
|
2b39c60dfc | ||
|
|
6641bf6a25 | ||
|
|
6c081a2876 | ||
|
|
de654c15c0 | ||
|
|
01daf552da | ||
|
|
bd048a0132 | ||
|
|
66a219c146 | ||
|
|
786c200267 | ||
|
|
4f3c78890b | ||
|
|
ec054cde2b | ||
|
|
4ddcef7988 | ||
|
|
34a1d4f81b | ||
|
|
4b13699ddd | ||
|
|
ba4065ee7b | ||
|
|
d833ea49c1 | ||
|
|
c489603ff6 | ||
|
|
8c50f30b7b | ||
|
|
a6de71384f | ||
|
|
78d4cd0b09 | ||
|
|
e28358acfe | ||
|
|
dec111f01c | ||
|
|
7be92ee8af | ||
|
|
d61e4c7a1b | ||
|
|
f9bb5dbdf9 | ||
|
|
ef7916d5e7 | ||
|
|
4d7f7c2ef3 | ||
|
|
3901506dd0 | ||
|
|
4f1e67e190 | ||
|
|
cbe0130d7e | ||
|
|
70ae78bfea | ||
|
|
06feaa029a | ||
|
|
4928a71787 | ||
|
|
0e30e88aba | ||
|
|
5c7e9f9518 | ||
|
|
bc678c3fa8 | ||
|
|
8b241c51ac | ||
|
|
64c274a523 | ||
|
|
12d573cec2 | ||
|
|
7366d3c588 | ||
|
|
3047ff690d | ||
|
|
96b85071a9 | ||
|
|
18edb76ead | ||
|
|
1ea5e014f3 | ||
|
|
818ab5b02b | ||
|
|
f87e1d08ba | ||
|
|
475564f1fa | ||
|
|
7a9794cdce | ||
|
|
745ed02482 | ||
|
|
b3dc04f670 | ||
|
|
201d6a3535 | ||
|
|
336162dda4 | ||
|
|
7cd2336998 | ||
|
|
3c8c13c906 | ||
|
|
b3b1fdd6ac | ||
|
|
73cd4c75ae | ||
|
|
32c54c3028 | ||
|
|
eadaf5c269 | ||
|
|
c3283710f2 | ||
|
|
9a4a33f1f5 | ||
|
|
fd5a391cf5 | ||
|
|
8982b77ee2 | ||
|
|
272e003785 | ||
|
|
7297768e9e | ||
|
|
4cfb651de1 | ||
|
|
8c7df4a721 | ||
|
|
4f40c5592e | ||
|
|
cba9537ae4 | ||
|
|
031836a206 | ||
|
|
247163f6aa | ||
|
|
8d686fc484 | ||
|
|
aaecd02885 | ||
|
|
0ede14a77d | ||
|
|
d6804bd610 | ||
|
|
99c9d7cbd3 | ||
|
|
5b28682cc7 | ||
|
|
c53aafa784 | ||
|
|
daead2382c | ||
|
|
43d8e01f67 | ||
|
|
34400585ac | ||
|
|
993c03a756 | ||
|
|
b5a955c3fb | ||
|
|
17bfcd4546 | ||
|
|
f6f4155648 | ||
|
|
d47e545518 | ||
|
|
934a1de021 | ||
|
|
b76726eab2 | ||
|
|
41891283e3 | ||
|
|
73eb736278 | ||
|
|
f624829ba5 | ||
|
|
5db59e5e03 | ||
|
|
8037239770 | ||
|
|
27abb7d53a | ||
|
|
47f6590b45 | ||
|
|
ac355978da | ||
|
|
b203047d8f |
4
.gitattributes
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
* text=auto eol=lf
|
||||||
|
*.pbxproj merge=union
|
||||||
|
Package.resolved text linguist-language=json linguist-generated=false
|
||||||
|
.swift-format text linguist-language=json
|
||||||
1
.github/CODEOWNERS
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
* @kkebo
|
||||||
10
.github/dependabot.yml
vendored
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
version: 2
|
||||||
|
updates:
|
||||||
|
- package-ecosystem: "github-actions"
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: "weekly"
|
||||||
|
assignees:
|
||||||
|
- "kkebo"
|
||||||
|
commit-message:
|
||||||
|
prefix: "ci"
|
||||||
27
.github/workflows/ci.yml
vendored
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
name: ci
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: ["main"]
|
||||||
|
pull_request:
|
||||||
|
branches: ["main"]
|
||||||
|
permissions: {}
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
shell: bash -euo pipefail {0}
|
||||||
|
jobs:
|
||||||
|
lint:
|
||||||
|
runs-on: ubuntu-24.04-arm
|
||||||
|
container: swift:6.2
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
|
- run: swift format lint -rsp .
|
||||||
|
yamllint:
|
||||||
|
runs-on: ubuntu-24.04-arm
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
|
- run: yamllint --version
|
||||||
|
- run: yamllint --strict --config-file .yamllint.yml .
|
||||||
89
.github/workflows/codeql.yml
vendored
Normal file
|
|
@ -0,0 +1,89 @@
|
||||||
|
# For most projects, this workflow file will not need changing; you simply need
|
||||||
|
# to commit it to your repository.
|
||||||
|
#
|
||||||
|
# You may wish to alter this file to override the set of languages analyzed,
|
||||||
|
# or to provide custom queries or build logic.
|
||||||
|
#
|
||||||
|
# ******** NOTE ********
|
||||||
|
# We have attempted to detect the languages in your repository. Please check
|
||||||
|
# the `language` matrix defined below to confirm you have the correct set of
|
||||||
|
# supported CodeQL languages.
|
||||||
|
#
|
||||||
|
name: "CodeQL Advanced"
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: ["main"]
|
||||||
|
pull_request:
|
||||||
|
branches: ["main"]
|
||||||
|
schedule:
|
||||||
|
- cron: '40 19 * * 3'
|
||||||
|
jobs:
|
||||||
|
analyze:
|
||||||
|
name: Analyze (${{ matrix.language }})
|
||||||
|
# Runner size impacts CodeQL analysis time. To learn more, please see:
|
||||||
|
# - https://gh.io/recommended-hardware-resources-for-running-codeql
|
||||||
|
# - https://gh.io/supported-runners-and-hardware-resources
|
||||||
|
# - https://gh.io/using-larger-runners (GitHub.com only)
|
||||||
|
# Consider using larger runners or machines with greater resources for possible analysis time improvements.
|
||||||
|
runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }}
|
||||||
|
permissions:
|
||||||
|
# required for all workflows
|
||||||
|
security-events: write
|
||||||
|
# required to fetch internal or private CodeQL packs
|
||||||
|
packages: read
|
||||||
|
# only required for workflows in private repositories
|
||||||
|
actions: read
|
||||||
|
contents: read
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- language: actions
|
||||||
|
build-mode: none
|
||||||
|
- language: swift
|
||||||
|
build-mode: manual
|
||||||
|
# CodeQL supports the following values keywords for 'language': 'actions', 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'rust', 'swift'
|
||||||
|
# Use `c-cpp` to analyze code written in C, C++ or both
|
||||||
|
# Use 'java-kotlin' to analyze code written in Java, Kotlin or both
|
||||||
|
# Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both
|
||||||
|
# To learn more about changing the languages that are analyzed or customizing the build mode for your analysis,
|
||||||
|
# see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning.
|
||||||
|
# If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how
|
||||||
|
# your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
|
# Add any setup steps before running the `github/codeql-action/init` action.
|
||||||
|
# This includes steps like installing compilers or runtimes (`actions/setup-node`
|
||||||
|
# or others). This is typically only required for manual builds.
|
||||||
|
# - name: Setup runtime (example)
|
||||||
|
# uses: actions/setup-example@v1
|
||||||
|
|
||||||
|
# Initializes the CodeQL tools for scanning.
|
||||||
|
- name: Initialize CodeQL
|
||||||
|
uses: github/codeql-action/init@0d579ffd059c29b07949a3cce3983f0780820c98 # v4.32.6
|
||||||
|
with:
|
||||||
|
languages: ${{ matrix.language }}
|
||||||
|
build-mode: ${{ matrix.build-mode }}
|
||||||
|
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||||
|
# By default, queries listed here will override any specified in a config file.
|
||||||
|
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||||
|
# If the analyze step fails for one of the languages you are analyzing with
|
||||||
|
# "We were unable to automatically build your code", modify the matrix above
|
||||||
|
# to set the build mode to "manual" for that language. Then modify this step
|
||||||
|
# to build your code.
|
||||||
|
# ℹ️ Command-line programs to run using the OS shell.
|
||||||
|
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
||||||
|
- name: Run manual build steps
|
||||||
|
# For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
|
||||||
|
# queries: security-extended,security-and-quality
|
||||||
|
|
||||||
|
if: matrix.build-mode == 'manual'
|
||||||
|
shell: bash
|
||||||
|
env:
|
||||||
|
DEVELOPER_DIR: /Applications/Xcode_26.1.1.app
|
||||||
|
run: xcodebuild build -project DNSecure.xcodeproj -scheme DNSecure -destination "platform=iOS Simulator,name=iPad Pro 13-inch (M5),OS=26.1" -quiet -showBuildTimingSummary
|
||||||
|
- name: Perform CodeQL Analysis
|
||||||
|
uses: github/codeql-action/analyze@0d579ffd059c29b07949a3cce3983f0780820c98 # v4.32.6
|
||||||
|
with:
|
||||||
|
category: "/language:${{matrix.language}}"
|
||||||
2
.gitignore
vendored
|
|
@ -47,7 +47,7 @@ playground.xcworkspace
|
||||||
# *.xcodeproj
|
# *.xcodeproj
|
||||||
# Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata
|
# Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata
|
||||||
# hence it is not needed unless you have added a package configuration file to your project
|
# hence it is not needed unless you have added a package configuration file to your project
|
||||||
# .swiftpm
|
.swiftpm
|
||||||
|
|
||||||
.build/
|
.build/
|
||||||
|
|
||||||
|
|
|
||||||
75
.swift-format
Normal file
|
|
@ -0,0 +1,75 @@
|
||||||
|
{
|
||||||
|
"fileScopedDeclarationPrivacy" : {
|
||||||
|
"accessLevel" : "private"
|
||||||
|
},
|
||||||
|
"indentConditionalCompilationBlocks" : true,
|
||||||
|
"indentSwitchCaseLabels" : false,
|
||||||
|
"indentation" : {
|
||||||
|
"spaces" : 4
|
||||||
|
},
|
||||||
|
"lineBreakAroundMultilineExpressionChainComponents" : true,
|
||||||
|
"lineBreakBeforeControlFlowKeywords" : false,
|
||||||
|
"lineBreakBeforeEachArgument" : true,
|
||||||
|
"lineBreakBeforeEachGenericRequirement" : true,
|
||||||
|
"lineBreakBetweenDeclarationAttributes" : false,
|
||||||
|
"lineLength" : 120,
|
||||||
|
"maximumBlankLines" : 1,
|
||||||
|
"multiElementCollectionTrailingCommas" : true,
|
||||||
|
"noAssignmentInExpressions" : {
|
||||||
|
"allowedFunctions" : [
|
||||||
|
"XCTAssertNoThrow"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"prioritizeKeepingFunctionOutputTogether" : true,
|
||||||
|
"reflowMultilineStringLiterals" : "never",
|
||||||
|
"respectsExistingLineBreaks" : true,
|
||||||
|
"rules" : {
|
||||||
|
"AllPublicDeclarationsHaveDocumentation" : false,
|
||||||
|
"AlwaysUseLiteralForEmptyCollectionInit" : true,
|
||||||
|
"AlwaysUseLowerCamelCase" : true,
|
||||||
|
"AmbiguousTrailingClosureOverload" : true,
|
||||||
|
"AvoidRetroactiveConformances" : false,
|
||||||
|
"BeginDocumentationCommentWithOneLineSummary" : true,
|
||||||
|
"DoNotUseSemicolons" : true,
|
||||||
|
"DontRepeatTypeInStaticProperties" : true,
|
||||||
|
"FileScopedDeclarationPrivacy" : true,
|
||||||
|
"FullyIndirectEnum" : true,
|
||||||
|
"GroupNumericLiterals" : true,
|
||||||
|
"IdentifiersMustBeASCII" : true,
|
||||||
|
"NeverForceUnwrap" : false,
|
||||||
|
"NeverUseForceTry" : true,
|
||||||
|
"NeverUseImplicitlyUnwrappedOptionals" : true,
|
||||||
|
"NoAccessLevelOnExtensionDeclaration" : true,
|
||||||
|
"NoAssignmentInExpressions" : true,
|
||||||
|
"NoBlockComments" : true,
|
||||||
|
"NoCasesWithOnlyFallthrough" : true,
|
||||||
|
"NoEmptyLinesOpeningClosingBraces" : true,
|
||||||
|
"NoEmptyTrailingClosureParentheses" : true,
|
||||||
|
"NoLabelsInCasePatterns" : true,
|
||||||
|
"NoLeadingUnderscores" : true,
|
||||||
|
"NoParensAroundConditions" : true,
|
||||||
|
"NoPlaygroundLiterals" : true,
|
||||||
|
"NoVoidReturnOnFunctionSignature" : true,
|
||||||
|
"OmitExplicitReturns" : true,
|
||||||
|
"OneCasePerLine" : true,
|
||||||
|
"OneVariableDeclarationPerLine" : true,
|
||||||
|
"OnlyOneTrailingClosureArgument" : true,
|
||||||
|
"OrderedImports" : true,
|
||||||
|
"ReplaceForEachWithForLoop" : true,
|
||||||
|
"ReturnVoidInsteadOfEmptyTuple" : true,
|
||||||
|
"TypeNamesShouldBeCapitalized" : true,
|
||||||
|
"UseEarlyExits" : true,
|
||||||
|
"UseExplicitNilCheckInConditions" : true,
|
||||||
|
"UseLetInEveryBoundCaseVariable" : true,
|
||||||
|
"UseShorthandTypeNames" : true,
|
||||||
|
"UseSingleLinePropertyGetter" : true,
|
||||||
|
"UseSynthesizedInitializer" : true,
|
||||||
|
"UseTripleSlashForDocumentationComments" : true,
|
||||||
|
"UseWhereClausesInForLoops" : true,
|
||||||
|
"ValidateDocumentationComments" : true
|
||||||
|
},
|
||||||
|
"spacesAroundRangeFormationOperators" : false,
|
||||||
|
"spacesBeforeEndOfLineComments" : 2,
|
||||||
|
"tabWidth" : 4,
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
7
.yamllint.yml
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
extends: default
|
||||||
|
|
||||||
|
rules:
|
||||||
|
line-length: false
|
||||||
|
document-start: false
|
||||||
|
truthy:
|
||||||
|
check-keys: false
|
||||||
|
|
@ -3,22 +3,11 @@
|
||||||
archiveVersion = 1;
|
archiveVersion = 1;
|
||||||
classes = {
|
classes = {
|
||||||
};
|
};
|
||||||
objectVersion = 50;
|
objectVersion = 77;
|
||||||
objects = {
|
objects = {
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
890B80D5251DC3A20046BAA0 /* DetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 890B80D4251DC3A20046BAA0 /* DetailView.swift */; };
|
|
||||||
890B80DF251DC6B50046BAA0 /* Presets.swift in Sources */ = {isa = PBXBuildFile; fileRef = 890B80DE251DC6B50046BAA0 /* Presets.swift */; };
|
|
||||||
8940023C24ACBD2700EBE74B /* DNSecureApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8940023B24ACBD2700EBE74B /* DNSecureApp.swift */; };
|
|
||||||
8940023E24ACBD2700EBE74B /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8940023D24ACBD2700EBE74B /* ContentView.swift */; };
|
|
||||||
8940024024ACBD2800EBE74B /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 8940023F24ACBD2800EBE74B /* Assets.xcassets */; };
|
|
||||||
8940024324ACBD2800EBE74B /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 8940024224ACBD2800EBE74B /* Preview Assets.xcassets */; };
|
|
||||||
8940024E24ACBD2800EBE74B /* DNSecureTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8940024D24ACBD2800EBE74B /* DNSecureTests.swift */; };
|
|
||||||
8940025924ACBD2800EBE74B /* DNSecureUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8940025824ACBD2800EBE74B /* DNSecureUITests.swift */; };
|
|
||||||
8940026924ACBE4900EBE74B /* NetworkExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8940026824ACBE4900EBE74B /* NetworkExtension.framework */; };
|
8940026924ACBE4900EBE74B /* NetworkExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8940026824ACBE4900EBE74B /* NetworkExtension.framework */; };
|
||||||
8963FDFB251DF1BC00E3DFE7 /* BundleExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8963FDFA251DF1BC00E3DFE7 /* BundleExtensions.swift */; };
|
|
||||||
8986CDCF251D9B3400D947CD /* Resolver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8986CDCE251D9B3400D947CD /* Resolver.swift */; };
|
|
||||||
89CB922125209DD100B6983C /* HowToActivateView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89CB922025209DD100B6983C /* HowToActivateView.swift */; };
|
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXContainerItemProxy section */
|
/* Begin PBXContainerItemProxy section */
|
||||||
|
|
@ -39,28 +28,44 @@
|
||||||
/* End PBXContainerItemProxy section */
|
/* End PBXContainerItemProxy section */
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
890B80D4251DC3A20046BAA0 /* DetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailView.swift; sourceTree = "<group>"; };
|
|
||||||
890B80DE251DC6B50046BAA0 /* Presets.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Presets.swift; sourceTree = "<group>"; };
|
|
||||||
8924EDF324C9CDF1004AF871 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
|
8924EDF324C9CDF1004AF871 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
|
||||||
8940023824ACBD2700EBE74B /* DNSecure.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = DNSecure.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
8940023824ACBD2700EBE74B /* DNSecure.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = DNSecure.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
8940023B24ACBD2700EBE74B /* DNSecureApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DNSecureApp.swift; sourceTree = "<group>"; };
|
|
||||||
8940023D24ACBD2700EBE74B /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
|
|
||||||
8940023F24ACBD2800EBE74B /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
|
||||||
8940024224ACBD2800EBE74B /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
|
|
||||||
8940024424ACBD2800EBE74B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
|
||||||
8940024924ACBD2800EBE74B /* DNSecureTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = DNSecureTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
8940024924ACBD2800EBE74B /* DNSecureTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = DNSecureTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
8940024D24ACBD2800EBE74B /* DNSecureTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DNSecureTests.swift; sourceTree = "<group>"; };
|
|
||||||
8940024F24ACBD2800EBE74B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
|
||||||
8940025424ACBD2800EBE74B /* DNSecureUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = DNSecureUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
8940025424ACBD2800EBE74B /* DNSecureUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = DNSecureUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
8940025824ACBD2800EBE74B /* DNSecureUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DNSecureUITests.swift; sourceTree = "<group>"; };
|
|
||||||
8940025A24ACBD2800EBE74B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
|
||||||
8940026624ACBE4900EBE74B /* DNSecure.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DNSecure.entitlements; sourceTree = "<group>"; };
|
|
||||||
8940026824ACBE4900EBE74B /* NetworkExtension.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = NetworkExtension.framework; path = System/Library/Frameworks/NetworkExtension.framework; sourceTree = SDKROOT; };
|
8940026824ACBE4900EBE74B /* NetworkExtension.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = NetworkExtension.framework; path = System/Library/Frameworks/NetworkExtension.framework; sourceTree = SDKROOT; };
|
||||||
8963FDFA251DF1BC00E3DFE7 /* BundleExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BundleExtensions.swift; sourceTree = "<group>"; };
|
|
||||||
8986CDCE251D9B3400D947CD /* Resolver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Resolver.swift; sourceTree = "<group>"; };
|
|
||||||
89CB922025209DD100B6983C /* HowToActivateView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HowToActivateView.swift; sourceTree = "<group>"; };
|
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
|
/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */
|
||||||
|
899AD03F2E66100500449710 /* Exceptions for "DNSecure" folder in "DNSecure" target */ = {
|
||||||
|
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
|
||||||
|
membershipExceptions = (
|
||||||
|
Info.plist,
|
||||||
|
);
|
||||||
|
target = 8940023724ACBD2700EBE74B /* DNSecure */;
|
||||||
|
};
|
||||||
|
/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */
|
||||||
|
|
||||||
|
/* Begin PBXFileSystemSynchronizedRootGroup section */
|
||||||
|
899AD0232E66100500449710 /* DNSecure */ = {
|
||||||
|
isa = PBXFileSystemSynchronizedRootGroup;
|
||||||
|
exceptions = (
|
||||||
|
899AD03F2E66100500449710 /* Exceptions for "DNSecure" folder in "DNSecure" target */,
|
||||||
|
);
|
||||||
|
path = DNSecure;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
899AD0422E66100E00449710 /* DNSecureTests */ = {
|
||||||
|
isa = PBXFileSystemSynchronizedRootGroup;
|
||||||
|
path = DNSecureTests;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
899AD0472E66101100449710 /* DNSecureUITests */ = {
|
||||||
|
isa = PBXFileSystemSynchronizedRootGroup;
|
||||||
|
path = DNSecureUITests;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
/* End PBXFileSystemSynchronizedRootGroup section */
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
8940023524ACBD2700EBE74B /* Frameworks */ = {
|
8940023524ACBD2700EBE74B /* Frameworks */ = {
|
||||||
isa = PBXFrameworksBuildPhase;
|
isa = PBXFrameworksBuildPhase;
|
||||||
|
|
@ -91,9 +96,9 @@
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
8924EDF324C9CDF1004AF871 /* README.md */,
|
8924EDF324C9CDF1004AF871 /* README.md */,
|
||||||
8940023A24ACBD2700EBE74B /* DNSecure */,
|
899AD0232E66100500449710 /* DNSecure */,
|
||||||
8940024C24ACBD2800EBE74B /* DNSecureTests */,
|
899AD0422E66100E00449710 /* DNSecureTests */,
|
||||||
8940025724ACBD2800EBE74B /* DNSecureUITests */,
|
899AD0472E66101100449710 /* DNSecureUITests */,
|
||||||
8940023924ACBD2700EBE74B /* Products */,
|
8940023924ACBD2700EBE74B /* Products */,
|
||||||
8940026724ACBE4900EBE74B /* Frameworks */,
|
8940026724ACBE4900EBE74B /* Frameworks */,
|
||||||
);
|
);
|
||||||
|
|
@ -109,50 +114,6 @@
|
||||||
name = Products;
|
name = Products;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
8940023A24ACBD2700EBE74B /* DNSecure */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
8940026624ACBE4900EBE74B /* DNSecure.entitlements */,
|
|
||||||
8940023B24ACBD2700EBE74B /* DNSecureApp.swift */,
|
|
||||||
8940023D24ACBD2700EBE74B /* ContentView.swift */,
|
|
||||||
89CB922025209DD100B6983C /* HowToActivateView.swift */,
|
|
||||||
8963FDFA251DF1BC00E3DFE7 /* BundleExtensions.swift */,
|
|
||||||
890B80DE251DC6B50046BAA0 /* Presets.swift */,
|
|
||||||
890B80D4251DC3A20046BAA0 /* DetailView.swift */,
|
|
||||||
8940023F24ACBD2800EBE74B /* Assets.xcassets */,
|
|
||||||
8940024424ACBD2800EBE74B /* Info.plist */,
|
|
||||||
8940024124ACBD2800EBE74B /* Preview Content */,
|
|
||||||
8986CDCE251D9B3400D947CD /* Resolver.swift */,
|
|
||||||
);
|
|
||||||
path = DNSecure;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
8940024124ACBD2800EBE74B /* Preview Content */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
8940024224ACBD2800EBE74B /* Preview Assets.xcassets */,
|
|
||||||
);
|
|
||||||
path = "Preview Content";
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
8940024C24ACBD2800EBE74B /* DNSecureTests */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
8940024D24ACBD2800EBE74B /* DNSecureTests.swift */,
|
|
||||||
8940024F24ACBD2800EBE74B /* Info.plist */,
|
|
||||||
);
|
|
||||||
path = DNSecureTests;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
8940025724ACBD2800EBE74B /* DNSecureUITests */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
8940025824ACBD2800EBE74B /* DNSecureUITests.swift */,
|
|
||||||
8940025A24ACBD2800EBE74B /* Info.plist */,
|
|
||||||
);
|
|
||||||
path = DNSecureUITests;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
8940026724ACBE4900EBE74B /* Frameworks */ = {
|
8940026724ACBE4900EBE74B /* Frameworks */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
|
@ -176,6 +137,9 @@
|
||||||
);
|
);
|
||||||
dependencies = (
|
dependencies = (
|
||||||
);
|
);
|
||||||
|
fileSystemSynchronizedGroups = (
|
||||||
|
899AD0232E66100500449710 /* DNSecure */,
|
||||||
|
);
|
||||||
name = DNSecure;
|
name = DNSecure;
|
||||||
productName = DNSecure;
|
productName = DNSecure;
|
||||||
productReference = 8940023824ACBD2700EBE74B /* DNSecure.app */;
|
productReference = 8940023824ACBD2700EBE74B /* DNSecure.app */;
|
||||||
|
|
@ -194,6 +158,9 @@
|
||||||
dependencies = (
|
dependencies = (
|
||||||
8940024B24ACBD2800EBE74B /* PBXTargetDependency */,
|
8940024B24ACBD2800EBE74B /* PBXTargetDependency */,
|
||||||
);
|
);
|
||||||
|
fileSystemSynchronizedGroups = (
|
||||||
|
899AD0422E66100E00449710 /* DNSecureTests */,
|
||||||
|
);
|
||||||
name = DNSecureTests;
|
name = DNSecureTests;
|
||||||
productName = DNSecureTests;
|
productName = DNSecureTests;
|
||||||
productReference = 8940024924ACBD2800EBE74B /* DNSecureTests.xctest */;
|
productReference = 8940024924ACBD2800EBE74B /* DNSecureTests.xctest */;
|
||||||
|
|
@ -212,6 +179,9 @@
|
||||||
dependencies = (
|
dependencies = (
|
||||||
8940025624ACBD2800EBE74B /* PBXTargetDependency */,
|
8940025624ACBD2800EBE74B /* PBXTargetDependency */,
|
||||||
);
|
);
|
||||||
|
fileSystemSynchronizedGroups = (
|
||||||
|
899AD0472E66101100449710 /* DNSecureUITests */,
|
||||||
|
);
|
||||||
name = DNSecureUITests;
|
name = DNSecureUITests;
|
||||||
productName = DNSecureUITests;
|
productName = DNSecureUITests;
|
||||||
productReference = 8940025424ACBD2800EBE74B /* DNSecureUITests.xctest */;
|
productReference = 8940025424ACBD2800EBE74B /* DNSecureUITests.xctest */;
|
||||||
|
|
@ -223,8 +193,9 @@
|
||||||
8940023024ACBD2700EBE74B /* Project object */ = {
|
8940023024ACBD2700EBE74B /* Project object */ = {
|
||||||
isa = PBXProject;
|
isa = PBXProject;
|
||||||
attributes = {
|
attributes = {
|
||||||
|
BuildIndependentTargetsInParallel = YES;
|
||||||
LastSwiftUpdateCheck = 1200;
|
LastSwiftUpdateCheck = 1200;
|
||||||
LastUpgradeCheck = 1200;
|
LastUpgradeCheck = 2600;
|
||||||
TargetAttributes = {
|
TargetAttributes = {
|
||||||
8940023724ACBD2700EBE74B = {
|
8940023724ACBD2700EBE74B = {
|
||||||
CreatedOnToolsVersion = 12.0;
|
CreatedOnToolsVersion = 12.0;
|
||||||
|
|
@ -240,7 +211,6 @@
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
buildConfigurationList = 8940023324ACBD2700EBE74B /* Build configuration list for PBXProject "DNSecure" */;
|
buildConfigurationList = 8940023324ACBD2700EBE74B /* Build configuration list for PBXProject "DNSecure" */;
|
||||||
compatibilityVersion = "Xcode 9.3";
|
|
||||||
developmentRegion = en;
|
developmentRegion = en;
|
||||||
hasScannedForEncodings = 0;
|
hasScannedForEncodings = 0;
|
||||||
knownRegions = (
|
knownRegions = (
|
||||||
|
|
@ -248,6 +218,8 @@
|
||||||
Base,
|
Base,
|
||||||
);
|
);
|
||||||
mainGroup = 8940022F24ACBD2700EBE74B;
|
mainGroup = 8940022F24ACBD2700EBE74B;
|
||||||
|
minimizedProjectReferenceProxies = 1;
|
||||||
|
preferredProjectObjectVersion = 77;
|
||||||
productRefGroup = 8940023924ACBD2700EBE74B /* Products */;
|
productRefGroup = 8940023924ACBD2700EBE74B /* Products */;
|
||||||
projectDirPath = "";
|
projectDirPath = "";
|
||||||
projectRoot = "";
|
projectRoot = "";
|
||||||
|
|
@ -264,8 +236,6 @@
|
||||||
isa = PBXResourcesBuildPhase;
|
isa = PBXResourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
8940024324ACBD2800EBE74B /* Preview Assets.xcassets in Resources */,
|
|
||||||
8940024024ACBD2800EBE74B /* Assets.xcassets in Resources */,
|
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
|
@ -290,13 +260,6 @@
|
||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
8986CDCF251D9B3400D947CD /* Resolver.swift in Sources */,
|
|
||||||
89CB922125209DD100B6983C /* HowToActivateView.swift in Sources */,
|
|
||||||
890B80D5251DC3A20046BAA0 /* DetailView.swift in Sources */,
|
|
||||||
8940023E24ACBD2700EBE74B /* ContentView.swift in Sources */,
|
|
||||||
890B80DF251DC6B50046BAA0 /* Presets.swift in Sources */,
|
|
||||||
8940023C24ACBD2700EBE74B /* DNSecureApp.swift in Sources */,
|
|
||||||
8963FDFB251DF1BC00E3DFE7 /* BundleExtensions.swift in Sources */,
|
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
|
@ -304,7 +267,6 @@
|
||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
8940024E24ACBD2800EBE74B /* DNSecureTests.swift in Sources */,
|
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
|
@ -312,7 +274,6 @@
|
||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
8940025924ACBD2800EBE74B /* DNSecureUITests.swift in Sources */,
|
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
|
@ -336,10 +297,10 @@
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||||
|
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||||
CLANG_ANALYZER_NONNULL = YES;
|
CLANG_ANALYZER_NONNULL = YES;
|
||||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||||
CLANG_CXX_LIBRARY = "libc++";
|
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CLANG_ENABLE_OBJC_ARC = YES;
|
CLANG_ENABLE_OBJC_ARC = YES;
|
||||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||||
|
|
@ -367,9 +328,11 @@
|
||||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||||
COPY_PHASE_STRIP = NO;
|
COPY_PHASE_STRIP = NO;
|
||||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||||
|
DEVELOPMENT_TEAM = X4678G5DL2;
|
||||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||||
ENABLE_TESTABILITY = YES;
|
ENABLE_TESTABILITY = YES;
|
||||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||||
|
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||||
GCC_DYNAMIC_NO_PIC = NO;
|
GCC_DYNAMIC_NO_PIC = NO;
|
||||||
GCC_NO_COMMON_BLOCKS = YES;
|
GCC_NO_COMMON_BLOCKS = YES;
|
||||||
GCC_OPTIMIZATION_LEVEL = 0;
|
GCC_OPTIMIZATION_LEVEL = 0;
|
||||||
|
|
@ -383,13 +346,15 @@
|
||||||
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 = 14.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
||||||
|
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||||
MTL_FAST_MATH = YES;
|
MTL_FAST_MATH = YES;
|
||||||
ONLY_ACTIVE_ARCH = YES;
|
ONLY_ACTIVE_ARCH = YES;
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
|
||||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||||
|
SWIFT_VERSION = 6.0;
|
||||||
};
|
};
|
||||||
name = Debug;
|
name = Debug;
|
||||||
};
|
};
|
||||||
|
|
@ -397,10 +362,10 @@
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||||
|
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||||
CLANG_ANALYZER_NONNULL = YES;
|
CLANG_ANALYZER_NONNULL = YES;
|
||||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||||
CLANG_CXX_LIBRARY = "libc++";
|
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CLANG_ENABLE_OBJC_ARC = YES;
|
CLANG_ENABLE_OBJC_ARC = YES;
|
||||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||||
|
|
@ -428,9 +393,11 @@
|
||||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||||
COPY_PHASE_STRIP = NO;
|
COPY_PHASE_STRIP = NO;
|
||||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||||
|
DEVELOPMENT_TEAM = X4678G5DL2;
|
||||||
ENABLE_NS_ASSERTIONS = NO;
|
ENABLE_NS_ASSERTIONS = NO;
|
||||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||||
|
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||||
GCC_NO_COMMON_BLOCKS = YES;
|
GCC_NO_COMMON_BLOCKS = YES;
|
||||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||||
|
|
@ -438,12 +405,13 @@
|
||||||
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 = 14.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
||||||
|
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||||
MTL_ENABLE_DEBUG_INFO = NO;
|
MTL_ENABLE_DEBUG_INFO = NO;
|
||||||
MTL_FAST_MATH = YES;
|
MTL_FAST_MATH = YES;
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
SWIFT_COMPILATION_MODE = wholemodule;
|
SWIFT_COMPILATION_MODE = wholemodule;
|
||||||
SWIFT_OPTIMIZATION_LEVEL = "-O";
|
SWIFT_VERSION = 6.0;
|
||||||
VALIDATE_PRODUCT = YES;
|
VALIDATE_PRODUCT = YES;
|
||||||
};
|
};
|
||||||
name = Release;
|
name = Release;
|
||||||
|
|
@ -453,23 +421,35 @@
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||||
|
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES;
|
||||||
CODE_SIGN_ENTITLEMENTS = DNSecure/DNSecure.entitlements;
|
CODE_SIGN_ENTITLEMENTS = DNSecure/DNSecure.entitlements;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 7;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
DEVELOPMENT_ASSET_PATHS = "\"DNSecure/Preview Content\"";
|
|
||||||
DEVELOPMENT_TEAM = X4678G5DL2;
|
DEVELOPMENT_TEAM = X4678G5DL2;
|
||||||
|
ENABLE_APP_SANDBOX = YES;
|
||||||
|
ENABLE_OUTGOING_NETWORK_CONNECTIONS = YES;
|
||||||
ENABLE_PREVIEWS = YES;
|
ENABLE_PREVIEWS = YES;
|
||||||
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
INFOPLIST_FILE = DNSecure/Info.plist;
|
INFOPLIST_FILE = DNSecure/Info.plist;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
INFOPLIST_KEY_ITSAppUsesNonExemptEncryption = NO;
|
||||||
|
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities";
|
||||||
|
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
||||||
|
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
||||||
|
INFOPLIST_KEY_UIRequiredDeviceCapabilities = armv7;
|
||||||
|
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||||
|
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.0.3;
|
MARKETING_VERSION = 1.6.2;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = xyz.kebo.DNSecure;
|
PRODUCT_BUNDLE_IDENTIFIER = xyz.kebo.DNSecure;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SUPPORTS_MACCATALYST = NO;
|
STRING_CATALOG_GENERATE_SYMBOLS = YES;
|
||||||
SWIFT_VERSION = 5.0;
|
SUPPORTS_MACCATALYST = YES;
|
||||||
|
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
|
||||||
|
SWIFT_APPROACHABLE_CONCURRENCY = YES;
|
||||||
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
TARGETED_DEVICE_FAMILY = "1,2";
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
};
|
};
|
||||||
name = Debug;
|
name = Debug;
|
||||||
|
|
@ -479,23 +459,35 @@
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||||
|
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES;
|
||||||
CODE_SIGN_ENTITLEMENTS = DNSecure/DNSecure.entitlements;
|
CODE_SIGN_ENTITLEMENTS = DNSecure/DNSecure.entitlements;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 7;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
DEVELOPMENT_ASSET_PATHS = "\"DNSecure/Preview Content\"";
|
|
||||||
DEVELOPMENT_TEAM = X4678G5DL2;
|
DEVELOPMENT_TEAM = X4678G5DL2;
|
||||||
|
ENABLE_APP_SANDBOX = YES;
|
||||||
|
ENABLE_OUTGOING_NETWORK_CONNECTIONS = YES;
|
||||||
ENABLE_PREVIEWS = YES;
|
ENABLE_PREVIEWS = YES;
|
||||||
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
INFOPLIST_FILE = DNSecure/Info.plist;
|
INFOPLIST_FILE = DNSecure/Info.plist;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
INFOPLIST_KEY_ITSAppUsesNonExemptEncryption = NO;
|
||||||
|
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities";
|
||||||
|
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
||||||
|
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
||||||
|
INFOPLIST_KEY_UIRequiredDeviceCapabilities = armv7;
|
||||||
|
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||||
|
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.0.3;
|
MARKETING_VERSION = 1.6.2;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = xyz.kebo.DNSecure;
|
PRODUCT_BUNDLE_IDENTIFIER = xyz.kebo.DNSecure;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SUPPORTS_MACCATALYST = NO;
|
STRING_CATALOG_GENERATE_SYMBOLS = YES;
|
||||||
SWIFT_VERSION = 5.0;
|
SUPPORTS_MACCATALYST = YES;
|
||||||
|
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
|
||||||
|
SWIFT_APPROACHABLE_CONCURRENCY = YES;
|
||||||
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
TARGETED_DEVICE_FAMILY = "1,2";
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
};
|
};
|
||||||
name = Release;
|
name = Release;
|
||||||
|
|
@ -503,62 +495,48 @@
|
||||||
8940026124ACBD2800EBE74B /* Debug */ = {
|
8940026124ACBD2800EBE74B /* Debug */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
|
||||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
DEVELOPMENT_TEAM = X4678G5DL2;
|
DEVELOPMENT_TEAM = X4678G5DL2;
|
||||||
INFOPLIST_FILE = DNSecureTests/Info.plist;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
MARKETING_VERSION = 1.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
|
||||||
"$(inherited)",
|
|
||||||
"@executable_path/Frameworks",
|
|
||||||
"@loader_path/Frameworks",
|
|
||||||
);
|
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = xyz.kebo.DNSecureTests;
|
PRODUCT_BUNDLE_IDENTIFIER = xyz.kebo.DNSecureTests;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_APPROACHABLE_CONCURRENCY = YES;
|
||||||
TARGETED_DEVICE_FAMILY = "1,2";
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/DNSecure.app/DNSecure";
|
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/DNSecure.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/DNSecure";
|
||||||
};
|
};
|
||||||
name = Debug;
|
name = Debug;
|
||||||
};
|
};
|
||||||
8940026224ACBD2800EBE74B /* Release */ = {
|
8940026224ACBD2800EBE74B /* Release */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
|
||||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
DEVELOPMENT_TEAM = X4678G5DL2;
|
DEVELOPMENT_TEAM = X4678G5DL2;
|
||||||
INFOPLIST_FILE = DNSecureTests/Info.plist;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
MARKETING_VERSION = 1.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
|
||||||
"$(inherited)",
|
|
||||||
"@executable_path/Frameworks",
|
|
||||||
"@loader_path/Frameworks",
|
|
||||||
);
|
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = xyz.kebo.DNSecureTests;
|
PRODUCT_BUNDLE_IDENTIFIER = xyz.kebo.DNSecureTests;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_APPROACHABLE_CONCURRENCY = YES;
|
||||||
TARGETED_DEVICE_FAMILY = "1,2";
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/DNSecure.app/DNSecure";
|
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/DNSecure.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/DNSecure";
|
||||||
};
|
};
|
||||||
name = Release;
|
name = Release;
|
||||||
};
|
};
|
||||||
8940026424ACBD2800EBE74B /* Debug */ = {
|
8940026424ACBD2800EBE74B /* Debug */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
DEVELOPMENT_TEAM = X4678G5DL2;
|
DEVELOPMENT_TEAM = X4678G5DL2;
|
||||||
INFOPLIST_FILE = DNSecureUITests/Info.plist;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
MARKETING_VERSION = 1.0;
|
||||||
"$(inherited)",
|
|
||||||
"@executable_path/Frameworks",
|
|
||||||
"@loader_path/Frameworks",
|
|
||||||
);
|
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = xyz.kebo.DNSecureUITests;
|
PRODUCT_BUNDLE_IDENTIFIER = xyz.kebo.DNSecureUITests;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_APPROACHABLE_CONCURRENCY = YES;
|
||||||
TARGETED_DEVICE_FAMILY = "1,2";
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
TEST_TARGET_NAME = DNSecure;
|
TEST_TARGET_NAME = DNSecure;
|
||||||
};
|
};
|
||||||
|
|
@ -567,18 +545,14 @@
|
||||||
8940026524ACBD2800EBE74B /* Release */ = {
|
8940026524ACBD2800EBE74B /* Release */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
DEVELOPMENT_TEAM = X4678G5DL2;
|
DEVELOPMENT_TEAM = X4678G5DL2;
|
||||||
INFOPLIST_FILE = DNSecureUITests/Info.plist;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
MARKETING_VERSION = 1.0;
|
||||||
"$(inherited)",
|
|
||||||
"@executable_path/Frameworks",
|
|
||||||
"@loader_path/Frameworks",
|
|
||||||
);
|
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = xyz.kebo.DNSecureUITests;
|
PRODUCT_BUNDLE_IDENTIFIER = xyz.kebo.DNSecureUITests;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_APPROACHABLE_CONCURRENCY = YES;
|
||||||
TARGETED_DEVICE_FAMILY = "1,2";
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
TEST_TARGET_NAME = DNSecure;
|
TEST_TARGET_NAME = DNSecure;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Scheme
|
<Scheme
|
||||||
LastUpgradeVersion = "1200"
|
LastUpgradeVersion = "2600"
|
||||||
version = "1.3">
|
version = "1.3">
|
||||||
<BuildAction
|
<BuildAction
|
||||||
parallelizeBuildables = "YES"
|
parallelizeBuildables = "YES"
|
||||||
|
|
|
||||||
8
DNSecure/AppIcon.icon/Assets/DNSecure-icon.svg
Normal file
|
After Width: | Height: | Size: 32 KiB |
29
DNSecure/AppIcon.icon/icon.json
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
{
|
||||||
|
"fill" : {
|
||||||
|
"automatic-gradient" : "srgb:0.96863,0.77255,0.28235,1.00000"
|
||||||
|
},
|
||||||
|
"groups" : [
|
||||||
|
{
|
||||||
|
"layers" : [
|
||||||
|
{
|
||||||
|
"image-name" : "DNSecure-icon.svg",
|
||||||
|
"name" : "DNSecure-icon"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"shadow" : {
|
||||||
|
"kind" : "neutral",
|
||||||
|
"opacity" : 0.5
|
||||||
|
},
|
||||||
|
"translucency" : {
|
||||||
|
"enabled" : true,
|
||||||
|
"value" : 0.5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"supported-platforms" : {
|
||||||
|
"circles" : [
|
||||||
|
"watchOS"
|
||||||
|
],
|
||||||
|
"squares" : "shared"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "Icon-1024.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"platform" : "ios",
|
||||||
|
"size" : "1024x1024"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
|
|
@ -1,116 +0,0 @@
|
||||||
{
|
|
||||||
"images" : [
|
|
||||||
{
|
|
||||||
"filename" : "Icon-40.png",
|
|
||||||
"idiom" : "iphone",
|
|
||||||
"scale" : "2x",
|
|
||||||
"size" : "20x20"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"filename" : "Icon-60.png",
|
|
||||||
"idiom" : "iphone",
|
|
||||||
"scale" : "3x",
|
|
||||||
"size" : "20x20"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"filename" : "Icon-58.png",
|
|
||||||
"idiom" : "iphone",
|
|
||||||
"scale" : "2x",
|
|
||||||
"size" : "29x29"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"filename" : "Icon-87.png",
|
|
||||||
"idiom" : "iphone",
|
|
||||||
"scale" : "3x",
|
|
||||||
"size" : "29x29"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"filename" : "Icon-80.png",
|
|
||||||
"idiom" : "iphone",
|
|
||||||
"scale" : "2x",
|
|
||||||
"size" : "40x40"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"filename" : "Icon-120.png",
|
|
||||||
"idiom" : "iphone",
|
|
||||||
"scale" : "3x",
|
|
||||||
"size" : "40x40"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"filename" : "Icon-121.png",
|
|
||||||
"idiom" : "iphone",
|
|
||||||
"scale" : "2x",
|
|
||||||
"size" : "60x60"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"filename" : "Icon-180.png",
|
|
||||||
"idiom" : "iphone",
|
|
||||||
"scale" : "3x",
|
|
||||||
"size" : "60x60"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"filename" : "Icon-20.png",
|
|
||||||
"idiom" : "ipad",
|
|
||||||
"scale" : "1x",
|
|
||||||
"size" : "20x20"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"filename" : "Icon-41.png",
|
|
||||||
"idiom" : "ipad",
|
|
||||||
"scale" : "2x",
|
|
||||||
"size" : "20x20"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"filename" : "Icon-29.png",
|
|
||||||
"idiom" : "ipad",
|
|
||||||
"scale" : "1x",
|
|
||||||
"size" : "29x29"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"filename" : "Icon-59.png",
|
|
||||||
"idiom" : "ipad",
|
|
||||||
"scale" : "2x",
|
|
||||||
"size" : "29x29"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"filename" : "Icon-42.png",
|
|
||||||
"idiom" : "ipad",
|
|
||||||
"scale" : "1x",
|
|
||||||
"size" : "40x40"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"filename" : "Icon-81.png",
|
|
||||||
"idiom" : "ipad",
|
|
||||||
"scale" : "2x",
|
|
||||||
"size" : "40x40"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"filename" : "Icon-76.png",
|
|
||||||
"idiom" : "ipad",
|
|
||||||
"scale" : "1x",
|
|
||||||
"size" : "76x76"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"filename" : "Icon-152.png",
|
|
||||||
"idiom" : "ipad",
|
|
||||||
"scale" : "2x",
|
|
||||||
"size" : "76x76"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"filename" : "Icon-167.png",
|
|
||||||
"idiom" : "ipad",
|
|
||||||
"scale" : "2x",
|
|
||||||
"size" : "83.5x83.5"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"filename" : "Icon-1024.png",
|
|
||||||
"idiom" : "ios-marketing",
|
|
||||||
"scale" : "1x",
|
|
||||||
"size" : "1024x1024"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"info" : {
|
|
||||||
"author" : "xcode",
|
|
||||||
"version" : 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
Before Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 4.7 KiB |
|
Before Width: | Height: | Size: 5.3 KiB |
|
Before Width: | Height: | Size: 5.7 KiB |
|
Before Width: | Height: | Size: 657 B |
|
Before Width: | Height: | Size: 902 B |
|
Before Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 2.7 KiB |
BIN
DNSecure/Assets.xcassets/HowToActivate/AllDone.imageset/AllDone.png
vendored
Normal file
|
After Width: | Height: | Size: 581 KiB |
21
DNSecure/Assets.xcassets/HowToActivate/AllDone.imageset/Contents.json
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "AllDone.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
21
DNSecure/Assets.xcassets/HowToActivate/FiltersSettings.imageset/Contents.json
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "FiltersSettings.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
DNSecure/Assets.xcassets/HowToActivate/FiltersSettings.imageset/FiltersSettings.png
vendored
Normal file
|
After Width: | Height: | Size: 498 KiB |
21
DNSecure/Assets.xcassets/HowToActivate/MakeItEnabled.imageset/Contents.json
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "MakeItEnabled.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
DNSecure/Assets.xcassets/HowToActivate/MakeItEnabled.imageset/MakeItEnabled.png
vendored
Normal file
|
After Width: | Height: | Size: 28 KiB |
21
DNSecure/Assets.xcassets/HowToActivate/MontereyMakeServiceActive.imageset/Contents.json
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "IMG_0244.jpg",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
DNSecure/Assets.xcassets/HowToActivate/MontereyMakeServiceActive.imageset/IMG_0244.jpg
vendored
Normal file
|
After Width: | Height: | Size: 44 KiB |
21
DNSecure/Assets.xcassets/HowToActivate/MontereyNetworkSettings.imageset/Contents.json
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "Screen Shot 2021-02-13 at 11.25.16 AM.jpg",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 155 KiB |
21
DNSecure/Assets.xcassets/HowToActivate/MontereyNetworkSettingsApply.imageset/Contents.json
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "Image 2-13-21 at 12.10 PM.jpg",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 69 KiB |
21
DNSecure/Assets.xcassets/HowToActivate/MontereySystemPreferences.imageset/Contents.json
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "Screen Shot 2021-02-13 at 11.36.47 AM.jpg",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 198 KiB |
21
DNSecure/Assets.xcassets/HowToActivate/MontereySystemPreferencesIcon.imageset/Contents.json
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "IMG_0250.jpg",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
DNSecure/Assets.xcassets/HowToActivate/MontereySystemPreferencesIcon.imageset/IMG_0250.jpg
vendored
Normal file
|
After Width: | Height: | Size: 73 KiB |
21
DNSecure/Assets.xcassets/HowToActivate/NetworkSettings.imageset/Contents.json
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "NetworkSettings.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
DNSecure/Assets.xcassets/HowToActivate/NetworkSettings.imageset/NetworkSettings.png
vendored
Normal file
|
After Width: | Height: | Size: 517 KiB |
21
DNSecure/Assets.xcassets/HowToActivate/SystemSettingsIcon.imageset/Contents.json
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "SystemSettingsIcon.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
DNSecure/Assets.xcassets/HowToActivate/SystemSettingsIcon.imageset/SystemSettingsIcon.png
vendored
Normal file
|
After Width: | Height: | Size: 88 KiB |
|
|
@ -1,229 +0,0 @@
|
||||||
//
|
|
||||||
// ContentView.swift
|
|
||||||
// DNSecure
|
|
||||||
//
|
|
||||||
// Created by Kenta Kubo on 7/1/20.
|
|
||||||
//
|
|
||||||
|
|
||||||
import NetworkExtension
|
|
||||||
import SwiftUI
|
|
||||||
|
|
||||||
struct ContentView {
|
|
||||||
@AppStorage("servers") var servers = Presets.servers
|
|
||||||
@AppStorage("usedID") var usedID: String?
|
|
||||||
@State var isEnabled = false
|
|
||||||
@State var selection: Int?
|
|
||||||
@State var alertIsPresented = false
|
|
||||||
@State var alertTitle = ""
|
|
||||||
@State var alertMessage = ""
|
|
||||||
@State var guideIsPresented = false
|
|
||||||
|
|
||||||
func addNewDoTServer() {
|
|
||||||
self.servers.append(
|
|
||||||
.init(
|
|
||||||
name: "New",
|
|
||||||
configuration: .dnsOverTLS(DoTConfiguration())
|
|
||||||
)
|
|
||||||
)
|
|
||||||
self.selection = self.servers.count - 1
|
|
||||||
}
|
|
||||||
|
|
||||||
func addNewDoHServer() {
|
|
||||||
self.servers.append(
|
|
||||||
.init(
|
|
||||||
name: "New",
|
|
||||||
configuration: .dnsOverHTTPS(DoHConfiguration())
|
|
||||||
)
|
|
||||||
)
|
|
||||||
self.selection = self.servers.count - 1
|
|
||||||
}
|
|
||||||
|
|
||||||
func removeServers(at indexSet: IndexSet) {
|
|
||||||
if let current = self.selection, indexSet.contains(current) {
|
|
||||||
self.selection = min(
|
|
||||||
current,
|
|
||||||
self.servers.count - 1 - indexSet.count
|
|
||||||
)
|
|
||||||
}
|
|
||||||
self.servers.remove(atOffsets: indexSet)
|
|
||||||
self.syncSettings()
|
|
||||||
}
|
|
||||||
|
|
||||||
func moveServers(from src: IndexSet, to dst: Int) {
|
|
||||||
// TODO: Change self.selection if needed
|
|
||||||
self.servers.move(fromOffsets: src, toOffset: dst)
|
|
||||||
}
|
|
||||||
|
|
||||||
func updateStatus() {
|
|
||||||
#if !targetEnvironment(simulator)
|
|
||||||
let manager = NEDNSSettingsManager.shared()
|
|
||||||
manager.loadFromPreferences {
|
|
||||||
if let err = $0 {
|
|
||||||
logger.error("\(err.localizedDescription)")
|
|
||||||
self.alert("Load Error", err.localizedDescription)
|
|
||||||
} else {
|
|
||||||
self.isEnabled = manager.isEnabled
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
func syncSettings() {
|
|
||||||
#if !targetEnvironment(simulator)
|
|
||||||
let manager = NEDNSSettingsManager.shared()
|
|
||||||
manager.loadFromPreferences { loadError in
|
|
||||||
if let loadError = loadError {
|
|
||||||
logger.error("\(loadError.localizedDescription)")
|
|
||||||
self.alert("Load Error", loadError.localizedDescription)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if let usedID = self.usedID,
|
|
||||||
let uuid = UUID(uuidString: usedID),
|
|
||||||
let server = self.servers.find(by: uuid) {
|
|
||||||
manager.dnsSettings = server.configuration.toDNSSettings()
|
|
||||||
manager.saveToPreferences { saveError in
|
|
||||||
self.updateStatus()
|
|
||||||
if let saveError = saveError as NSError? {
|
|
||||||
guard saveError.domain != "NEConfigurationErrorDomain"
|
|
||||||
|| saveError.code != 9 else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
logger.error("\(saveError.localizedDescription)")
|
|
||||||
self.alert("Save Error", saveError.localizedDescription)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
logger.debug("DNS settings was saved")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
guard manager.dnsSettings != nil else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
manager.removeFromPreferences { removeError in
|
|
||||||
self.updateStatus()
|
|
||||||
if let removeError = removeError {
|
|
||||||
logger.error("\(removeError.localizedDescription)")
|
|
||||||
self.alert("Remove Error", removeError.localizedDescription)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
logger.debug("DNS settings was removed")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
func alert(_ title: String, _ message: String) {
|
|
||||||
self.alertTitle = title
|
|
||||||
self.alertMessage = message
|
|
||||||
self.alertIsPresented = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension ContentView: View {
|
|
||||||
var body: some View {
|
|
||||||
NavigationView {
|
|
||||||
List {
|
|
||||||
Section(header: Text("Servers")) {
|
|
||||||
ForEach(Array(self.servers.enumerated()), id: \.offset.self) { i, server in
|
|
||||||
NavigationLink(
|
|
||||||
destination: DetailView(
|
|
||||||
server: .init(
|
|
||||||
get: { server },
|
|
||||||
set: {
|
|
||||||
self.servers[i] = $0
|
|
||||||
self.syncSettings()
|
|
||||||
}
|
|
||||||
),
|
|
||||||
isOn: .init(
|
|
||||||
get: {
|
|
||||||
self.usedID == server.id.uuidString
|
|
||||||
},
|
|
||||||
set: {
|
|
||||||
if $0 {
|
|
||||||
self.usedID = server.id.uuidString
|
|
||||||
} else {
|
|
||||||
self.usedID = nil
|
|
||||||
}
|
|
||||||
self.syncSettings()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
),
|
|
||||||
tag: i,
|
|
||||||
selection: self.$selection
|
|
||||||
) {
|
|
||||||
VStack(alignment: .leading) {
|
|
||||||
Text(server.name)
|
|
||||||
Text(server.configuration.description)
|
|
||||||
.foregroundColor(.secondary)
|
|
||||||
}
|
|
||||||
if self.usedID == server.id.uuidString {
|
|
||||||
Spacer()
|
|
||||||
Image(systemName: "checkmark")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.onDelete(perform: self.removeServers)
|
|
||||||
.onMove(perform: self.moveServers)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.listStyle(SidebarListStyle())
|
|
||||||
.navigationTitle(Bundle.main.displayName!)
|
|
||||||
.toolbar {
|
|
||||||
ToolbarItemGroup(placement: .navigationBarTrailing) {
|
|
||||||
Menu {
|
|
||||||
Button("DNS-over-TLS", action: self.addNewDoTServer)
|
|
||||||
Button("DNS-over-HTTPS", action: self.addNewDoHServer)
|
|
||||||
} label: {
|
|
||||||
Image(systemName: "plus")
|
|
||||||
}
|
|
||||||
EditButton()
|
|
||||||
}
|
|
||||||
ToolbarItem(placement: .status) {
|
|
||||||
VStack {
|
|
||||||
HStack {
|
|
||||||
Circle()
|
|
||||||
.frame(width: 10, height: 10)
|
|
||||||
.foregroundColor(self.isEnabled ? .green : .secondary)
|
|
||||||
Text(self.isEnabled ? "Active" : "Inactive")
|
|
||||||
}
|
|
||||||
if !self.isEnabled {
|
|
||||||
Button("How to Activate") {
|
|
||||||
self.guideIsPresented = true
|
|
||||||
}
|
|
||||||
.sheet(isPresented: self.$guideIsPresented) {
|
|
||||||
HowToActivateView(isSheet: true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.onAppear(perform: self.updateStatus)
|
|
||||||
.onReceive(
|
|
||||||
NotificationCenter.default.publisher(
|
|
||||||
for: UIScene.willEnterForegroundNotification
|
|
||||||
)
|
|
||||||
) { _ in
|
|
||||||
self.updateStatus()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.alert(isPresented: self.$alertIsPresented) {
|
|
||||||
Alert(
|
|
||||||
title: Text(self.alertTitle),
|
|
||||||
message: Text(self.alertMessage)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !self.isEnabled {
|
|
||||||
HowToActivateView(isSheet: false)
|
|
||||||
} else {
|
|
||||||
Text("Select a server on the sidebar")
|
|
||||||
.navigationBarHidden(true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ContentView_Previews: PreviewProvider {
|
|
||||||
static var previews: some View {
|
|
||||||
ContentView()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -6,9 +6,7 @@
|
||||||
<array>
|
<array>
|
||||||
<string>dns-settings</string>
|
<string>dns-settings</string>
|
||||||
</array>
|
</array>
|
||||||
<key>com.apple.security.app-sandbox</key>
|
<key>com.apple.developer.networking.wifi-info</key>
|
||||||
<true/>
|
|
||||||
<key>com.apple.security.network.client</key>
|
|
||||||
<true/>
|
<true/>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|
|
||||||
|
|
@ -5,18 +5,28 @@
|
||||||
// Created by Kenta Kubo on 7/1/20.
|
// Created by Kenta Kubo on 7/1/20.
|
||||||
//
|
//
|
||||||
|
|
||||||
import os
|
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
import os.log
|
||||||
|
|
||||||
let logger = Logger()
|
let logger = Logger()
|
||||||
|
|
||||||
@main
|
@main
|
||||||
struct DNSecureApp {}
|
struct DNSecureApp {
|
||||||
|
@AppStorage("servers") private var servers: Resolvers = []
|
||||||
|
@AppStorage("usedID") private var usedID: String?
|
||||||
|
|
||||||
|
init() {
|
||||||
|
if UserDefaults.standard.object(forKey: "servers") == nil {
|
||||||
|
// Set the default value in order to fix UUIDs
|
||||||
|
self.servers = Presets.servers
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
extension DNSecureApp: App {
|
extension DNSecureApp: App {
|
||||||
var body: some Scene {
|
var body: some Scene {
|
||||||
WindowGroup {
|
WindowGroup {
|
||||||
ContentView()
|
ContentView(servers: self.$servers, usedID: self.$usedID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,176 +0,0 @@
|
||||||
//
|
|
||||||
// DetailView.swift
|
|
||||||
// DNSecure
|
|
||||||
//
|
|
||||||
// Created by Kenta Kubo on 9/25/20.
|
|
||||||
//
|
|
||||||
|
|
||||||
import SwiftUI
|
|
||||||
|
|
||||||
struct DetailView {
|
|
||||||
@Binding var server: Resolver
|
|
||||||
@Binding var isOn: Bool
|
|
||||||
}
|
|
||||||
|
|
||||||
extension DetailView: View {
|
|
||||||
var body: some View {
|
|
||||||
Form {
|
|
||||||
Section {
|
|
||||||
Toggle("Use This Server", isOn: self.$isOn)
|
|
||||||
}
|
|
||||||
Section {
|
|
||||||
HStack {
|
|
||||||
Text("Name")
|
|
||||||
TextField("Name", text: self.$server.name)
|
|
||||||
.multilineTextAlignment(.trailing)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
switch self.server.configuration {
|
|
||||||
case var .dnsOverTLS(configuration):
|
|
||||||
Section(
|
|
||||||
header: EditButton()
|
|
||||||
.foregroundColor(.blue)
|
|
||||||
.frame(maxWidth: .infinity, alignment: .trailing)
|
|
||||||
.overlay(Text("Servers"), alignment: .leading),
|
|
||||||
footer: Text("The DNS server IP addresses.")
|
|
||||||
) {
|
|
||||||
ForEach(0..<configuration.servers.count, id: \.self) { i in
|
|
||||||
TextField(
|
|
||||||
"IP address",
|
|
||||||
text: .init(
|
|
||||||
get: { configuration.servers[i] },
|
|
||||||
set: { configuration.servers[i] = $0 }
|
|
||||||
),
|
|
||||||
onCommit: {
|
|
||||||
self.server.configuration = .dnsOverTLS(configuration)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.textContentType(.URL)
|
|
||||||
.keyboardType(.numbersAndPunctuation)
|
|
||||||
.autocapitalization(.none)
|
|
||||||
.disableAutocorrection(true)
|
|
||||||
}
|
|
||||||
.onDelete {
|
|
||||||
configuration.servers.remove(atOffsets: $0)
|
|
||||||
self.server.configuration = .dnsOverTLS(configuration)
|
|
||||||
}
|
|
||||||
.onMove {
|
|
||||||
configuration.servers.move(fromOffsets: $0, toOffset: $1)
|
|
||||||
self.server.configuration = .dnsOverTLS(configuration)
|
|
||||||
}
|
|
||||||
Button("Add New Server") {
|
|
||||||
configuration.servers.append("")
|
|
||||||
self.server.configuration = .dnsOverTLS(configuration)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Section(
|
|
||||||
header: Text("DNS-over-TLS Settings"),
|
|
||||||
footer: Text("The TLS name of a DNS-over-TLS server.")
|
|
||||||
) {
|
|
||||||
HStack {
|
|
||||||
Text("Server Name")
|
|
||||||
Spacer()
|
|
||||||
TextField(
|
|
||||||
"Server Name",
|
|
||||||
text: .init(
|
|
||||||
get: {
|
|
||||||
configuration.serverName ?? ""
|
|
||||||
},
|
|
||||||
set: {
|
|
||||||
configuration.serverName = $0
|
|
||||||
}
|
|
||||||
),
|
|
||||||
onCommit: {
|
|
||||||
self.server.configuration = .dnsOverTLS(configuration)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.multilineTextAlignment(.trailing)
|
|
||||||
.textContentType(.URL)
|
|
||||||
.keyboardType(.URL)
|
|
||||||
.autocapitalization(.none)
|
|
||||||
.disableAutocorrection(true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case var .dnsOverHTTPS(configuration):
|
|
||||||
Section(
|
|
||||||
header: EditButton()
|
|
||||||
.frame(maxWidth: .infinity, alignment: .trailing)
|
|
||||||
.overlay(Text("Servers"), alignment: .leading),
|
|
||||||
footer: Text("The DNS server IP addresses.")
|
|
||||||
) {
|
|
||||||
ForEach(0..<configuration.servers.count, id: \.self) { i in
|
|
||||||
TextField(
|
|
||||||
"IP address",
|
|
||||||
text: .init(
|
|
||||||
get: { configuration.servers[i] },
|
|
||||||
set: { configuration.servers[i] = $0 }
|
|
||||||
),
|
|
||||||
onCommit: {
|
|
||||||
self.server.configuration = .dnsOverHTTPS(configuration)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.textContentType(.URL)
|
|
||||||
.keyboardType(.numbersAndPunctuation)
|
|
||||||
.autocapitalization(.none)
|
|
||||||
.disableAutocorrection(true)
|
|
||||||
}
|
|
||||||
.onDelete {
|
|
||||||
configuration.servers.remove(atOffsets: $0)
|
|
||||||
self.server.configuration = .dnsOverHTTPS(configuration)
|
|
||||||
}
|
|
||||||
.onMove {
|
|
||||||
configuration.servers.move(fromOffsets: $0, toOffset: $1)
|
|
||||||
self.server.configuration = .dnsOverHTTPS(configuration)
|
|
||||||
}
|
|
||||||
Button("Add New Server") {
|
|
||||||
configuration.servers.append("")
|
|
||||||
self.server.configuration = .dnsOverHTTPS(configuration)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Section(
|
|
||||||
header: Text("DNS over HTTPS Settings"),
|
|
||||||
footer: Text("The URL of a DNS-over-HTTPS server.")
|
|
||||||
) {
|
|
||||||
HStack {
|
|
||||||
Text("Server URL")
|
|
||||||
Spacer()
|
|
||||||
TextField(
|
|
||||||
"Server URL",
|
|
||||||
text: .init(
|
|
||||||
get: {
|
|
||||||
configuration.serverURL?.absoluteString ?? ""
|
|
||||||
},
|
|
||||||
set: {
|
|
||||||
configuration.serverURL = URL(string: $0)
|
|
||||||
}
|
|
||||||
),
|
|
||||||
onCommit: {
|
|
||||||
self.server.configuration = .dnsOverHTTPS(configuration)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.multilineTextAlignment(.trailing)
|
|
||||||
.textContentType(.URL)
|
|
||||||
.keyboardType(.URL)
|
|
||||||
.autocapitalization(.none)
|
|
||||||
.disableAutocorrection(true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.navigationTitle(self.server.name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct DetailView_Previews: PreviewProvider {
|
|
||||||
static var previews: some View {
|
|
||||||
DetailView(
|
|
||||||
server: .constant(
|
|
||||||
.init(
|
|
||||||
name: "My Server",
|
|
||||||
configuration: .dnsOverTLS(DoTConfiguration())
|
|
||||||
)
|
|
||||||
),
|
|
||||||
isOn: .constant(true)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
//
|
||||||
|
// NEEvaluateConnectionRuleAction+Codable.swift
|
||||||
|
// DNSecure
|
||||||
|
//
|
||||||
|
// Created by Kenta Kubo on 8/31/25.
|
||||||
|
//
|
||||||
|
|
||||||
|
import NetworkExtension
|
||||||
|
|
||||||
|
extension NEEvaluateConnectionRuleAction: @retroactive Codable {}
|
||||||
10
DNSecure/Extensions/NEOnDemandRuleAction+Codable.swift
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
//
|
||||||
|
// NEOnDemandRuleAction+Codable.swift
|
||||||
|
// DNSecure
|
||||||
|
//
|
||||||
|
// Created by Kenta Kubo on 12/20/20.
|
||||||
|
//
|
||||||
|
|
||||||
|
import NetworkExtension
|
||||||
|
|
||||||
|
extension NEOnDemandRuleAction: @retroactive Codable {}
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
//
|
||||||
|
// NEOnDemandRuleAction+CustomStringConvertible.swift
|
||||||
|
// DNSecure
|
||||||
|
//
|
||||||
|
// Created by Kenta Kubo on 12/20/20.
|
||||||
|
//
|
||||||
|
|
||||||
|
import NetworkExtension
|
||||||
|
|
||||||
|
extension NEOnDemandRuleAction: @retroactive CustomStringConvertible {
|
||||||
|
public var description: String {
|
||||||
|
switch self {
|
||||||
|
case .connect: "Apply settings"
|
||||||
|
case .disconnect: "Do not apply settings"
|
||||||
|
case .evaluateConnection: "Apply with excluded domains"
|
||||||
|
case .ignore: "As is"
|
||||||
|
@unknown case _: "Unknown"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
//
|
||||||
|
// NEOnDemandRuleInterfaceType+CaseIterable.swift
|
||||||
|
// DNSecure
|
||||||
|
//
|
||||||
|
// Created by Kenta Kubo on 12/20/20.
|
||||||
|
//
|
||||||
|
|
||||||
|
import NetworkExtension
|
||||||
|
|
||||||
|
extension NEOnDemandRuleInterfaceType: @retroactive CaseIterable {
|
||||||
|
public static var allCases: [Self] {
|
||||||
|
#if os(macOS)
|
||||||
|
[.any, .ethernet, .wiFi]
|
||||||
|
#else
|
||||||
|
[.any, .wiFi, .cellular]
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
//
|
||||||
|
// NEOnDemandRuleInterfaceType+Codable.swift
|
||||||
|
// DNSecure
|
||||||
|
//
|
||||||
|
// Created by Kenta Kubo on 12/20/20.
|
||||||
|
//
|
||||||
|
|
||||||
|
import NetworkExtension
|
||||||
|
|
||||||
|
extension NEOnDemandRuleInterfaceType: @retroactive Codable {}
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
//
|
||||||
|
// NEOnDemandRuleInterfaceType+CustomStringConvertible.swift
|
||||||
|
// DNSecure
|
||||||
|
//
|
||||||
|
// Created by Kenta Kubo on 12/20/20.
|
||||||
|
//
|
||||||
|
|
||||||
|
import NetworkExtension
|
||||||
|
|
||||||
|
extension NEOnDemandRuleInterfaceType: @retroactive CustomStringConvertible {
|
||||||
|
public var description: String {
|
||||||
|
switch self {
|
||||||
|
case .any: "Any"
|
||||||
|
#if os(macOS)
|
||||||
|
case .ethernet: "Ethernet"
|
||||||
|
#endif
|
||||||
|
case .wiFi: "Wi-Fi"
|
||||||
|
#if os(iOS)
|
||||||
|
case .cellular: "Cellular"
|
||||||
|
#endif
|
||||||
|
@unknown case _: "Unknown"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
//
|
||||||
|
// NEOnDemandRuleInterfaceType+isSSIDUsed.swift
|
||||||
|
// DNSecure
|
||||||
|
//
|
||||||
|
// Created by Kenta Kubo on 12/20/20.
|
||||||
|
//
|
||||||
|
|
||||||
|
import NetworkExtension
|
||||||
|
|
||||||
|
extension NEOnDemandRuleInterfaceType {
|
||||||
|
var isSSIDUsed: Bool {
|
||||||
|
switch self {
|
||||||
|
case .any, .wiFi: true
|
||||||
|
case _: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,92 +0,0 @@
|
||||||
//
|
|
||||||
// HowToActivateView.swift
|
|
||||||
// DNSecure
|
|
||||||
//
|
|
||||||
// Created by Kenta Kubo on 9/27/20.
|
|
||||||
//
|
|
||||||
|
|
||||||
import SwiftUI
|
|
||||||
|
|
||||||
struct HowToActivateView {
|
|
||||||
@Environment(\.presentationMode) var presentationMode
|
|
||||||
@State var isSheet: Bool
|
|
||||||
}
|
|
||||||
|
|
||||||
extension HowToActivateView: View {
|
|
||||||
var body: some View {
|
|
||||||
VStack {
|
|
||||||
if self.isSheet {
|
|
||||||
Text("How to Activate")
|
|
||||||
.font(.title)
|
|
||||||
Spacer()
|
|
||||||
}
|
|
||||||
ScrollView {
|
|
||||||
VStack(alignment: .leading) {
|
|
||||||
VStack(alignment: .leading) {
|
|
||||||
Text("1. Select a DNS server you like, or add another one")
|
|
||||||
Image("SelectServer")
|
|
||||||
.resizable()
|
|
||||||
.scaledToFit()
|
|
||||||
.frame(maxHeight: 200)
|
|
||||||
}
|
|
||||||
VStack(alignment: .leading) {
|
|
||||||
Text("2. Enable \"Use This Server\"")
|
|
||||||
Image("UseThisServer")
|
|
||||||
.resizable()
|
|
||||||
.scaledToFit()
|
|
||||||
.frame(maxHeight: 200)
|
|
||||||
}
|
|
||||||
VStack(alignment: .leading) {
|
|
||||||
Text("3. Open the Settings")
|
|
||||||
Image("Settings")
|
|
||||||
.resizable()
|
|
||||||
.scaledToFit()
|
|
||||||
.frame(maxHeight: 200)
|
|
||||||
}
|
|
||||||
VStack(alignment: .leading) {
|
|
||||||
Text("4. Go to \"General\" > \"VPN & Network\" > \"DNS\"")
|
|
||||||
ScrollView(.horizontal) {
|
|
||||||
HStack {
|
|
||||||
Image("GeneralVPNNetwork")
|
|
||||||
.resizable()
|
|
||||||
.scaledToFit()
|
|
||||||
.frame(maxHeight: 200)
|
|
||||||
Image("DNS")
|
|
||||||
.resizable()
|
|
||||||
.scaledToFit()
|
|
||||||
.frame(maxHeight: 200)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
VStack(alignment: .leading) {
|
|
||||||
Text("5. \"Automatic\" is selected by default, so select \"\(Bundle.main.displayName!)\"")
|
|
||||||
Image("DNSProvider")
|
|
||||||
.resizable()
|
|
||||||
.scaledToFit()
|
|
||||||
.frame(maxHeight: 200)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if self.isSheet {
|
|
||||||
Spacer()
|
|
||||||
Button {
|
|
||||||
self.presentationMode.wrappedValue.dismiss()
|
|
||||||
} label: {
|
|
||||||
Text("Dismiss")
|
|
||||||
.padding()
|
|
||||||
.foregroundColor(.white)
|
|
||||||
.background(Color.blue)
|
|
||||||
.cornerRadius(10)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.padding()
|
|
||||||
.navigationTitle("How to Activate")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct HowToActivateView_Previews: PreviewProvider {
|
|
||||||
static var previews: some View {
|
|
||||||
HowToActivateView(isSheet: true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -2,51 +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>CFBundleDevelopmentRegion</key>
|
|
||||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
|
||||||
<key>CFBundleExecutable</key>
|
|
||||||
<string>$(EXECUTABLE_NAME)</string>
|
|
||||||
<key>CFBundleIdentifier</key>
|
|
||||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
|
||||||
<key>CFBundleInfoDictionaryVersion</key>
|
|
||||||
<string>6.0</string>
|
|
||||||
<key>CFBundleName</key>
|
|
||||||
<string>$(PRODUCT_NAME)</string>
|
|
||||||
<key>CFBundlePackageType</key>
|
|
||||||
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
|
||||||
<key>CFBundleShortVersionString</key>
|
|
||||||
<string>$(MARKETING_VERSION)</string>
|
|
||||||
<key>CFBundleVersion</key>
|
|
||||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
|
||||||
<key>LSApplicationCategoryType</key>
|
|
||||||
<string>public.app-category.utilities</string>
|
|
||||||
<key>LSRequiresIPhoneOS</key>
|
|
||||||
<true/>
|
|
||||||
<key>UIApplicationSceneManifest</key>
|
<key>UIApplicationSceneManifest</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>UIApplicationSupportsMultipleScenes</key>
|
<key>UIApplicationSupportsMultipleScenes</key>
|
||||||
<false/>
|
<false/>
|
||||||
</dict>
|
</dict>
|
||||||
<key>UIApplicationSupportsIndirectInputEvents</key>
|
|
||||||
<true/>
|
|
||||||
<key>UILaunchScreen</key>
|
|
||||||
<dict/>
|
|
||||||
<key>UIRequiredDeviceCapabilities</key>
|
|
||||||
<array>
|
|
||||||
<string>armv7</string>
|
|
||||||
</array>
|
|
||||||
<key>UISupportedInterfaceOrientations</key>
|
|
||||||
<array>
|
|
||||||
<string>UIInterfaceOrientationPortrait</string>
|
|
||||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
|
||||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
|
||||||
</array>
|
|
||||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
|
||||||
<array>
|
|
||||||
<string>UIInterfaceOrientationPortrait</string>
|
|
||||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
|
||||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
|
||||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|
|
||||||
86
DNSecure/Models/OnDemandRule.swift
Normal file
|
|
@ -0,0 +1,86 @@
|
||||||
|
//
|
||||||
|
// OnDemandRule.swift
|
||||||
|
// DNSecure
|
||||||
|
//
|
||||||
|
// Created by Kenta Kubo on 8/31/25.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import NetworkExtension
|
||||||
|
|
||||||
|
struct OnDemandRule {
|
||||||
|
var id = UUID()
|
||||||
|
var name: String
|
||||||
|
var action: NEOnDemandRuleAction = .connect
|
||||||
|
var interfaceType: NEOnDemandRuleInterfaceType = .any
|
||||||
|
var ssidMatch: [String] = []
|
||||||
|
var dnsSearchDomainMatch: [String] = []
|
||||||
|
var dnsServerAddressMatch: [String] = []
|
||||||
|
var probeURL: URL?
|
||||||
|
var excludedDomains: [String]?
|
||||||
|
}
|
||||||
|
|
||||||
|
extension OnDemandRule: Identifiable {}
|
||||||
|
|
||||||
|
extension OnDemandRule: Equatable {}
|
||||||
|
|
||||||
|
extension OnDemandRule: Hashable {}
|
||||||
|
|
||||||
|
extension OnDemandRule: Codable {}
|
||||||
|
|
||||||
|
extension [OnDemandRule] {
|
||||||
|
func toNEOnDemandRules() -> [NEOnDemandRule] {
|
||||||
|
self.map { rule in
|
||||||
|
switch rule.action {
|
||||||
|
case .connect:
|
||||||
|
let newRule = NEOnDemandRuleConnect()
|
||||||
|
newRule.interfaceTypeMatch = rule.interfaceType
|
||||||
|
if rule.interfaceType.isSSIDUsed {
|
||||||
|
newRule.ssidMatch = rule.ssidMatch
|
||||||
|
}
|
||||||
|
newRule.dnsSearchDomainMatch = rule.dnsSearchDomainMatch
|
||||||
|
newRule.dnsServerAddressMatch = rule.dnsServerAddressMatch
|
||||||
|
newRule.probeURL = rule.probeURL
|
||||||
|
return newRule
|
||||||
|
case .disconnect:
|
||||||
|
let newRule = NEOnDemandRuleDisconnect()
|
||||||
|
newRule.interfaceTypeMatch = rule.interfaceType
|
||||||
|
if rule.interfaceType.isSSIDUsed {
|
||||||
|
newRule.ssidMatch = rule.ssidMatch
|
||||||
|
}
|
||||||
|
newRule.dnsSearchDomainMatch = rule.dnsSearchDomainMatch
|
||||||
|
newRule.dnsServerAddressMatch = rule.dnsServerAddressMatch
|
||||||
|
newRule.probeURL = rule.probeURL
|
||||||
|
return newRule
|
||||||
|
case .evaluateConnection:
|
||||||
|
let newRule = NEOnDemandRuleEvaluateConnection()
|
||||||
|
newRule.interfaceTypeMatch = rule.interfaceType
|
||||||
|
if rule.interfaceType.isSSIDUsed {
|
||||||
|
newRule.ssidMatch = rule.ssidMatch
|
||||||
|
}
|
||||||
|
newRule.dnsSearchDomainMatch = rule.dnsSearchDomainMatch
|
||||||
|
newRule.dnsServerAddressMatch = rule.dnsServerAddressMatch
|
||||||
|
newRule.probeURL = rule.probeURL
|
||||||
|
newRule.connectionRules =
|
||||||
|
switch rule.excludedDomains {
|
||||||
|
case let domains? where !domains.isEmpty:
|
||||||
|
[.init(matchDomains: domains, andAction: .neverConnect)]
|
||||||
|
case _: []
|
||||||
|
}
|
||||||
|
return newRule
|
||||||
|
case .ignore:
|
||||||
|
let newRule = NEOnDemandRuleIgnore()
|
||||||
|
newRule.interfaceTypeMatch = rule.interfaceType
|
||||||
|
if rule.interfaceType.isSSIDUsed {
|
||||||
|
newRule.ssidMatch = rule.ssidMatch
|
||||||
|
}
|
||||||
|
newRule.dnsSearchDomainMatch = rule.dnsSearchDomainMatch
|
||||||
|
newRule.dnsServerAddressMatch = rule.dnsServerAddressMatch
|
||||||
|
newRule.probeURL = rule.probeURL
|
||||||
|
return newRule
|
||||||
|
@unknown case _:
|
||||||
|
preconditionFailure("Unexpected NEOnDemandRuleAction")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
242
DNSecure/Models/Presets.swift
Normal file
|
|
@ -0,0 +1,242 @@
|
||||||
|
//
|
||||||
|
// Presets.swift
|
||||||
|
// DNSecure
|
||||||
|
//
|
||||||
|
// Created by Kenta Kubo on 9/25/20.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
enum Presets {
|
||||||
|
static let servers: Resolvers = [
|
||||||
|
.init(
|
||||||
|
name: "Google Public DNS",
|
||||||
|
configuration: .dnsOverTLS(
|
||||||
|
DoTConfiguration(
|
||||||
|
servers: [
|
||||||
|
"8.8.8.8",
|
||||||
|
"8.8.4.4",
|
||||||
|
"2001:4860:4860::8888",
|
||||||
|
"2001:4860:4860::8844",
|
||||||
|
],
|
||||||
|
serverName: "dns.google"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
.init(
|
||||||
|
name: "Google Public DNS",
|
||||||
|
configuration: .dnsOverHTTPS(
|
||||||
|
DoHConfiguration(
|
||||||
|
servers: [
|
||||||
|
"8.8.8.8",
|
||||||
|
"8.8.4.4",
|
||||||
|
"2001:4860:4860::8888",
|
||||||
|
"2001:4860:4860::8844",
|
||||||
|
],
|
||||||
|
serverURL: URL(string: "https://dns.google/dns-query")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
.init(
|
||||||
|
name: "1.1.1.1",
|
||||||
|
configuration: .dnsOverTLS(
|
||||||
|
DoTConfiguration(
|
||||||
|
servers: [
|
||||||
|
"1.1.1.1",
|
||||||
|
"1.0.0.1",
|
||||||
|
"2606:4700:4700::1111",
|
||||||
|
"2606:4700:4700::1001",
|
||||||
|
],
|
||||||
|
serverName: "cloudflare-dns.com"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
.init(
|
||||||
|
name: "1.1.1.1",
|
||||||
|
configuration: .dnsOverHTTPS(
|
||||||
|
DoHConfiguration(
|
||||||
|
servers: [
|
||||||
|
"1.1.1.1",
|
||||||
|
"1.0.0.1",
|
||||||
|
"2606:4700:4700::1111",
|
||||||
|
"2606:4700:4700::1001",
|
||||||
|
],
|
||||||
|
serverURL: URL(string: "https://cloudflare-dns.com/dns-query")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
.init(
|
||||||
|
name: "1.1.1.1 (Block Malware)",
|
||||||
|
configuration: .dnsOverTLS(
|
||||||
|
DoTConfiguration(
|
||||||
|
servers: [
|
||||||
|
"1.1.1.2",
|
||||||
|
"1.0.0.2",
|
||||||
|
"2606:4700:4700::1112",
|
||||||
|
"2606:4700:4700::1002",
|
||||||
|
],
|
||||||
|
serverName: "cloudflare-dns.com"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
.init(
|
||||||
|
name: "1.1.1.1 (Block Malware)",
|
||||||
|
configuration: .dnsOverHTTPS(
|
||||||
|
DoHConfiguration(
|
||||||
|
servers: [
|
||||||
|
"1.1.1.2",
|
||||||
|
"1.0.0.2",
|
||||||
|
"2606:4700:4700::1112",
|
||||||
|
"2606:4700:4700::1002",
|
||||||
|
],
|
||||||
|
serverURL: URL(string: "https://security.cloudflare-dns.com/dns-query")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
.init(
|
||||||
|
name: "Quad9 (Block Malware)",
|
||||||
|
configuration: .dnsOverTLS(
|
||||||
|
DoTConfiguration(
|
||||||
|
servers: [
|
||||||
|
"9.9.9.9",
|
||||||
|
"149.112.112.112",
|
||||||
|
"2620:fe::fe",
|
||||||
|
"2620:fe::9",
|
||||||
|
],
|
||||||
|
serverName: "dns.quad9.net"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
.init(
|
||||||
|
name: "Quad9 (Block Malware)",
|
||||||
|
configuration: .dnsOverHTTPS(
|
||||||
|
DoHConfiguration(
|
||||||
|
servers: [
|
||||||
|
"9.9.9.9",
|
||||||
|
"149.112.112.112",
|
||||||
|
"2620:fe::fe",
|
||||||
|
"2620:fe::9",
|
||||||
|
],
|
||||||
|
serverURL: URL(string: "https://dns.quad9.net/dns-query")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
.init(
|
||||||
|
name: "LibreDNS",
|
||||||
|
configuration: .dnsOverTLS(
|
||||||
|
DoTConfiguration(
|
||||||
|
servers: [
|
||||||
|
"116.202.176.26"
|
||||||
|
],
|
||||||
|
serverName: "dot.libredns.gr"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
.init(
|
||||||
|
name: "LibreDNS",
|
||||||
|
configuration: .dnsOverHTTPS(
|
||||||
|
DoHConfiguration(
|
||||||
|
servers: [
|
||||||
|
"116.202.176.26"
|
||||||
|
],
|
||||||
|
serverURL: URL(string: "https://doh.libredns.gr/dns-query")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
.init(
|
||||||
|
name: "LibreDNS (Block Ads / Trackers)",
|
||||||
|
configuration: .dnsOverHTTPS(
|
||||||
|
DoHConfiguration(
|
||||||
|
servers: [
|
||||||
|
"116.202.176.26"
|
||||||
|
],
|
||||||
|
serverURL: URL(string: "https://doh.libredns.gr/ads")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
.init(
|
||||||
|
name: "AdGuard DNS",
|
||||||
|
configuration: .dnsOverTLS(
|
||||||
|
DoTConfiguration(
|
||||||
|
servers: [
|
||||||
|
"94.140.14.140",
|
||||||
|
"94.140.14.141",
|
||||||
|
"2a10:50c0::1:ff",
|
||||||
|
"2a10:50c0::2:ff",
|
||||||
|
],
|
||||||
|
serverName: "dns-unfiltered.adguard.com"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
.init(
|
||||||
|
name: "AdGuard DNS",
|
||||||
|
configuration: .dnsOverHTTPS(
|
||||||
|
DoHConfiguration(
|
||||||
|
servers: [
|
||||||
|
"94.140.14.140",
|
||||||
|
"94.140.14.141",
|
||||||
|
"2a10:50c0::1:ff",
|
||||||
|
"2a10:50c0::2:ff",
|
||||||
|
],
|
||||||
|
serverURL: URL(string: "https://dns-unfiltered.adguard.com/dns-query")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
.init(
|
||||||
|
name: "AdGuard DNS (Block Ads / Trackers)",
|
||||||
|
configuration: .dnsOverTLS(
|
||||||
|
DoTConfiguration(
|
||||||
|
servers: [
|
||||||
|
"94.140.14.14",
|
||||||
|
"94.140.15.15",
|
||||||
|
"2a10:50c0::ad1:ff",
|
||||||
|
"2a10:50c0::ad2:ff",
|
||||||
|
],
|
||||||
|
serverName: "dns.adguard.com"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
.init(
|
||||||
|
name: "AdGuard DNS (Block Ads / Trackers)",
|
||||||
|
configuration: .dnsOverHTTPS(
|
||||||
|
DoHConfiguration(
|
||||||
|
servers: [
|
||||||
|
"94.140.14.14",
|
||||||
|
"94.140.15.15",
|
||||||
|
"2a10:50c0::ad1:ff",
|
||||||
|
"2a10:50c0::ad2:ff",
|
||||||
|
],
|
||||||
|
serverURL: URL(string: "https://dns.adguard.com/dns-query")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
.init(
|
||||||
|
name: "Freifunk Muenchen DNS",
|
||||||
|
configuration: .dnsOverTLS(
|
||||||
|
DoTConfiguration(
|
||||||
|
servers: [
|
||||||
|
"5.1.66.255",
|
||||||
|
"185.150.99.255",
|
||||||
|
"2001:678:e68:f000::",
|
||||||
|
"2001:678:ed0:f000::",
|
||||||
|
],
|
||||||
|
serverName: "dot.ffmuc.net"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
.init(
|
||||||
|
name: "Freifunk Muenchen DNS",
|
||||||
|
configuration: .dnsOverHTTPS(
|
||||||
|
DoHConfiguration(
|
||||||
|
servers: [
|
||||||
|
"5.1.66.255",
|
||||||
|
"185.150.99.255",
|
||||||
|
"2001:678:e68:f000::",
|
||||||
|
"2001:678:ed0:f000::",
|
||||||
|
],
|
||||||
|
serverURL: URL(string: "https://doh.ffmuc.net/dns-query")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
@ -19,6 +19,10 @@ struct DoTConfiguration {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension DoTConfiguration: Equatable {}
|
||||||
|
|
||||||
|
extension DoTConfiguration: Hashable {}
|
||||||
|
|
||||||
extension DoTConfiguration: Codable {}
|
extension DoTConfiguration: Codable {}
|
||||||
|
|
||||||
struct DoHConfiguration {
|
struct DoHConfiguration {
|
||||||
|
|
@ -32,6 +36,10 @@ struct DoHConfiguration {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension DoHConfiguration: Equatable {}
|
||||||
|
|
||||||
|
extension DoHConfiguration: Hashable {}
|
||||||
|
|
||||||
extension DoHConfiguration: Codable {}
|
extension DoHConfiguration: Codable {}
|
||||||
|
|
||||||
enum Configuration {
|
enum Configuration {
|
||||||
|
|
@ -40,20 +48,24 @@ enum Configuration {
|
||||||
|
|
||||||
func toDNSSettings() -> NEDNSSettings {
|
func toDNSSettings() -> NEDNSSettings {
|
||||||
switch self {
|
switch self {
|
||||||
case let .dnsOverTLS(configuration):
|
case .dnsOverTLS(let configuration):
|
||||||
return configuration.toDNSSettings()
|
return configuration.toDNSSettings()
|
||||||
case let .dnsOverHTTPS(configuration):
|
case .dnsOverHTTPS(let configuration):
|
||||||
return configuration.toDNSSettings()
|
return configuration.toDNSSettings()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension Configuration: Equatable {}
|
||||||
|
|
||||||
|
extension Configuration: Hashable {}
|
||||||
|
|
||||||
extension Configuration: Codable {
|
extension Configuration: Codable {
|
||||||
enum CodingKeys: String, CodingKey {
|
private enum CodingKeys: String, CodingKey {
|
||||||
case base, dotConfiguration, dohConfiguration
|
case base, dotConfiguration, dohConfiguration
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Base: String, Codable {
|
private enum Base: String, Codable {
|
||||||
case dnsOverTLS, dnsOverHTTPS
|
case dnsOverTLS, dnsOverHTTPS
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -75,10 +87,10 @@ extension Configuration: Codable {
|
||||||
var container = encoder.container(keyedBy: Self.CodingKeys.self)
|
var container = encoder.container(keyedBy: Self.CodingKeys.self)
|
||||||
|
|
||||||
switch self {
|
switch self {
|
||||||
case let .dnsOverTLS(configuration):
|
case .dnsOverTLS(let configuration):
|
||||||
try container.encode(Self.Base.dnsOverTLS, forKey: .base)
|
try container.encode(Self.Base.dnsOverTLS, forKey: .base)
|
||||||
try container.encode(configuration, forKey: .dotConfiguration)
|
try container.encode(configuration, forKey: .dotConfiguration)
|
||||||
case let .dnsOverHTTPS(configuration):
|
case .dnsOverHTTPS(let configuration):
|
||||||
try container.encode(Self.Base.dnsOverHTTPS, forKey: .base)
|
try container.encode(Self.Base.dnsOverHTTPS, forKey: .base)
|
||||||
try container.encode(configuration, forKey: .dohConfiguration)
|
try container.encode(configuration, forKey: .dohConfiguration)
|
||||||
}
|
}
|
||||||
|
|
@ -98,11 +110,32 @@ struct Resolver {
|
||||||
var id = UUID()
|
var id = UUID()
|
||||||
var name: String
|
var name: String
|
||||||
var configuration: Configuration
|
var configuration: Configuration
|
||||||
|
var onDemandRules: [OnDemandRule] = []
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Resolver: Identifiable {}
|
extension Resolver: Identifiable {}
|
||||||
|
|
||||||
extension Resolver: Codable {}
|
extension Resolver: Equatable {}
|
||||||
|
|
||||||
|
extension Resolver: Hashable {}
|
||||||
|
|
||||||
|
extension Resolver: Codable {
|
||||||
|
private enum CodingKeys: String, CodingKey {
|
||||||
|
case id, name, configuration, onDemandRules
|
||||||
|
}
|
||||||
|
|
||||||
|
init(from decoder: Decoder) throws {
|
||||||
|
let container = try decoder.container(keyedBy: Self.CodingKeys.self)
|
||||||
|
self.id = try container.decode(UUID.self, forKey: .id)
|
||||||
|
self.name = try container.decode(String.self, forKey: .name)
|
||||||
|
self.configuration = try container.decode(Configuration.self, forKey: .configuration)
|
||||||
|
self.onDemandRules =
|
||||||
|
try container.decodeIfPresent(
|
||||||
|
[OnDemandRule].self,
|
||||||
|
forKey: .onDemandRules
|
||||||
|
) ?? []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
typealias Resolvers = [Resolver]
|
typealias Resolvers = [Resolver]
|
||||||
|
|
||||||
|
|
@ -112,10 +145,10 @@ extension Resolvers {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Resolvers: RawRepresentable {
|
extension Resolvers: @retroactive RawRepresentable {
|
||||||
public init?(rawValue: String) {
|
public init?(rawValue: String) {
|
||||||
guard let data = rawValue.data(using: .utf8),
|
guard let data = rawValue.data(using: .utf8),
|
||||||
let result = try? JSONDecoder().decode(Self.self, from: data)
|
let result = try? JSONDecoder().decode(Self.self, from: data)
|
||||||
else {
|
else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -124,7 +157,7 @@ extension Resolvers: RawRepresentable {
|
||||||
|
|
||||||
public var rawValue: String {
|
public var rawValue: String {
|
||||||
guard let data = try? JSONEncoder().encode(self),
|
guard let data = try? JSONEncoder().encode(self),
|
||||||
let result = String(data: data, encoding: .utf8)
|
let result = String(data: data, encoding: .utf8)
|
||||||
else {
|
else {
|
||||||
return "[]"
|
return "[]"
|
||||||
}
|
}
|
||||||
|
|
@ -1,97 +0,0 @@
|
||||||
//
|
|
||||||
// Presets.swift
|
|
||||||
// DNSecure
|
|
||||||
//
|
|
||||||
// Created by Kenta Kubo on 9/25/20.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
enum Presets {
|
|
||||||
static let servers: Resolvers = [
|
|
||||||
.init(
|
|
||||||
name: "Google Public DNS",
|
|
||||||
configuration: .dnsOverTLS(
|
|
||||||
DoTConfiguration(
|
|
||||||
servers: [
|
|
||||||
"8.8.8.8",
|
|
||||||
"8.8.4.4",
|
|
||||||
"2001:4860:4860::8888",
|
|
||||||
"2001:4860:4860::8844",
|
|
||||||
],
|
|
||||||
serverName: "dns.google"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
.init(
|
|
||||||
name: "Google Public DNS",
|
|
||||||
configuration: .dnsOverHTTPS(
|
|
||||||
DoHConfiguration(
|
|
||||||
servers: [
|
|
||||||
"8.8.8.8",
|
|
||||||
"8.8.4.4",
|
|
||||||
"2001:4860:4860::8888",
|
|
||||||
"2001:4860:4860::8844",
|
|
||||||
],
|
|
||||||
serverURL: URL(string: "https://dns.google/dns-query")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
.init(
|
|
||||||
name: "1.1.1.1",
|
|
||||||
configuration: .dnsOverTLS(
|
|
||||||
DoTConfiguration(
|
|
||||||
servers: [
|
|
||||||
"1.1.1.1",
|
|
||||||
"1.0.0.1",
|
|
||||||
"2606:4700:4700::64",
|
|
||||||
"2606:4700:4700::6400",
|
|
||||||
],
|
|
||||||
serverName: "cloudflare-dns.com"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
.init(
|
|
||||||
name: "1.1.1.1",
|
|
||||||
configuration: .dnsOverHTTPS(
|
|
||||||
DoHConfiguration(
|
|
||||||
servers: [
|
|
||||||
"1.1.1.1",
|
|
||||||
"1.0.0.1",
|
|
||||||
"2606:4700:4700::64",
|
|
||||||
"2606:4700:4700::6400",
|
|
||||||
],
|
|
||||||
serverURL: URL(string: "https://cloudflare-dns.com/dns-query")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
.init(
|
|
||||||
name: "Quad9",
|
|
||||||
configuration: .dnsOverTLS(
|
|
||||||
DoTConfiguration(
|
|
||||||
servers: [
|
|
||||||
"9.9.9.9",
|
|
||||||
"149.112.112.112",
|
|
||||||
"2620:fe::fe",
|
|
||||||
"2620:fe::fe:9",
|
|
||||||
],
|
|
||||||
serverName: "dns.quad9.net"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
.init(
|
|
||||||
name: "Quad9",
|
|
||||||
configuration: .dnsOverHTTPS(
|
|
||||||
DoHConfiguration(
|
|
||||||
servers: [
|
|
||||||
"9.9.9.9",
|
|
||||||
"149.112.112.112",
|
|
||||||
"2620:fe::fe",
|
|
||||||
"2620:fe::fe:9",
|
|
||||||
],
|
|
||||||
serverURL: URL(string: "https://dns.quad9.net/dns-query")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
{
|
|
||||||
"info" : {
|
|
||||||
"author" : "xcode",
|
|
||||||
"version" : 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
419
DNSecure/Views/ContentView.swift
Normal file
|
|
@ -0,0 +1,419 @@
|
||||||
|
//
|
||||||
|
// ContentView.swift
|
||||||
|
// DNSecure
|
||||||
|
//
|
||||||
|
// Created by Kenta Kubo on 7/1/20.
|
||||||
|
//
|
||||||
|
|
||||||
|
@preconcurrency import NetworkExtension
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct ContentView {
|
||||||
|
@Environment(\.horizontalSizeClass) private var hSizeClass
|
||||||
|
@Binding var servers: Resolvers
|
||||||
|
@Binding var usedID: String?
|
||||||
|
@State private var isActivated = false
|
||||||
|
@State private var selection: Int?
|
||||||
|
@State private var isAlertPresented = false
|
||||||
|
@State private var alertTitle = ""
|
||||||
|
@State private var alertMessage = ""
|
||||||
|
@State private var isGuidePresented = false
|
||||||
|
@State private var isRestoring = false
|
||||||
|
#if targetEnvironment(macCatalyst)
|
||||||
|
// Workaround for https://github.com/kkebo/DNSecure/issues/139
|
||||||
|
@State private var sidebarEditMode: EditMode = .inactive
|
||||||
|
@State private var detailEditMode: EditMode = .inactive
|
||||||
|
#endif
|
||||||
|
|
||||||
|
private var navigationBarTitleDisplayMode: NavigationBarItem.TitleDisplayMode {
|
||||||
|
if #available(iOS 26, *) {
|
||||||
|
.inline
|
||||||
|
} else {
|
||||||
|
.automatic
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func addNewDoTServer() {
|
||||||
|
self.servers.append(
|
||||||
|
.init(
|
||||||
|
name: "New",
|
||||||
|
configuration: .dnsOverTLS(DoTConfiguration())
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.selection = self.servers.count - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
private func addNewDoHServer() {
|
||||||
|
self.servers.append(
|
||||||
|
.init(
|
||||||
|
name: "New",
|
||||||
|
configuration: .dnsOverHTTPS(DoHConfiguration())
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.selection = self.servers.count - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
private func restoreFromPresets(resolver: Resolver) {
|
||||||
|
self.servers.append(resolver)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func removeServers(at indexSet: IndexSet) {
|
||||||
|
if let current = self.selection, indexSet.contains(where: { $0 <= current }) {
|
||||||
|
// FIXME: This is a workaround not to crash on deletion.
|
||||||
|
self.selection = -1
|
||||||
|
}
|
||||||
|
if indexSet.map({ self.servers[$0].id.uuidString }).contains(self.usedID) {
|
||||||
|
self.removeSettings()
|
||||||
|
}
|
||||||
|
// FIXME: This is a workaround not to crash on deletion.
|
||||||
|
// Wait for closing DetailView.
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(300)) {
|
||||||
|
self.servers.remove(atOffsets: indexSet)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func moveServers(from src: IndexSet, to dst: Int) {
|
||||||
|
// TODO: Change self.selection if needed
|
||||||
|
self.servers.move(fromOffsets: src, toOffset: dst)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func updateStatus() {
|
||||||
|
#if !targetEnvironment(simulator)
|
||||||
|
// Early return if running on Swift Playground or Xcode Previews
|
||||||
|
guard ProcessInfo.processInfo.environment["XCODE_RUNNING_FOR_PREVIEWS"] != "1" else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let manager = NEDNSSettingsManager.shared()
|
||||||
|
manager.loadFromPreferences {
|
||||||
|
if let err = $0 {
|
||||||
|
logger.error("\(err.localizedDescription)")
|
||||||
|
self.alert("Load Error", err.localizedDescription)
|
||||||
|
} else {
|
||||||
|
self.isActivated = manager.isEnabled
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
private func saveSettings(of server: Resolver) {
|
||||||
|
if self.usedID != server.id.uuidString {
|
||||||
|
self.usedID = server.id.uuidString
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !targetEnvironment(simulator)
|
||||||
|
// Early return if running on Swift Playground or Xcode Previews
|
||||||
|
guard ProcessInfo.processInfo.environment["XCODE_RUNNING_FOR_PREVIEWS"] != "1" else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let manager = NEDNSSettingsManager.shared()
|
||||||
|
manager.localizedDescription = server.name
|
||||||
|
manager.dnsSettings = server.configuration.toDNSSettings()
|
||||||
|
manager.onDemandRules = server.onDemandRules.toNEOnDemandRules()
|
||||||
|
manager.saveToPreferences { saveError in
|
||||||
|
if let saveError = saveError as NSError? {
|
||||||
|
guard
|
||||||
|
saveError.domain != "NEConfigurationErrorDomain"
|
||||||
|
|| saveError.code != 9
|
||||||
|
else {
|
||||||
|
// Nothing was changed
|
||||||
|
return
|
||||||
|
}
|
||||||
|
logger.error("\(saveError.localizedDescription)")
|
||||||
|
self.alert("Save Error", saveError.localizedDescription)
|
||||||
|
self.removeSettings()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
logger.debug("The DNS settings were saved")
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
private func removeSettings() {
|
||||||
|
self.usedID = nil
|
||||||
|
|
||||||
|
#if !targetEnvironment(simulator)
|
||||||
|
// Early return if running on Swift Playground or Xcode Previews
|
||||||
|
guard ProcessInfo.processInfo.environment["XCODE_RUNNING_FOR_PREVIEWS"] != "1" else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let manager = NEDNSSettingsManager.shared()
|
||||||
|
guard manager.dnsSettings != nil else {
|
||||||
|
// Already removed
|
||||||
|
return
|
||||||
|
}
|
||||||
|
manager.removeFromPreferences { removeError in
|
||||||
|
self.updateStatus()
|
||||||
|
if let removeError = removeError {
|
||||||
|
logger.error("\(removeError.localizedDescription)")
|
||||||
|
self.alert("Remove Error", removeError.localizedDescription)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
logger.debug("The DNS settings were removed")
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
private func alert(_ title: String, _ message: String) {
|
||||||
|
self.alertTitle = title
|
||||||
|
self.alertMessage = message
|
||||||
|
self.isAlertPresented = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@MainActor
|
||||||
|
extension ContentView: View {
|
||||||
|
var body: some View {
|
||||||
|
if #available(iOS 16, *) {
|
||||||
|
self.modernBody
|
||||||
|
} else if self.hSizeClass == .compact {
|
||||||
|
// Workaround for iOS 15
|
||||||
|
self.legacyBody.navigationViewStyle(.stack)
|
||||||
|
} else {
|
||||||
|
self.legacyBody
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@available(iOS 16, *)
|
||||||
|
private var modernBody: some View {
|
||||||
|
NavigationSplitView {
|
||||||
|
List(selection: self.$selection) {
|
||||||
|
if !self.isActivated && self.hSizeClass == .compact {
|
||||||
|
NavigationLink("Instructions", value: -1)
|
||||||
|
}
|
||||||
|
Section("Servers") {
|
||||||
|
ForEach(0..<self.servers.count, id: \.self) { i in
|
||||||
|
NavigationLink(value: i) {
|
||||||
|
self.sidebarRow(at: i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.onDelete(perform: self.removeServers)
|
||||||
|
.onMove(perform: self.moveServers)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.navigationTitle(Bundle.main.displayName!)
|
||||||
|
.navigationBarTitleDisplayMode(self.navigationBarTitleDisplayMode)
|
||||||
|
.toolbar {
|
||||||
|
#if canImport(SwiftUI, _version: 7)
|
||||||
|
if #available(iOS 26, *) {
|
||||||
|
ToolbarItem(placement: .subtitle) {
|
||||||
|
self.statusIndicator
|
||||||
|
.foregroundStyle(.secondary)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
self.toolbarContent
|
||||||
|
}
|
||||||
|
.alert(self.alertTitle, isPresented: self.$isAlertPresented) {
|
||||||
|
} message: {
|
||||||
|
Text(self.alertMessage)
|
||||||
|
}
|
||||||
|
#if targetEnvironment(macCatalyst)
|
||||||
|
// Workaround for https://github.com/kkebo/DNSecure/issues/139
|
||||||
|
.environment(\.editMode, self.$sidebarEditMode)
|
||||||
|
#endif
|
||||||
|
} detail: {
|
||||||
|
if let i = self.selection, i >= 0 {
|
||||||
|
NavigationStack {
|
||||||
|
self.detailView(at: i)
|
||||||
|
.navigationBarTitleDisplayMode(self.navigationBarTitleDisplayMode)
|
||||||
|
}
|
||||||
|
#if targetEnvironment(macCatalyst)
|
||||||
|
// Workaround for https://github.com/kkebo/DNSecure/issues/139
|
||||||
|
.environment(\.editMode, self.$detailEditMode)
|
||||||
|
#endif
|
||||||
|
} else if !self.isActivated || self.selection == -1 {
|
||||||
|
HowToActivateView()
|
||||||
|
.navigationBarTitleDisplayMode(self.navigationBarTitleDisplayMode)
|
||||||
|
} else {
|
||||||
|
Text("Select a server on the sidebar")
|
||||||
|
.navigationBarTitleDisplayMode(self.navigationBarTitleDisplayMode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.onAppear(perform: self.updateStatus)
|
||||||
|
.task {
|
||||||
|
for await _ in NotificationCenter.default
|
||||||
|
.notifications(named: .NEDNSSettingsConfigurationDidChange)
|
||||||
|
.map(\.name)
|
||||||
|
{
|
||||||
|
self.updateStatus()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@available(iOS, deprecated: 16)
|
||||||
|
private var legacyBody: some View {
|
||||||
|
NavigationView {
|
||||||
|
List {
|
||||||
|
if !self.isActivated && self.hSizeClass == .compact {
|
||||||
|
NavigationLink(
|
||||||
|
"Instructions",
|
||||||
|
tag: -1,
|
||||||
|
selection: self.$selection
|
||||||
|
) {
|
||||||
|
HowToActivateView()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Section("Servers") {
|
||||||
|
ForEach(0..<self.servers.count, id: \.self) { i in
|
||||||
|
if self.hSizeClass == .compact {
|
||||||
|
NavigationLink(
|
||||||
|
tag: i,
|
||||||
|
selection: self.$selection
|
||||||
|
) {
|
||||||
|
self.detailView(at: i)
|
||||||
|
} label: {
|
||||||
|
self.sidebarRow(at: i)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Workaround for iOS 15
|
||||||
|
Button {
|
||||||
|
self.selection = i
|
||||||
|
} label: {
|
||||||
|
self.sidebarRow(at: i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.onDelete(perform: self.removeServers)
|
||||||
|
.onMove(perform: self.moveServers)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.navigationTitle(Bundle.main.displayName!)
|
||||||
|
.toolbar { self.toolbarContent }
|
||||||
|
.alert(self.alertTitle, isPresented: self.$isAlertPresented) {
|
||||||
|
} message: {
|
||||||
|
Text(self.alertMessage)
|
||||||
|
}
|
||||||
|
|
||||||
|
if let i = self.selection, i >= 0 {
|
||||||
|
self.detailView(at: i)
|
||||||
|
} else if !self.isActivated || self.selection == -1 {
|
||||||
|
HowToActivateView()
|
||||||
|
} else {
|
||||||
|
Text("Select a server on the sidebar")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.onAppear(perform: self.updateStatus)
|
||||||
|
.task {
|
||||||
|
for await _ in NotificationCenter.default
|
||||||
|
.notifications(named: .NEDNSSettingsConfigurationDidChange)
|
||||||
|
.map(\.name)
|
||||||
|
{
|
||||||
|
self.updateStatus()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ToolbarContentBuilder private var toolbarContent: some ToolbarContent {
|
||||||
|
ToolbarItem(placement: .topBarLeading) {
|
||||||
|
if #available(iOS 26, *) {
|
||||||
|
#if targetEnvironment(macCatalyst)
|
||||||
|
// Workaround for the issue that the checkmark icon is hard to see if System Settings > Appearance > Theme > Color is Multicolor.
|
||||||
|
Button {
|
||||||
|
withAnimation {
|
||||||
|
if !self.sidebarEditMode.isEditing {
|
||||||
|
self.sidebarEditMode = .active
|
||||||
|
} else {
|
||||||
|
self.sidebarEditMode = .inactive
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} label: {
|
||||||
|
if !self.sidebarEditMode.isEditing {
|
||||||
|
Text("Edit")
|
||||||
|
} else {
|
||||||
|
Label("Done", systemImage: "checkmark")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.tint(self.sidebarEditMode.isEditing ? .accentColor : .primary)
|
||||||
|
#else
|
||||||
|
EditButton()
|
||||||
|
#endif
|
||||||
|
} else {
|
||||||
|
self.addMenu
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ToolbarItem(placement: .topBarTrailing) {
|
||||||
|
if #available(iOS 26, *) {
|
||||||
|
self.addMenu
|
||||||
|
} else {
|
||||||
|
EditButton()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ToolbarItem(placement: .status) {
|
||||||
|
if #available(iOS 26, *) {
|
||||||
|
#if !canImport(SwiftUI, _version: 7)
|
||||||
|
self.statusIndicator
|
||||||
|
#endif
|
||||||
|
} else {
|
||||||
|
self.statusIndicator
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var addMenu: some View {
|
||||||
|
Menu("Add", systemImage: "plus") {
|
||||||
|
Button("DNS-over-TLS", action: self.addNewDoTServer)
|
||||||
|
Button("DNS-over-HTTPS", action: self.addNewDoHServer)
|
||||||
|
Button("Restore from Presets") {
|
||||||
|
self.isRestoring = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.sheet(isPresented: self.$isRestoring) {
|
||||||
|
RestorationView(onAdd: self.restoreFromPresets)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var statusIndicator: some View {
|
||||||
|
Label {
|
||||||
|
Text(self.isActivated ? "Active" : "Inactive")
|
||||||
|
} icon: {
|
||||||
|
Circle()
|
||||||
|
.fill(self.isActivated ? .green : .secondary)
|
||||||
|
.frame(width: 10, height: 10)
|
||||||
|
}
|
||||||
|
.labelStyle(.titleAndIcon)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func sidebarRow(at i: Int) -> some View {
|
||||||
|
HStack {
|
||||||
|
VStack(alignment: .leading) {
|
||||||
|
Text(self.servers[i].name)
|
||||||
|
Text(self.servers[i].configuration.description)
|
||||||
|
.foregroundStyle(.secondary)
|
||||||
|
}
|
||||||
|
if self.usedID == self.servers[i].id.uuidString {
|
||||||
|
Spacer()
|
||||||
|
if !self.isActivated {
|
||||||
|
Image(systemName: "exclamationmark.triangle")
|
||||||
|
.foregroundStyle(.red)
|
||||||
|
}
|
||||||
|
Image(systemName: "checkmark")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func detailView(at i: Int) -> some View {
|
||||||
|
DetailView(
|
||||||
|
server: self.$servers[i],
|
||||||
|
isSelected: .init(
|
||||||
|
get: {
|
||||||
|
self.usedID == self.servers[i].id.uuidString
|
||||||
|
},
|
||||||
|
set: {
|
||||||
|
if $0 {
|
||||||
|
self.saveSettings(of: self.servers[i])
|
||||||
|
} else {
|
||||||
|
self.removeSettings()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
),
|
||||||
|
isActivated: self.$isActivated
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#Preview {
|
||||||
|
ContentView(servers: .constant(Presets.servers), usedID: .constant(nil))
|
||||||
|
}
|
||||||
50
DNSecure/Views/DNSSearchDomainMatchView.swift
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
import NetworkExtension
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct DNSSearchDomainMatchView {
|
||||||
|
@Binding var rule: OnDemandRule
|
||||||
|
}
|
||||||
|
|
||||||
|
extension DNSSearchDomainMatchView: View {
|
||||||
|
var body: some View {
|
||||||
|
Form {
|
||||||
|
Section {
|
||||||
|
ForEach(0..<self.rule.dnsSearchDomainMatch.count, id: \.self) { i in
|
||||||
|
LazyTextField(
|
||||||
|
"Search Domain",
|
||||||
|
// self.$rule.dnsSearchDomainMatch[i] causes crash on deletion
|
||||||
|
text: .init(
|
||||||
|
get: { self.rule.dnsSearchDomainMatch[i] },
|
||||||
|
set: { self.rule.dnsSearchDomainMatch[i] = $0 }
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.textContentType(.URL)
|
||||||
|
.keyboardType(.URL)
|
||||||
|
.textInputAutocapitalization(.never)
|
||||||
|
.autocorrectionDisabled()
|
||||||
|
}
|
||||||
|
.onDelete { self.rule.dnsSearchDomainMatch.remove(atOffsets: $0) }
|
||||||
|
.onMove { self.rule.dnsSearchDomainMatch.move(fromOffsets: $0, toOffset: $1) }
|
||||||
|
Button("Add DNS Search Domain") {
|
||||||
|
self.rule.dnsSearchDomainMatch.append("")
|
||||||
|
}
|
||||||
|
} footer: {
|
||||||
|
Text(
|
||||||
|
"If the current default search domain is equal to one of the strings in this array and all of the other conditions in the rule match, then the rule matches."
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.navigationTitle("DNS Search Domain Match")
|
||||||
|
.toolbar {
|
||||||
|
EditButton()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@available(iOS 17, *)
|
||||||
|
#Preview {
|
||||||
|
@Previewable @State var rule = OnDemandRule(name: "Preview Rule")
|
||||||
|
NavigationStack {
|
||||||
|
DNSSearchDomainMatchView(rule: $rule)
|
||||||
|
}
|
||||||
|
}
|
||||||
50
DNSecure/Views/DNSServerAddressMatchView.swift
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
import NetworkExtension
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct DNSServerAddressMatchView {
|
||||||
|
@Binding var rule: OnDemandRule
|
||||||
|
}
|
||||||
|
|
||||||
|
extension DNSServerAddressMatchView: View {
|
||||||
|
var body: some View {
|
||||||
|
Form {
|
||||||
|
Section {
|
||||||
|
ForEach(0..<self.rule.dnsServerAddressMatch.count, id: \.self) { i in
|
||||||
|
LazyTextField(
|
||||||
|
"IP Address",
|
||||||
|
// self.$rule.dnsServerAddressMatch[i] causes crash on deletion
|
||||||
|
text: .init(
|
||||||
|
get: { self.rule.dnsServerAddressMatch[i] },
|
||||||
|
set: { self.rule.dnsServerAddressMatch[i] = $0 }
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.textContentType(.URL)
|
||||||
|
.keyboardType(.numbersAndPunctuation)
|
||||||
|
.textInputAutocapitalization(.never)
|
||||||
|
.autocorrectionDisabled()
|
||||||
|
}
|
||||||
|
.onDelete { self.rule.dnsServerAddressMatch.remove(atOffsets: $0) }
|
||||||
|
.onMove { self.rule.dnsServerAddressMatch.move(fromOffsets: $0, toOffset: $1) }
|
||||||
|
Button("Add DNS Server Address") {
|
||||||
|
self.rule.dnsServerAddressMatch.append("")
|
||||||
|
}
|
||||||
|
} footer: {
|
||||||
|
Text(
|
||||||
|
"If each of the current default DNS servers is equal to one of the strings in this array and all of the other conditions in the rule match, then the rule matches."
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.navigationTitle("DNS Server Address Match")
|
||||||
|
.toolbar {
|
||||||
|
EditButton()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@available(iOS 17, *)
|
||||||
|
#Preview {
|
||||||
|
@Previewable @State var rule = OnDemandRule(name: "Preview Rule")
|
||||||
|
NavigationStack {
|
||||||
|
DNSServerAddressMatchView(rule: $rule)
|
||||||
|
}
|
||||||
|
}
|
||||||
126
DNSecure/Views/DetailView.swift
Normal file
|
|
@ -0,0 +1,126 @@
|
||||||
|
//
|
||||||
|
// DetailView.swift
|
||||||
|
// DNSecure
|
||||||
|
//
|
||||||
|
// Created by Kenta Kubo on 9/25/20.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct DetailView {
|
||||||
|
@Binding var server: Resolver
|
||||||
|
@Binding var isSelected: Bool
|
||||||
|
@Binding var isActivated: Bool
|
||||||
|
@State private var isGuidePresented = false
|
||||||
|
|
||||||
|
private func binding(for id: UUID) -> Binding<OnDemandRule> {
|
||||||
|
guard let index = self.server.onDemandRules.map(\.id).firstIndex(of: id) else {
|
||||||
|
preconditionFailure("Can't find rule in array")
|
||||||
|
}
|
||||||
|
return self.$server.onDemandRules[index]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@MainActor
|
||||||
|
extension DetailView: View {
|
||||||
|
var body: some View {
|
||||||
|
Form {
|
||||||
|
Section {
|
||||||
|
Toggle("Use This Server", isOn: self.$isSelected)
|
||||||
|
if self.isSelected && !self.isActivated {
|
||||||
|
Button("One more step is required.", systemImage: "exclamationmark.triangle") {
|
||||||
|
self.isGuidePresented = true
|
||||||
|
}
|
||||||
|
.labelStyle(.titleAndIcon)
|
||||||
|
.tint(.red)
|
||||||
|
.sheet(isPresented: self.$isGuidePresented) {
|
||||||
|
NavigationView {
|
||||||
|
HowToActivateView()
|
||||||
|
.toolbar {
|
||||||
|
ToolbarItem(placement: .cancellationAction) {
|
||||||
|
#if canImport(SwiftUI, _version: 7)
|
||||||
|
if #available(iOS 26, *) {
|
||||||
|
Button("Close", systemImage: "xmark", role: .close) {
|
||||||
|
self.isGuidePresented = false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Button("Close", systemImage: "xmark", role: .cancel) {
|
||||||
|
self.isGuidePresented = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
Button("Close", systemImage: "xmark", role: .cancel) {
|
||||||
|
self.isGuidePresented = false
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.navigationViewStyle(.stack)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Section("Name") {
|
||||||
|
LazyTextField("Name", text: self.$server.name)
|
||||||
|
}
|
||||||
|
self.serverConfigurationSections
|
||||||
|
Section {
|
||||||
|
ForEach(self.server.onDemandRules) { rule in
|
||||||
|
NavigationLink(rule.name) {
|
||||||
|
RuleView(rule: self.binding(for: rule.id))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.onDelete { self.server.onDemandRules.remove(atOffsets: $0) }
|
||||||
|
.onMove { self.server.onDemandRules.move(fromOffsets: $0, toOffset: $1) }
|
||||||
|
Button("Add New Rule") {
|
||||||
|
self.server.onDemandRules
|
||||||
|
.append(OnDemandRule(name: "New Rule"))
|
||||||
|
}
|
||||||
|
} header: {
|
||||||
|
EditButton()
|
||||||
|
.frame(maxWidth: .infinity, alignment: .trailing)
|
||||||
|
.overlay(alignment: .leading) {
|
||||||
|
Text("On-Demand Rules")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.navigationTitle(self.server.name)
|
||||||
|
.onChange(of: self.server) { [oldValue = self.server] newValue in
|
||||||
|
guard self.isSelected, oldValue.id == newValue.id else { return }
|
||||||
|
// When the selected server's configuration is modified, the server will be deactivated or deselected so that the user can save the changes by toggling the “Use This Server” switch again.
|
||||||
|
self.isSelected = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ViewBuilder private var serverConfigurationSections: some View {
|
||||||
|
switch self.server.configuration {
|
||||||
|
case .dnsOverTLS(let configuration):
|
||||||
|
DoTSections(
|
||||||
|
configuration: .init(
|
||||||
|
get: { configuration },
|
||||||
|
set: { self.server.configuration = .dnsOverTLS($0) }
|
||||||
|
)
|
||||||
|
)
|
||||||
|
case .dnsOverHTTPS(let configuration):
|
||||||
|
DoHSections(
|
||||||
|
configuration: .init(
|
||||||
|
get: { configuration },
|
||||||
|
set: { self.server.configuration = .dnsOverHTTPS($0) }
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#Preview {
|
||||||
|
DetailView(
|
||||||
|
server: .constant(
|
||||||
|
.init(
|
||||||
|
name: "My Server",
|
||||||
|
configuration: .dnsOverTLS(DoTConfiguration())
|
||||||
|
)
|
||||||
|
),
|
||||||
|
isSelected: .constant(true),
|
||||||
|
isActivated: .constant(true)
|
||||||
|
)
|
||||||
|
}
|
||||||
74
DNSecure/Views/DoHSections.swift
Normal file
|
|
@ -0,0 +1,74 @@
|
||||||
|
//
|
||||||
|
// DoHSections.swift
|
||||||
|
// DNSecure
|
||||||
|
//
|
||||||
|
// Created by Kenta Kubo on 9/23/22.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct DoHSections {
|
||||||
|
@Binding var configuration: DoHConfiguration
|
||||||
|
}
|
||||||
|
|
||||||
|
extension DoHSections: View {
|
||||||
|
var body: some View {
|
||||||
|
Section {
|
||||||
|
ForEach(0..<self.configuration.servers.count, id: \.self) { i in
|
||||||
|
LazyTextField("IP address", text: self.$configuration.servers[i])
|
||||||
|
.textContentType(.URL)
|
||||||
|
.keyboardType(.numbersAndPunctuation)
|
||||||
|
.textInputAutocapitalization(.never)
|
||||||
|
.autocorrectionDisabled()
|
||||||
|
}
|
||||||
|
.onDelete { self.configuration.servers.remove(atOffsets: $0) }
|
||||||
|
.onMove { self.configuration.servers.move(fromOffsets: $0, toOffset: $1) }
|
||||||
|
Button("Add New Server") {
|
||||||
|
self.configuration.servers.append("")
|
||||||
|
}
|
||||||
|
} header: {
|
||||||
|
EditButton()
|
||||||
|
.frame(maxWidth: .infinity, alignment: .trailing)
|
||||||
|
.overlay(alignment: .leading) {
|
||||||
|
Text("Servers")
|
||||||
|
}
|
||||||
|
} footer: {
|
||||||
|
Text("The DNS server IP addresses.")
|
||||||
|
}
|
||||||
|
Section {
|
||||||
|
LazyTextField(
|
||||||
|
"Server URL",
|
||||||
|
text: .init(
|
||||||
|
get: { self.configuration.serverURL?.absoluteString ?? "" },
|
||||||
|
set: { self.configuration.serverURL = URL(string: $0) }
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.textContentType(.URL)
|
||||||
|
.keyboardType(.URL)
|
||||||
|
.textInputAutocapitalization(.never)
|
||||||
|
.autocorrectionDisabled()
|
||||||
|
} header: {
|
||||||
|
Text("Server URL")
|
||||||
|
} footer: {
|
||||||
|
Text("The URL of a DNS-over-HTTPS server.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#Preview {
|
||||||
|
Form {
|
||||||
|
DoHSections(
|
||||||
|
configuration: .constant(
|
||||||
|
.init(
|
||||||
|
servers: [
|
||||||
|
"1.1.1.1",
|
||||||
|
"1.0.0.1",
|
||||||
|
"2606:4700:4700::1111",
|
||||||
|
"2606:4700:4700::1001",
|
||||||
|
],
|
||||||
|
serverURL: URL(string: "https://cloudflare-dns.com/dns-query")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
74
DNSecure/Views/DoTSections.swift
Normal file
|
|
@ -0,0 +1,74 @@
|
||||||
|
//
|
||||||
|
// DoTSections.swift
|
||||||
|
// DNSecure
|
||||||
|
//
|
||||||
|
// Created by Kenta Kubo on 9/23/22.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct DoTSections {
|
||||||
|
@Binding var configuration: DoTConfiguration
|
||||||
|
}
|
||||||
|
|
||||||
|
extension DoTSections: View {
|
||||||
|
var body: some View {
|
||||||
|
Section {
|
||||||
|
ForEach(0..<self.configuration.servers.count, id: \.self) { i in
|
||||||
|
LazyTextField("IP address", text: self.$configuration.servers[i])
|
||||||
|
.textContentType(.URL)
|
||||||
|
.keyboardType(.numbersAndPunctuation)
|
||||||
|
.textInputAutocapitalization(.never)
|
||||||
|
.autocorrectionDisabled()
|
||||||
|
}
|
||||||
|
.onDelete { self.configuration.servers.remove(atOffsets: $0) }
|
||||||
|
.onMove { self.configuration.servers.move(fromOffsets: $0, toOffset: $1) }
|
||||||
|
Button("Add New Server") {
|
||||||
|
self.configuration.servers.append("")
|
||||||
|
}
|
||||||
|
} header: {
|
||||||
|
EditButton()
|
||||||
|
.frame(maxWidth: .infinity, alignment: .trailing)
|
||||||
|
.overlay(alignment: .leading) {
|
||||||
|
Text("Servers")
|
||||||
|
}
|
||||||
|
} footer: {
|
||||||
|
Text("The DNS server IP addresses.")
|
||||||
|
}
|
||||||
|
Section {
|
||||||
|
LazyTextField(
|
||||||
|
"Server Name",
|
||||||
|
text: .init(
|
||||||
|
get: { self.configuration.serverName ?? "" },
|
||||||
|
set: { self.configuration.serverName = $0 }
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.textContentType(.URL)
|
||||||
|
.keyboardType(.URL)
|
||||||
|
.textInputAutocapitalization(.never)
|
||||||
|
.autocorrectionDisabled()
|
||||||
|
} header: {
|
||||||
|
Text("Server Name")
|
||||||
|
} footer: {
|
||||||
|
Text("The TLS name of a DNS-over-TLS server.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#Preview {
|
||||||
|
Form {
|
||||||
|
DoTSections(
|
||||||
|
configuration: .constant(
|
||||||
|
.init(
|
||||||
|
servers: [
|
||||||
|
"1.1.1.1",
|
||||||
|
"1.0.0.1",
|
||||||
|
"2606:4700:4700::1111",
|
||||||
|
"2606:4700:4700::1001",
|
||||||
|
],
|
||||||
|
serverName: "cloudflare-dns.com"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
49
DNSecure/Views/ExcludedDomainsView.swift
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct ExcludedDomainsView {
|
||||||
|
@Binding var domains: [String]
|
||||||
|
}
|
||||||
|
|
||||||
|
extension ExcludedDomainsView: View {
|
||||||
|
var body: some View {
|
||||||
|
Form {
|
||||||
|
Section {
|
||||||
|
ForEach(0..<self.domains.count, id: \.self) { i in
|
||||||
|
LazyTextField(
|
||||||
|
"Domain",
|
||||||
|
// self.$rule.excludedDomains[i] causes crash on deletion
|
||||||
|
text: .init(
|
||||||
|
get: { self.domains[i] },
|
||||||
|
set: { self.domains[i] = $0 }
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.textContentType(.URL)
|
||||||
|
.keyboardType(.URL)
|
||||||
|
.textInputAutocapitalization(.never)
|
||||||
|
.autocorrectionDisabled()
|
||||||
|
}
|
||||||
|
.onDelete { self.domains.remove(atOffsets: $0) }
|
||||||
|
.onMove { self.domains.move(fromOffsets: $0, toOffset: $1) }
|
||||||
|
Button("Add Domain") {
|
||||||
|
self.domains.append("")
|
||||||
|
}
|
||||||
|
} footer: {
|
||||||
|
Text(
|
||||||
|
"Each domain is matched against the destination hostname using suffix matching, and each label in the domain must match an entire label in the hostname. For example, the domain `example.com` will match the hostname `www.example.com` but not `www.anotherexample.com`."
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.navigationTitle("Excluded Domains")
|
||||||
|
.toolbar {
|
||||||
|
EditButton()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@available(iOS 17, *)
|
||||||
|
#Preview {
|
||||||
|
@Previewable @State var domains = ["example.com"]
|
||||||
|
NavigationStack {
|
||||||
|
ExcludedDomainsView(domains: $domains)
|
||||||
|
}
|
||||||
|
}
|
||||||
164
DNSecure/Views/HowToActivateView.swift
Normal file
|
|
@ -0,0 +1,164 @@
|
||||||
|
//
|
||||||
|
// HowToActivateView.swift
|
||||||
|
// DNSecure
|
||||||
|
//
|
||||||
|
// Created by Kenta Kubo on 9/27/20.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
private enum MacOSVersion {
|
||||||
|
case venturaOrLater
|
||||||
|
case monterey
|
||||||
|
}
|
||||||
|
|
||||||
|
struct HowToActivateView {
|
||||||
|
@State private var macOSVersion: MacOSVersion = .venturaOrLater
|
||||||
|
}
|
||||||
|
|
||||||
|
extension HowToActivateView: View {
|
||||||
|
var body: some View {
|
||||||
|
ScrollView {
|
||||||
|
VStack {
|
||||||
|
#if targetEnvironment(macCatalyst)
|
||||||
|
Picker("", selection: self.$macOSVersion) {
|
||||||
|
Text("macOS 13 or later").tag(MacOSVersion.venturaOrLater)
|
||||||
|
Text("macOS 12").tag(MacOSVersion.monterey)
|
||||||
|
}
|
||||||
|
.pickerStyle(.segmented)
|
||||||
|
.fixedSize()
|
||||||
|
#endif
|
||||||
|
VStack(alignment: .leading) {
|
||||||
|
VStack(alignment: .leading) {
|
||||||
|
Text("1. Select a DNS server you like, or add another one")
|
||||||
|
Image("SelectServer")
|
||||||
|
.resizable()
|
||||||
|
.scaledToFit()
|
||||||
|
.frame(maxHeight: 200)
|
||||||
|
}
|
||||||
|
VStack(alignment: .leading) {
|
||||||
|
Text("2. Enable \"Use This Server\"")
|
||||||
|
Image("UseThisServer")
|
||||||
|
.resizable()
|
||||||
|
.scaledToFit()
|
||||||
|
.frame(maxHeight: 200)
|
||||||
|
}
|
||||||
|
#if targetEnvironment(macCatalyst)
|
||||||
|
switch self.macOSVersion {
|
||||||
|
case .venturaOrLater:
|
||||||
|
VStack(alignment: .leading) {
|
||||||
|
Text("3. Open the System Settings")
|
||||||
|
Image(.systemSettingsIcon)
|
||||||
|
.resizable()
|
||||||
|
.scaledToFit()
|
||||||
|
.frame(maxHeight: 200)
|
||||||
|
}
|
||||||
|
VStack(alignment: .leading) {
|
||||||
|
Text("4. Go to Network settings and click \"Filters\"")
|
||||||
|
Image(.networkSettings)
|
||||||
|
.resizable()
|
||||||
|
.scaledToFit()
|
||||||
|
.frame(maxHeight: 400)
|
||||||
|
}
|
||||||
|
VStack(alignment: .leading) {
|
||||||
|
Text("5. Click on the status of \"\(Bundle.main.displayName!)\"")
|
||||||
|
Image(.filtersSettings)
|
||||||
|
.resizable()
|
||||||
|
.scaledToFit()
|
||||||
|
.frame(maxHeight: 400)
|
||||||
|
}
|
||||||
|
VStack(alignment: .leading) {
|
||||||
|
Text("6. Click \"Enabled\"")
|
||||||
|
Image(.makeItEnabled)
|
||||||
|
.resizable()
|
||||||
|
.scaledToFit()
|
||||||
|
.frame(maxHeight: 200)
|
||||||
|
}
|
||||||
|
VStack(alignment: .leading) {
|
||||||
|
Text("7. All done 🎉")
|
||||||
|
Image(.allDone)
|
||||||
|
.resizable()
|
||||||
|
.scaledToFit()
|
||||||
|
.frame(maxHeight: 400)
|
||||||
|
}
|
||||||
|
case .monterey:
|
||||||
|
VStack(alignment: .leading) {
|
||||||
|
Text("3. Open the System Preferences")
|
||||||
|
Image(.montereySystemPreferencesIcon)
|
||||||
|
.resizable()
|
||||||
|
.scaledToFit()
|
||||||
|
.frame(maxHeight: 200)
|
||||||
|
}
|
||||||
|
VStack(alignment: .leading) {
|
||||||
|
Text("4. Go to Network settings")
|
||||||
|
Image(.montereySystemPreferences)
|
||||||
|
.resizable()
|
||||||
|
.scaledToFit()
|
||||||
|
.frame(maxHeight: 400)
|
||||||
|
}
|
||||||
|
VStack(alignment: .leading) {
|
||||||
|
Text(
|
||||||
|
"5. Select \"\(Bundle.main.displayName!)\" and click \(Image(systemName: "ellipsis.circle")) button"
|
||||||
|
)
|
||||||
|
Image(.montereyNetworkSettings)
|
||||||
|
.resizable()
|
||||||
|
.scaledToFit()
|
||||||
|
.frame(maxHeight: 400)
|
||||||
|
}
|
||||||
|
VStack(alignment: .leading) {
|
||||||
|
Text("6. Click \"Make Service Active\"")
|
||||||
|
Image(.montereyMakeServiceActive)
|
||||||
|
.resizable()
|
||||||
|
.scaledToFit()
|
||||||
|
.frame(maxHeight: 200)
|
||||||
|
}
|
||||||
|
VStack(alignment: .leading) {
|
||||||
|
Text("7. Click \"Apply\" button")
|
||||||
|
Image(.montereyNetworkSettingsApply)
|
||||||
|
.resizable()
|
||||||
|
.scaledToFit()
|
||||||
|
.frame(maxHeight: 400)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
VStack(alignment: .leading) {
|
||||||
|
Text("3. Open the Settings")
|
||||||
|
Image("Settings")
|
||||||
|
.resizable()
|
||||||
|
.scaledToFit()
|
||||||
|
.frame(maxHeight: 200)
|
||||||
|
}
|
||||||
|
VStack(alignment: .leading) {
|
||||||
|
Text("4. Go to \"General\" > \"VPN & Network\" > \"DNS\"")
|
||||||
|
ScrollView(.horizontal) {
|
||||||
|
HStack {
|
||||||
|
Image("GeneralVPNNetwork")
|
||||||
|
.resizable()
|
||||||
|
.scaledToFit()
|
||||||
|
.frame(maxHeight: 200)
|
||||||
|
Image("DNS")
|
||||||
|
.resizable()
|
||||||
|
.scaledToFit()
|
||||||
|
.frame(maxHeight: 200)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
VStack(alignment: .leading) {
|
||||||
|
Text("5. \"Automatic\" is selected by default, so select \"\(Bundle.main.displayName!)\"")
|
||||||
|
Image("DNSProvider")
|
||||||
|
.resizable()
|
||||||
|
.scaledToFit()
|
||||||
|
.frame(maxHeight: 200)
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.padding()
|
||||||
|
}
|
||||||
|
.navigationTitle("How to Activate")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#Preview {
|
||||||
|
HowToActivateView()
|
||||||
|
}
|
||||||
35
DNSecure/Views/InterfaceTypeMatchView.swift
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
import NetworkExtension
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct InterfaceTypeMatchView {
|
||||||
|
@Binding var rule: OnDemandRule
|
||||||
|
}
|
||||||
|
|
||||||
|
extension InterfaceTypeMatchView: View {
|
||||||
|
var body: some View {
|
||||||
|
Form {
|
||||||
|
Section {
|
||||||
|
Picker("Interface Type", selection: self.$rule.interfaceType) {
|
||||||
|
ForEach(NEOnDemandRuleInterfaceType.allCases, id: \.self) {
|
||||||
|
Text($0.description)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.pickerStyle(.inline)
|
||||||
|
.labelsHidden()
|
||||||
|
} footer: {
|
||||||
|
Text(
|
||||||
|
"If the current primary network interface is of this type and all of the other conditions in the rule match, then the rule matches."
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.navigationTitle("Interface Type Match")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@available(iOS 17, *)
|
||||||
|
#Preview {
|
||||||
|
@Previewable @State var rule = OnDemandRule(name: "Preview Rule")
|
||||||
|
NavigationStack {
|
||||||
|
InterfaceTypeMatchView(rule: $rule)
|
||||||
|
}
|
||||||
|
}
|
||||||
53
DNSecure/Views/LazyTextField.swift
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
//
|
||||||
|
// LazyTextField.swift
|
||||||
|
// DNSecure
|
||||||
|
//
|
||||||
|
// Created by Kenta Kubo on 4/25/23.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct LazyTextField {
|
||||||
|
private let title: String
|
||||||
|
@Binding private var text: String
|
||||||
|
@State private var localText: String
|
||||||
|
@FocusState private var isFocused
|
||||||
|
|
||||||
|
init(_ title: some StringProtocol, text: Binding<String>) {
|
||||||
|
self.title = String(title)
|
||||||
|
self._text = text
|
||||||
|
self._localText = .init(initialValue: text.wrappedValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension LazyTextField: View {
|
||||||
|
var body: some View {
|
||||||
|
HStack {
|
||||||
|
TextField(self.title, text: self.$localText)
|
||||||
|
.focused(self.$isFocused)
|
||||||
|
if self.isFocused && !self.localText.isEmpty {
|
||||||
|
Button("Clear", systemImage: "xmark.circle.fill") {
|
||||||
|
self.text.removeAll()
|
||||||
|
self.localText.removeAll()
|
||||||
|
}
|
||||||
|
.foregroundStyle(Color.primary)
|
||||||
|
.opacity(0.2)
|
||||||
|
.labelStyle(.iconOnly)
|
||||||
|
.buttonStyle(.borderless)
|
||||||
|
.hoverEffect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.onChange(of: self.isFocused) { isFocused in
|
||||||
|
if !isFocused {
|
||||||
|
self.text = self.localText
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.onChange(of: self.text) { text in
|
||||||
|
self.localText = text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#Preview {
|
||||||
|
LazyTextField("Name", text: .constant(""))
|
||||||
|
}
|
||||||
40
DNSecure/Views/ProbeURLView.swift
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
import NetworkExtension
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct ProbeURLView {
|
||||||
|
@Binding var rule: OnDemandRule
|
||||||
|
|
||||||
|
private var probeURL: Binding<String> {
|
||||||
|
.init(
|
||||||
|
get: { self.rule.probeURL?.absoluteString ?? "" },
|
||||||
|
set: { self.rule.probeURL = URL(string: $0) }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension ProbeURLView: View {
|
||||||
|
var body: some View {
|
||||||
|
Form {
|
||||||
|
Section {
|
||||||
|
LazyTextField("Probe URL", text: self.probeURL)
|
||||||
|
.textContentType(.URL)
|
||||||
|
.keyboardType(.URL)
|
||||||
|
.textInputAutocapitalization(.never)
|
||||||
|
.autocorrectionDisabled()
|
||||||
|
} footer: {
|
||||||
|
Text(
|
||||||
|
"If a request sent to this URL results in a HTTP 200 OK response and all of the other conditions in the rule match, then the rule matches. If you don't want to use this rule, leave it empty."
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.navigationTitle("Probe URL")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@available(iOS 17, *)
|
||||||
|
#Preview {
|
||||||
|
@Previewable @State var rule = OnDemandRule(name: "Preview Rule")
|
||||||
|
NavigationStack {
|
||||||
|
ProbeURLView(rule: $rule)
|
||||||
|
}
|
||||||
|
}
|
||||||
71
DNSecure/Views/RestorationView.swift
Normal file
|
|
@ -0,0 +1,71 @@
|
||||||
|
//
|
||||||
|
// RestorationView.swift
|
||||||
|
// DNSecure
|
||||||
|
//
|
||||||
|
// Created by Kenta Kubo on 7/17/24.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct RestorationView {
|
||||||
|
@Environment(\.dismiss) private var dismiss
|
||||||
|
@State private var added = Set<Resolver>()
|
||||||
|
@State private var keyword = ""
|
||||||
|
let onAdd: (Resolver) -> Void
|
||||||
|
|
||||||
|
private var servers: Resolvers {
|
||||||
|
guard !self.keyword.isEmpty else { return Presets.servers }
|
||||||
|
return Presets.servers.filter { $0.name.localizedCaseInsensitiveContains(self.keyword) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension RestorationView: View {
|
||||||
|
var body: some View {
|
||||||
|
NavigationView {
|
||||||
|
List(self.servers, id: \.self) { resolver in
|
||||||
|
HStack {
|
||||||
|
VStack(alignment: .leading) {
|
||||||
|
Text(resolver.name)
|
||||||
|
Text(resolver.configuration.description)
|
||||||
|
.foregroundStyle(.secondary)
|
||||||
|
}
|
||||||
|
Spacer()
|
||||||
|
if self.added.contains(resolver) {
|
||||||
|
HStack(spacing: 5) {
|
||||||
|
Image(systemName: "checkmark")
|
||||||
|
Text("Added")
|
||||||
|
}
|
||||||
|
.foregroundStyle(.secondary)
|
||||||
|
} else {
|
||||||
|
Button {
|
||||||
|
self.added.insert(resolver)
|
||||||
|
self.onAdd(resolver)
|
||||||
|
} label: {
|
||||||
|
HStack(spacing: 5) {
|
||||||
|
Image(systemName: "plus")
|
||||||
|
Text("Add")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.buttonStyle(.borderedProminent)
|
||||||
|
.buttonBorderShape(.capsule)
|
||||||
|
.hoverEffect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.navigationTitle("Presets")
|
||||||
|
.toolbar {
|
||||||
|
ToolbarItem(placement: .cancellationAction) {
|
||||||
|
Button("Cancel", systemImage: "xmark", role: .cancel) {
|
||||||
|
self.dismiss()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.navigationViewStyle(.stack)
|
||||||
|
.searchable(text: self.$keyword, placement: .navigationBarDrawer(displayMode: .always))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#Preview {
|
||||||
|
RestorationView { _ in }
|
||||||
|
}
|
||||||
212
DNSecure/Views/RuleView.swift
Normal file
|
|
@ -0,0 +1,212 @@
|
||||||
|
//
|
||||||
|
// RuleView.swift
|
||||||
|
// DNSecure
|
||||||
|
//
|
||||||
|
// Created by Kenta Kubo on 10/27/20.
|
||||||
|
//
|
||||||
|
|
||||||
|
import NetworkExtension
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct RuleView {
|
||||||
|
@Binding var rule: OnDemandRule
|
||||||
|
}
|
||||||
|
|
||||||
|
@MainActor
|
||||||
|
extension RuleView: View {
|
||||||
|
var body: some View {
|
||||||
|
Form {
|
||||||
|
Section("Name") {
|
||||||
|
LazyTextField("Name", text: self.$rule.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
Section("Matching Conditions") {
|
||||||
|
NavigationLink {
|
||||||
|
InterfaceTypeMatchView(rule: self.$rule)
|
||||||
|
} label: {
|
||||||
|
self.interfaceTypeMatchLabel
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.rule.interfaceType.isSSIDUsed {
|
||||||
|
NavigationLink {
|
||||||
|
SSIDMatchView(rule: self.$rule)
|
||||||
|
} label: {
|
||||||
|
self.ssidMatchLabel
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NavigationLink {
|
||||||
|
DNSSearchDomainMatchView(rule: self.$rule)
|
||||||
|
} label: {
|
||||||
|
self.dnsSearchDomainMatchLabel
|
||||||
|
}
|
||||||
|
|
||||||
|
NavigationLink {
|
||||||
|
DNSServerAddressMatchView(rule: self.$rule)
|
||||||
|
} label: {
|
||||||
|
self.dnsServerAddressMatchLabel
|
||||||
|
}
|
||||||
|
|
||||||
|
NavigationLink {
|
||||||
|
ProbeURLView(rule: self.$rule)
|
||||||
|
} label: {
|
||||||
|
self.probeURLLabel
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Section {
|
||||||
|
Picker("Action", selection: self.$rule.action) {
|
||||||
|
ForEach(
|
||||||
|
[
|
||||||
|
NEOnDemandRuleAction.connect,
|
||||||
|
.evaluateConnection,
|
||||||
|
.disconnect,
|
||||||
|
],
|
||||||
|
id: \.self
|
||||||
|
) {
|
||||||
|
Text($0.description)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if self.rule.action == .evaluateConnection {
|
||||||
|
NavigationLink {
|
||||||
|
ExcludedDomainsView(
|
||||||
|
domains: .init(
|
||||||
|
get: { self.rule.excludedDomains ?? [] },
|
||||||
|
set: { self.rule.excludedDomains = $0 }
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} label: {
|
||||||
|
HStack {
|
||||||
|
Text("Excluded Domains")
|
||||||
|
Spacer()
|
||||||
|
Text("\(self.rule.excludedDomains?.count ?? 0)")
|
||||||
|
.foregroundStyle(.secondary)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.navigationTitle(self.rule.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
@ViewBuilder private var interfaceTypeMatchLabel: some View {
|
||||||
|
if #available(iOS 16, *) {
|
||||||
|
LabeledContent("Interface Type Match", value: self.rule.interfaceType.description)
|
||||||
|
} else {
|
||||||
|
HStack {
|
||||||
|
Text("Interface Type Match")
|
||||||
|
Spacer()
|
||||||
|
Text(self.rule.interfaceType.description)
|
||||||
|
.foregroundStyle(.secondary)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ViewBuilder private var ssidMatchLabel: some View {
|
||||||
|
if #available(iOS 16, *) {
|
||||||
|
LabeledContent("SSID Match") {
|
||||||
|
if !self.rule.ssidMatch.isEmpty {
|
||||||
|
Text("\(self.rule.ssidMatch.count)")
|
||||||
|
} else {
|
||||||
|
Text("Not Used")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
HStack {
|
||||||
|
Text("SSID Match")
|
||||||
|
Spacer()
|
||||||
|
Group {
|
||||||
|
if !self.rule.ssidMatch.isEmpty {
|
||||||
|
Text("\(self.rule.ssidMatch.count)")
|
||||||
|
} else {
|
||||||
|
Text("Not Used")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.foregroundStyle(.secondary)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ViewBuilder private var dnsSearchDomainMatchLabel: some View {
|
||||||
|
if #available(iOS 16, *) {
|
||||||
|
LabeledContent("DNS Search Domain Match") {
|
||||||
|
if !self.rule.dnsSearchDomainMatch.isEmpty {
|
||||||
|
Text("\(self.rule.dnsSearchDomainMatch.count)")
|
||||||
|
} else {
|
||||||
|
Text("Not Used")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
HStack {
|
||||||
|
Text("DNS Search Domain Match")
|
||||||
|
Spacer()
|
||||||
|
Group {
|
||||||
|
if !self.rule.dnsSearchDomainMatch.isEmpty {
|
||||||
|
Text("\(self.rule.dnsSearchDomainMatch.count)")
|
||||||
|
} else {
|
||||||
|
Text("Not Used")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.foregroundStyle(.secondary)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ViewBuilder private var dnsServerAddressMatchLabel: some View {
|
||||||
|
if #available(iOS 16, *) {
|
||||||
|
LabeledContent("DNS Server Address Match") {
|
||||||
|
if !self.rule.dnsServerAddressMatch.isEmpty {
|
||||||
|
Text("\(self.rule.dnsServerAddressMatch.count)")
|
||||||
|
} else {
|
||||||
|
Text("Not Used")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
HStack {
|
||||||
|
Text("DNS Server Address Match")
|
||||||
|
Spacer()
|
||||||
|
Group {
|
||||||
|
if !self.rule.dnsServerAddressMatch.isEmpty {
|
||||||
|
Text("\(self.rule.dnsServerAddressMatch.count)")
|
||||||
|
} else {
|
||||||
|
Text("Not Used")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.foregroundStyle(.secondary)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ViewBuilder private var probeURLLabel: some View {
|
||||||
|
if #available(iOS 16, *) {
|
||||||
|
LabeledContent("Probe URL") {
|
||||||
|
if let url = self.rule.probeURL?.absoluteString {
|
||||||
|
Text(url)
|
||||||
|
} else {
|
||||||
|
Text("Not Used")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
HStack {
|
||||||
|
Text("Probe URL")
|
||||||
|
Spacer()
|
||||||
|
Group {
|
||||||
|
if let url = self.rule.probeURL?.absoluteString {
|
||||||
|
Text(url)
|
||||||
|
} else {
|
||||||
|
Text("Not Used")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.foregroundStyle(.secondary)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@available(iOS 17, *)
|
||||||
|
#Preview {
|
||||||
|
@Previewable @State var rule = OnDemandRule(name: "Preview Rule")
|
||||||
|
NavigationStack {
|
||||||
|
RuleView(rule: $rule)
|
||||||
|
}
|
||||||
|
}
|
||||||
50
DNSecure/Views/SSIDMatchView.swift
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
import NetworkExtension
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct SSIDMatchView {
|
||||||
|
@Binding var rule: OnDemandRule
|
||||||
|
}
|
||||||
|
|
||||||
|
extension SSIDMatchView: View {
|
||||||
|
var body: some View {
|
||||||
|
Form {
|
||||||
|
Section {
|
||||||
|
ForEach(0..<self.rule.ssidMatch.count, id: \.self) { i in
|
||||||
|
LazyTextField(
|
||||||
|
"SSID",
|
||||||
|
// self.$rule.ssidMatch[i] causes crash on deletion
|
||||||
|
text: .init(
|
||||||
|
get: { self.rule.ssidMatch[i] },
|
||||||
|
set: { self.rule.ssidMatch[i] = $0 }
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.textInputAutocapitalization(.never)
|
||||||
|
.autocorrectionDisabled()
|
||||||
|
}
|
||||||
|
.onDelete { self.rule.ssidMatch.remove(atOffsets: $0) }
|
||||||
|
.onMove { self.rule.ssidMatch.move(fromOffsets: $0, toOffset: $1) }
|
||||||
|
Button("Add SSID") {
|
||||||
|
NEHotspotNetwork.fetchCurrent { network in
|
||||||
|
self.rule.ssidMatch.append(network?.ssid ?? "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} footer: {
|
||||||
|
Text(
|
||||||
|
"If the Service Set Identifier (SSID) of the current primary connected network matches one of the strings in this array and all of the other conditions in the rule match, then the rule matches."
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.navigationTitle("SSID Match")
|
||||||
|
.toolbar {
|
||||||
|
EditButton()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@available(iOS 17, *)
|
||||||
|
#Preview {
|
||||||
|
@Previewable @State var rule = OnDemandRule(name: "Preview Rule")
|
||||||
|
NavigationStack {
|
||||||
|
SSIDMatchView(rule: $rule)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -5,29 +5,6 @@
|
||||||
// Created by Kenta Kubo on 7/1/20.
|
// Created by Kenta Kubo on 7/1/20.
|
||||||
//
|
//
|
||||||
|
|
||||||
import XCTest
|
import Testing
|
||||||
|
|
||||||
@testable import DNSecure
|
@testable import DNSecure
|
||||||
|
|
||||||
class DNSecureTests: XCTestCase {
|
|
||||||
|
|
||||||
override func setUpWithError() throws {
|
|
||||||
// Put setup code here. This method is called before the invocation of each test method in the class.
|
|
||||||
}
|
|
||||||
|
|
||||||
override func tearDownWithError() throws {
|
|
||||||
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
|
||||||
}
|
|
||||||
|
|
||||||
func testExample() throws {
|
|
||||||
// This is an example of a functional test case.
|
|
||||||
// Use XCTAssert and related functions to verify your tests produce the correct results.
|
|
||||||
}
|
|
||||||
|
|
||||||
func testPerformanceExample() throws {
|
|
||||||
// This is an example of a performance test case.
|
|
||||||
self.measure {
|
|
||||||
// Put the code you want to measure the time of here.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,22 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
||||||
<plist version="1.0">
|
|
||||||
<dict>
|
|
||||||
<key>CFBundleDevelopmentRegion</key>
|
|
||||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
|
||||||
<key>CFBundleExecutable</key>
|
|
||||||
<string>$(EXECUTABLE_NAME)</string>
|
|
||||||
<key>CFBundleIdentifier</key>
|
|
||||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
|
||||||
<key>CFBundleInfoDictionaryVersion</key>
|
|
||||||
<string>6.0</string>
|
|
||||||
<key>CFBundleName</key>
|
|
||||||
<string>$(PRODUCT_NAME)</string>
|
|
||||||
<key>CFBundlePackageType</key>
|
|
||||||
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
|
||||||
<key>CFBundleShortVersionString</key>
|
|
||||||
<string>1.0</string>
|
|
||||||
<key>CFBundleVersion</key>
|
|
||||||
<string>1</string>
|
|
||||||
</dict>
|
|
||||||
</plist>
|
|
||||||
|
|
@ -7,8 +7,7 @@
|
||||||
|
|
||||||
import XCTest
|
import XCTest
|
||||||
|
|
||||||
class DNSecureUITests: XCTestCase {
|
final class DNSecureUITests: XCTestCase {
|
||||||
|
|
||||||
override func setUpWithError() throws {
|
override func setUpWithError() throws {
|
||||||
// Put setup code here. This method is called before the invocation of each test method in the class.
|
// Put setup code here. This method is called before the invocation of each test method in the class.
|
||||||
|
|
||||||
|
|
@ -22,21 +21,20 @@ class DNSecureUITests: XCTestCase {
|
||||||
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@MainActor
|
||||||
func testExample() throws {
|
func testExample() throws {
|
||||||
// UI tests must launch the application that they test.
|
// UI tests must launch the application that they test.
|
||||||
let app = XCUIApplication()
|
let app = XCUIApplication()
|
||||||
app.launch()
|
app.launch()
|
||||||
|
|
||||||
// Use recording to get started writing UI tests.
|
|
||||||
// Use XCTAssert and related functions to verify your tests produce the correct results.
|
// Use XCTAssert and related functions to verify your tests produce the correct results.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@MainActor
|
||||||
func testLaunchPerformance() throws {
|
func testLaunchPerformance() throws {
|
||||||
if #available(macOS 10.15, iOS 13.0, tvOS 13.0, *) {
|
// This measures how long it takes to launch your application.
|
||||||
// This measures how long it takes to launch your application.
|
measure(metrics: [XCTApplicationLaunchMetric()]) {
|
||||||
measure(metrics: [XCTApplicationLaunchMetric()]) {
|
XCUIApplication().launch()
|
||||||
XCUIApplication().launch()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
32
DNSecureUITests/DNSecureUITestsLaunchTests.swift
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
//
|
||||||
|
// DNSecureUITestsLaunchTests.swift
|
||||||
|
// DNSecureUITests
|
||||||
|
//
|
||||||
|
// Created by Kenta Kubo on 9/3/25.
|
||||||
|
//
|
||||||
|
|
||||||
|
import XCTest
|
||||||
|
|
||||||
|
final class DNSecureUITestsLaunchTests: XCTestCase {
|
||||||
|
override class var runsForEachTargetApplicationUIConfiguration: Bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
override func setUpWithError() throws {
|
||||||
|
continueAfterFailure = false
|
||||||
|
}
|
||||||
|
|
||||||
|
@MainActor
|
||||||
|
func testLaunch() throws {
|
||||||
|
let app = XCUIApplication()
|
||||||
|
app.launch()
|
||||||
|
|
||||||
|
// Insert steps here to perform after app launch but before taking a screenshot,
|
||||||
|
// such as logging into a test account or navigating somewhere in the app
|
||||||
|
|
||||||
|
let attachment = XCTAttachment(screenshot: app.screenshot())
|
||||||
|
attachment.name = "Launch Screen"
|
||||||
|
attachment.lifetime = .keepAlways
|
||||||
|
add(attachment)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,22 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
||||||
<plist version="1.0">
|
|
||||||
<dict>
|
|
||||||
<key>CFBundleDevelopmentRegion</key>
|
|
||||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
|
||||||
<key>CFBundleExecutable</key>
|
|
||||||
<string>$(EXECUTABLE_NAME)</string>
|
|
||||||
<key>CFBundleIdentifier</key>
|
|
||||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
|
||||||
<key>CFBundleInfoDictionaryVersion</key>
|
|
||||||
<string>6.0</string>
|
|
||||||
<key>CFBundleName</key>
|
|
||||||
<string>$(PRODUCT_NAME)</string>
|
|
||||||
<key>CFBundlePackageType</key>
|
|
||||||
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
|
||||||
<key>CFBundleShortVersionString</key>
|
|
||||||
<string>1.0</string>
|
|
||||||
<key>CFBundleVersion</key>
|
|
||||||
<string>1</string>
|
|
||||||
</dict>
|
|
||||||
</plist>
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2020 Kenta Kubo
|
Copyright (c) 2020-2026 Kenta Kubo
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
51
Package.swift
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
// swift-tools-version: 6.0
|
||||||
|
|
||||||
|
// WARNING:
|
||||||
|
// This file is automatically generated.
|
||||||
|
// Do not edit it by hand because the contents will be replaced.
|
||||||
|
|
||||||
|
import AppleProductTypes
|
||||||
|
import PackageDescription
|
||||||
|
|
||||||
|
let package = Package(
|
||||||
|
name: "DNSecure",
|
||||||
|
platforms: [
|
||||||
|
.iOS("16.1")
|
||||||
|
],
|
||||||
|
products: [
|
||||||
|
.iOSApplication(
|
||||||
|
name: "DNSecure",
|
||||||
|
targets: ["DNSecure"],
|
||||||
|
bundleIdentifier: "xyz.kebo.DNSecure",
|
||||||
|
teamIdentifier: "X4678G5DL2",
|
||||||
|
displayVersion: "1.6.2",
|
||||||
|
bundleVersion: "1",
|
||||||
|
appIcon: .asset("AppIcon-legacy"),
|
||||||
|
accentColor: .asset("AccentColor"),
|
||||||
|
supportedDeviceFamilies: [
|
||||||
|
.pad,
|
||||||
|
.phone,
|
||||||
|
],
|
||||||
|
supportedInterfaceOrientations: [
|
||||||
|
.portrait,
|
||||||
|
.landscapeRight,
|
||||||
|
.landscapeLeft,
|
||||||
|
.portraitUpsideDown(.when(deviceFamilies: [.pad])),
|
||||||
|
],
|
||||||
|
appCategory: .utilities
|
||||||
|
)
|
||||||
|
],
|
||||||
|
targets: [
|
||||||
|
.executableTarget(
|
||||||
|
name: "DNSecure",
|
||||||
|
path: "DNSecure"
|
||||||
|
),
|
||||||
|
.testTarget(
|
||||||
|
name: "DNSecureTests",
|
||||||
|
dependencies: [
|
||||||
|
.target(name: "DNSecure")
|
||||||
|
],
|
||||||
|
path: "DNSecureTests"
|
||||||
|
),
|
||||||
|
]
|
||||||
|
)
|
||||||
29
README.md
|
|
@ -1,12 +1,15 @@
|
||||||
# DNSecure
|
# DNSecure
|
||||||
|
|
||||||
<a href="https://apps.apple.com/us/app/dnsecure/id1533413232?itsct=apps_box&itscg=30200" style="display: inline-block; overflow: hidden; border-top-left-radius: 13px; border-top-right-radius: 13px; border-bottom-right-radius: 13px; border-bottom-left-radius: 13px; width: 250px; height: 83px;"><img src="https://tools.applemediaservices.com/api/badges/download-on-the-app-store/black/en-US?size=250x83&releaseDate=1601251200&h=77f35e8e1cad98287ffaa894b10bb6e2" alt="Download on the App Store" style="border-top-left-radius: 13px; border-top-right-radius: 13px; border-bottom-right-radius: 13px; border-bottom-left-radius: 13px; width: 250px; height: 83px;"></a>
|
iOS 14+, iPadOS 14+, and macOS 11+ have supported encrypted DNS (e.g. DNS-over-TLS (DoT) and DNS-over-HTTPS (DoH)), but they don't have a native UI for enabling it. To solve that, DNSecure was created. DNSecure is a configuration tool of DoT and DoH.
|
||||||
|
|
||||||
DNSecure makes encrypted DNS available to your entire system.
|
This app uses the new [DNS Settings API](https://developer.apple.com/documentation/networkextension/dns_settings), so it requires iOS 14+, iPadOS 14+, or macOS 11+.
|
||||||
|
|
||||||
This app uses the new [DNS Settings API](https://developer.apple.com/documentation/networkextension/dns_settings), so it requires iOS 14, iPadOS 14, or later.
|
## Installation (iOS/iPadOS/macOS)
|
||||||
|
|
||||||
## How to use
|
<a href="https://apps.apple.com/us/app/dnsecure/id1533413232?itscg=30200&itsct=apps_box_badge&mttnsubad=1533413232" style="display: inline-block;"><img src="https://toolbox.marketingtools.apple.com/api/v2/badges/download-on-the-app-store/black/en-us?releaseDate=1601251200" alt="Download on the App Store" style="width: 245px; height: 82px; vertical-align: middle; object-fit: contain;" /></a>
|
||||||
|
or [TestFlight Beta](https://testflight.apple.com/join/A8GwCnq8)
|
||||||
|
|
||||||
|
## How to use (iOS/iPadOS)
|
||||||
|
|
||||||
1. Select a DNS server you like, or add another one
|
1. Select a DNS server you like, or add another one
|
||||||
1. Enable "Use This Server"
|
1. Enable "Use This Server"
|
||||||
|
|
@ -14,6 +17,24 @@ This app uses the new [DNS Settings API](https://developer.apple.com/documentati
|
||||||
1. Go to "General" > "VPN & Network" > "DNS"
|
1. Go to "General" > "VPN & Network" > "DNS"
|
||||||
1. "Automatic" is selected by default, so select "DNSecure"
|
1. "Automatic" is selected by default, so select "DNSecure"
|
||||||
|
|
||||||
|
## How to use (macOS 13+)
|
||||||
|
|
||||||
|
1. Select a DNS server you like, or add another one
|
||||||
|
1. Enable "Use This Server"
|
||||||
|
1. Open the System Settings
|
||||||
|
1. Go to Network settings and click "Filters"
|
||||||
|
1. Enable "DNSecure"
|
||||||
|
|
||||||
|
## How to use (macOS 12)
|
||||||
|
|
||||||
|
1. Select a DNS server you like, or add another one
|
||||||
|
1. Enable "Use This Server"
|
||||||
|
1. Open the System Preferences
|
||||||
|
1. Go to Network settings
|
||||||
|
1. Select "DNSecure" and click "..." button on the bottom
|
||||||
|
1. Click "Make Service Active"
|
||||||
|
1. Click "Apply" button
|
||||||
|
|
||||||
## References
|
## References
|
||||||
|
|
||||||
- https://developer.apple.com/documentation/networkextension/dns_settings
|
- https://developer.apple.com/documentation/networkextension/dns_settings
|
||||||
|
|
|
||||||