Initial commit

This commit is contained in:
Conrad Kramer 2023-04-10 16:49:23 -04:00
commit c1e7415871
56 changed files with 3225 additions and 0 deletions

8
.devcontainer.json Normal file
View file

@ -0,0 +1,8 @@
{
"image": "mcr.microsoft.com/devcontainers/rust",
"customizations": {
"vscode": {
"extensions": ["rust-lang.rust-analyzer"]
}
}
}

1
.github/CODEOWNERS vendored Normal file
View file

@ -0,0 +1 @@
* @conradev

43
.github/actions/archive/action.yml vendored Normal file
View file

@ -0,0 +1,43 @@
name: Archive
inputs:
scheme:
description: Scheme
required: true
destination:
description: Destination
required: true
app-store-key:
description: App Store key in PEM PKCS#8 format
required: true
app-store-key-id:
description: App Store key ID
required: true
app-store-key-issuer-id:
description: App Store key issuer ID
required: true
archive-path:
description: Xcode archive path
required: true
runs:
using: composite
steps:
- shell: bash
working-directory: Apple
run: |
echo "${{ inputs.app-store-key }}" > AuthKey_${{ inputs.app-store-key-id }}.p8
xcodebuild archive \
-allowProvisioningUpdates \
-allowProvisioningDeviceRegistration \
-authenticationKeyID ${{ inputs.app-store-key-id }} \
-authenticationKeyIssuerID ${{ inputs.app-store-key-issuer-id }} \
-authenticationKeyPath "${PWD}/AuthKey_${{ inputs.app-store-key-id }}.p8" \
-onlyUsePackageVersionsFromResolvedFile \
-scheme '${{ inputs.scheme }}' \
-destination '${{ inputs.destination }}' \
-archivePath '${{ inputs.archive-path }}' \
-resultBundlePath BuildResults.xcresult
./Tools/xcresulttool-github BuildResults.xcresult
rm -rf AuthKey_${{ inputs.app-store-key-id }}.p8

View file

@ -0,0 +1,37 @@
name: Build For Testing
inputs:
scheme:
description: Scheme
required: true
destination:
description: Destination
required: true
app-store-key:
description: App Store key in PEM PKCS#8 format
required: true
app-store-key-id:
description: App Store key ID
required: true
app-store-key-issuer-id:
description: App Store key issuer ID
required: true
runs:
using: composite
steps:
- shell: bash
working-directory: Apple
run: |
echo "${{ inputs.app-store-key }}" > AuthKey_${{ inputs.app-store-key-id }}.p8
xcodebuild clean build-for-testing \
-allowProvisioningUpdates \
-allowProvisioningDeviceRegistration \
-authenticationKeyID ${{ inputs.app-store-key-id }} \
-authenticationKeyIssuerID ${{ inputs.app-store-key-issuer-id }} \
-authenticationKeyPath "${PWD}/AuthKey_${{ inputs.app-store-key-id }}.p8" \
-onlyUsePackageVersionsFromResolvedFile \
-scheme '${{ inputs.scheme }}' \
-destination '${{ inputs.destination }}' \
-resultBundlePath BuildResults.xcresult
rm -rf AuthKey_${{ inputs.app-store-key-id }}.p8

47
.github/actions/export/action.yml vendored Normal file
View file

@ -0,0 +1,47 @@
name: Notarize
inputs:
app-store-key:
description: App Store key in PEM PKCS#8 format
required: true
app-store-key-id:
description: App Store key ID
required: true
app-store-key-issuer-id:
description: App Store key issuer ID
required: true
archive-path:
description: Xcode archive path
required: true
destination:
description: The Xcode export destination. This can either be "export" or "upload"
required: true
method:
description: The Xcode export method. This can be one of app-store, validation, ad-hoc, package, enterprise, development, developer-id, or mac-application.
required: true
export-path:
description: The path to export the archive to
required: true
runs:
using: composite
steps:
- id: notarize
shell: bash
working-directory: Apple
run: |
echo "${{ inputs.app-store-key }}" > AuthKey_${{ inputs.app-store-key-id }}.p8
echo '{"destination":"${{ inputs.destination }}","method":"${{ inputs.method }}"}' \
| plutil -convert xml1 -o ExportOptions.plist -
xcodebuild \
-exportArchive \
-allowProvisioningUpdates \
-allowProvisioningDeviceRegistration \
-authenticationKeyID ${{ inputs.app-store-key-id }} \
-authenticationKeyIssuerID ${{ inputs.app-store-key-issuer-id }} \
-authenticationKeyPath "${PWD}/AuthKey_${{ inputs.app-store-key-id }}.p8" \
-archivePath '${{ inputs.archive-path }}' \
-exportPath '${{ inputs.export-path }}' \
-exportOptionsPlist ExportOptions.plist
rm -rf AuthKey_${{ inputs.app-store-key-id }}.p8 ExportOptions.plist

26
.github/actions/import-cert/action.yml vendored Normal file
View file

@ -0,0 +1,26 @@
name: Import Certificate
inputs:
certificate:
description: 'The certificate in p12 format, encoded as base64'
required: true
password:
description: 'The certificate password'
required: true
runs:
using: composite
steps:
- shell: bash
run: |
echo -n "${{ inputs.certificate }}" | base64 -d > Developer.p12
security create-keychain -p password Developer.keychain
security set-keychain-settings -lut 21600 Developer.keychain
security unlock-keychain -p password Developer.keychain
security import Developer.p12 \
-k Developer.keychain \
-f pkcs12 \
-A \
-T /usr/bin/codesign \
-T /usr/bin/security \
-P ${{ inputs.password }}
security set-key-partition-list -S apple-tool:,apple: -k password Developer.keychain
security list-keychains -d user -s login.keychain Developer.keychain

View file

@ -0,0 +1,37 @@
name: Test Without Building
inputs:
scheme:
description: Scheme
required: true
destination:
description: Destination
required: true
test-plan:
description: Test Plan
required: false
artifact-prefix:
description: The prefix for the filename of the uploaded xcresults file
required: true
check-name:
description: The check name
required: true
runs:
using: composite
steps:
- shell: bash
id: vars
run: echo "sha_short=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
- shell: bash
working-directory: Apple
run: |
xcodebuild test-without-building \
-scheme '${{ inputs.scheme }}' \
-destination '${{ inputs.destination }}' \
${{ inputs.test-plan && '-testPlan ' }}${{ inputs.test-plan }} \
-resultBundlePath "${{ inputs.artifact-prefix }}-${{ steps.vars.outputs.sha_short }}.xcresult"
- uses: kishikawakatsumi/xcresulttool@v1
if: always()
with:
path: Apple/${{ inputs.artifact-prefix }}-${{ steps.vars.outputs.sha_short }}.xcresult
title: ${{ inputs.check-name }}
show-passed-tests: false

70
.github/workflows/build-apple.yml vendored Normal file
View file

@ -0,0 +1,70 @@
name: Apple Build
on:
push:
branches:
- main
pull_request:
branches:
- "*"
jobs:
build:
name: Build App (${{ matrix.platform }})
runs-on: macos-12
strategy:
fail-fast: false
matrix:
include:
- scheme: App (iOS)
destination: generic/platform=iOS
platform: iOS
sdk-name: iphoneos
- scheme: App (iOS)
destination: platform=iOS Simulator,OS=16.2,name=iPhone 14 Pro
platform: iOS Simulator
sdk-name: iphonesimulator
- scheme: App (macOS)
destination: platform=macOS
platform: macOS
sdk-name: macos
env:
DEVELOPER_DIR: /Applications/Xcode_14.2.app/Contents/Developer
steps:
- name: Checkout
uses: actions/checkout@v3
with:
ssh-key: ${{ secrets.DEPLOY_KEY }}
submodules: recursive
- name: Import Certificate
uses: ./.github/actions/import-cert
with:
certificate: ${{ secrets.DEVELOPER_CERT }}
password: ${{ secrets.DEVELOPER_CERT_PASSWORD }}
- name: Build
id: build
uses: ./.github/actions/build-for-testing
with:
scheme: ${{ matrix.scheme }}
destination: ${{ matrix.destination }}
app-store-key: ${{ secrets.APPSTORE_KEY }}
app-store-key-id: ${{ secrets.APPSTORE_KEY_ID }}
app-store-key-issuer-id: ${{ secrets.APPSTORE_KEY_ISSUER_ID }}
- name: Xcode Unit Test
if: ${{ matrix.xcode-unit-test != '' }}
continue-on-error: true
uses: ./.github/actions/test-without-building
with:
scheme: ${{ matrix.scheme }}
destination: ${{ matrix.destination }}
test-plan: ${{ matrix.xcode-unit-test }}
artifact-prefix: unit-tests-${{ matrix.sdk-name }}
check-name: Xcode Unit Tests (${{ matrix.platform }})
- name: Xcode UI Test
if: ${{ matrix.xcode-ui-test != '' }}
continue-on-error: true
uses: ./.github/actions/test-without-building
with:
scheme: ${{ matrix.scheme }}
destination: ${{ matrix.destination }}
test-plan: ${{ matrix.xcode-ui-test }}
artifact-prefix: ui-tests-${{ matrix.sdk-name }}
check-name: Xcode UI Tests (${{ matrix.platform }})

55
.github/workflows/build-rust.yml vendored Normal file
View file

@ -0,0 +1,55 @@
name: Rust Build
on:
push:
branches:
- main
pull_request:
branches:
- "*"
jobs:
build:
name: Build Crate (${{ matrix.platform }})
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-latest
platform: Linux
targets:
- x86_64-unknown-linux-gnu
- aarch64-unknown-linux-gnu
- os: macos-12
platform: macOS
targets:
- x86_64-apple-darwin
- aarch64-apple-darwin
- aarch64-apple-ios
- aarch64-apple-ios-sim
- x86_64-apple-ios
- os: windows-2022
platform: Windows
targets:
- x86_64-pc-windows-msvc
- aarch64-pc-windows-msvc
runs-on: ${{ matrix.os }}
env:
DEVELOPER_DIR: /Applications/Xcode_14.2.app/Contents/Developer
steps:
- name: Checkout
uses: actions/checkout@v3
with:
ssh-key: ${{ secrets.DEPLOY_KEY }}
submodules: recursive
- name: Install Rust
uses: dtolnay/rust-toolchain@master
with:
toolchain: stable
components: rustfmt
targets: ${{ join(matrix.targets, ', ') }}
- name: Build
shell: bash
run: cargo build --verbose --workspace --all-features
- uses: actions-rs/cargo@v1
with:
command: build
args: --workspace --all-features --target ${{ join(matrix.targets, ' --target ') }}

20
.github/workflows/lint-git.yml vendored Normal file
View file

@ -0,0 +1,20 @@
name: Git Lint
on:
pull_request:
branches:
- "*"
jobs:
lint:
name: Git Lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
ref: ${{ github.event.pull_request.head.sha }}
fetch-depth: 0
- name: Install Gitlint
shell: bash
run: python -m pip install gitlint
- name: Run Gitlint
shell: bash
run: gitlint --commits "${{ github.event.pull_request.base.sha }}..HEAD"

21
.github/workflows/lint-swift.yml vendored Normal file
View file

@ -0,0 +1,21 @@
name: Swift Lint
on:
push:
branches:
- main
pull_request:
branches:
- "*"
jobs:
lint:
name: Swift Lint
runs-on: ubuntu-latest
container:
image: ghcr.io/realm/swiftlint:latest
steps:
- name: Checkout
uses: actions/checkout@v3
with:
ssh-key: ${{ secrets.DEPLOY_KEY }}
- name: Lint
run: swiftlint lint --reporter github-actions-logging

65
.github/workflows/release-apple.yml vendored Normal file
View file

@ -0,0 +1,65 @@
name: Build Apple Release
on:
release:
types:
- created
jobs:
build:
name: Build ${{ matrix.configuration['platform'] }} Release
runs-on: macos-12
strategy:
fail-fast: false
matrix:
configuration:
- scheme: App (iOS)
destination: generic/platform=iOS
platform: iOS
method: ad-hoc
artifact-file: Apple/Release/Burrow.ipa
- scheme: App (macOS)
destination: generic/platform=macOS
platform: macOS
method: mac-application
artifact-file: Burrow.app.txz
env:
DEVELOPER_DIR: /Applications/Xcode_14.2.app/Contents/Developer
steps:
- name: Checkout
uses: actions/checkout@v3
with:
ssh-key: ${{ secrets.DEPLOY_KEY }}
submodules: recursive
- name: Import Certificate
uses: ./.github/actions/import-cert
with:
certificate: ${{ secrets.DEVELOPER_CERT }}
password: ${{ secrets.DEVELOPER_CERT_PASSWORD }}
- name: Archive
uses: ./.github/actions/archive
with:
scheme: ${{ matrix.configuration['scheme'] }}
destination: ${{ matrix.configuration['destination'] }}
app-store-key: ${{ secrets.APPSTORE_KEY }}
app-store-key-id: ${{ secrets.APPSTORE_KEY_ID }}
app-store-key-issuer-id: ${{ secrets.APPSTORE_KEY_ISSUER_ID }}
archive-path: Burrow.xcarchive
- name: Export Locally
uses: ./.github/actions/export
with:
method: ${{ matrix.configuration['method'] }}
destination: export
app-store-key: ${{ secrets.APPSTORE_KEY }}
app-store-key-id: ${{ secrets.APPSTORE_KEY_ID }}
app-store-key-issuer-id: ${{ secrets.APPSTORE_KEY_ISSUER_ID }}
archive-path: Burrow.xcarchive
export-path: Release
- name: Compress
if: ${{ matrix.configuration['platform'] == 'macOS' }}
shell: bash
run: tar --options xz:compression-level=9 -C Apple/Release -cJf Burrow.app.txz ./
- name: Attach Artifact
uses: SierraSoftworks/gh-releases@v1.0.6
with:
token: ${{ secrets.GITHUB_TOKEN }}
overwrite: 'false'
files: ${{ matrix.configuration['artifact-file'] }}

5
.gitignore vendored Normal file
View file

@ -0,0 +1,5 @@
# Xcode
xcuserdata
# Rust
target/

19
.gitlint Normal file
View file

@ -0,0 +1,19 @@
[general]
ignore=body-changed-file-mention
ignore-merge-commits=false
ignore-fixup-commits=false
ignore-squash-commits=false
ignore-stdin=true
fail-without-commits=true
[title-max-length]
line-length=50
[body-max-line-length]
line-length=72
[title-match-regex]
regex=^(?!.*(#\d+))
[body-match-regex]
regex=^(?!.*(#\d+))

99
.swiftlint.yml Normal file
View file

@ -0,0 +1,99 @@
opt_in_rules:
- anonymous_argument_in_multiline_closure
- array_init
- attributes
- closure_end_indentation
- closure_spacing
- collection_alignment
- contains_over_filter_count
- contains_over_filter_is_empty
- contains_over_first_not_nil
- contains_over_range_nil_comparison
- convenience_type
- discarded_notification_center_observer
- discouraged_assert
- discouraged_none_name
- discouraged_object_literal
- empty_collection_literal
- empty_count
- empty_string
- empty_xctest_method
- enum_case_associated_values_count
- expiring_todo
- explicit_init
- fallthrough
- fatal_error_message
- file_header
- file_name_no_space
- first_where
- flatmap_over_map_reduce
- function_default_parameter_at_end
- ibinspectable_in_extension
- identical_operands
- implicitly_unwrapped_optional
- indentation_width
- joined_default_parameter
- last_where
- legacy_multiple
- legacy_random
- literal_expression_end_indentation
- lower_acl_than_parent
- modifier_order
- multiline_arguments
- multiline_arguments_brackets
- multiline_function_chains
- multiline_literal_brackets
- multiline_parameters
- multiline_parameters_brackets
- no_extension_access_modifier
- no_grouping_extension
- nslocalizedstring_key
- nslocalizedstring_require_bundle
- number_separator
- object_literal
- operator_usage_whitespace
- optional_enum_case_matching
- overridden_super_call
- override_in_extension
- pattern_matching_keywords
- prefer_self_in_static_references
- prefer_self_type_over_type_of_self
- prefer_zero_over_explicit_init
- private_action
- private_outlet
- private_subject
- prohibited_interface_builder
- prohibited_super_call
- quick_discouraged_call
- quick_discouraged_focused_test
- quick_discouraged_pending_test
- raw_value_for_camel_cased_codable_enum
- reduce_into
- redundant_nil_coalescing
- redundant_type_annotation
- required_enum_case
- single_test_class
- sorted_first_last
- sorted_imports
- static_operator
- strict_fileprivate
- strong_iboutlet
- switch_case_on_newline
- test_case_accessibility
- toggle_bool
- trailing_closure
- type_contents_order
- unavailable_function
- unneeded_parentheses_in_closure_argument
- unowned_variable_capture
- untyped_error_in_catch
- vertical_parameter_alignment_on_call
- vertical_whitespace_closing_braces
- vertical_whitespace_opening_braces
- weak_delegate
- xct_specific_matcher
- yoda_condition
disabled_rules:
- force_try
- nesting
- todo

5
.vscode/extensions.json vendored Normal file
View file

@ -0,0 +1,5 @@
{
"recommendations": [
"rust-lang.rust-analyzer",
]
}

16
.vscode/settings.json vendored Normal file
View file

@ -0,0 +1,16 @@
{
"files.autoSave": "onFocusChange",
"files.defaultLanguage": "rust",
"explorer.excludeGitIgnore": true,
"editor.formatOnPaste": true,
"editor.formatOnSave": true,
"files.trimTrailingWhitespace": true,
"editor.suggest.preview": true,
"editor.acceptSuggestionOnEnter": "on",
"rust-analyzer.checkOnSave.command": "clippy",
"rust-analyzer.restartServerOnConfigChange": true,
"rust-analyzer.cargo.features": "all",
"[rust]": {
"editor.defaultFormatter": "rust-lang.rust-analyzer",
},
}

17
.vscode/tasks.json vendored Normal file
View file

@ -0,0 +1,17 @@
{
"version": "2.0.0",
"tasks": [
{
"type": "cargo",
"command": "check",
"problemMatcher": [
"$rustc"
],
"group": {
"kind": "build",
"isDefault": true
},
"label": "Check"
}
]
}

View file

@ -0,0 +1,10 @@
<?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>com.apple.security.application-groups</key>
<array>
<string>$(APP_GROUP_IDENTIFIER)</string>
</array>
</dict>
</plist>

View file

@ -0,0 +1,10 @@
<?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>com.apple.security.application-groups</key>
<array>
<string>$(APP_GROUP_IDENTIFIER)</string>
</array>
</dict>
</plist>

View file

@ -0,0 +1,11 @@
{
"colors" : [
{
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View file

@ -0,0 +1,63 @@
{
"images" : [
{
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
},
{
"idiom" : "mac",
"scale" : "1x",
"size" : "16x16"
},
{
"idiom" : "mac",
"scale" : "2x",
"size" : "16x16"
},
{
"idiom" : "mac",
"scale" : "1x",
"size" : "32x32"
},
{
"idiom" : "mac",
"scale" : "2x",
"size" : "32x32"
},
{
"idiom" : "mac",
"scale" : "1x",
"size" : "128x128"
},
{
"idiom" : "mac",
"scale" : "2x",
"size" : "128x128"
},
{
"idiom" : "mac",
"scale" : "1x",
"size" : "256x256"
},
{
"idiom" : "mac",
"scale" : "2x",
"size" : "256x256"
},
{
"idiom" : "mac",
"scale" : "1x",
"size" : "512x512"
},
{
"idiom" : "mac",
"scale" : "2x",
"size" : "512x512"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View file

@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}

17
Apple/App/Burrow.xcconfig Normal file
View file

@ -0,0 +1,17 @@
#include "../Configuration/App.xcconfig"
PRODUCT_NAME = Burrow
PRODUCT_BUNDLE_IDENTIFIER = $(APP_BUNDLE_IDENTIFIER)
INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphone*] = YES
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphone*] = YES
INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphone*] = YES
INFOPLIST_KEY_UIStatusBarStyle[sdk=iphone*] = UIStatusBarStyleDefault
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad[sdk=iphone*] = UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone[sdk=iphone*] = UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight
TARGETED_DEVICE_FAMILY[sdk=iphone*] = 1,2
INFOPLIST_KEY_LSApplicationCategoryType[sdk=macos*] = public.app-category.utilities
CODE_SIGN_ENTITLEMENTS = App/App-iOS.entitlements
CODE_SIGN_ENTITLEMENTS[sdk=macos*] = App/App-macOS.entitlements

10
Apple/App/BurrowApp.swift Normal file
View file

@ -0,0 +1,10 @@
import SwiftUI
@main
struct BurrowApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}

View file

@ -0,0 +1,19 @@
import SwiftUI
struct ContentView: View {
var body: some View {
VStack {
Image(systemName: "globe")
.imageScale(.large)
.foregroundColor(.accentColor)
Text("Hello, world!")
}
.padding()
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}

View file

@ -0,0 +1,334 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 56;
objects = {
/* Begin PBXBuildFile section */
D020F65829E4A697002790F6 /* PacketTunnelProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = D020F65729E4A697002790F6 /* PacketTunnelProvider.swift */; };
D020F65D29E4A697002790F6 /* BurrowNetworkExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = D020F65329E4A697002790F6 /* BurrowNetworkExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
D05B9F7629E39EEC008CB1F9 /* BurrowApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = D05B9F7529E39EEC008CB1F9 /* BurrowApp.swift */; };
D05B9F7829E39EEC008CB1F9 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D05B9F7729E39EEC008CB1F9 /* ContentView.swift */; };
D05B9F7A29E39EED008CB1F9 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D05B9F7929E39EED008CB1F9 /* Assets.xcassets */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
D020F65B29E4A697002790F6 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = D05B9F6A29E39EEC008CB1F9 /* Project object */;
proxyType = 1;
remoteGlobalIDString = D020F65229E4A697002790F6;
remoteInfo = BurrowNetworkExtension;
};
/* End PBXContainerItemProxy section */
/* Begin PBXCopyFilesBuildPhase section */
D020F66129E4A697002790F6 /* Embed Foundation Extensions */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 13;
files = (
D020F65D29E4A697002790F6 /* BurrowNetworkExtension.appex in Embed Foundation Extensions */,
);
name = "Embed Foundation Extensions";
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
D020F63D29E4A1FF002790F6 /* Identity.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Identity.xcconfig; sourceTree = "<group>"; };
D020F64029E4A1FF002790F6 /* Compiler.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Compiler.xcconfig; sourceTree = "<group>"; };
D020F64229E4A1FF002790F6 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
D020F64929E4A34B002790F6 /* Burrow.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Burrow.xcconfig; sourceTree = "<group>"; };
D020F64A29E4A452002790F6 /* App.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = App.xcconfig; sourceTree = "<group>"; };
D020F65329E4A697002790F6 /* BurrowNetworkExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = BurrowNetworkExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; };
D020F65729E4A697002790F6 /* PacketTunnelProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PacketTunnelProvider.swift; sourceTree = "<group>"; };
D020F65929E4A697002790F6 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
D020F66229E4A6E5002790F6 /* NetworkExtension.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = NetworkExtension.xcconfig; sourceTree = "<group>"; };
D020F66329E4A703002790F6 /* Extension.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Extension.xcconfig; sourceTree = "<group>"; };
D020F66629E4A95D002790F6 /* NetworkExtension-macOS.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "NetworkExtension-macOS.entitlements"; sourceTree = "<group>"; };
D020F66729E4A95D002790F6 /* NetworkExtension-iOS.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "NetworkExtension-iOS.entitlements"; sourceTree = "<group>"; };
D020F66829E4AA74002790F6 /* App-iOS.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "App-iOS.entitlements"; sourceTree = "<group>"; };
D020F66929E4AA74002790F6 /* App-macOS.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "App-macOS.entitlements"; sourceTree = "<group>"; };
D05B9F7229E39EEC008CB1F9 /* Burrow.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Burrow.app; sourceTree = BUILT_PRODUCTS_DIR; };
D05B9F7529E39EEC008CB1F9 /* BurrowApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BurrowApp.swift; sourceTree = "<group>"; };
D05B9F7729E39EEC008CB1F9 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
D05B9F7929E39EED008CB1F9 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
D020F65029E4A697002790F6 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
D05B9F6F29E39EEC008CB1F9 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
D020F63C29E4A1FF002790F6 /* Configuration */ = {
isa = PBXGroup;
children = (
D020F63D29E4A1FF002790F6 /* Identity.xcconfig */,
D020F64A29E4A452002790F6 /* App.xcconfig */,
D020F66329E4A703002790F6 /* Extension.xcconfig */,
D020F64029E4A1FF002790F6 /* Compiler.xcconfig */,
D020F64229E4A1FF002790F6 /* Info.plist */,
);
path = Configuration;
sourceTree = "<group>";
};
D020F65629E4A697002790F6 /* NetworkExtension */ = {
isa = PBXGroup;
children = (
D020F65729E4A697002790F6 /* PacketTunnelProvider.swift */,
D020F65929E4A697002790F6 /* Info.plist */,
D020F66729E4A95D002790F6 /* NetworkExtension-iOS.entitlements */,
D020F66629E4A95D002790F6 /* NetworkExtension-macOS.entitlements */,
D020F66229E4A6E5002790F6 /* NetworkExtension.xcconfig */,
);
path = NetworkExtension;
sourceTree = "<group>";
};
D05B9F6929E39EEC008CB1F9 = {
isa = PBXGroup;
children = (
D05B9F7429E39EEC008CB1F9 /* App */,
D020F65629E4A697002790F6 /* NetworkExtension */,
D020F63C29E4A1FF002790F6 /* Configuration */,
D05B9F7329E39EEC008CB1F9 /* Products */,
);
sourceTree = "<group>";
};
D05B9F7329E39EEC008CB1F9 /* Products */ = {
isa = PBXGroup;
children = (
D05B9F7229E39EEC008CB1F9 /* Burrow.app */,
D020F65329E4A697002790F6 /* BurrowNetworkExtension.appex */,
);
name = Products;
sourceTree = "<group>";
};
D05B9F7429E39EEC008CB1F9 /* App */ = {
isa = PBXGroup;
children = (
D05B9F7529E39EEC008CB1F9 /* BurrowApp.swift */,
D05B9F7729E39EEC008CB1F9 /* ContentView.swift */,
D05B9F7929E39EED008CB1F9 /* Assets.xcassets */,
D020F66829E4AA74002790F6 /* App-iOS.entitlements */,
D020F66929E4AA74002790F6 /* App-macOS.entitlements */,
D020F64929E4A34B002790F6 /* Burrow.xcconfig */,
);
path = App;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
D020F65229E4A697002790F6 /* NetworkExtension */ = {
isa = PBXNativeTarget;
buildConfigurationList = D020F65E29E4A697002790F6 /* Build configuration list for PBXNativeTarget "NetworkExtension" */;
buildPhases = (
D020F64F29E4A697002790F6 /* Sources */,
D020F65029E4A697002790F6 /* Frameworks */,
D020F65129E4A697002790F6 /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = NetworkExtension;
productName = BurrowNetworkExtension;
productReference = D020F65329E4A697002790F6 /* BurrowNetworkExtension.appex */;
productType = "com.apple.product-type.app-extension";
};
D05B9F7129E39EEC008CB1F9 /* Burrow */ = {
isa = PBXNativeTarget;
buildConfigurationList = D05B9F8129E39EED008CB1F9 /* Build configuration list for PBXNativeTarget "Burrow" */;
buildPhases = (
D05B9F6E29E39EEC008CB1F9 /* Sources */,
D05B9F6F29E39EEC008CB1F9 /* Frameworks */,
D05B9F7029E39EEC008CB1F9 /* Resources */,
D020F66129E4A697002790F6 /* Embed Foundation Extensions */,
);
buildRules = (
);
dependencies = (
D020F65C29E4A697002790F6 /* PBXTargetDependency */,
);
name = Burrow;
productName = Burrow;
productReference = D05B9F7229E39EEC008CB1F9 /* Burrow.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
D05B9F6A29E39EEC008CB1F9 /* Project object */ = {
isa = PBXProject;
attributes = {
BuildIndependentTargetsInParallel = 1;
LastSwiftUpdateCheck = 1430;
LastUpgradeCheck = 1430;
TargetAttributes = {
D020F65229E4A697002790F6 = {
CreatedOnToolsVersion = 14.3;
};
D05B9F7129E39EEC008CB1F9 = {
CreatedOnToolsVersion = 14.3;
};
};
};
buildConfigurationList = D05B9F6D29E39EEC008CB1F9 /* Build configuration list for PBXProject "Burrow" */;
compatibilityVersion = "Xcode 14.0";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = D05B9F6929E39EEC008CB1F9;
productRefGroup = D05B9F7329E39EEC008CB1F9 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
D05B9F7129E39EEC008CB1F9 /* Burrow */,
D020F65229E4A697002790F6 /* NetworkExtension */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
D020F65129E4A697002790F6 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
D05B9F7029E39EEC008CB1F9 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
D05B9F7A29E39EED008CB1F9 /* Assets.xcassets in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
D020F64F29E4A697002790F6 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
D020F65829E4A697002790F6 /* PacketTunnelProvider.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
D05B9F6E29E39EEC008CB1F9 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
D05B9F7829E39EEC008CB1F9 /* ContentView.swift in Sources */,
D05B9F7629E39EEC008CB1F9 /* BurrowApp.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
D020F65C29E4A697002790F6 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = D020F65229E4A697002790F6 /* NetworkExtension */;
targetProxy = D020F65B29E4A697002790F6 /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin XCBuildConfiguration section */
D020F65F29E4A697002790F6 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = D020F66229E4A6E5002790F6 /* NetworkExtension.xcconfig */;
buildSettings = {
};
name = Debug;
};
D020F66029E4A697002790F6 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = D020F66229E4A6E5002790F6 /* NetworkExtension.xcconfig */;
buildSettings = {
};
name = Release;
};
D05B9F7F29E39EED008CB1F9 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = D020F64029E4A1FF002790F6 /* Compiler.xcconfig */;
buildSettings = {
};
name = Debug;
};
D05B9F8029E39EED008CB1F9 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = D020F64029E4A1FF002790F6 /* Compiler.xcconfig */;
buildSettings = {
};
name = Release;
};
D05B9F8229E39EED008CB1F9 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = D020F64929E4A34B002790F6 /* Burrow.xcconfig */;
buildSettings = {
};
name = Debug;
};
D05B9F8329E39EED008CB1F9 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = D020F64929E4A34B002790F6 /* Burrow.xcconfig */;
buildSettings = {
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
D020F65E29E4A697002790F6 /* Build configuration list for PBXNativeTarget "NetworkExtension" */ = {
isa = XCConfigurationList;
buildConfigurations = (
D020F65F29E4A697002790F6 /* Debug */,
D020F66029E4A697002790F6 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
D05B9F6D29E39EEC008CB1F9 /* Build configuration list for PBXProject "Burrow" */ = {
isa = XCConfigurationList;
buildConfigurations = (
D05B9F7F29E39EED008CB1F9 /* Debug */,
D05B9F8029E39EED008CB1F9 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
D05B9F8129E39EED008CB1F9 /* Build configuration list for PBXNativeTarget "Burrow" */ = {
isa = XCConfigurationList;
buildConfigurations = (
D05B9F8229E39EED008CB1F9 /* Debug */,
D05B9F8329E39EED008CB1F9 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = D05B9F6A29E39EEC008CB1F9 /* Project object */;
}

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:">
</FileRef>
</Workspace>

View file

@ -0,0 +1,8 @@
<?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>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>

View file

@ -0,0 +1,10 @@
SKIP_INSTALL = NO
LD_RUNPATH_SEARCH_PATHS[sdk=iphone*] = $(inherited) @executable_path/Frameworks
LD_RUNPATH_SEARCH_PATHS[sdk=macosx*] = $(inherited) @executable_path/../Frameworks
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor
ENABLE_PREVIEWS = YES

View file

@ -0,0 +1,61 @@
#include "Identity.xcconfig"
SDKROOT = auto
ALLOW_TARGET_PLATFORM_SPECIALIZATION = YES
SUPPORTED_PLATFORMS = iphoneos iphonesimulator macosx
IPHONEOS_DEPLOYMENT_TARGET = 15.0
MACOSX_DEPLOYMENT_TARGET = 12.0
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO
SUPPORTS_MACCATALYST = NO
ALWAYS_SEARCH_USER_PATHS = NO
PRODUCT_NAME = $(TARGET_NAME:c99extidentifier)
PRODUCT_BUNDLE_IDENTIFIER = $(APP_BUNDLE_IDENTIFIER).$(PRODUCT_NAME)
CURRENT_PROJECT_VERSION = 1
MARKETING_VERSION = 0.1
SKIP_INSTALL = YES
CODE_SIGN_IDENTITY = Apple Development
INFOPLIST_FILE = Configuration/Info.plist
GENERATE_INFOPLIST_FILE = YES
INFOPLIST_KEY_NSHumanReadableCopyright = Copyright © 2023 Hack Club
INFOPLIST_KEY_CFBundleDisplayName = Burrow
ENABLE_BITCODE = NO
ENABLE_APP_SANDBOX[sdk=macosx*] = YES
ENABLE_HARDENED_RUNTIME[sdk=macosx*] = YES
COMBINE_HIDPI_IMAGES = YES
COPY_PHASE_STRIP = NO
FUSE_BUILD_SCRIPT_PHASES = YES
APP_GROUP_IDENTIFIER = group.$(APP_BUNDLE_IDENTIFIER)
APP_GROUP_IDENTIFIER[sdk=macosx*] = $(DEVELOPMENT_TEAM).$(APP_BUNDLE_IDENTIFIER)
// Swift
SWIFT_VERSION = 5.0
SWIFT_EMIT_LOC_STRINGS = YES
// Release
DEBUG_INFORMATION_FORMAT = dwarf-with-dsym
SWIFT_REFLECTION_METADATA_LEVEL = none
SWIFT_COMPILATION_MODE = wholemodule
SWIFT_OPTIMIZATION_LEVEL = -Osize
LLVM_LTO = YES
DEAD_CODE_STRIPPING = YES
VALIDATE_PRODUCT = YES
// Debug
ONLY_ACTIVE_ARCH[config=Debug] = YES
DEBUG_INFORMATION_FORMAT[config=Debug] = dwarf
ENABLE_TESTABILITY[config=Debug] = YES
SWIFT_OPTIMIZATION_LEVEL[config=Debug] = -Onone
SWIFT_ACTIVE_COMPILATION_CONDITIONS[config=Debug] = DEBUG
SWIFT_COMPILATION_MODE[config=Debug] = singlefile
LLVM_LTO[config=Debug] = NO
DEAD_CODE_STRIPPING[config=Debug] = NO
VALIDATE_PRODUCT[config=Debug] = NO

View file

@ -0,0 +1,2 @@
LD_RUNPATH_SEARCH_PATHS = $(inherited) @executable_path/Frameworks @executable_path/../../Frameworks
LD_RUNPATH_SEARCH_PATHS[sdk=macos*] = $(inherited) @executable_path/../Frameworks @executable_path/../../../../Frameworks

View file

@ -0,0 +1,2 @@
DEVELOPMENT_TEAM = 87PW93R2ZR
APP_BUNDLE_IDENTIFIER = com.hackclub.burrow

View file

@ -0,0 +1,8 @@
<?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>CFBundleName</key>
<string>$(INFOPLIST_KEY_CFBundleDisplayName)</string>
</dict>
</plist>

View file

@ -0,0 +1,13 @@
<?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>NSExtension</key>
<dict>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.networkextension.packet-tunnel</string>
<key>NSExtensionPrincipalClass</key>
<string>$(PRODUCT_MODULE_NAME).PacketTunnelProvider</string>
</dict>
</dict>
</plist>

View file

@ -0,0 +1,10 @@
<?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>com.apple.security.application-groups</key>
<array>
<string>$(APP_GROUP_IDENTIFIER)</string>
</array>
</dict>
</plist>

View file

@ -0,0 +1,10 @@
<?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>com.apple.security.application-groups</key>
<array>
<string>$(APP_GROUP_IDENTIFIER)</string>
</array>
</dict>
</plist>

View file

@ -0,0 +1,8 @@
#include "../Configuration/Extension.xcconfig"
PRODUCT_NAME = BurrowNetworkExtension
PRODUCT_BUNDLE_IDENTIFIER = $(APP_BUNDLE_IDENTIFIER).network
INFOPLIST_FILE = NetworkExtension/Info.plist
CODE_SIGN_ENTITLEMENTS = NetworkExtension/NetworkExtension-iOS.entitlements
CODE_SIGN_ENTITLEMENTS[sdk=macos*] = NetworkExtension/NetworkExtension-macOS.entitlements

View file

@ -0,0 +1,29 @@
import NetworkExtension
class PacketTunnelProvider: NEPacketTunnelProvider {
override func startTunnel(options: [String : NSObject]?, completionHandler: @escaping (Error?) -> Void) {
// Add code here to start the process of connecting the tunnel.
}
override func stopTunnel(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) {
// Add code here to start the process of stopping the tunnel.
completionHandler()
}
override func handleAppMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)?) {
// Add code here to handle the message.
if let handler = completionHandler {
handler(messageData)
}
}
override func sleep(completionHandler: @escaping () -> Void) {
// Add code here to get ready to sleep.
completionHandler()
}
override func wake() {
// Add code here to wake up.
}
}

1523
Cargo.lock generated Normal file

File diff suppressed because it is too large Load diff

6
Cargo.toml Normal file
View file

@ -0,0 +1,6 @@
[workspace]
members = [
"burrow",
"tun-async",
"tun"
]

10
burrow/Cargo.toml Normal file
View file

@ -0,0 +1,10 @@
[package]
name = "burrow"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
tokio = { version = "1.21", features = ["rt", "macros"] }
tun = { version = "0.1", path = "../tun" }

14
burrow/src/main.rs Normal file
View file

@ -0,0 +1,14 @@
use tokio::io::Result;
use tun::TunInterface;
async fn lol() -> Result<()> {
let iface = TunInterface::new()?;
println!("{:?}", iface.name());
Ok(())
}
#[tokio::main(flavor = "current_thread")]
async fn main() {
lol().await.unwrap();
}

9
tun-async/Cargo.toml Normal file
View file

@ -0,0 +1,9 @@
[package]
name = "tun-async"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
tun = { version = "0.1", path = "../tun" }

14
tun-async/src/lib.rs Normal file
View file

@ -0,0 +1,14 @@
pub fn add(left: usize, right: usize) -> usize {
left + right
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_works() {
let result = add(2, 2);
assert_eq!(result, 4);
}
}

24
tun/Cargo.toml Normal file
View file

@ -0,0 +1,24 @@
[package]
name = "tun"
version = "0.1.0"
edition = "2021"
[dependencies]
libc = "0.2"
nix = { version = "0.25", features = ["ioctl"] }
socket2 = "0.4"
tokio = { version = "1.21", features = [] }
[target.'cfg(windows)'.dependencies]
libloading = "0.7"
widestring = "1.0"
[target.'cfg(windows)'.build-dependencies]
anyhow = "1.0"
bindgen = "0.61"
hex-literal = "0.3"
platforms = "3.0"
reqwest = { version = "0.11", features = ["native-tls"] }
sha2 = "0.10"
tokio = { version = "1.21", features = ["rt"] }
zip = { version = "0.6", features = ["deflate"] }

79
tun/build.rs Normal file
View file

@ -0,0 +1,79 @@
#[cfg(windows)]
#[tokio::main(flavor = "current_thread")]
async fn main() -> anyhow::Result<()> {
use std::io::{Cursor, Read};
let buf = reqwest::get("https://www.wintun.net/builds/wintun-0.14.1.zip")
.await?
.bytes()
.await?;
assert_content_hash(
&buf,
hex_literal::hex!("07c256185d6ee3652e09fa55c0b673e2624b565e02c4b9091c79ca7d2f24ef51"),
);
let mut archive = zip::ZipArchive::new(Cursor::new(buf))?;
let out_dir = std::path::PathBuf::from(std::env::var("OUT_DIR")?);
let mut header = String::new();
archive
.by_name("wintun/include/wintun.h")?
.read_to_string(&mut header)?;
header.push_str(
"WINTUN_CLOSE_ADAPTER_FUNC WintunCloseAdapter;
WINTUN_OPEN_ADAPTER_FUNC WintunOpenAdapter;
WINTUN_GET_ADAPTER_LUID_FUNC WintunGetAdapterLUID;
WINTUN_GET_RUNNING_DRIVER_VERSION_FUNC WintunGetRunningDriverVersion;
WINTUN_DELETE_DRIVER_FUNC WintunDeleteDriver;
WINTUN_SET_LOGGER_FUNC WintunSetLogger;
WINTUN_START_SESSION_FUNC WintunStartSession;
WINTUN_END_SESSION_FUNC WintunEndSession;
WINTUN_CREATE_ADAPTER_FUNC WintunCreateAdapter;
WINTUN_GET_READ_WAIT_EVENT_FUNC WintunGetReadWaitEvent;
WINTUN_RECEIVE_PACKET_FUNC WintunReceivePacket;
WINTUN_RELEASE_RECEIVE_PACKET_FUNC WintunReleaseReceivePacket;
WINTUN_ALLOCATE_SEND_PACKET_FUNC WintunAllocateSendPacket;
WINTUN_SEND_PACKET_FUNC WintunSendPacket;",
);
let bindings = bindgen::Builder::default()
.header_contents("wintun.h", &header)
.allowlist_function("Wintun.*")
.allowlist_type("WINTUN_.*")
.dynamic_library_name("wintun")
.dynamic_link_require_all(true)
.generate()
.unwrap();
bindings.write_to_file(out_dir.join("wintun.rs"))?;
let mut library = Vec::new();
let platform = platforms::Platform::find(&env::var("TARGET")?).unwrap();
let arch = match platform.target_arch {
platforms::target::Arch::Arm => "arm",
platforms::Arch::AArch64 => "arm64",
platforms::Arch::X86 => "x86",
platforms::Arch::X86_64 => "amd64",
arch => panic!("{} is not a supported architecture", arch),
};
archive
.by_name(&format!("wintun/bin/{}/wintun.dll", arch))?
.read_to_end(&mut library)?;
std::fs::write(out_dir.join("wintun.dll"), library)?;
println!("cargo:rerun-if-changed=build.rs");
Ok(())
}
#[cfg(not(windows))]
fn main() {
println!("cargo:rerun-if-changed=build.rs");
}
#[cfg(windows)]
fn assert_content_hash(content: &[u8], hash: [u8; 32]) {
use sha2::digest::Update;
use sha2::Digest;
let computed = sha2::Sha256::new().chain(content).finalize();
assert_eq!(computed.as_slice(), &hash[..]);
}

View file

@ -0,0 +1,53 @@
use libc::{sockaddr_ctl, AF_SYSTEM, AF_SYS_CONTROL};
use std::io::Result;
use std::mem::size_of;
use std::os::unix::io::AsRawFd;
/// Trait to connect to kernel extensions on Apple platforms
///
/// Pulled from XNU source: https://github.com/apple/darwin-xnu/blob/main/bsd/sys/kern_control.h
pub trait SysControlSocket {
fn resolve(&self, name: &str, index: u32) -> Result<socket2::SockAddr>;
}
impl SysControlSocket for socket2::Socket {
fn resolve(&self, name: &str, index: u32) -> Result<socket2::SockAddr> {
let mut info = sys::ctl_info {
ctl_id: 0,
ctl_name: [0; 96],
};
info.ctl_name[..name.len()].copy_from_slice(name.as_bytes());
unsafe { sys::resolve_ctl_info(self.as_raw_fd(), &mut info as *mut sys::ctl_info)? };
let (_, addr) = unsafe {
socket2::SockAddr::init(|addr_storage, len| {
*len = size_of::<sockaddr_ctl>() as u32;
let mut addr: &mut sockaddr_ctl = &mut *addr_storage.cast();
addr.sc_len = *len as u8;
addr.sc_family = AF_SYSTEM as u8;
addr.ss_sysaddr = AF_SYS_CONTROL as u16;
addr.sc_id = info.ctl_id;
addr.sc_unit = index;
Ok(())
})
}?;
Ok(addr)
}
}
mod sys {
use nix::ioctl_readwrite;
const MAX_KCTL_NAME: usize = 96;
#[repr(C)]
pub struct ctl_info {
pub ctl_id: u32,
pub ctl_name: [u8; MAX_KCTL_NAME],
}
ioctl_readwrite!(resolve_ctl_info, b'N', 3, ctl_info);
}

90
tun/src/apple/mod.rs Normal file
View file

@ -0,0 +1,90 @@
use socket2::SockAddr;
use std::io::Result;
use std::os::unix::io::{AsRawFd, IntoRawFd, RawFd};
mod kern_control;
mod queue;
pub use queue::TunQueue;
use crate::syscall;
use crate::unix::copy_if_name;
use kern_control::SysControlSocket;
pub struct TunInterface {
socket: socket2::Socket,
}
impl TunInterface {
pub fn new() -> Result<TunInterface> {
TunInterface::connect(None)
}
fn connect(addr: Option<SockAddr>) -> Result<TunInterface> {
use socket2::{Domain, Protocol, Socket, Type};
let socket = Socket::new(
Domain::from(libc::AF_SYSTEM),
Type::DGRAM,
Some(Protocol::from(libc::SYSPROTO_CONTROL)),
)?;
let addr = match addr {
Some(addr) => addr,
None => socket.resolve(sys::UTUN_CONTROL_NAME, 0)?,
};
socket.connect(&addr)?;
Ok(TunInterface { socket })
}
pub fn name(&self) -> Result<String> {
let mut buf = [0u8; libc::IFNAMSIZ];
let mut len = buf.len() as libc::socklen_t;
syscall!(getsockopt(
self.as_raw_fd(),
libc::SYSPROTO_CONTROL,
sys::UTUN_OPT_IFNAME,
buf.as_mut_ptr() as *mut libc::c_void,
&mut len,
))?;
let name = copy_if_name(buf);
Ok(name)
}
pub fn queue(&self) -> Result<TunQueue> {
todo!()
}
}
impl AsRawFd for TunInterface {
fn as_raw_fd(&self) -> RawFd {
self.socket.as_raw_fd()
}
}
impl IntoRawFd for TunInterface {
fn into_raw_fd(self) -> RawFd {
self.socket.into_raw_fd()
}
}
mod sys {
pub const UTUN_CONTROL_NAME: &str = "com.apple.net.utun_control";
pub const UTUN_OPT_IFNAME: libc::c_int = 2;
/// Copied from https://github.com/rust-lang/socket2/blob/61314a231f73964b3db969ef72c0e9479df320f3/src/sys/unix.rs#L168-L178
/// getsockopt is not exposed by socket2
#[macro_export]
macro_rules! syscall {
($fn: ident ( $($arg: expr),* $(,)* ) ) => {{
#[allow(unused_unsafe)]
let res = unsafe { libc::$fn($($arg, )*) };
if res == -1 {
Err(std::io::Error::last_os_error())
} else {
Ok(res)
}
}};
}
}

17
tun/src/apple/queue.rs Normal file
View file

@ -0,0 +1,17 @@
use std::os::unix::io::{AsRawFd, IntoRawFd, RawFd};
pub struct TunQueue {
socket: socket2::Socket,
}
impl AsRawFd for TunQueue {
fn as_raw_fd(&self) -> RawFd {
self.socket.as_raw_fd()
}
}
impl IntoRawFd for TunQueue {
fn into_raw_fd(self) -> RawFd {
self.socket.into_raw_fd()
}
}

16
tun/src/lib.rs Normal file
View file

@ -0,0 +1,16 @@
#[cfg(target_vendor = "apple")]
#[path = "apple/mod.rs"]
mod imp;
#[cfg(target_os = "linux")]
#[path = "linux.rs"]
mod imp;
#[cfg(target_os = "windows")]
#[path = "windows/mod.rs"]
mod imp;
#[cfg(any(target_os = "linux", target_vendor = "apple"))]
pub(crate) mod unix;
pub use imp::{TunInterface, TunQueue};

56
tun/src/linux.rs Normal file
View file

@ -0,0 +1,56 @@
use std::fs::OpenOptions;
use std::io::Result;
use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd};
use crate::unix::copy_if_name;
pub struct TunInterface {
inner: socket2::Socket,
}
impl TunInterface {
pub fn new() -> Result<TunInterface> {
let file = OpenOptions::new()
.read(true)
.write(true)
.open("/dev/net/tun")?;
let mut iff = libc::ifreq {
ifr_name: [0; libc::IFNAMSIZ],
ifr_ifru: libc::__c_anonymous_ifr_ifru {
ifru_flags: (libc::IFF_TUN | libc::IFF_TUN_EXCL | libc::IFF_NO_PI) as i16,
},
};
unsafe { sys::tun_set_iff(file.as_raw_fd(), &mut iff)? };
let inner = unsafe { socket2::Socket::from_raw_fd(file.into_raw_fd()) };
Ok(TunInterface { inner })
}
pub fn name(&self) -> Result<String> {
let mut iff = libc::ifreq {
ifr_name: [0; libc::IFNAMSIZ],
ifr_ifru: libc::__c_anonymous_ifr_ifru { ifru_flags: 0 },
};
unsafe { sys::tun_get_iff(self.inner.as_raw_fd(), &mut iff)? };
let name = copy_if_name(iff.ifr_name);
Ok(name)
}
}
mod sys {
use nix::{ioctl_read_bad, ioctl_write_ptr_bad, request_code_read, request_code_write};
use std::mem::size_of;
ioctl_write_ptr_bad!(
tun_set_iff,
request_code_write!(b'T', 202, size_of::<libc::c_int>()),
libc::ifreq
);
ioctl_read_bad!(
tun_get_iff,
request_code_read!(b'T', 210, size_of::<libc::c_int>()),
libc::ifreq
);
}

1
tun/src/queue.rs Normal file
View file

@ -0,0 +1 @@

11
tun/src/unix.rs Normal file
View file

@ -0,0 +1,11 @@
use std::ffi::CStr;
pub fn copy_if_name(buf: [u8; libc::IFNAMSIZ]) -> String {
// TODO: Switch to `CStr::from_bytes_until_nul` when stabilized
unsafe {
CStr::from_ptr(buf.as_ptr() as *const _)
.to_str()
.unwrap()
.to_string()
}
}

44
tun/src/windows/mod.rs Normal file
View file

@ -0,0 +1,44 @@
use std::io::Result;
use std::ptr;
use widestring::{u16cstr, U16CString};
pub struct TunInterface {
wintun: sys::wintun,
handle: sys::WINTUN_ADAPTER_HANDLE,
name: String,
}
impl TunInterface {
pub fn new() -> Result<TunInterface> {
let name = U16CString::from(u16cstr!("ConradNet"));
let wintun = sys::wintun::default();
let handle =
unsafe { wintun.WintunCreateAdapter(name.as_ptr(), name.as_ptr(), ptr::null()) };
Ok(TunInterface {
wintun,
handle,
name: String::from("ConradNet"),
})
}
pub fn name(&self) -> String {
self.name.clone()
}
}
impl Drop for TunInterface {
fn drop(&mut self) {
unsafe { self.wintun.WintunCloseAdapter(self.handle) }
}
}
pub(crate) mod sys {
#![allow(dead_code, non_camel_case_types, non_snake_case)]
include!(concat!(env!("OUT_DIR"), "/wintun.rs"));
impl Default for wintun {
fn default() -> Self {
unsafe { wintun::new(format!("{}/wintun.dll", env!("OUT_DIR"))).unwrap() }
}
}
}

19
tun/src/windows/queue.rs Normal file
View file

@ -0,0 +1,19 @@
use crate::TunInterface;
use std::os::unix::io::{AsRawFd, IntoRawFd, RawFd};
pub struct TunQueue {
inner: crate::windows::sys::
inner: socket2::Socket,
}
impl AsRawFd for TunQueue {
fn as_raw_fd(&self) -> RawFd {
self.socket.as_raw_fd()
}
}
impl IntoRawFd for TunQueue {
fn into_raw_fd(self) -> RawFd {
self.socket.into_raw_fd()
}
}