diff --git a/.github/actions/archive/action.yml b/.github/actions/archive/action.yml index c34bd3c..e49eb0d 100644 --- a/.github/actions/archive/action.yml +++ b/.github/actions/archive/action.yml @@ -26,18 +26,18 @@ runs: run: | echo "${{ inputs.app-store-key }}" > AuthKey_${{ inputs.app-store-key-id }}.p8 - xcodebuild archive \ + xcodebuild clean archive \ -allowProvisioningUpdates \ -allowProvisioningDeviceRegistration \ + -skipPackagePluginValidation \ + -skipMacroValidation \ + -onlyUsePackageVersionsFromResolvedFile \ -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 diff --git a/.github/actions/build-for-testing/action.yml b/.github/actions/build-for-testing/action.yml index ce91b43..185c4ab 100644 --- a/.github/actions/build-for-testing/action.yml +++ b/.github/actions/build-for-testing/action.yml @@ -18,32 +18,36 @@ inputs: runs: using: composite steps: - - name: Cache Swift Packages + - name: Xcode Cache uses: actions/cache@v3 with: path: | Apple/PackageCache Apple/SourcePackages + Apple/DerivedData key: ${{ runner.os }}-${{ inputs.scheme }}-${{ hashFiles('**/Package.resolved') }} restore-keys: | + ${{ runner.os }}-${{ inputs.scheme }}-${{ hashFiles('**/Package.resolved') }} ${{ runner.os }}-${{ inputs.scheme }}- + ${{ runner.os }}- - name: Build shell: bash working-directory: Apple run: | echo "${{ inputs.app-store-key }}" > AuthKey_${{ inputs.app-store-key-id }}.p8 - xcodebuild clean build-for-testing \ + xcodebuild build-for-testing \ -allowProvisioningUpdates \ -allowProvisioningDeviceRegistration \ + -skipPackagePluginValidation \ + -skipMacroValidation \ + -onlyUsePackageVersionsFromResolvedFile \ -authenticationKeyID ${{ inputs.app-store-key-id }} \ -authenticationKeyIssuerID ${{ inputs.app-store-key-issuer-id }} \ -authenticationKeyPath "${PWD}/AuthKey_${{ inputs.app-store-key-id }}.p8" \ - -onlyUsePackageVersionsFromResolvedFile \ -clonedSourcePackagesDirPath SourcePackages \ -packageCachePath $PWD/PackageCache \ - -skipPackagePluginValidation \ - -skipMacroValidation \ + -derivedDataPath $PWD/DerivedData \ -scheme '${{ inputs.scheme }}' \ -destination '${{ inputs.destination }}' \ -resultBundlePath BuildResults.xcresult diff --git a/.github/actions/download-profiles/action.yml b/.github/actions/download-profiles/action.yml new file mode 100644 index 0000000..32b615c --- /dev/null +++ b/.github/actions/download-profiles/action.yml @@ -0,0 +1,30 @@ +name: Download Provisioning Profiles +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 +runs: + using: composite + steps: + - shell: bash + env: + FASTLANE_OPT_OUT_USAGE: 'YES' + run: | + APP_STORE_KEY=$(echo "${{ inputs.app-store-key }}" | jq -sR .) + cat << EOF > api-key.json + { + "key_id": "${{ inputs.app-store-key-id }}", + "issuer_id": "${{ inputs.app-store-key-issuer-id }}", + "key": $APP_STORE_KEY + } + EOF + + fastlane sigh download_all --api_key_path api-key.json + + rm -rf api-key.json diff --git a/.github/actions/export/action.yml b/.github/actions/export/action.yml index bf007a7..75b748f 100644 --- a/.github/actions/export/action.yml +++ b/.github/actions/export/action.yml @@ -1,4 +1,4 @@ -name: Notarize +name: Export inputs: app-store-key: description: App Store key in PEM PKCS#8 format @@ -12,11 +12,8 @@ inputs: 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. + export-options: + description: The export options in JSON format required: true export-path: description: The path to export the archive to @@ -24,19 +21,20 @@ inputs: runs: using: composite steps: - - id: notarize - shell: bash + - 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 - + echo '${{ inputs.export-options }}' | plutil -convert xml1 -o ExportOptions.plist - xcodebuild \ -exportArchive \ -allowProvisioningUpdates \ -allowProvisioningDeviceRegistration \ + -skipPackagePluginValidation \ + -skipMacroValidation \ + -onlyUsePackageVersionsFromResolvedFile \ -authenticationKeyID ${{ inputs.app-store-key-id }} \ -authenticationKeyIssuerID ${{ inputs.app-store-key-issuer-id }} \ -authenticationKeyPath "${PWD}/AuthKey_${{ inputs.app-store-key-id }}.p8" \ diff --git a/.github/actions/notarize/action.yml b/.github/actions/notarize/action.yml new file mode 100644 index 0000000..efd2159 --- /dev/null +++ b/.github/actions/notarize/action.yml @@ -0,0 +1,25 @@ +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 +runs: + using: composite + steps: + - id: notarize + shell: bash + working-directory: Apple + run: | + echo "${{ inputs.app-store-key }}" > AuthKey_${{ inputs.app-store-key-id }}.p8 + + ditto -c -k --keepParent Release/Burrow.app Upload.zip + xcrun notarytool submit --wait --issuer ${{ inputs.app-store-key-issuer-id }} --key-id ${{ inputs.app-store-key-id }} --key "${PWD}/AuthKey_${{ inputs.app-store-key-id }}.p8" Upload.zip + xcrun stapler staple Release/Burrow.app + + rm -rf AuthKey_${{ inputs.app-store-key-id }}.p8 Release diff --git a/.github/actions/test-without-building/action.yml b/.github/actions/test-without-building/action.yml index 5903d07..a097d4a 100644 --- a/.github/actions/test-without-building/action.yml +++ b/.github/actions/test-without-building/action.yml @@ -18,9 +18,6 @@ inputs: 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: | @@ -28,10 +25,10 @@ runs: -scheme '${{ inputs.scheme }}' \ -destination '${{ inputs.destination }}' \ ${{ inputs.test-plan && '-testPlan ' }}${{ inputs.test-plan }} \ - -resultBundlePath "${{ inputs.artifact-prefix }}-${{ steps.vars.outputs.sha_short }}.xcresult" + -resultBundlePath "${{ inputs.artifact-prefix }}.xcresult" - uses: kishikawakatsumi/xcresulttool@v1 if: always() with: - path: Apple/${{ inputs.artifact-prefix }}-${{ steps.vars.outputs.sha_short }}.xcresult + path: Apple/${{ inputs.artifact-prefix }}.xcresult title: ${{ inputs.check-name }} show-passed-tests: false diff --git a/.github/workflows/build-appimage.yml b/.github/workflows/build-appimage.yml index ef5c525..bd29b07 100644 --- a/.github/workflows/build-appimage.yml +++ b/.github/workflows/build-appimage.yml @@ -1,8 +1,14 @@ name: Build AppImage on: push: - branches: [main] + branches: + - main pull_request: + branches: + - "*" +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true jobs: appimage: name: Build AppImage @@ -17,7 +23,7 @@ jobs: docker cp temp:/app/burrow-gtk/build-appimage/Burrow-x86_64.AppImage . docker rm temp - uses: actions/upload-artifact@v4 + name: Upload to GitHub with: name: AppImage path: Burrow-x86_64.AppImage - diff --git a/.github/workflows/build-apple.yml b/.github/workflows/build-apple.yml index da0f56a..7ae8c4c 100644 --- a/.github/workflows/build-apple.yml +++ b/.github/workflows/build-apple.yml @@ -1,7 +1,7 @@ -name: Apple Build +name: Build Apple Apps on: push: - branches: + branches: - main pull_request: branches: @@ -12,7 +12,7 @@ concurrency: jobs: build: name: Build App (${{ matrix.platform }}) - runs-on: macos-13 + runs-on: macos-14 strategy: fail-fast: false matrix: @@ -24,7 +24,7 @@ jobs: rust-targets: - aarch64-apple-ios - scheme: App - destination: platform=iOS Simulator,OS=17.2,name=iPhone 15 Pro + destination: platform=iOS Simulator,OS=18.0,name=iPhone 15 Pro platform: iOS Simulator sdk-name: iphonesimulator rust-targets: @@ -38,7 +38,8 @@ jobs: - x86_64-apple-darwin - aarch64-apple-darwin env: - DEVELOPER_DIR: /Applications/Xcode_15.2.app/Contents/Developer + DEVELOPER_DIR: /Applications/Xcode_16.0.app/Contents/Developer + PROTOC_PATH: /opt/homebrew/bin/protoc steps: - name: Checkout uses: actions/checkout@v3 @@ -53,8 +54,10 @@ jobs: - name: Install Rust uses: dtolnay/rust-toolchain@stable with: - toolchain: stable targets: ${{ join(matrix.rust-targets, ', ') }} + - name: Install Protobuf + shell: bash + run: brew install protobuf - name: Build id: build uses: ./.github/actions/build-for-testing @@ -64,7 +67,7 @@ jobs: 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 + - name: Run Unit Tests if: ${{ matrix.xcode-unit-test != '' }} continue-on-error: true uses: ./.github/actions/test-without-building @@ -74,7 +77,7 @@ jobs: test-plan: ${{ matrix.xcode-unit-test }} artifact-prefix: unit-tests-${{ matrix.sdk-name }} check-name: Xcode Unit Tests (${{ matrix.platform }}) - - name: Xcode UI Test + - name: Run UI Tests if: ${{ matrix.xcode-ui-test != '' }} continue-on-error: true uses: ./.github/actions/test-without-building @@ -83,4 +86,4 @@ jobs: destination: ${{ matrix.destination }} test-plan: ${{ matrix.xcode-ui-test }} artifact-prefix: ui-tests-${{ matrix.sdk-name }} - check-name: Xcode UI Tests (${{ matrix.platform }}) + check-name: Xcode UI Tests (${{ matrix.platform }}) \ No newline at end of file diff --git a/.github/workflows/build-docker.yml b/.github/workflows/build-docker.yml index 1ce7a9a..6a3dae1 100644 --- a/.github/workflows/build-docker.yml +++ b/.github/workflows/build-docker.yml @@ -6,6 +6,9 @@ on: pull_request: branches: - "*" +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true jobs: build: name: Build Docker Image @@ -33,6 +36,7 @@ jobs: images: ghcr.io/${{ github.repository }} tags: | type=sha + type=match,pattern=builds/(.*),group=1 type=raw,value=latest,enable={{is_default_branch}} - name: Build and Push uses: docker/build-push-action@v4 diff --git a/.github/workflows/build-rpm.yml b/.github/workflows/build-rpm.yml index fd5837c..029bf16 100644 --- a/.github/workflows/build-rpm.yml +++ b/.github/workflows/build-rpm.yml @@ -1,16 +1,11 @@ +on: workflow_dispatch name: Build RPM -on: - push: - branches: [ "main" ] - pull_request: - branches: - - "*" jobs: build: name: Build RPM runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: Swatinem/rust-cache@v2 - name: Install RPM run: cargo install cargo-generate-rpm @@ -20,4 +15,3 @@ jobs: strip -s target/release/burrow - name: Build RPM run: cargo generate-rpm -p burrow - diff --git a/.github/workflows/build-rust.yml b/.github/workflows/build-rust.yml index 4c3782a..95fc628 100644 --- a/.github/workflows/build-rust.yml +++ b/.github/workflows/build-rust.yml @@ -1,4 +1,4 @@ -name: Rust Build +name: Build Rust Crate on: push: branches: @@ -21,15 +21,21 @@ jobs: - x86_64-unknown-linux-gnu targets: - aarch64-unknown-linux-gnu - - os: macos-12 - platform: macOS + - os: macos-13 + platform: macOS (Intel) + xcode: /Applications/Xcode_15.2.app test-targets: - x86_64-apple-darwin targets: + - x86_64-apple-ios + - os: macos-14 + platform: macOS + xcode: /Applications/Xcode_16.0.app + test-targets: - aarch64-apple-darwin + targets: - aarch64-apple-ios - aarch64-apple-ios-sim - - x86_64-apple-ios - os: windows-2022 platform: Windows test-targets: @@ -38,10 +44,11 @@ jobs: - aarch64-pc-windows-msvc runs-on: ${{ matrix.os }} env: - DEVELOPER_DIR: /Applications/Xcode_14.2.app/Contents/Developer + DEVELOPER_DIR: ${{ matrix.xcode }}/Contents/Developer CARGO_INCREMENTAL: 0 CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: aarch64-linux-gnu-gcc RUST_BACKTRACE: short + PROTOC_VERSION: 3.25.1 steps: - name: Checkout uses: actions/checkout@v3 @@ -54,10 +61,14 @@ jobs: run: | sudo apt-get update sudo apt-get install -y ${{ join(matrix.packages, ' ') }} - - name: Install Windows Deps + - name: Configure LLVM if: matrix.os == 'windows-2022' shell: bash run: echo "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Tools\Llvm\x64\bin" >> $GITHUB_PATH + - name: Install protoc + uses: taiki-e/install-action@v2 + with: + tool: protoc@${{ env.PROTOC_VERSION }} - name: Install Rust uses: dtolnay/rust-toolchain@stable with: @@ -71,4 +82,4 @@ jobs: run: cargo build --verbose --workspace --all-features --target ${{ join(matrix.targets, ' --target ') }} --target ${{ join(matrix.test-targets, ' --target ') }} - name: Test shell: bash - run: cargo test --verbose --workspace --all-features --target ${{ join(matrix.test-targets, ' --target ') }} + run: cargo test --verbose --workspace --all-features --target ${{ join(matrix.test-targets, ' --target ') }} \ No newline at end of file diff --git a/.github/workflows/lint-git.yml b/.github/workflows/lint-git.yml index aefe199..2f7c72e 100644 --- a/.github/workflows/lint-git.yml +++ b/.github/workflows/lint-git.yml @@ -8,13 +8,14 @@ jobs: 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" + - name: Checkout + uses: actions/checkout@v4 + with: + ref: ${{ github.event.pull_request.head.sha }} + fetch-depth: 0 + - name: Install + shell: bash + run: python -m pip install gitlint + - name: Lint + shell: bash + run: gitlint --commits "${{ github.event.pull_request.base.sha }}..HEAD" diff --git a/.github/workflows/lint-swift.yml b/.github/workflows/lint-swift.yml index 7e62afd..857f575 100644 --- a/.github/workflows/lint-swift.yml +++ b/.github/workflows/lint-swift.yml @@ -1,8 +1,5 @@ name: Swift Lint on: - push: - branches: - - main pull_request: branches: - "*" @@ -14,8 +11,6 @@ jobs: image: ghcr.io/realm/swiftlint:latest steps: - name: Checkout - uses: actions/checkout@v3 - with: - ssh-key: ${{ secrets.DEPLOY_KEY }} + uses: actions/checkout@v4 - name: Lint - run: swiftlint lint --reporter github-actions-logging + run: swiftlint lint --strict --reporter github-actions-logging diff --git a/.github/workflows/release-apple.yml b/.github/workflows/release-apple.yml index 3ea185d..c869d6a 100644 --- a/.github/workflows/release-apple.yml +++ b/.github/workflows/release-apple.yml @@ -1,65 +1,119 @@ -name: Build Apple Release +name: Release (Apple) on: release: types: - created jobs: build: - name: Build ${{ matrix.configuration['platform'] }} Release - runs-on: macos-13 + name: Build ${{ matrix.platform }} Release + runs-on: macos-14 + permissions: + contents: write 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 + include: + - platform: iOS + rust-targets: + - aarch64-apple-ios + - platform: macOS + rust-targets: + - x86_64-apple-darwin + - aarch64-apple-darwin env: - DEVELOPER_DIR: /Applications/Xcode_15.2.app/Contents/Developer + DEVELOPER_DIR: /Applications/Xcode_16.0.app/Contents/Developer + PROTOC_PATH: /opt/homebrew/bin/protoc steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: - ssh-key: ${{ secrets.DEPLOY_KEY }} - submodules: recursive + fetch-depth: 0 - name: Import Certificate uses: ./.github/actions/import-cert with: certificate: ${{ secrets.DEVELOPER_CERT }} password: ${{ secrets.DEVELOPER_CERT_PASSWORD }} + - name: Download Provisioning Profiles + uses: ./.github/actions/download-profiles + with: + 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: Install Provisioning Profiles + shell: bash + run: | + mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles/ + cp -f Apple/Profiles/* ~/Library/MobileDevice/Provisioning\ Profiles/ + - name: Install Rust + uses: dtolnay/rust-toolchain@stable + with: + targets: ${{ join(matrix.rust-targets, ', ') }} + - name: Install Protobuf + shell: bash + run: brew install protobuf + - name: Configure Version + id: version + shell: bash + run: echo "BUILD_NUMBER=$(Tools/version.sh)" >> $GITHUB_OUTPUT - name: Archive uses: ./.github/actions/archive with: - scheme: ${{ matrix.configuration['scheme'] }} - destination: ${{ matrix.configuration['destination'] }} + scheme: App + destination: generic/platform=${{ matrix.platform }} 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 + - name: Export uses: ./.github/actions/export with: - method: ${{ matrix.configuration['method'] }} + method: ${{ matrix.platform == 'macOS' && 'developer-id' || 'ad-hoc' }} 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-options: | + {"teamID":"P6PV2R9443","destination":"export","method":"developer-id","provisioningProfiles":{"com.hackclub.burrow":"Burrow Developer ID","com.hackclub.burrow.network":"Burrow Network Developer ID"},"signingCertificate":"Developer ID Application","signingStyle":"manual"} export-path: Release - - name: Compress - if: ${{ matrix.configuration['platform'] == 'macOS' }} + - name: Notarize + if: ${{ matrix.platform == 'macOS' }} + uses: ./.github/actions/notarize + with: + 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: Compress (iOS) + if: ${{ matrix.platform == 'iOS' }} 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 + run: | + cp Apple/Release/Burrow.ipa Burrow.ipa + aa archive -a lzma -b 8m -d Apple -subdir Burrow.xcarchive -o Burrow-${{ matrix.platform }}.xcarchive.aar + rm -rf Apple/Release + - name: Compress (macOS) + if: ${{ matrix.platform == 'macOS' }} + shell: bash + run: | + aa archive -a lzma -b 8m -d Apple/Release -subdir Burrow.app -o Burrow.app.aar + aa archive -a lzma -b 8m -d Apple -subdir Burrow.xcarchive -o Burrow-${{ matrix.platform }}.xcarchive.aar + rm -rf Apple/Release + - name: Upload to GitHub + uses: SierraSoftworks/gh-releases@v1.0.7 with: token: ${{ secrets.GITHUB_TOKEN }} - overwrite: 'false' - files: ${{ matrix.configuration['artifact-file'] }} + release_tag: ${{ github.ref_name }} + overwrite: 'true' + files: | + ${{ matrix.platform == 'macOS' && 'Burrow.aap.aar' || 'Burrow.ipa' }} + Burrow-${{ matrix.platform }}.xcarchive.aar + - name: Upload to App Store Connect + if: ${{ matrix.platform == 'iOS' }} + uses: ./.github/actions/export + with: + 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-options: | + {"method": "app-store", "destination": "upload"} + export-path: Release diff --git a/.github/workflows/release-if-needed.yaml b/.github/workflows/release-if-needed.yaml new file mode 100644 index 0000000..79f0d63 --- /dev/null +++ b/.github/workflows/release-if-needed.yaml @@ -0,0 +1,23 @@ +name: Create Release If Needed +on: + workflow_dispatch: + schedule: + - cron: '0 10 * * *' +concurrency: + group: ${{ github.workflow }} +jobs: + create: + name: Create Release If Needed + runs-on: ubuntu-latest + env: + GH_TOKEN: ${{ github.token }} + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - shell: bash + run: | + if [[ $(Tools/version.sh status) == "dirty" ]]; then + gh workflow run release-now.yml + fi diff --git a/.github/workflows/release-linux.yml b/.github/workflows/release-linux.yml new file mode 100644 index 0000000..7db9bcf --- /dev/null +++ b/.github/workflows/release-linux.yml @@ -0,0 +1,29 @@ +name: Release (Linux) +on: + release: + types: + - created +jobs: + appimage: + name: Build AppImage + runs-on: ubuntu-latest + container: docker + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Build AppImage + run: | + docker build -t appimage-builder . -f burrow-gtk/build-aux/Dockerfile + docker create --name temp appimage-builder + docker cp temp:/app/burrow-gtk/build-appimage/Burrow-x86_64.AppImage . + docker rm temp + - name: Attach Artifacts + uses: SierraSoftworks/gh-releases@v1.0.7 + with: + token: ${{ secrets.GITHUB_TOKEN }} + release_tag: ${{ github.ref_name }} + overwrite: "true" + files: | + Burrow-x86_64.AppImage diff --git a/.github/workflows/release-now.yml b/.github/workflows/release-now.yml new file mode 100644 index 0000000..229f6c9 --- /dev/null +++ b/.github/workflows/release-now.yml @@ -0,0 +1,17 @@ +name: Create Release +on: workflow_dispatch +concurrency: + group: ${{ github.workflow }} +jobs: + create: + env: + GH_TOKEN: ${{ secrets.GH_RELEASE_TOKEN }} + name: Create Release + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - shell: bash + run: Tools/version.sh increment diff --git a/.gitignore b/.gitignore index dc886ed..1b300b4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,17 @@ # Xcode xcuserdata +# Swift +Apple/Package/.swiftpm/ + # Rust target/ +.env .DS_STORE .idea/ + +tmp/ + +*.db +*.sock \ No newline at end of file diff --git a/.swiftlint.yml b/.swiftlint.yml index 22ef035..8efc85e 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -30,7 +30,6 @@ opt_in_rules: - function_default_parameter_at_end - ibinspectable_in_extension - identical_operands -- implicitly_unwrapped_optional - indentation_width - joined_default_parameter - last_where diff --git a/.vscode/settings.json b/.vscode/settings.json index 3c714be..eb85504 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,18 +1,26 @@ { - "files.autoSave": "onFocusChange", - "files.defaultLanguage": "rust", - "editor.formatOnPaste": true, - "editor.formatOnSave": true, - "files.trimTrailingWhitespace": true, - "editor.suggest.preview": true, - "editor.acceptSuggestionOnEnter": "on", - "rust-analyzer.restartServerOnConfigChange": true, - "rust-analyzer.cargo.features": "all", - "rust-analyzer.rustfmt.extraArgs": [ - "+nightly" - ], - "[rust]": { - "editor.defaultFormatter": "rust-lang.rust-analyzer", - }, - "rust-analyzer.inlayHints.typeHints.enable": false -} \ No newline at end of file + "files.autoSave": "onFocusChange", + "files.defaultLanguage": "rust", + "editor.formatOnPaste": true, + "editor.formatOnSave": true, + "files.trimTrailingWhitespace": true, + "editor.suggest.preview": true, + "editor.acceptSuggestionOnEnter": "on", + "rust-analyzer.restartServerOnConfigChange": true, + "rust-analyzer.cargo.features": "all", + "rust-analyzer.rustfmt.extraArgs": ["+nightly"], + "[rust]": { + "editor.defaultFormatter": "rust-lang.rust-analyzer" + }, + "rust-analyzer.inlayHints.typeHints.enable": false, + "rust-analyzer.linkedProjects": [ + "./burrow/Cargo.toml" + ], + "[yaml]": { + "editor.insertSpaces": true, + "editor.tabSize": 2, + "editor.autoIndent": "advanced", + "diffEditor.ignoreTrimWhitespace": false, + "editor.formatOnSave": false + } +} diff --git a/Apple/App/App-iOS.entitlements b/Apple/App/App-iOS.entitlements index 02ee960..53fcbb7 100644 --- a/Apple/App/App-iOS.entitlements +++ b/Apple/App/App-iOS.entitlements @@ -2,6 +2,11 @@ + com.apple.developer.associated-domains + + applinks:burrow.rs?mode=developer + webcredentials:burrow.rs?mode=developer + com.apple.developer.networking.networkextension packet-tunnel-provider diff --git a/Apple/App/App-macOS.entitlements b/Apple/App/App-macOS.entitlements index 02ee960..53fcbb7 100644 --- a/Apple/App/App-macOS.entitlements +++ b/Apple/App/App-macOS.entitlements @@ -2,6 +2,11 @@ + com.apple.developer.associated-domains + + applinks:burrow.rs?mode=developer + webcredentials:burrow.rs?mode=developer + com.apple.developer.networking.networkextension packet-tunnel-provider diff --git a/Apple/App/AppDelegate.swift b/Apple/App/AppDelegate.swift index 6085d85..0ea93f4 100644 --- a/Apple/App/AppDelegate.swift +++ b/Apple/App/AppDelegate.swift @@ -1,9 +1,10 @@ #if os(macOS) import AppKit +import BurrowUI import SwiftUI +@main @MainActor -@NSApplicationMain class AppDelegate: NSObject, NSApplicationDelegate { private let quitItem: NSMenuItem = { let quitItem = NSMenuItem( diff --git a/Apple/App/Assets.xcassets/AppIcon.appiconset/Contents.json b/Apple/App/Assets.xcassets/AppIcon.appiconset/Contents.json deleted file mode 100644 index 532cd72..0000000 --- a/Apple/App/Assets.xcassets/AppIcon.appiconset/Contents.json +++ /dev/null @@ -1,63 +0,0 @@ -{ - "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 - } -} diff --git a/Apple/App/BurrowApp.swift b/Apple/App/BurrowApp.swift index 21ebf84..838ef54 100644 --- a/Apple/App/BurrowApp.swift +++ b/Apple/App/BurrowApp.swift @@ -1,6 +1,7 @@ +#if !os(macOS) +import BurrowUI import SwiftUI -#if !os(macOS) @MainActor @main struct BurrowApp: App { diff --git a/Apple/App/BurrowView.swift b/Apple/App/BurrowView.swift deleted file mode 100644 index b78b1e1..0000000 --- a/Apple/App/BurrowView.swift +++ /dev/null @@ -1,26 +0,0 @@ -import SwiftUI - -struct BurrowView: View { - var body: some View { - NavigationStack { - VStack { - NetworkCarouselView() - Spacer() - TunnelStatusView() - TunnelButton() - .padding(.bottom) - } - .padding() - .navigationTitle("Networks") - } - } -} - -#if DEBUG -struct NetworkView_Previews: PreviewProvider { - static var previews: some View { - BurrowView() - .environment(\.tunnel, PreviewTunnel()) - } -} -#endif diff --git a/Apple/App/MainMenu.xib b/Apple/App/MainMenu.xib index 8933f30..50ba431 100644 --- a/Apple/App/MainMenu.xib +++ b/Apple/App/MainMenu.xib @@ -1,7 +1,7 @@ - + - + diff --git a/Apple/App/NetworkView.swift b/Apple/App/NetworkView.swift deleted file mode 100644 index 290254c..0000000 --- a/Apple/App/NetworkView.swift +++ /dev/null @@ -1,88 +0,0 @@ -import SwiftUI - -struct NetworkView: View { - var color: Color - var content: () -> Content - - private var gradient: LinearGradient { - LinearGradient( - colors: [ - color.opacity(0.8), - color - ], - startPoint: .init(x: 0.2, y: 0), - endPoint: .init(x: 0.8, y: 1) - ) - } - - var body: some View { - content() - .frame(maxWidth: .infinity, minHeight: 175, maxHeight: 175) - .background( - RoundedRectangle(cornerRadius: 10) - .fill(gradient) - .background( - RoundedRectangle(cornerRadius: 10) - .fill(.white) - ) - ) - .shadow(color: .black.opacity(0.1), radius: 3.0, x: 0, y: 2) - } -} - -struct AddNetworkView: View { - var body: some View { - Text("Add Network") - .frame(maxWidth: .infinity, minHeight: 175, maxHeight: 175) - .background( - RoundedRectangle(cornerRadius: 10) - .stroke(style: .init(lineWidth: 2, dash: [6])) - ) - } -} - -extension NetworkView where Content == AnyView { - init(network: any Network) { - color = network.backgroundColor - content = { AnyView(network.label) } - } -} - -struct NetworkCarouselView: View { - var networks: [any Network] = [ - HackClub(id: "1"), - HackClub(id: "2"), - WireGuard(id: "4"), - HackClub(id: "5"), - ] - - var body: some View { - ScrollView(.horizontal) { - LazyHStack { - ForEach(networks, id: \.id) { network in - NetworkView(network: network) - .containerRelativeFrame(.horizontal, count: 10, span: 7, spacing: 0, alignment: .center) - .scrollTransition(.interactive, axis: .horizontal) { content, phase in - content - .scaleEffect(1.0 - abs(phase.value) * 0.1) - } - } - AddNetworkView() - } - .scrollTargetLayout() - } - .scrollClipDisabled() - .scrollIndicators(.hidden) - .defaultScrollAnchor(.center) - .scrollTargetBehavior(.viewAligned) - .containerRelativeFrame(.horizontal) - } -} - -#if DEBUG -struct NetworkCarouselView_Previews: PreviewProvider { - static var previews: some View { - NetworkCarouselView() - } -} -#endif diff --git a/Apple/App/Networks/Network.swift b/Apple/App/Networks/Network.swift deleted file mode 100644 index d441d24..0000000 --- a/Apple/App/Networks/Network.swift +++ /dev/null @@ -1,10 +0,0 @@ -import SwiftUI - -protocol Network { - associatedtype Label: View - - var id: String { get } - var backgroundColor: Color { get } - - var label: Label { get } -} diff --git a/Apple/App/Tunnel.swift b/Apple/App/Tunnel.swift deleted file mode 100644 index 8db366f..0000000 --- a/Apple/App/Tunnel.swift +++ /dev/null @@ -1,50 +0,0 @@ -import SwiftUI - -protocol Tunnel { - var status: TunnelStatus { get } - - func start() - func stop() - func enable() -} - -enum TunnelStatus: Equatable, Hashable { - case unknown - case permissionRequired - case disabled - case connecting - case connected(Date) - case disconnecting - case disconnected - case reasserting - case invalid - case configurationReadWriteFailed -} - -struct TunnelKey: EnvironmentKey { - static let defaultValue: any Tunnel = NetworkExtensionTunnel() -} - -extension EnvironmentValues { - var tunnel: any Tunnel { - get { self[TunnelKey.self] } - set { self[TunnelKey.self] = newValue } - } -} - -#if DEBUG -@Observable -class PreviewTunnel: Tunnel { - var status: TunnelStatus = .permissionRequired - - func start() { - status = .connected(.now) - } - func stop() { - status = .disconnected - } - func enable() { - status = .disconnected - } -} -#endif diff --git a/Apple/Burrow.xcodeproj/project.pbxproj b/Apple/Burrow.xcodeproj/project.pbxproj index 8717a30..617b88f 100644 --- a/Apple/Burrow.xcodeproj/project.pbxproj +++ b/Apple/Burrow.xcodeproj/project.pbxproj @@ -7,51 +7,50 @@ objects = { /* Begin PBXBuildFile section */ - 0B28F1562ABF463A000D44B0 /* DataTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B28F1552ABF463A000D44B0 /* DataTypes.swift */; }; - 0B46E8E02AC918CA00BA2A3C /* Client.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B46E8DF2AC918CA00BA2A3C /* Client.swift */; }; - 43AA26D82A10004900F14CE6 /* MenuItemToggleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43AA26D72A10004900F14CE6 /* MenuItemToggleView.swift */; }; - D00117312B2FFFC900D87C25 /* NWConnection+Async.swift in Sources */ = {isa = PBXBuildFile; fileRef = D00117302B2FFFC900D87C25 /* NWConnection+Async.swift */; }; - D00117332B3001A400D87C25 /* NewlineProtocolFramer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D00117322B3001A400D87C25 /* NewlineProtocolFramer.swift */; }; - D001173B2B30341C00D87C25 /* Logging.swift in Sources */ = {isa = PBXBuildFile; fileRef = D001173A2B30341C00D87C25 /* Logging.swift */; }; - D00117442B30372900D87C25 /* libBurrowShared.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D00117382B30341C00D87C25 /* libBurrowShared.a */; }; - D00117452B30372C00D87C25 /* libBurrowShared.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D00117382B30341C00D87C25 /* libBurrowShared.a */; }; D00AA8972A4669BC005C8102 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D00AA8962A4669BC005C8102 /* AppDelegate.swift */; }; - D01A79312B81630D0024EC91 /* NetworkView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D01A79302B81630D0024EC91 /* NetworkView.swift */; }; 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, ); }; }; - D032E6522B8A79C20006B8AD /* HackClub.swift in Sources */ = {isa = PBXBuildFile; fileRef = D032E6512B8A79C20006B8AD /* HackClub.swift */; }; - D032E6542B8A79DA0006B8AD /* WireGuard.swift in Sources */ = {isa = PBXBuildFile; fileRef = D032E6532B8A79DA0006B8AD /* WireGuard.swift */; }; + D03383AD2C8E67E300F7C44E /* SwiftProtobuf in Frameworks */ = {isa = PBXBuildFile; productRef = D078F7E22C8DA375008A8CEC /* SwiftProtobuf */; }; + D03383AE2C8E67E300F7C44E /* NIO in Frameworks */ = {isa = PBXBuildFile; productRef = D044EE902C8DAB2000778185 /* NIO */; }; + D03383AF2C8E67E300F7C44E /* NIOConcurrencyHelpers in Frameworks */ = {isa = PBXBuildFile; productRef = D044EE922C8DAB2000778185 /* NIOConcurrencyHelpers */; }; + D03383B02C8E67E300F7C44E /* NIOTransportServices in Frameworks */ = {isa = PBXBuildFile; productRef = D044EE952C8DAB2800778185 /* NIOTransportServices */; }; D05B9F7629E39EEC008CB1F9 /* BurrowApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = D05B9F7529E39EEC008CB1F9 /* BurrowApp.swift */; }; - D05B9F7829E39EEC008CB1F9 /* BurrowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D05B9F7729E39EEC008CB1F9 /* BurrowView.swift */; }; - D05B9F7A29E39EED008CB1F9 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D05B9F7929E39EED008CB1F9 /* Assets.xcassets */; }; - D05EF8C82B81818D0017AB4F /* FloatingButtonStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = D05EF8C72B81818D0017AB4F /* FloatingButtonStyle.swift */; }; - D08252762B5C9FC4005DA378 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08252752B5C9FC4005DA378 /* Constants.swift */; }; - D09150422B9D2AF700BE3CB0 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = D09150412B9D2AF700BE3CB0 /* MainMenu.xib */; }; - D0BCC5FD2A086D4700AD070D /* NetworkExtension+Async.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0BCC5FC2A086D4700AD070D /* NetworkExtension+Async.swift */; }; - D0BCC6082A0981FE00AD070D /* Tunnel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0B98FC629FDC5B5004E7149 /* Tunnel.swift */; }; + D09150422B9D2AF700BE3CB0 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = D09150412B9D2AF700BE3CB0 /* MainMenu.xib */; platformFilters = (macos, ); }; + D0B1D1102C436152004B7823 /* AsyncAlgorithms in Frameworks */ = {isa = PBXBuildFile; productRef = D0B1D10F2C436152004B7823 /* AsyncAlgorithms */; }; D0BCC6092A09A03E00AD070D /* libburrow.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D0BCC6032A09535900AD070D /* libburrow.a */; }; - D0BCC60A2A09A0B800AD070D /* build-rust.sh in Resources */ = {isa = PBXBuildFile; fileRef = D0B98FBF29FD8072004E7149 /* build-rust.sh */; }; - D0FAB5922B818A5900F6A84B /* NetworkExtensionTunnel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0FAB5912B818A5900F6A84B /* NetworkExtensionTunnel.swift */; }; - D0FAB5962B818B2900F6A84B /* TunnelButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0FAB5952B818B2900F6A84B /* TunnelButton.swift */; }; - D0FAB5982B818B8200F6A84B /* TunnelStatusView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0FAB5972B818B8200F6A84B /* TunnelStatusView.swift */; }; - D0FAB59A2B818B9600F6A84B /* Network.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0FAB5992B818B9600F6A84B /* Network.swift */; }; + D0BF09522C8E66F6000D8DEC /* BurrowConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0D4E5622C8D9BF4007F820A /* BurrowConfiguration.framework */; }; + D0BF09552C8E66FD000D8DEC /* BurrowConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0D4E5622C8D9BF4007F820A /* BurrowConfiguration.framework */; }; + D0D4E53A2C8D996F007F820A /* BurrowCore.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = D0D4E5312C8D996F007F820A /* BurrowCore.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + D0D4E56B2C8D9C2F007F820A /* Logging.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0D4E49A2C8D921A007F820A /* Logging.swift */; }; + D0D4E5702C8D9C62007F820A /* BurrowCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0D4E5312C8D996F007F820A /* BurrowCore.framework */; }; + D0D4E5712C8D9C6F007F820A /* HackClub.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0D4E49D2C8D921A007F820A /* HackClub.swift */; }; + D0D4E5722C8D9C6F007F820A /* Network.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0D4E49E2C8D921A007F820A /* Network.swift */; }; + D0D4E5732C8D9C6F007F820A /* WireGuard.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0D4E49F2C8D921A007F820A /* WireGuard.swift */; }; + D0D4E5742C8D9C6F007F820A /* BurrowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0D4E4A22C8D921A007F820A /* BurrowView.swift */; }; + D0D4E5752C8D9C6F007F820A /* FloatingButtonStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0D4E4A32C8D921A007F820A /* FloatingButtonStyle.swift */; }; + D0D4E5762C8D9C6F007F820A /* MenuItemToggleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0D4E4A42C8D921A007F820A /* MenuItemToggleView.swift */; }; + D0D4E5772C8D9C6F007F820A /* NetworkCarouselView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0D4E4A52C8D921A007F820A /* NetworkCarouselView.swift */; }; + D0D4E5782C8D9C6F007F820A /* NetworkExtension+Async.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0D4E4A62C8D921A007F820A /* NetworkExtension+Async.swift */; }; + D0D4E5792C8D9C6F007F820A /* NetworkExtensionTunnel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0D4E4A72C8D921A007F820A /* NetworkExtensionTunnel.swift */; }; + D0D4E57A2C8D9C6F007F820A /* NetworkView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0D4E4A82C8D921A007F820A /* NetworkView.swift */; }; + D0D4E57B2C8D9C6F007F820A /* OAuth2.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0D4E4A92C8D921A007F820A /* OAuth2.swift */; }; + D0D4E57C2C8D9C6F007F820A /* Tunnel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0D4E4AA2C8D921A007F820A /* Tunnel.swift */; }; + D0D4E57D2C8D9C6F007F820A /* TunnelButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0D4E4AB2C8D921A007F820A /* TunnelButton.swift */; }; + D0D4E57E2C8D9C6F007F820A /* TunnelStatusView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0D4E4AC2C8D921A007F820A /* TunnelStatusView.swift */; }; + D0D4E5892C8D9C94007F820A /* BurrowUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0D4E5582C8D9BF2007F820A /* BurrowUI.framework */; }; + D0D4E58A2C8D9C9E007F820A /* BurrowUI.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = D0D4E5582C8D9BF2007F820A /* BurrowUI.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + D0D4E58B2C8D9CA4007F820A /* BurrowConfiguration.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = D0D4E5622C8D9BF4007F820A /* BurrowConfiguration.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + D0D4E5922C8D9D15007F820A /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0D4E58F2C8D9D0A007F820A /* Constants.swift */; }; + D0D4E5A62C8D9E65007F820A /* BurrowCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0D4E5312C8D996F007F820A /* BurrowCore.framework */; }; + D0F4FAD32C8DC79C0068730A /* BurrowCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0D4E5312C8D996F007F820A /* BurrowCore.framework */; }; + D0F7594E2C8DAB6B00126CF3 /* GRPC in Frameworks */ = {isa = PBXBuildFile; productRef = D078F7E02C8DA375008A8CEC /* GRPC */; }; + D0F759612C8DB24B00126CF3 /* grpc-swift-config.json in Sources */ = {isa = PBXBuildFile; fileRef = D0D4E4962C8D921A007F820A /* grpc-swift-config.json */; }; + D0F759622C8DB24B00126CF3 /* swift-protobuf-config.json in Sources */ = {isa = PBXBuildFile; fileRef = D0D4E4972C8D921A007F820A /* swift-protobuf-config.json */; }; + D0F7597E2C8DB30500126CF3 /* CGRPCZlib in Frameworks */ = {isa = PBXBuildFile; productRef = D0F7597D2C8DB30500126CF3 /* CGRPCZlib */; }; + D0F7598D2C8DB3DA00126CF3 /* Client.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0D4E4992C8D921A007F820A /* Client.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ - D00117462B30373100D87C25 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = D05B9F6A29E39EEC008CB1F9 /* Project object */; - proxyType = 1; - remoteGlobalIDString = D00117372B30341C00D87C25; - remoteInfo = Shared; - }; - D00117482B30373500D87C25 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = D05B9F6A29E39EEC008CB1F9 /* Project object */; - proxyType = 1; - remoteGlobalIDString = D00117372B30341C00D87C25; - remoteInfo = Shared; - }; D020F65B29E4A697002790F6 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = D05B9F6A29E39EEC008CB1F9 /* Project object */; @@ -59,6 +58,48 @@ remoteGlobalIDString = D020F65229E4A697002790F6; remoteInfo = BurrowNetworkExtension; }; + D0BF09502C8E66F1000D8DEC /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = D05B9F6A29E39EEC008CB1F9 /* Project object */; + proxyType = 1; + remoteGlobalIDString = D0D4E55A2C8D9BF4007F820A; + remoteInfo = Configuration; + }; + D0BF09532C8E66FA000D8DEC /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = D05B9F6A29E39EEC008CB1F9 /* Project object */; + proxyType = 1; + remoteGlobalIDString = D0D4E55A2C8D9BF4007F820A; + remoteInfo = Configuration; + }; + D0D4E56E2C8D9C5D007F820A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = D05B9F6A29E39EEC008CB1F9 /* Project object */; + proxyType = 1; + remoteGlobalIDString = D0D4E5302C8D996F007F820A; + remoteInfo = Core; + }; + D0D4E57F2C8D9C78007F820A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = D05B9F6A29E39EEC008CB1F9 /* Project object */; + proxyType = 1; + remoteGlobalIDString = D0D4E5302C8D996F007F820A; + remoteInfo = Core; + }; + D0D4E5872C8D9C88007F820A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = D05B9F6A29E39EEC008CB1F9 /* Project object */; + proxyType = 1; + remoteGlobalIDString = D0D4E5502C8D9BF2007F820A; + remoteInfo = UI; + }; + D0F4FAD12C8DC7960068730A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = D05B9F6A29E39EEC008CB1F9 /* Project object */; + proxyType = 1; + remoteGlobalIDString = D0D4E5302C8D996F007F820A; + remoteInfo = Core; + }; /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -73,20 +114,24 @@ name = "Embed Foundation Extensions"; runOnlyForDeploymentPostprocessing = 0; }; + D0D4E53F2C8D996F007F820A /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + D0D4E58B2C8D9CA4007F820A /* BurrowConfiguration.framework in Embed Frameworks */, + D0D4E58A2C8D9C9E007F820A /* BurrowUI.framework in Embed Frameworks */, + D0D4E53A2C8D996F007F820A /* BurrowCore.framework in Embed Frameworks */, + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - 0B28F1552ABF463A000D44B0 /* DataTypes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataTypes.swift; sourceTree = ""; }; - 0B46E8DF2AC918CA00BA2A3C /* Client.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Client.swift; sourceTree = ""; }; - 43AA26D72A10004900F14CE6 /* MenuItemToggleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuItemToggleView.swift; sourceTree = ""; }; - D00117302B2FFFC900D87C25 /* NWConnection+Async.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NWConnection+Async.swift"; sourceTree = ""; }; - D00117322B3001A400D87C25 /* NewlineProtocolFramer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewlineProtocolFramer.swift; sourceTree = ""; }; - D00117382B30341C00D87C25 /* libBurrowShared.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libBurrowShared.a; sourceTree = BUILT_PRODUCTS_DIR; }; - D001173A2B30341C00D87C25 /* Logging.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Logging.swift; sourceTree = ""; }; - D00117412B30347800D87C25 /* module.modulemap */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.module-map"; path = module.modulemap; sourceTree = ""; }; - D00117422B30348D00D87C25 /* Shared.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Shared.xcconfig; sourceTree = ""; }; + D00117422B30348D00D87C25 /* Configuration.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Configuration.xcconfig; sourceTree = ""; }; D00AA8962A4669BC005C8102 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; - D01A79302B81630D0024EC91 /* NetworkView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkView.swift; sourceTree = ""; }; D020F63D29E4A1FF002790F6 /* Identity.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Identity.xcconfig; sourceTree = ""; }; D020F64029E4A1FF002790F6 /* Compiler.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Compiler.xcconfig; sourceTree = ""; }; D020F64229E4A1FF002790F6 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -101,42 +146,54 @@ D020F66729E4A95D002790F6 /* NetworkExtension-iOS.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "NetworkExtension-iOS.entitlements"; sourceTree = ""; }; D020F66829E4AA74002790F6 /* App-iOS.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "App-iOS.entitlements"; sourceTree = ""; }; D020F66929E4AA74002790F6 /* App-macOS.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "App-macOS.entitlements"; sourceTree = ""; }; - D032E6512B8A79C20006B8AD /* HackClub.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HackClub.swift; sourceTree = ""; }; - D032E6532B8A79DA0006B8AD /* WireGuard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WireGuard.swift; sourceTree = ""; }; + D04A3E1D2BAF465F0043EC85 /* Version.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Version.xcconfig; sourceTree = ""; }; 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 = ""; }; - D05B9F7729E39EEC008CB1F9 /* BurrowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BurrowView.swift; sourceTree = ""; }; - D05B9F7929E39EED008CB1F9 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; - D05EF8C72B81818D0017AB4F /* FloatingButtonStyle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FloatingButtonStyle.swift; sourceTree = ""; }; - D08252742B5C9DEB005DA378 /* Constants.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Constants.h; sourceTree = ""; }; - D08252752B5C9FC4005DA378 /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = ""; }; D09150412B9D2AF700BE3CB0 /* MainMenu.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MainMenu.xib; sourceTree = ""; }; D0B98FBF29FD8072004E7149 /* build-rust.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = "build-rust.sh"; sourceTree = ""; }; - D0B98FC629FDC5B5004E7149 /* Tunnel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tunnel.swift; sourceTree = ""; }; D0B98FD829FDDB6F004E7149 /* libburrow.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = libburrow.h; sourceTree = ""; }; D0B98FDC29FDDDCF004E7149 /* module.modulemap */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.module-map"; path = module.modulemap; sourceTree = ""; }; - D0BCC5FC2A086D4700AD070D /* NetworkExtension+Async.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NetworkExtension+Async.swift"; sourceTree = ""; }; D0BCC6032A09535900AD070D /* libburrow.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libburrow.a; sourceTree = BUILT_PRODUCTS_DIR; }; - D0FAB5912B818A5900F6A84B /* NetworkExtensionTunnel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkExtensionTunnel.swift; sourceTree = ""; }; - D0FAB5952B818B2900F6A84B /* TunnelButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelButton.swift; sourceTree = ""; }; - D0FAB5972B818B8200F6A84B /* TunnelStatusView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelStatusView.swift; sourceTree = ""; }; - D0FAB5992B818B9600F6A84B /* Network.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Network.swift; sourceTree = ""; }; + D0BF09582C8E6789000D8DEC /* UI.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = UI.xcconfig; sourceTree = ""; }; + D0D4E4952C8D921A007F820A /* burrow.proto */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.protobuf; path = burrow.proto; sourceTree = ""; }; + D0D4E4962C8D921A007F820A /* grpc-swift-config.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = "grpc-swift-config.json"; sourceTree = ""; }; + D0D4E4972C8D921A007F820A /* swift-protobuf-config.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = "swift-protobuf-config.json"; sourceTree = ""; }; + D0D4E4992C8D921A007F820A /* Client.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Client.swift; sourceTree = ""; }; + D0D4E49A2C8D921A007F820A /* Logging.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Logging.swift; sourceTree = ""; }; + D0D4E49D2C8D921A007F820A /* HackClub.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HackClub.swift; sourceTree = ""; }; + D0D4E49E2C8D921A007F820A /* Network.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Network.swift; sourceTree = ""; }; + D0D4E49F2C8D921A007F820A /* WireGuard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WireGuard.swift; sourceTree = ""; }; + D0D4E4A12C8D921A007F820A /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + D0D4E4A22C8D921A007F820A /* BurrowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BurrowView.swift; sourceTree = ""; }; + D0D4E4A32C8D921A007F820A /* FloatingButtonStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FloatingButtonStyle.swift; sourceTree = ""; }; + D0D4E4A42C8D921A007F820A /* MenuItemToggleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuItemToggleView.swift; sourceTree = ""; }; + D0D4E4A52C8D921A007F820A /* NetworkCarouselView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkCarouselView.swift; sourceTree = ""; }; + D0D4E4A62C8D921A007F820A /* NetworkExtension+Async.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NetworkExtension+Async.swift"; sourceTree = ""; }; + D0D4E4A72C8D921A007F820A /* NetworkExtensionTunnel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkExtensionTunnel.swift; sourceTree = ""; }; + D0D4E4A82C8D921A007F820A /* NetworkView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkView.swift; sourceTree = ""; }; + D0D4E4A92C8D921A007F820A /* OAuth2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OAuth2.swift; sourceTree = ""; }; + D0D4E4AA2C8D921A007F820A /* Tunnel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tunnel.swift; sourceTree = ""; }; + D0D4E4AB2C8D921A007F820A /* TunnelButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelButton.swift; sourceTree = ""; }; + D0D4E4AC2C8D921A007F820A /* TunnelStatusView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelStatusView.swift; sourceTree = ""; }; + D0D4E4F62C8D932D007F820A /* Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; + D0D4E4F72C8D941D007F820A /* Framework.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Framework.xcconfig; sourceTree = ""; }; + D0D4E5312C8D996F007F820A /* BurrowCore.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = BurrowCore.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + D0D4E5582C8D9BF2007F820A /* BurrowUI.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = BurrowUI.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + D0D4E5622C8D9BF4007F820A /* BurrowConfiguration.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = BurrowConfiguration.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + D0D4E58E2C8D9D0A007F820A /* Constants.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Constants.h; sourceTree = ""; }; + D0D4E58F2C8D9D0A007F820A /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = ""; }; + D0D4E5902C8D9D0A007F820A /* module.modulemap */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.module-map"; path = module.modulemap; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ - D00117352B30341C00D87C25 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; D020F65029E4A697002790F6 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - D00117442B30372900D87C25 /* libBurrowShared.a in Frameworks */, + D0BF09522C8E66F6000D8DEC /* BurrowConfiguration.framework in Frameworks */, + D0D4E5A62C8D9E65007F820A /* BurrowCore.framework in Frameworks */, D0BCC6092A09A03E00AD070D /* libburrow.a in Frameworks */, + D0B1D1102C436152004B7823 /* AsyncAlgorithms in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -144,33 +201,36 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - D00117452B30372C00D87C25 /* libBurrowShared.a in Frameworks */, + D0BF09552C8E66FD000D8DEC /* BurrowConfiguration.framework in Frameworks */, + D0F4FAD32C8DC79C0068730A /* BurrowCore.framework in Frameworks */, + D0D4E5892C8D9C94007F820A /* BurrowUI.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D078F7CF2C8DA213008A8CEC /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + D03383B02C8E67E300F7C44E /* NIOTransportServices in Frameworks */, + D03383AF2C8E67E300F7C44E /* NIOConcurrencyHelpers in Frameworks */, + D03383AE2C8E67E300F7C44E /* NIO in Frameworks */, + D03383AD2C8E67E300F7C44E /* SwiftProtobuf in Frameworks */, + D0F7594E2C8DAB6B00126CF3 /* GRPC in Frameworks */, + D0F7597E2C8DB30500126CF3 /* CGRPCZlib in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D0D4E5532C8D9BF2007F820A /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + D0D4E5702C8D9C62007F820A /* BurrowCore.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - D00117392B30341C00D87C25 /* Shared */ = { - isa = PBXGroup; - children = ( - D001173A2B30341C00D87C25 /* Logging.swift */, - D08252752B5C9FC4005DA378 /* Constants.swift */, - D00117422B30348D00D87C25 /* Shared.xcconfig */, - D001173F2B30347800D87C25 /* Constants */, - ); - path = Shared; - sourceTree = ""; - }; - D001173F2B30347800D87C25 /* Constants */ = { - isa = PBXGroup; - children = ( - D08252742B5C9DEB005DA378 /* Constants.h */, - D00117412B30347800D87C25 /* module.modulemap */, - ); - path = Constants; - sourceTree = ""; - }; D00117432B30372900D87C25 /* Frameworks */ = { isa = PBXGroup; children = ( @@ -184,8 +244,13 @@ D020F63D29E4A1FF002790F6 /* Identity.xcconfig */, D020F64A29E4A452002790F6 /* App.xcconfig */, D020F66329E4A703002790F6 /* Extension.xcconfig */, + D0D4E4F72C8D941D007F820A /* Framework.xcconfig */, D020F64029E4A1FF002790F6 /* Compiler.xcconfig */, + D0D4E4F62C8D932D007F820A /* Debug.xcconfig */, + D04A3E1D2BAF465F0043EC85 /* Version.xcconfig */, D020F64229E4A1FF002790F6 /* Info.plist */, + D0D4E5912C8D9D0A007F820A /* Constants */, + D00117422B30348D00D87C25 /* Configuration.xcconfig */, ); path = Configuration; sourceTree = ""; @@ -194,10 +259,6 @@ isa = PBXGroup; children = ( D020F65729E4A697002790F6 /* PacketTunnelProvider.swift */, - 0B46E8DF2AC918CA00BA2A3C /* Client.swift */, - 0B28F1552ABF463A000D44B0 /* DataTypes.swift */, - D00117322B3001A400D87C25 /* NewlineProtocolFramer.swift */, - D00117302B2FFFC900D87C25 /* NWConnection+Async.swift */, D020F65929E4A697002790F6 /* Info.plist */, D020F66729E4A95D002790F6 /* NetworkExtension-iOS.entitlements */, D020F66629E4A95D002790F6 /* NetworkExtension-macOS.entitlements */, @@ -207,22 +268,13 @@ path = NetworkExtension; sourceTree = ""; }; - D032E64D2B8A69C90006B8AD /* Networks */ = { - isa = PBXGroup; - children = ( - D0FAB5992B818B9600F6A84B /* Network.swift */, - D032E6512B8A79C20006B8AD /* HackClub.swift */, - D032E6532B8A79DA0006B8AD /* WireGuard.swift */, - ); - path = Networks; - sourceTree = ""; - }; D05B9F6929E39EEC008CB1F9 = { isa = PBXGroup; children = ( D05B9F7429E39EEC008CB1F9 /* App */, D020F65629E4A697002790F6 /* NetworkExtension */, - D00117392B30341C00D87C25 /* Shared */, + D0D4E49C2C8D921A007F820A /* Core */, + D0D4E4AD2C8D921A007F820A /* UI */, D020F63C29E4A1FF002790F6 /* Configuration */, D05B9F7329E39EEC008CB1F9 /* Products */, D00117432B30372900D87C25 /* Frameworks */, @@ -234,7 +286,10 @@ children = ( D05B9F7229E39EEC008CB1F9 /* Burrow.app */, D020F65329E4A697002790F6 /* BurrowNetworkExtension.appex */, - D00117382B30341C00D87C25 /* libBurrowShared.a */, + D0BCC6032A09535900AD070D /* libburrow.a */, + D0D4E5312C8D996F007F820A /* BurrowCore.framework */, + D0D4E5582C8D9BF2007F820A /* BurrowUI.framework */, + D0D4E5622C8D9BF4007F820A /* BurrowConfiguration.framework */, ); name = Products; sourceTree = ""; @@ -244,17 +299,6 @@ children = ( D05B9F7529E39EEC008CB1F9 /* BurrowApp.swift */, D00AA8962A4669BC005C8102 /* AppDelegate.swift */, - 43AA26D72A10004900F14CE6 /* MenuItemToggleView.swift */, - D05B9F7729E39EEC008CB1F9 /* BurrowView.swift */, - D01A79302B81630D0024EC91 /* NetworkView.swift */, - D032E64D2B8A69C90006B8AD /* Networks */, - D0FAB5972B818B8200F6A84B /* TunnelStatusView.swift */, - D0FAB5952B818B2900F6A84B /* TunnelButton.swift */, - D0B98FC629FDC5B5004E7149 /* Tunnel.swift */, - D0FAB5912B818A5900F6A84B /* NetworkExtensionTunnel.swift */, - D0BCC5FC2A086D4700AD070D /* NetworkExtension+Async.swift */, - D05EF8C72B81818D0017AB4F /* FloatingButtonStyle.swift */, - D05B9F7929E39EED008CB1F9 /* Assets.xcassets */, D09150412B9D2AF700BE3CB0 /* MainMenu.xib */, D020F66829E4AA74002790F6 /* App-iOS.entitlements */, D020F66929E4AA74002790F6 /* App-macOS.entitlements */, @@ -269,31 +313,74 @@ D0B98FBF29FD8072004E7149 /* build-rust.sh */, D0B98FDC29FDDDCF004E7149 /* module.modulemap */, D0B98FD829FDDB6F004E7149 /* libburrow.h */, - D0BCC6032A09535900AD070D /* libburrow.a */, ); path = libburrow; sourceTree = ""; }; + D0D4E4982C8D921A007F820A /* Client */ = { + isa = PBXGroup; + children = ( + D0D4E4952C8D921A007F820A /* burrow.proto */, + D0D4E4962C8D921A007F820A /* grpc-swift-config.json */, + D0D4E4972C8D921A007F820A /* swift-protobuf-config.json */, + ); + path = Client; + sourceTree = ""; + }; + D0D4E49C2C8D921A007F820A /* Core */ = { + isa = PBXGroup; + children = ( + D0D4E49A2C8D921A007F820A /* Logging.swift */, + D0D4E4992C8D921A007F820A /* Client.swift */, + D0D4E4982C8D921A007F820A /* Client */, + ); + path = Core; + sourceTree = ""; + }; + D0D4E4A02C8D921A007F820A /* Networks */ = { + isa = PBXGroup; + children = ( + D0D4E49D2C8D921A007F820A /* HackClub.swift */, + D0D4E49E2C8D921A007F820A /* Network.swift */, + D0D4E49F2C8D921A007F820A /* WireGuard.swift */, + ); + path = Networks; + sourceTree = ""; + }; + D0D4E4AD2C8D921A007F820A /* UI */ = { + isa = PBXGroup; + children = ( + D0D4E4A22C8D921A007F820A /* BurrowView.swift */, + D0D4E4A02C8D921A007F820A /* Networks */, + D0D4E4A32C8D921A007F820A /* FloatingButtonStyle.swift */, + D0D4E4A42C8D921A007F820A /* MenuItemToggleView.swift */, + D0D4E4A52C8D921A007F820A /* NetworkCarouselView.swift */, + D0D4E4A62C8D921A007F820A /* NetworkExtension+Async.swift */, + D0D4E4A72C8D921A007F820A /* NetworkExtensionTunnel.swift */, + D0D4E4A82C8D921A007F820A /* NetworkView.swift */, + D0D4E4A92C8D921A007F820A /* OAuth2.swift */, + D0D4E4AA2C8D921A007F820A /* Tunnel.swift */, + D0D4E4AB2C8D921A007F820A /* TunnelButton.swift */, + D0D4E4AC2C8D921A007F820A /* TunnelStatusView.swift */, + D0D4E4A12C8D921A007F820A /* Assets.xcassets */, + D0BF09582C8E6789000D8DEC /* UI.xcconfig */, + ); + path = UI; + sourceTree = ""; + }; + D0D4E5912C8D9D0A007F820A /* Constants */ = { + isa = PBXGroup; + children = ( + D0D4E58E2C8D9D0A007F820A /* Constants.h */, + D0D4E58F2C8D9D0A007F820A /* Constants.swift */, + D0D4E5902C8D9D0A007F820A /* module.modulemap */, + ); + path = Constants; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ - D00117372B30341C00D87C25 /* Shared */ = { - isa = PBXNativeTarget; - buildConfigurationList = D001173C2B30341C00D87C25 /* Build configuration list for PBXNativeTarget "Shared" */; - buildPhases = ( - D00117342B30341C00D87C25 /* Sources */, - D00117352B30341C00D87C25 /* Frameworks */, - ); - buildRules = ( - ); - dependencies = ( - D082527D2B5DEB80005DA378 /* PBXTargetDependency */, - ); - name = Shared; - productName = Shared; - productReference = D00117382B30341C00D87C25 /* libBurrowShared.a */; - productType = "com.apple.product-type.library.static"; - }; D020F65229E4A697002790F6 /* NetworkExtension */ = { isa = PBXNativeTarget; buildConfigurationList = D020F65E29E4A697002790F6 /* Build configuration list for PBXNativeTarget "NetworkExtension" */; @@ -301,13 +388,12 @@ D0BCC60B2A09A0C100AD070D /* Compile Rust */, D020F64F29E4A697002790F6 /* Sources */, D020F65029E4A697002790F6 /* Frameworks */, - D020F65129E4A697002790F6 /* Resources */, ); buildRules = ( ); dependencies = ( - D08252792B5DEB78005DA378 /* PBXTargetDependency */, - D00117492B30373500D87C25 /* PBXTargetDependency */, + D0BF09512C8E66F1000D8DEC /* PBXTargetDependency */, + D0D4E5802C8D9C78007F820A /* PBXTargetDependency */, ); name = NetworkExtension; productName = BurrowNetworkExtension; @@ -321,13 +407,15 @@ D05B9F6E29E39EEC008CB1F9 /* Sources */, D05B9F6F29E39EEC008CB1F9 /* Frameworks */, D05B9F7029E39EEC008CB1F9 /* Resources */, + D0D4E53F2C8D996F007F820A /* Embed Frameworks */, D020F66129E4A697002790F6 /* Embed Foundation Extensions */, ); buildRules = ( ); dependencies = ( - D082527B2B5DEB7D005DA378 /* PBXTargetDependency */, - D00117472B30373100D87C25 /* PBXTargetDependency */, + D0BF09542C8E66FA000D8DEC /* PBXTargetDependency */, + D0F4FAD22C8DC7960068730A /* PBXTargetDependency */, + D0D4E5882C8D9C88007F820A /* PBXTargetDependency */, D020F65C29E4A697002790F6 /* PBXTargetDependency */, ); name = App; @@ -335,6 +423,71 @@ productReference = D05B9F7229E39EEC008CB1F9 /* Burrow.app */; productType = "com.apple.product-type.application"; }; + D0D4E5302C8D996F007F820A /* Core */ = { + isa = PBXNativeTarget; + buildConfigurationList = D0D4E53C2C8D996F007F820A /* Build configuration list for PBXNativeTarget "Core" */; + buildPhases = ( + D0D4E52D2C8D996F007F820A /* Sources */, + D078F7CF2C8DA213008A8CEC /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + D0F7598A2C8DB34200126CF3 /* PBXTargetDependency */, + D0F7595E2C8DB24400126CF3 /* PBXTargetDependency */, + D0F759602C8DB24400126CF3 /* PBXTargetDependency */, + ); + name = Core; + packageProductDependencies = ( + D078F7E02C8DA375008A8CEC /* GRPC */, + D078F7E22C8DA375008A8CEC /* SwiftProtobuf */, + D044EE902C8DAB2000778185 /* NIO */, + D044EE922C8DAB2000778185 /* NIOConcurrencyHelpers */, + D044EE952C8DAB2800778185 /* NIOTransportServices */, + D0F7597D2C8DB30500126CF3 /* CGRPCZlib */, + ); + productName = Core; + productReference = D0D4E5312C8D996F007F820A /* BurrowCore.framework */; + productType = "com.apple.product-type.framework"; + }; + D0D4E5502C8D9BF2007F820A /* UI */ = { + isa = PBXNativeTarget; + buildConfigurationList = D0D4E5552C8D9BF2007F820A /* Build configuration list for PBXNativeTarget "UI" */; + buildPhases = ( + D0D4E5522C8D9BF2007F820A /* Sources */, + D0D4E5532C8D9BF2007F820A /* Frameworks */, + D0D4E5542C8D9BF2007F820A /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + D0D4E56F2C8D9C5D007F820A /* PBXTargetDependency */, + ); + name = UI; + packageProductDependencies = ( + ); + productName = Core; + productReference = D0D4E5582C8D9BF2007F820A /* BurrowUI.framework */; + productType = "com.apple.product-type.framework"; + }; + D0D4E55A2C8D9BF4007F820A /* Configuration */ = { + isa = PBXNativeTarget; + buildConfigurationList = D0D4E55F2C8D9BF4007F820A /* Build configuration list for PBXNativeTarget "Configuration" */; + buildPhases = ( + D0F759912C8DB49E00126CF3 /* Configure Version */, + D0D4E55C2C8D9BF4007F820A /* Sources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Configuration; + packageProductDependencies = ( + ); + productName = Core; + productReference = D0D4E5622C8D9BF4007F820A /* BurrowConfiguration.framework */; + productType = "com.apple.product-type.framework"; + }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ @@ -342,18 +495,18 @@ isa = PBXProject; attributes = { BuildIndependentTargetsInParallel = 1; - LastSwiftUpdateCheck = 1510; + LastSwiftUpdateCheck = 1600; LastUpgradeCheck = 1520; TargetAttributes = { - D00117372B30341C00D87C25 = { - CreatedOnToolsVersion = 15.1; - }; D020F65229E4A697002790F6 = { CreatedOnToolsVersion = 14.3; }; D05B9F7129E39EEC008CB1F9 = { CreatedOnToolsVersion = 14.3; }; + D0D4E5302C8D996F007F820A = { + CreatedOnToolsVersion = 16.0; + }; }; }; buildConfigurationList = D05B9F6D29E39EEC008CB1F9 /* Build configuration list for PBXProject "Burrow" */; @@ -366,7 +519,11 @@ ); mainGroup = D05B9F6929E39EEC008CB1F9; packageReferences = ( - D08252772B5DEB6E005DA378 /* XCRemoteSwiftPackageReference "SwiftLint" */, + D0B1D10E2C436152004B7823 /* XCRemoteSwiftPackageReference "swift-async-algorithms" */, + D0D4E4822C8D8EF6007F820A /* XCRemoteSwiftPackageReference "grpc-swift" */, + D0D4E4852C8D8F29007F820A /* XCRemoteSwiftPackageReference "swift-protobuf" */, + D044EE8F2C8DAB2000778185 /* XCRemoteSwiftPackageReference "swift-nio" */, + D044EE942C8DAB2800778185 /* XCRemoteSwiftPackageReference "swift-nio-transport-services" */, ); productRefGroup = D05B9F7329E39EEC008CB1F9 /* Products */; projectDirPath = ""; @@ -374,29 +531,29 @@ targets = ( D05B9F7129E39EEC008CB1F9 /* App */, D020F65229E4A697002790F6 /* NetworkExtension */, - D00117372B30341C00D87C25 /* Shared */, + D0D4E5502C8D9BF2007F820A /* UI */, + D0D4E5302C8D996F007F820A /* Core */, + D0D4E55A2C8D9BF4007F820A /* Configuration */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ - D020F65129E4A697002790F6 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - D0BCC60A2A09A0B800AD070D /* build-rust.sh in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; D05B9F7029E39EEC008CB1F9 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - D05B9F7A29E39EED008CB1F9 /* Assets.xcassets in Resources */, D09150422B9D2AF700BE3CB0 /* MainMenu.xib in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; + D0D4E5542C8D9BF2007F820A /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ @@ -420,26 +577,35 @@ shellScript = "\"${PROJECT_DIR}/NetworkExtension/libburrow/build-rust.sh\"\n"; showEnvVarsInLog = 0; }; + D0F759912C8DB49E00126CF3 /* Configure Version */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "$(PROJECT_DIR)/../Tools/version.sh", + "$(PROJECT_DIR)/../.git", + ); + name = "Configure Version"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(PROJECT_DIR)/Configuration/Version.xcconfig", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"$PROJECT_DIR/../Tools/version.sh\"\n"; + showEnvVarsInLog = 0; + }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ - D00117342B30341C00D87C25 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - D001173B2B30341C00D87C25 /* Logging.swift in Sources */, - D08252762B5C9FC4005DA378 /* Constants.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; D020F64F29E4A697002790F6 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - D00117332B3001A400D87C25 /* NewlineProtocolFramer.swift in Sources */, - 0B28F1562ABF463A000D44B0 /* DataTypes.swift in Sources */, - D00117312B2FFFC900D87C25 /* NWConnection+Async.swift in Sources */, - 0B46E8E02AC918CA00BA2A3C /* Client.swift in Sources */, D020F65829E4A697002790F6 /* PacketTunnelProvider.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -448,70 +614,104 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - D0FAB59A2B818B9600F6A84B /* Network.swift in Sources */, - D0BCC6082A0981FE00AD070D /* Tunnel.swift in Sources */, - D0FAB5982B818B8200F6A84B /* TunnelStatusView.swift in Sources */, - 43AA26D82A10004900F14CE6 /* MenuItemToggleView.swift in Sources */, - D05B9F7829E39EEC008CB1F9 /* BurrowView.swift in Sources */, - D0FAB5922B818A5900F6A84B /* NetworkExtensionTunnel.swift in Sources */, - D0FAB5962B818B2900F6A84B /* TunnelButton.swift in Sources */, D00AA8972A4669BC005C8102 /* AppDelegate.swift in Sources */, - D05EF8C82B81818D0017AB4F /* FloatingButtonStyle.swift in Sources */, - D032E6522B8A79C20006B8AD /* HackClub.swift in Sources */, D05B9F7629E39EEC008CB1F9 /* BurrowApp.swift in Sources */, - D01A79312B81630D0024EC91 /* NetworkView.swift in Sources */, - D032E6542B8A79DA0006B8AD /* WireGuard.swift in Sources */, - D0BCC5FD2A086D4700AD070D /* NetworkExtension+Async.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D0D4E52D2C8D996F007F820A /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D0F759612C8DB24B00126CF3 /* grpc-swift-config.json in Sources */, + D0F759622C8DB24B00126CF3 /* swift-protobuf-config.json in Sources */, + D0F7598D2C8DB3DA00126CF3 /* Client.swift in Sources */, + D0D4E56B2C8D9C2F007F820A /* Logging.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D0D4E5522C8D9BF2007F820A /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D0D4E5712C8D9C6F007F820A /* HackClub.swift in Sources */, + D0D4E5722C8D9C6F007F820A /* Network.swift in Sources */, + D0D4E5732C8D9C6F007F820A /* WireGuard.swift in Sources */, + D0D4E5742C8D9C6F007F820A /* BurrowView.swift in Sources */, + D0D4E5752C8D9C6F007F820A /* FloatingButtonStyle.swift in Sources */, + D0D4E5762C8D9C6F007F820A /* MenuItemToggleView.swift in Sources */, + D0D4E5772C8D9C6F007F820A /* NetworkCarouselView.swift in Sources */, + D0D4E5782C8D9C6F007F820A /* NetworkExtension+Async.swift in Sources */, + D0D4E5792C8D9C6F007F820A /* NetworkExtensionTunnel.swift in Sources */, + D0D4E57A2C8D9C6F007F820A /* NetworkView.swift in Sources */, + D0D4E57B2C8D9C6F007F820A /* OAuth2.swift in Sources */, + D0D4E57C2C8D9C6F007F820A /* Tunnel.swift in Sources */, + D0D4E57D2C8D9C6F007F820A /* TunnelButton.swift in Sources */, + D0D4E57E2C8D9C6F007F820A /* TunnelStatusView.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D0D4E55C2C8D9BF4007F820A /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D0D4E5922C8D9D15007F820A /* Constants.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ - D00117472B30373100D87C25 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = D00117372B30341C00D87C25 /* Shared */; - targetProxy = D00117462B30373100D87C25 /* PBXContainerItemProxy */; - }; - D00117492B30373500D87C25 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = D00117372B30341C00D87C25 /* Shared */; - targetProxy = D00117482B30373500D87C25 /* PBXContainerItemProxy */; - }; D020F65C29E4A697002790F6 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = D020F65229E4A697002790F6 /* NetworkExtension */; targetProxy = D020F65B29E4A697002790F6 /* PBXContainerItemProxy */; }; - D08252792B5DEB78005DA378 /* PBXTargetDependency */ = { + D0BF09512C8E66F1000D8DEC /* PBXTargetDependency */ = { isa = PBXTargetDependency; - productRef = D08252782B5DEB78005DA378 /* SwiftLintPlugin */; + target = D0D4E55A2C8D9BF4007F820A /* Configuration */; + targetProxy = D0BF09502C8E66F1000D8DEC /* PBXContainerItemProxy */; }; - D082527B2B5DEB7D005DA378 /* PBXTargetDependency */ = { + D0BF09542C8E66FA000D8DEC /* PBXTargetDependency */ = { isa = PBXTargetDependency; - productRef = D082527A2B5DEB7D005DA378 /* SwiftLintPlugin */; + target = D0D4E55A2C8D9BF4007F820A /* Configuration */; + targetProxy = D0BF09532C8E66FA000D8DEC /* PBXContainerItemProxy */; }; - D082527D2B5DEB80005DA378 /* PBXTargetDependency */ = { + D0D4E56F2C8D9C5D007F820A /* PBXTargetDependency */ = { isa = PBXTargetDependency; - productRef = D082527C2B5DEB80005DA378 /* SwiftLintPlugin */; + target = D0D4E5302C8D996F007F820A /* Core */; + targetProxy = D0D4E56E2C8D9C5D007F820A /* PBXContainerItemProxy */; + }; + D0D4E5802C8D9C78007F820A /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = D0D4E5302C8D996F007F820A /* Core */; + targetProxy = D0D4E57F2C8D9C78007F820A /* PBXContainerItemProxy */; + }; + D0D4E5882C8D9C88007F820A /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = D0D4E5502C8D9BF2007F820A /* UI */; + targetProxy = D0D4E5872C8D9C88007F820A /* PBXContainerItemProxy */; + }; + D0F4FAD22C8DC7960068730A /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = D0D4E5302C8D996F007F820A /* Core */; + targetProxy = D0F4FAD12C8DC7960068730A /* PBXContainerItemProxy */; + }; + D0F7595E2C8DB24400126CF3 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + productRef = D0F7595D2C8DB24400126CF3 /* GRPCSwiftPlugin */; + }; + D0F759602C8DB24400126CF3 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + productRef = D0F7595F2C8DB24400126CF3 /* SwiftProtobufPlugin */; + }; + D0F7598A2C8DB34200126CF3 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + productRef = D0F759892C8DB34200126CF3 /* GRPC */; }; /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ - D001173D2B30341C00D87C25 /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = D00117422B30348D00D87C25 /* Shared.xcconfig */; - buildSettings = { - }; - name = Debug; - }; - D001173E2B30341C00D87C25 /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = D00117422B30348D00D87C25 /* Shared.xcconfig */; - buildSettings = { - }; - name = Release; - }; D020F65F29E4A697002790F6 /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = D020F66229E4A6E5002790F6 /* NetworkExtension.xcconfig */; @@ -554,18 +754,51 @@ }; name = Release; }; + D0D4E53D2C8D996F007F820A /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D0D4E4F72C8D941D007F820A /* Framework.xcconfig */; + buildSettings = { + }; + name = Debug; + }; + D0D4E53E2C8D996F007F820A /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D0D4E4F72C8D941D007F820A /* Framework.xcconfig */; + buildSettings = { + }; + name = Release; + }; + D0D4E5562C8D9BF2007F820A /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D0BF09582C8E6789000D8DEC /* UI.xcconfig */; + buildSettings = { + }; + name = Debug; + }; + D0D4E5572C8D9BF2007F820A /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D0BF09582C8E6789000D8DEC /* UI.xcconfig */; + buildSettings = { + }; + name = Release; + }; + D0D4E5602C8D9BF4007F820A /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D00117422B30348D00D87C25 /* Configuration.xcconfig */; + buildSettings = { + }; + name = Debug; + }; + D0D4E5612C8D9BF4007F820A /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D00117422B30348D00D87C25 /* Configuration.xcconfig */; + buildSettings = { + }; + name = Release; + }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ - D001173C2B30341C00D87C25 /* Build configuration list for PBXNativeTarget "Shared" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - D001173D2B30341C00D87C25 /* Debug */, - D001173E2B30341C00D87C25 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; D020F65E29E4A697002790F6 /* Build configuration list for PBXNativeTarget "NetworkExtension" */ = { isa = XCConfigurationList; buildConfigurations = ( @@ -593,34 +826,128 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + D0D4E53C2C8D996F007F820A /* Build configuration list for PBXNativeTarget "Core" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + D0D4E53D2C8D996F007F820A /* Debug */, + D0D4E53E2C8D996F007F820A /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + D0D4E5552C8D9BF2007F820A /* Build configuration list for PBXNativeTarget "UI" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + D0D4E5562C8D9BF2007F820A /* Debug */, + D0D4E5572C8D9BF2007F820A /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + D0D4E55F2C8D9BF4007F820A /* Build configuration list for PBXNativeTarget "Configuration" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + D0D4E5602C8D9BF4007F820A /* Debug */, + D0D4E5612C8D9BF4007F820A /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; /* End XCConfigurationList section */ /* Begin XCRemoteSwiftPackageReference section */ - D08252772B5DEB6E005DA378 /* XCRemoteSwiftPackageReference "SwiftLint" */ = { + D044EE8F2C8DAB2000778185 /* XCRemoteSwiftPackageReference "swift-nio" */ = { isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/realm/SwiftLint.git"; + repositoryURL = "https://github.com/apple/swift-nio.git"; requirement = { - branch = main; - kind = branch; + kind = upToNextMajorVersion; + minimumVersion = 2.72.0; + }; + }; + D044EE942C8DAB2800778185 /* XCRemoteSwiftPackageReference "swift-nio-transport-services" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/apple/swift-nio-transport-services.git"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 1.21.0; + }; + }; + D0B1D10E2C436152004B7823 /* XCRemoteSwiftPackageReference "swift-async-algorithms" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/apple/swift-async-algorithms.git"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 1.0.1; + }; + }; + D0D4E4822C8D8EF6007F820A /* XCRemoteSwiftPackageReference "grpc-swift" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/grpc/grpc-swift.git"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 1.23.0; + }; + }; + D0D4E4852C8D8F29007F820A /* XCRemoteSwiftPackageReference "swift-protobuf" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/apple/swift-protobuf.git"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 1.28.1; }; }; /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ - D08252782B5DEB78005DA378 /* SwiftLintPlugin */ = { + D044EE902C8DAB2000778185 /* NIO */ = { isa = XCSwiftPackageProductDependency; - package = D08252772B5DEB6E005DA378 /* XCRemoteSwiftPackageReference "SwiftLint" */; - productName = "plugin:SwiftLintPlugin"; + package = D044EE8F2C8DAB2000778185 /* XCRemoteSwiftPackageReference "swift-nio" */; + productName = NIO; }; - D082527A2B5DEB7D005DA378 /* SwiftLintPlugin */ = { + D044EE922C8DAB2000778185 /* NIOConcurrencyHelpers */ = { isa = XCSwiftPackageProductDependency; - package = D08252772B5DEB6E005DA378 /* XCRemoteSwiftPackageReference "SwiftLint" */; - productName = "plugin:SwiftLintPlugin"; + package = D044EE8F2C8DAB2000778185 /* XCRemoteSwiftPackageReference "swift-nio" */; + productName = NIOConcurrencyHelpers; }; - D082527C2B5DEB80005DA378 /* SwiftLintPlugin */ = { + D044EE952C8DAB2800778185 /* NIOTransportServices */ = { isa = XCSwiftPackageProductDependency; - package = D08252772B5DEB6E005DA378 /* XCRemoteSwiftPackageReference "SwiftLint" */; - productName = "plugin:SwiftLintPlugin"; + package = D044EE942C8DAB2800778185 /* XCRemoteSwiftPackageReference "swift-nio-transport-services" */; + productName = NIOTransportServices; + }; + D078F7E02C8DA375008A8CEC /* GRPC */ = { + isa = XCSwiftPackageProductDependency; + package = D0D4E4822C8D8EF6007F820A /* XCRemoteSwiftPackageReference "grpc-swift" */; + productName = GRPC; + }; + D078F7E22C8DA375008A8CEC /* SwiftProtobuf */ = { + isa = XCSwiftPackageProductDependency; + package = D0D4E4852C8D8F29007F820A /* XCRemoteSwiftPackageReference "swift-protobuf" */; + productName = SwiftProtobuf; + }; + D0B1D10F2C436152004B7823 /* AsyncAlgorithms */ = { + isa = XCSwiftPackageProductDependency; + package = D0B1D10E2C436152004B7823 /* XCRemoteSwiftPackageReference "swift-async-algorithms" */; + productName = AsyncAlgorithms; + }; + D0F7595D2C8DB24400126CF3 /* GRPCSwiftPlugin */ = { + isa = XCSwiftPackageProductDependency; + package = D0D4E4822C8D8EF6007F820A /* XCRemoteSwiftPackageReference "grpc-swift" */; + productName = "plugin:GRPCSwiftPlugin"; + }; + D0F7595F2C8DB24400126CF3 /* SwiftProtobufPlugin */ = { + isa = XCSwiftPackageProductDependency; + package = D0D4E4852C8D8F29007F820A /* XCRemoteSwiftPackageReference "swift-protobuf" */; + productName = "plugin:SwiftProtobufPlugin"; + }; + D0F7597D2C8DB30500126CF3 /* CGRPCZlib */ = { + isa = XCSwiftPackageProductDependency; + package = D0D4E4822C8D8EF6007F820A /* XCRemoteSwiftPackageReference "grpc-swift" */; + productName = CGRPCZlib; + }; + D0F759892C8DB34200126CF3 /* GRPC */ = { + isa = XCSwiftPackageProductDependency; + package = D0D4E4822C8D8EF6007F820A /* XCRemoteSwiftPackageReference "grpc-swift" */; + productName = GRPC; }; /* End XCSwiftPackageProductDependency section */ }; diff --git a/Apple/Burrow.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Apple/Burrow.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 9378372..739b77c 100644 --- a/Apple/Burrow.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Apple/Burrow.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,86 +1,123 @@ { + "originHash" : "fa512b990383b7e309c5854a5279817052294a8191a6d3c55c49cfb38e88c0c3", "pins" : [ { - "identity" : "collectionconcurrencykit", + "identity" : "grpc-swift", "kind" : "remoteSourceControl", - "location" : "https://github.com/JohnSundell/CollectionConcurrencyKit.git", + "location" : "https://github.com/grpc/grpc-swift.git", "state" : { - "revision" : "b4f23e24b5a1bff301efc5e70871083ca029ff95", - "version" : "0.2.0" + "revision" : "6a90b7e77e29f9bda6c2b3a4165a40d6c02cfda1", + "version" : "1.23.0" } }, { - "identity" : "cryptoswift", + "identity" : "swift-async-algorithms", "kind" : "remoteSourceControl", - "location" : "https://github.com/krzyzanowskim/CryptoSwift.git", + "location" : "https://github.com/apple/swift-async-algorithms.git", "state" : { - "revision" : "7892a123f7e8d0fe62f9f03728b17bbd4f94df5c", - "version" : "1.8.1" + "revision" : "6ae9a051f76b81cc668305ceed5b0e0a7fd93d20", + "version" : "1.0.1" } }, { - "identity" : "sourcekitten", + "identity" : "swift-atomics", "kind" : "remoteSourceControl", - "location" : "https://github.com/jpsim/SourceKitten.git", + "location" : "https://github.com/apple/swift-atomics.git", "state" : { - "revision" : "b6dc09ee51dfb0c66e042d2328c017483a1a5d56", - "version" : "0.34.1" + "revision" : "cd142fd2f64be2100422d658e7411e39489da985", + "version" : "1.2.0" } }, { - "identity" : "swift-argument-parser", + "identity" : "swift-collections", "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-argument-parser.git", + "location" : "https://github.com/apple/swift-collections.git", "state" : { - "revision" : "8f4d2753f0e4778c76d5f05ad16c74f707390531", - "version" : "1.2.3" + "revision" : "9bf03ff58ce34478e66aaee630e491823326fd06", + "version" : "1.1.3" } }, { - "identity" : "swift-syntax", + "identity" : "swift-http-types", "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-syntax.git", + "location" : "https://github.com/apple/swift-http-types", "state" : { - "revision" : "64889f0c732f210a935a0ad7cda38f77f876262d", - "version" : "509.1.1" + "revision" : "ae67c8178eb46944fd85e4dc6dd970e1f3ed6ccd", + "version" : "1.3.0" } }, { - "identity" : "swiftlint", + "identity" : "swift-log", "kind" : "remoteSourceControl", - "location" : "https://github.com/realm/SwiftLint.git", + "location" : "https://github.com/apple/swift-log.git", "state" : { - "branch" : "main", - "revision" : "7595ad3fafc1a31086dc40ba01fd898bf6b42d5f" + "revision" : "9cb486020ebf03bfa5b5df985387a14a98744537", + "version" : "1.6.1" } }, { - "identity" : "swiftytexttable", + "identity" : "swift-nio", "kind" : "remoteSourceControl", - "location" : "https://github.com/scottrhoyt/SwiftyTextTable.git", + "location" : "https://github.com/apple/swift-nio.git", "state" : { - "revision" : "c6df6cf533d120716bff38f8ff9885e1ce2a4ac3", - "version" : "0.9.0" + "revision" : "9746cf80e29edfef2a39924a66731249223f42a3", + "version" : "2.72.0" } }, { - "identity" : "swxmlhash", + "identity" : "swift-nio-extras", "kind" : "remoteSourceControl", - "location" : "https://github.com/drmohundro/SWXMLHash.git", + "location" : "https://github.com/apple/swift-nio-extras.git", "state" : { - "revision" : "a853604c9e9a83ad9954c7e3d2a565273982471f", - "version" : "7.0.2" + "revision" : "d1ead62745cc3269e482f1c51f27608057174379", + "version" : "1.24.0" } }, { - "identity" : "yams", + "identity" : "swift-nio-http2", "kind" : "remoteSourceControl", - "location" : "https://github.com/jpsim/Yams.git", + "location" : "https://github.com/apple/swift-nio-http2.git", "state" : { - "revision" : "0d9ee7ea8c4ebd4a489ad7a73d5c6cad55d6fed3", - "version" : "5.0.6" + "revision" : "b5f7062b60e4add1e8c343ba4eb8da2e324b3a94", + "version" : "1.34.0" + } + }, + { + "identity" : "swift-nio-ssl", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-nio-ssl.git", + "state" : { + "revision" : "7b84abbdcef69cc3be6573ac12440220789dcd69", + "version" : "2.27.2" + } + }, + { + "identity" : "swift-nio-transport-services", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-nio-transport-services.git", + "state" : { + "revision" : "38ac8221dd20674682148d6451367f89c2652980", + "version" : "1.21.0" + } + }, + { + "identity" : "swift-protobuf", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-protobuf.git", + "state" : { + "revision" : "edb6ed4919f7756157fe02f2552b7e3850a538e5", + "version" : "1.28.1" + } + }, + { + "identity" : "swift-system", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-system.git", + "state" : { + "revision" : "d2ba781702a1d8285419c15ee62fd734a9437ff5", + "version" : "1.3.2" } } ], - "version" : 2 + "version" : 3 } diff --git a/Apple/Burrow.xcodeproj/xcshareddata/xcschemes/App.xcscheme b/Apple/Burrow.xcodeproj/xcshareddata/xcschemes/App.xcscheme index 670823d..a524e87 100644 --- a/Apple/Burrow.xcodeproj/xcshareddata/xcschemes/App.xcscheme +++ b/Apple/Burrow.xcodeproj/xcshareddata/xcschemes/App.xcscheme @@ -1,10 +1,11 @@ + buildImplicitDependencies = "YES" + buildArchitectures = "Automatic"> + buildImplicitDependencies = "YES" + buildArchitectures = "Automatic"> = { + switch FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: appGroupIdentifier) { + case .some(let url): .success(url) + case .none: .failure(.invalidAppGroupIdentifier) + } + }() +} + +extension Logger { + @_dynamicReplacement(for: subsystem) + public static var subsystem: String { Constants.bundleIdentifier } +} diff --git a/Apple/Shared/Constants/module.modulemap b/Apple/Configuration/Constants/module.modulemap similarity index 66% rename from Apple/Shared/Constants/module.modulemap rename to Apple/Configuration/Constants/module.modulemap index 7ee21fc..0e60f32 100644 --- a/Apple/Shared/Constants/module.modulemap +++ b/Apple/Configuration/Constants/module.modulemap @@ -1,4 +1,4 @@ -module Constants { +module CConstants { header "Constants.h" export * } diff --git a/Apple/Configuration/Debug.xcconfig b/Apple/Configuration/Debug.xcconfig new file mode 100644 index 0000000..9529dbd --- /dev/null +++ b/Apple/Configuration/Debug.xcconfig @@ -0,0 +1,26 @@ +// Release +DEBUG_INFORMATION_FORMAT = dwarf-with-dsym +SWIFT_COMPILATION_MODE = wholemodule +SWIFT_OPTIMIZATION_LEVEL = -Osize +LLVM_LTO = YES +DEAD_CODE_STRIPPING = YES +STRIP_INSTALLED_PRODUCT = YES +STRIP_SWIFT_SYMBOLS = YES +COPY_PHASE_STRIP = NO +VALIDATE_PRODUCT = YES +ENABLE_MODULE_VERIFIER = YES + +// Debug +ONLY_ACTIVE_ARCH[config=Debug] = YES +DEBUG_INFORMATION_FORMAT[config=Debug] = dwarf +ENABLE_TESTABILITY[config=Debug] = YES +GCC_PREPROCESSOR_DEFINITIONS[config=Debug] = DEBUG=1 $(inherited) +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 +STRIP_INSTALLED_PRODUCT[config=Debug] = NO +STRIP_SWIFT_SYMBOLS[config=Debug] = NO +ENABLE_MODULE_VERIFIER[config=Debug] = NO diff --git a/Apple/Configuration/Extension.xcconfig b/Apple/Configuration/Extension.xcconfig index f8d90a3..5885c31 100644 --- a/Apple/Configuration/Extension.xcconfig +++ b/Apple/Configuration/Extension.xcconfig @@ -1,4 +1,6 @@ -MERGED_BINARY_TYPE = manual +LD_EXPORT_SYMBOLS = NO + +OTHER_SWIFT_FLAGS = $(inherited) -Xfrontend -disable-autolink-framework -Xfrontend UIKit -Xfrontend -disable-autolink-framework -Xfrontend AppKit -Xfrontend -disable-autolink-framework -Xfrontend SwiftUI LD_RUNPATH_SEARCH_PATHS = $(inherited) @executable_path/Frameworks @executable_path/../../Frameworks -LD_RUNPATH_SEARCH_PATHS[sdk=macos*] = $(inherited) @executable_path/../Frameworks @executable_path/../../../../Frameworks +LD_RUNPATH_SEARCH_PATHS[sdk=macosx*] = $(inherited) @executable_path/../Frameworks @executable_path/../../../../Frameworks diff --git a/Apple/Configuration/Framework.xcconfig b/Apple/Configuration/Framework.xcconfig new file mode 100644 index 0000000..6fa4f19 --- /dev/null +++ b/Apple/Configuration/Framework.xcconfig @@ -0,0 +1,14 @@ +PRODUCT_NAME = Burrow$(TARGET_NAME:c99extidentifier) +PRODUCT_BUNDLE_IDENTIFIER = $(APP_BUNDLE_IDENTIFIER).$(TARGET_NAME:c99extidentifier) +APPLICATION_EXTENSION_API_ONLY = YES +SWIFT_INSTALL_OBJC_HEADER = NO +SWIFT_SKIP_AUTOLINKING_FRAMEWORKS = YES +SWIFT_SKIP_AUTOLINKING_LIBRARIES = YES + +LD_RUNPATH_SEARCH_PATHS = $(inherited) @executable_path/Frameworks @loader_path/Frameworks +LD_RUNPATH_SEARCH_PATHS[sdk=macosx*] = $(inherited) @executable_path/../Frameworks @loader_path/Frameworks + +DYLIB_INSTALL_NAME_BASE = @rpath +DYLIB_COMPATIBILITY_VERSION = 1 +DYLIB_CURRENT_VERSION = 1 +VERSIONING_SYSTEM = diff --git a/Apple/Configuration/Version.xcconfig b/Apple/Configuration/Version.xcconfig new file mode 100644 index 0000000..e69de29 diff --git a/Apple/Core/Client.swift b/Apple/Core/Client.swift new file mode 100644 index 0000000..8874e3b --- /dev/null +++ b/Apple/Core/Client.swift @@ -0,0 +1,32 @@ +import GRPC +import NIOTransportServices + +public typealias TunnelClient = Burrow_TunnelAsyncClient +public typealias NetworksClient = Burrow_NetworksAsyncClient + +public protocol Client { + init(channel: GRPCChannel) +} + +extension Client { + public static func unix(socketURL: URL) -> Self { + let group = NIOTSEventLoopGroup() + let configuration = ClientConnection.Configuration.default( + target: .unixDomainSocket(socketURL.path), + eventLoopGroup: group + ) + return Self(channel: ClientConnection(configuration: configuration)) + } +} + +extension TunnelClient: Client { + public init(channel: any GRPCChannel) { + self.init(channel: channel, defaultCallOptions: .init(), interceptors: .none) + } +} + +extension NetworksClient: Client { + public init(channel: any GRPCChannel) { + self.init(channel: channel, defaultCallOptions: .init(), interceptors: .none) + } +} diff --git a/Apple/Core/Client/burrow.proto b/Apple/Core/Client/burrow.proto new file mode 120000 index 0000000..03e86a5 --- /dev/null +++ b/Apple/Core/Client/burrow.proto @@ -0,0 +1 @@ +../../../proto/burrow.proto \ No newline at end of file diff --git a/Apple/Core/Client/grpc-swift-config.json b/Apple/Core/Client/grpc-swift-config.json new file mode 100644 index 0000000..2d89698 --- /dev/null +++ b/Apple/Core/Client/grpc-swift-config.json @@ -0,0 +1,11 @@ +{ + "invocations": [ + { + "protoFiles": [ + "burrow.proto", + ], + "server": false, + "visibility": "public" + } + ] +} diff --git a/Apple/Core/Client/swift-protobuf-config.json b/Apple/Core/Client/swift-protobuf-config.json new file mode 100644 index 0000000..87aaec3 --- /dev/null +++ b/Apple/Core/Client/swift-protobuf-config.json @@ -0,0 +1,10 @@ +{ + "invocations": [ + { + "protoFiles": [ + "burrow.proto", + ], + "visibility": "public" + } + ] +} diff --git a/Apple/Shared/Logging.swift b/Apple/Core/Logging.swift similarity index 88% rename from Apple/Shared/Logging.swift rename to Apple/Core/Logging.swift index 36f024c..ba40888 100644 --- a/Apple/Shared/Logging.swift +++ b/Apple/Core/Logging.swift @@ -4,7 +4,7 @@ import os extension Logger { private static let loggers: OSAllocatedUnfairLock<[String: Logger]> = OSAllocatedUnfairLock(initialState: [:]) - public static let subsystem = Constants.bundleIdentifier + public dynamic static var subsystem: String { "com.hackclub.burrow" } public static func logger(for type: Any.Type) -> Logger { let category = String(describing: type) diff --git a/Apple/NetworkExtension/Client.swift b/Apple/NetworkExtension/Client.swift deleted file mode 100644 index e7c1bc8..0000000 --- a/Apple/NetworkExtension/Client.swift +++ /dev/null @@ -1,60 +0,0 @@ -import BurrowShared -import Foundation -import Network - -final class Client { - let connection: NWConnection - - private let logger = Logger.logger(for: Client.self) - private var generator = SystemRandomNumberGenerator() - - convenience init() throws { - self.init(url: try Constants.socketURL) - } - - init(url: URL) { - let endpoint: NWEndpoint - if url.isFileURL { - endpoint = .unix(path: url.path(percentEncoded: false)) - } else { - endpoint = .url(url) - } - - let parameters = NWParameters.tcp - parameters.defaultProtocolStack - .applicationProtocols - .insert(NWProtocolFramer.Options(definition: NewlineProtocolFramer.definition), at: 0) - connection = NWConnection(to: endpoint, using: parameters) - connection.start(queue: .global()) - } - - func request(_ request: any Request, type: U.Type = U.self) async throws -> U { - do { - var copy = request - copy.id = generator.next(upperBound: UInt.max) - let content = try JSONEncoder().encode(copy) - logger.debug("> \(String(decoding: content, as: UTF8.self))") - - try await self.connection.send(content: content) - let (response, _, _) = try await connection.receiveMessage() - - logger.debug("< \(String(decoding: response, as: UTF8.self))") - return try JSONDecoder().decode(U.self, from: response) - } catch { - logger.error("\(error, privacy: .public)") - throw error - } - } - - deinit { - connection.cancel() - } -} - -extension Constants { - static var socketURL: URL { - get throws { - try groupContainerURL.appending(component: "burrow.sock", directoryHint: .notDirectory) - } - } -} diff --git a/Apple/NetworkExtension/DataTypes.swift b/Apple/NetworkExtension/DataTypes.swift deleted file mode 100644 index 1409fde..0000000 --- a/Apple/NetworkExtension/DataTypes.swift +++ /dev/null @@ -1,61 +0,0 @@ -import Foundation - -// swiftlint:disable identifier_name -enum BurrowError: Error { - case addrDoesntExist - case resultIsError - case cantParseResult - case resultIsNone -} - -protocol Request: Codable where Command: Codable { - associatedtype Command - - var id: UInt { get set } - var command: Command { get set } -} - -struct BurrowSingleCommand: Request { - var id: UInt - var command: String -} - -struct BurrowRequest: Request where T: Codable { - var id: UInt - var command: T -} - -struct BurrowStartRequest: Codable { - struct TunOptions: Codable { - let name: String? - let no_pi: Bool - let tun_excl: Bool - let tun_retrieve: Bool - let address: [String] - } - struct StartOptions: Codable { - let tun: TunOptions - } - let Start: StartOptions -} - -struct Response: Decodable where T: Decodable { - var id: UInt - var result: T -} - -struct BurrowResult: Codable where T: Codable { - var Ok: T? - var Err: String? -} - -struct ServerConfigData: Codable { - struct InternalConfig: Codable { - let address: [String] - let name: String? - let mtu: Int32? - } - let ServerConfig: InternalConfig -} - -// swiftlint:enable identifier_name diff --git a/Apple/NetworkExtension/NWConnection+Async.swift b/Apple/NetworkExtension/NWConnection+Async.swift deleted file mode 100644 index c21fdc0..0000000 --- a/Apple/NetworkExtension/NWConnection+Async.swift +++ /dev/null @@ -1,32 +0,0 @@ -import Foundation -import Network - -extension NWConnection { - // swiftlint:disable:next large_tuple - func receiveMessage() async throws -> (Data, NWConnection.ContentContext?, Bool) { - try await withUnsafeThrowingContinuation { continuation in - receiveMessage { completeContent, contentContext, isComplete, error in - if let error { - continuation.resume(throwing: error) - } else { - guard let completeContent = completeContent else { - fatalError("Both error and completeContent were nil") - } - continuation.resume(returning: (completeContent, contentContext, isComplete)) - } - } - } - } - - func send(content: Data) async throws { - try await withCheckedThrowingContinuation { (continuation: CheckedContinuation) in - send(content: content, completion: .contentProcessed { error in - if let error { - continuation.resume(throwing: error) - } else { - continuation.resume(returning: ()) - } - }) - } - } -} diff --git a/Apple/NetworkExtension/NewlineProtocolFramer.swift b/Apple/NetworkExtension/NewlineProtocolFramer.swift deleted file mode 100644 index d2f71e5..0000000 --- a/Apple/NetworkExtension/NewlineProtocolFramer.swift +++ /dev/null @@ -1,54 +0,0 @@ -import Foundation -import Network - -final class NewlineProtocolFramer: NWProtocolFramerImplementation { - private static let delimeter: UInt8 = 10 // `\n` - - static let definition = NWProtocolFramer.Definition(implementation: NewlineProtocolFramer.self) - static let label = "Lines" - - init(framer: NWProtocolFramer.Instance) { } - - func start(framer: NWProtocolFramer.Instance) -> NWProtocolFramer.StartResult { .ready } - func stop(framer: NWProtocolFramer.Instance) -> Bool { true } - - func wakeup(framer: NWProtocolFramer.Instance) { } - func cleanup(framer: NWProtocolFramer.Instance) { } - - func handleInput(framer: NWProtocolFramer.Instance) -> Int { - while true { - var result: [Data] = [] - let parsed = framer.parseInput(minimumIncompleteLength: 1, maximumLength: 16_000) { buffer, _ in - guard let buffer else { return 0 } - var lines = buffer - .split(separator: Self.delimeter, omittingEmptySubsequences: false) - .map { Data($0) } - guard lines.count > 1 else { return 0 } - _ = lines.popLast() - - result = lines - return lines.reduce(lines.count) { $0 + $1.count } - } - - guard parsed && !result.isEmpty else { break } - - for line in result { - framer.deliverInput(data: line, message: .init(instance: framer), isComplete: true) - } - } - return 0 - } - - func handleOutput( - framer: NWProtocolFramer.Instance, - message: NWProtocolFramer.Message, - messageLength: Int, - isComplete: Bool - ) { - do { - try framer.writeOutputNoCopy(length: messageLength) - framer.writeOutput(data: [Self.delimeter]) - } catch { - } - } -} diff --git a/Apple/NetworkExtension/PacketTunnelProvider.swift b/Apple/NetworkExtension/PacketTunnelProvider.swift index a07daa3..a8e42e0 100644 --- a/Apple/NetworkExtension/PacketTunnelProvider.swift +++ b/Apple/NetworkExtension/PacketTunnelProvider.swift @@ -1,84 +1,74 @@ -import BurrowShared +import AsyncAlgorithms +import BurrowConfiguration +import BurrowCore import libburrow import NetworkExtension import os class PacketTunnelProvider: NEPacketTunnelProvider { + enum Error: Swift.Error { + case missingTunnelConfiguration + } + private let logger = Logger.logger(for: PacketTunnelProvider.self) + private var client: TunnelClient { + get throws { try _client.get() } + } + private let _client: Result = Result { + try TunnelClient.unix(socketURL: Constants.socketURL) + } + override init() { do { - libburrow.spawnInProcess(socketPath: try Constants.socketURL.path) + libburrow.spawnInProcess( + socketPath: try Constants.socketURL.path(percentEncoded: false), + databasePath: try Constants.databaseURL.path(percentEncoded: false) + ) } catch { - logger.error("Failed to spawn: \(error)") + logger.error("Failed to spawn networking thread: \(error)") } } override func startTunnel(options: [String: NSObject]? = nil) async throws { do { - let client = try Client() - - let command = BurrowRequest(id: 0, command: "ServerConfig") - let data = try await client.request(command, type: Response>.self) - - let encoded = try JSONEncoder().encode(data.result) - self.logger.log("Received final data: \(String(decoding: encoded, as: UTF8.self))") - guard let serverconfig = data.result.Ok else { - throw BurrowError.resultIsError + let configuration = try await Array(client.tunnelConfiguration(.init()).prefix(1)).first + guard let settings = configuration?.settings else { + throw Error.missingTunnelConfiguration } - guard let tunNs = generateTunSettings(from: serverconfig) else { - throw BurrowError.addrDoesntExist - } - try await self.setTunnelNetworkSettings(tunNs) - self.logger.info("Set remote tunnel address to \(tunNs.tunnelRemoteAddress)") - - let startRequest = BurrowRequest( - id: .random(in: (.min)..<(.max)), - command: BurrowStartRequest( - Start: BurrowStartRequest.StartOptions( - tun: BurrowStartRequest.TunOptions( - name: nil, no_pi: false, tun_excl: false, tun_retrieve: true, address: [] - ) - ) - ) - ) - let response = try await client.request(startRequest, type: Response>.self) - self.logger.log("Received start server response: \(String(describing: response.result))") + try await setTunnelNetworkSettings(settings) + _ = try await client.tunnelStart(.init()) + logger.log("Started tunnel with network settings: \(settings)") } catch { - self.logger.error("Failed to start tunnel: \(error)") + logger.error("Failed to start tunnel: \(error)") throw error } } override func stopTunnel(with reason: NEProviderStopReason) async { do { - let client = try Client() - let command = BurrowRequest(id: 0, command: "Stop") - let data = try await client.request(command, type: Response>.self) - self.logger.log("Stopped client.") + _ = try await client.tunnelStop(.init()) + logger.log("Stopped client") } catch { - self.logger.error("Failed to stop tunnel: \(error)") + logger.error("Failed to stop tunnel: \(error)") } } - - private func generateTunSettings(from: ServerConfigData) -> NETunnelNetworkSettings? { - let cfig = from.ServerConfig - let nst = NEPacketTunnelNetworkSettings(tunnelRemoteAddress: "1.1.1.1") - var v4Addresses = [String]() - var v6Addresses = [String]() - for addr in cfig.address { - if IPv4Address(addr) != nil { - v6Addresses.append(addr) - } - if IPv6Address(addr) != nil { - v4Addresses.append(addr) - } - } - nst.ipv4Settings = NEIPv4Settings(addresses: v4Addresses, subnetMasks: v4Addresses.map { _ in - "255.255.255.0" - }) - nst.ipv6Settings = NEIPv6Settings(addresses: v6Addresses, networkPrefixLengths: v6Addresses.map { _ in 64 }) - logger.log("Initialized ipv4 settings: \(nst.ipv4Settings)") - return nst - } +} + +extension Burrow_TunnelConfigurationResponse { + fileprivate var settings: NEPacketTunnelNetworkSettings { + let ipv6Addresses = addresses.filter { IPv6Address($0) != nil } + + let settings = NEPacketTunnelNetworkSettings(tunnelRemoteAddress: "1.1.1.1") + settings.mtu = NSNumber(value: mtu) + settings.ipv4Settings = NEIPv4Settings( + addresses: addresses.filter { IPv4Address($0) != nil }, + subnetMasks: ["255.255.255.0"] + ) + settings.ipv6Settings = NEIPv6Settings( + addresses: ipv6Addresses, + networkPrefixLengths: ipv6Addresses.map { _ in 64 } + ) + return settings + } } diff --git a/Apple/NetworkExtension/libburrow/build-rust.sh b/Apple/NetworkExtension/libburrow/build-rust.sh index 1ac73fb..6f455a9 100755 --- a/Apple/NetworkExtension/libburrow/build-rust.sh +++ b/Apple/NetworkExtension/libburrow/build-rust.sh @@ -56,10 +56,10 @@ CARGO_ARGS+=("--lib") # Pass the configuration (Debug or Release) through to cargo if [[ $SWIFT_ACTIVE_COMPILATION_CONDITIONS == *DEBUG* ]]; then - CARGO_DIR="debug" + CARGO_TARGET_SUBDIR="debug" else CARGO_ARGS+=("--release") - CARGO_DIR="release" + CARGO_TARGET_SUBDIR="release" fi if [[ -x "$(command -v rustup)" ]]; then @@ -68,13 +68,16 @@ else CARGO_PATH="$(dirname $(readlink -f $(which cargo))):/usr/bin" fi +PROTOC=$(readlink -f $(which protoc)) +CARGO_PATH="$(dirname $PROTOC):$CARGO_PATH" + # Run cargo without the various environment variables set by Xcode. # Those variables can confuse cargo and the build scripts it runs. -env -i PATH="$CARGO_PATH" cargo build "${CARGO_ARGS[@]}" +env -i PATH="$CARGO_PATH" PROTOC="$PROTOC" CARGO_TARGET_DIR="${CONFIGURATION_TEMP_DIR}/target" IPHONEOS_DEPLOYMENT_TARGET="$IPHONEOS_DEPLOYMENT_TARGET" MACOSX_DEPLOYMENT_TARGET="$MACOSX_DEPLOYMENT_TARGET" cargo build "${CARGO_ARGS[@]}" mkdir -p "${BUILT_PRODUCTS_DIR}" # Use `lipo` to merge the architectures together into BUILT_PRODUCTS_DIR /usr/bin/xcrun --sdk $PLATFORM_NAME lipo \ - -create $(printf "${PROJECT_DIR}/../target/%q/${CARGO_DIR}/libburrow.a " "${RUST_TARGETS[@]}") \ + -create $(printf "${CONFIGURATION_TEMP_DIR}/target/%q/${CARGO_TARGET_SUBDIR}/libburrow.a " "${RUST_TARGETS[@]}") \ -output "${BUILT_PRODUCTS_DIR}/libburrow.a" diff --git a/Apple/NetworkExtension/libburrow/libburrow.h b/Apple/NetworkExtension/libburrow/libburrow.h index e500de4..59b4734 100644 --- a/Apple/NetworkExtension/libburrow/libburrow.h +++ b/Apple/NetworkExtension/libburrow/libburrow.h @@ -1,2 +1,2 @@ -__attribute__((__swift_name__("spawnInProcess(socketPath:)"))) -extern void spawn_in_process(const char * __nullable path); +__attribute__((__swift_name__("spawnInProcess(socketPath:databasePath:)"))) +extern void spawn_in_process(const char * __nullable socket_path, const char * __nullable db_path); diff --git a/Apple/Profiles/Burrow_Developer_ID.provisionprofile b/Apple/Profiles/Burrow_Developer_ID.provisionprofile new file mode 100644 index 0000000..3ecd831 Binary files /dev/null and b/Apple/Profiles/Burrow_Developer_ID.provisionprofile differ diff --git a/Apple/Profiles/Burrow_Network_Developer_ID.provisionprofile b/Apple/Profiles/Burrow_Network_Developer_ID.provisionprofile new file mode 100644 index 0000000..3ce7e37 Binary files /dev/null and b/Apple/Profiles/Burrow_Network_Developer_ID.provisionprofile differ diff --git a/Apple/Shared/Constants.swift b/Apple/Shared/Constants.swift deleted file mode 100644 index 634c500..0000000 --- a/Apple/Shared/Constants.swift +++ /dev/null @@ -1,23 +0,0 @@ -@_implementationOnly import Constants - -public enum Constants { - enum Error: Swift.Error { - case invalidAppGroupIdentifier - } - - public static let bundleIdentifier = AppBundleIdentifier - public static let appGroupIdentifier = AppGroupIdentifier - public static let networkExtensionBundleIdentifier = NetworkExtensionBundleIdentifier - - public static var groupContainerURL: URL { - get throws { try _groupContainerURL.get() } - } - - private static let _groupContainerURL: Result = { - guard let groupContainerURL = FileManager.default - .containerURL(forSecurityApplicationGroupIdentifier: appGroupIdentifier) else { - return .failure(.invalidAppGroupIdentifier) - } - return .success(groupContainerURL) - }() -} diff --git a/Apple/App/Assets.xcassets/AccentColor.colorset/Contents.json b/Apple/UI/Assets.xcassets/AccentColor.colorset/Contents.json similarity index 100% rename from Apple/App/Assets.xcassets/AccentColor.colorset/Contents.json rename to Apple/UI/Assets.xcassets/AccentColor.colorset/Contents.json diff --git a/Apple/UI/Assets.xcassets/AppIcon.appiconset/100.png b/Apple/UI/Assets.xcassets/AppIcon.appiconset/100.png new file mode 100644 index 0000000..f86c139 Binary files /dev/null and b/Apple/UI/Assets.xcassets/AppIcon.appiconset/100.png differ diff --git a/Apple/UI/Assets.xcassets/AppIcon.appiconset/1024.png b/Apple/UI/Assets.xcassets/AppIcon.appiconset/1024.png new file mode 100644 index 0000000..872c9ce Binary files /dev/null and b/Apple/UI/Assets.xcassets/AppIcon.appiconset/1024.png differ diff --git a/Apple/UI/Assets.xcassets/AppIcon.appiconset/114.png b/Apple/UI/Assets.xcassets/AppIcon.appiconset/114.png new file mode 100644 index 0000000..3bb278d Binary files /dev/null and b/Apple/UI/Assets.xcassets/AppIcon.appiconset/114.png differ diff --git a/Apple/UI/Assets.xcassets/AppIcon.appiconset/120.png b/Apple/UI/Assets.xcassets/AppIcon.appiconset/120.png new file mode 100644 index 0000000..185615e Binary files /dev/null and b/Apple/UI/Assets.xcassets/AppIcon.appiconset/120.png differ diff --git a/Apple/UI/Assets.xcassets/AppIcon.appiconset/128.png b/Apple/UI/Assets.xcassets/AppIcon.appiconset/128.png new file mode 100644 index 0000000..51bd97c Binary files /dev/null and b/Apple/UI/Assets.xcassets/AppIcon.appiconset/128.png differ diff --git a/Apple/UI/Assets.xcassets/AppIcon.appiconset/144.png b/Apple/UI/Assets.xcassets/AppIcon.appiconset/144.png new file mode 100644 index 0000000..b05e371 Binary files /dev/null and b/Apple/UI/Assets.xcassets/AppIcon.appiconset/144.png differ diff --git a/Apple/UI/Assets.xcassets/AppIcon.appiconset/152.png b/Apple/UI/Assets.xcassets/AppIcon.appiconset/152.png new file mode 100644 index 0000000..c95ea8a Binary files /dev/null and b/Apple/UI/Assets.xcassets/AppIcon.appiconset/152.png differ diff --git a/Apple/UI/Assets.xcassets/AppIcon.appiconset/16.png b/Apple/UI/Assets.xcassets/AppIcon.appiconset/16.png new file mode 100644 index 0000000..3cb15a5 Binary files /dev/null and b/Apple/UI/Assets.xcassets/AppIcon.appiconset/16.png differ diff --git a/Apple/UI/Assets.xcassets/AppIcon.appiconset/167.png b/Apple/UI/Assets.xcassets/AppIcon.appiconset/167.png new file mode 100644 index 0000000..a3ad6a2 Binary files /dev/null and b/Apple/UI/Assets.xcassets/AppIcon.appiconset/167.png differ diff --git a/Apple/UI/Assets.xcassets/AppIcon.appiconset/172.png b/Apple/UI/Assets.xcassets/AppIcon.appiconset/172.png new file mode 100644 index 0000000..9f3bdb4 Binary files /dev/null and b/Apple/UI/Assets.xcassets/AppIcon.appiconset/172.png differ diff --git a/Apple/UI/Assets.xcassets/AppIcon.appiconset/180.png b/Apple/UI/Assets.xcassets/AppIcon.appiconset/180.png new file mode 100644 index 0000000..53c1237 Binary files /dev/null and b/Apple/UI/Assets.xcassets/AppIcon.appiconset/180.png differ diff --git a/Apple/UI/Assets.xcassets/AppIcon.appiconset/196.png b/Apple/UI/Assets.xcassets/AppIcon.appiconset/196.png new file mode 100644 index 0000000..ea95961 Binary files /dev/null and b/Apple/UI/Assets.xcassets/AppIcon.appiconset/196.png differ diff --git a/Apple/UI/Assets.xcassets/AppIcon.appiconset/20.png b/Apple/UI/Assets.xcassets/AppIcon.appiconset/20.png new file mode 100644 index 0000000..aec8236 Binary files /dev/null and b/Apple/UI/Assets.xcassets/AppIcon.appiconset/20.png differ diff --git a/Apple/UI/Assets.xcassets/AppIcon.appiconset/216.png b/Apple/UI/Assets.xcassets/AppIcon.appiconset/216.png new file mode 100644 index 0000000..9f0e3ce Binary files /dev/null and b/Apple/UI/Assets.xcassets/AppIcon.appiconset/216.png differ diff --git a/Apple/UI/Assets.xcassets/AppIcon.appiconset/256.png b/Apple/UI/Assets.xcassets/AppIcon.appiconset/256.png new file mode 100644 index 0000000..a82ce93 Binary files /dev/null and b/Apple/UI/Assets.xcassets/AppIcon.appiconset/256.png differ diff --git a/Apple/UI/Assets.xcassets/AppIcon.appiconset/29.png b/Apple/UI/Assets.xcassets/AppIcon.appiconset/29.png new file mode 100644 index 0000000..8dc25c1 Binary files /dev/null and b/Apple/UI/Assets.xcassets/AppIcon.appiconset/29.png differ diff --git a/Apple/UI/Assets.xcassets/AppIcon.appiconset/32.png b/Apple/UI/Assets.xcassets/AppIcon.appiconset/32.png new file mode 100644 index 0000000..655a424 Binary files /dev/null and b/Apple/UI/Assets.xcassets/AppIcon.appiconset/32.png differ diff --git a/Apple/UI/Assets.xcassets/AppIcon.appiconset/40.png b/Apple/UI/Assets.xcassets/AppIcon.appiconset/40.png new file mode 100644 index 0000000..1f7f5e9 Binary files /dev/null and b/Apple/UI/Assets.xcassets/AppIcon.appiconset/40.png differ diff --git a/Apple/UI/Assets.xcassets/AppIcon.appiconset/48.png b/Apple/UI/Assets.xcassets/AppIcon.appiconset/48.png new file mode 100644 index 0000000..4a67ebf Binary files /dev/null and b/Apple/UI/Assets.xcassets/AppIcon.appiconset/48.png differ diff --git a/Apple/UI/Assets.xcassets/AppIcon.appiconset/50.png b/Apple/UI/Assets.xcassets/AppIcon.appiconset/50.png new file mode 100644 index 0000000..88985d8 Binary files /dev/null and b/Apple/UI/Assets.xcassets/AppIcon.appiconset/50.png differ diff --git a/Apple/UI/Assets.xcassets/AppIcon.appiconset/512.png b/Apple/UI/Assets.xcassets/AppIcon.appiconset/512.png new file mode 100644 index 0000000..e5cbf6a Binary files /dev/null and b/Apple/UI/Assets.xcassets/AppIcon.appiconset/512.png differ diff --git a/Apple/UI/Assets.xcassets/AppIcon.appiconset/55.png b/Apple/UI/Assets.xcassets/AppIcon.appiconset/55.png new file mode 100644 index 0000000..dc079ea Binary files /dev/null and b/Apple/UI/Assets.xcassets/AppIcon.appiconset/55.png differ diff --git a/Apple/UI/Assets.xcassets/AppIcon.appiconset/57.png b/Apple/UI/Assets.xcassets/AppIcon.appiconset/57.png new file mode 100644 index 0000000..de4fddc Binary files /dev/null and b/Apple/UI/Assets.xcassets/AppIcon.appiconset/57.png differ diff --git a/Apple/UI/Assets.xcassets/AppIcon.appiconset/58.png b/Apple/UI/Assets.xcassets/AppIcon.appiconset/58.png new file mode 100644 index 0000000..961adad Binary files /dev/null and b/Apple/UI/Assets.xcassets/AppIcon.appiconset/58.png differ diff --git a/Apple/UI/Assets.xcassets/AppIcon.appiconset/60.png b/Apple/UI/Assets.xcassets/AppIcon.appiconset/60.png new file mode 100644 index 0000000..2a9e939 Binary files /dev/null and b/Apple/UI/Assets.xcassets/AppIcon.appiconset/60.png differ diff --git a/Apple/UI/Assets.xcassets/AppIcon.appiconset/64.png b/Apple/UI/Assets.xcassets/AppIcon.appiconset/64.png new file mode 100644 index 0000000..c67e407 Binary files /dev/null and b/Apple/UI/Assets.xcassets/AppIcon.appiconset/64.png differ diff --git a/Apple/UI/Assets.xcassets/AppIcon.appiconset/72.png b/Apple/UI/Assets.xcassets/AppIcon.appiconset/72.png new file mode 100644 index 0000000..d09aebe Binary files /dev/null and b/Apple/UI/Assets.xcassets/AppIcon.appiconset/72.png differ diff --git a/Apple/UI/Assets.xcassets/AppIcon.appiconset/76.png b/Apple/UI/Assets.xcassets/AppIcon.appiconset/76.png new file mode 100644 index 0000000..3e649b6 Binary files /dev/null and b/Apple/UI/Assets.xcassets/AppIcon.appiconset/76.png differ diff --git a/Apple/UI/Assets.xcassets/AppIcon.appiconset/80.png b/Apple/UI/Assets.xcassets/AppIcon.appiconset/80.png new file mode 100644 index 0000000..6dad29f Binary files /dev/null and b/Apple/UI/Assets.xcassets/AppIcon.appiconset/80.png differ diff --git a/Apple/UI/Assets.xcassets/AppIcon.appiconset/87.png b/Apple/UI/Assets.xcassets/AppIcon.appiconset/87.png new file mode 100644 index 0000000..a8ccb38 Binary files /dev/null and b/Apple/UI/Assets.xcassets/AppIcon.appiconset/87.png differ diff --git a/Apple/UI/Assets.xcassets/AppIcon.appiconset/88.png b/Apple/UI/Assets.xcassets/AppIcon.appiconset/88.png new file mode 100644 index 0000000..b1a478a Binary files /dev/null and b/Apple/UI/Assets.xcassets/AppIcon.appiconset/88.png differ diff --git a/Apple/UI/Assets.xcassets/AppIcon.appiconset/Contents.json b/Apple/UI/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..f78687a --- /dev/null +++ b/Apple/UI/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,344 @@ +{ + "images" : [ + { + "filename" : "40.png", + "idiom" : "iphone", + "scale" : "2x", + "size" : "20x20" + }, + { + "filename" : "60.png", + "idiom" : "iphone", + "scale" : "3x", + "size" : "20x20" + }, + { + "filename" : "29.png", + "idiom" : "iphone", + "scale" : "1x", + "size" : "29x29" + }, + { + "filename" : "58.png", + "idiom" : "iphone", + "scale" : "2x", + "size" : "29x29" + }, + { + "filename" : "87.png", + "idiom" : "iphone", + "scale" : "3x", + "size" : "29x29" + }, + { + "filename" : "80.png", + "idiom" : "iphone", + "scale" : "2x", + "size" : "40x40" + }, + { + "filename" : "120.png", + "idiom" : "iphone", + "scale" : "3x", + "size" : "40x40" + }, + { + "filename" : "57.png", + "idiom" : "iphone", + "scale" : "1x", + "size" : "57x57" + }, + { + "filename" : "114.png", + "idiom" : "iphone", + "scale" : "2x", + "size" : "57x57" + }, + { + "filename" : "120.png", + "idiom" : "iphone", + "scale" : "2x", + "size" : "60x60" + }, + { + "filename" : "180.png", + "idiom" : "iphone", + "scale" : "3x", + "size" : "60x60" + }, + { + "filename" : "20.png", + "idiom" : "ipad", + "scale" : "1x", + "size" : "20x20" + }, + { + "filename" : "40.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "20x20" + }, + { + "filename" : "29.png", + "idiom" : "ipad", + "scale" : "1x", + "size" : "29x29" + }, + { + "filename" : "58.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "29x29" + }, + { + "filename" : "40.png", + "idiom" : "ipad", + "scale" : "1x", + "size" : "40x40" + }, + { + "filename" : "80.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "40x40" + }, + { + "filename" : "50.png", + "idiom" : "ipad", + "scale" : "1x", + "size" : "50x50" + }, + { + "filename" : "100.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "50x50" + }, + { + "filename" : "72.png", + "idiom" : "ipad", + "scale" : "1x", + "size" : "72x72" + }, + { + "filename" : "144.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "72x72" + }, + { + "filename" : "76.png", + "idiom" : "ipad", + "scale" : "1x", + "size" : "76x76" + }, + { + "filename" : "152.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "76x76" + }, + { + "filename" : "167.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "83.5x83.5" + }, + { + "filename" : "1024.png", + "idiom" : "ios-marketing", + "scale" : "1x", + "size" : "1024x1024" + }, + { + "filename" : "16.png", + "idiom" : "mac", + "scale" : "1x", + "size" : "16x16" + }, + { + "filename" : "32.png", + "idiom" : "mac", + "scale" : "2x", + "size" : "16x16" + }, + { + "filename" : "32.png", + "idiom" : "mac", + "scale" : "1x", + "size" : "32x32" + }, + { + "filename" : "64.png", + "idiom" : "mac", + "scale" : "2x", + "size" : "32x32" + }, + { + "filename" : "128.png", + "idiom" : "mac", + "scale" : "1x", + "size" : "128x128" + }, + { + "filename" : "256.png", + "idiom" : "mac", + "scale" : "2x", + "size" : "128x128" + }, + { + "filename" : "256.png", + "idiom" : "mac", + "scale" : "1x", + "size" : "256x256" + }, + { + "filename" : "512.png", + "idiom" : "mac", + "scale" : "2x", + "size" : "256x256" + }, + { + "filename" : "512.png", + "idiom" : "mac", + "scale" : "1x", + "size" : "512x512" + }, + { + "filename" : "1024.png", + "idiom" : "mac", + "scale" : "2x", + "size" : "512x512" + }, + { + "filename" : "48.png", + "idiom" : "watch", + "role" : "notificationCenter", + "scale" : "2x", + "size" : "24x24", + "subtype" : "38mm" + }, + { + "filename" : "55.png", + "idiom" : "watch", + "role" : "notificationCenter", + "scale" : "2x", + "size" : "27.5x27.5", + "subtype" : "42mm" + }, + { + "filename" : "58.png", + "idiom" : "watch", + "role" : "companionSettings", + "scale" : "2x", + "size" : "29x29" + }, + { + "filename" : "87.png", + "idiom" : "watch", + "role" : "companionSettings", + "scale" : "3x", + "size" : "29x29" + }, + { + "idiom" : "watch", + "role" : "notificationCenter", + "scale" : "2x", + "size" : "33x33", + "subtype" : "45mm" + }, + { + "filename" : "80.png", + "idiom" : "watch", + "role" : "appLauncher", + "scale" : "2x", + "size" : "40x40", + "subtype" : "38mm" + }, + { + "filename" : "88.png", + "idiom" : "watch", + "role" : "appLauncher", + "scale" : "2x", + "size" : "44x44", + "subtype" : "40mm" + }, + { + "idiom" : "watch", + "role" : "appLauncher", + "scale" : "2x", + "size" : "46x46", + "subtype" : "41mm" + }, + { + "filename" : "100.png", + "idiom" : "watch", + "role" : "appLauncher", + "scale" : "2x", + "size" : "50x50", + "subtype" : "44mm" + }, + { + "idiom" : "watch", + "role" : "appLauncher", + "scale" : "2x", + "size" : "51x51", + "subtype" : "45mm" + }, + { + "idiom" : "watch", + "role" : "appLauncher", + "scale" : "2x", + "size" : "54x54", + "subtype" : "49mm" + }, + { + "filename" : "172.png", + "idiom" : "watch", + "role" : "quickLook", + "scale" : "2x", + "size" : "86x86", + "subtype" : "38mm" + }, + { + "filename" : "196.png", + "idiom" : "watch", + "role" : "quickLook", + "scale" : "2x", + "size" : "98x98", + "subtype" : "42mm" + }, + { + "filename" : "216.png", + "idiom" : "watch", + "role" : "quickLook", + "scale" : "2x", + "size" : "108x108", + "subtype" : "44mm" + }, + { + "idiom" : "watch", + "role" : "quickLook", + "scale" : "2x", + "size" : "117x117", + "subtype" : "45mm" + }, + { + "idiom" : "watch", + "role" : "quickLook", + "scale" : "2x", + "size" : "129x129", + "subtype" : "49mm" + }, + { + "filename" : "1024.png", + "idiom" : "watch-marketing", + "scale" : "1x", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Apple/App/Assets.xcassets/Contents.json b/Apple/UI/Assets.xcassets/Contents.json similarity index 100% rename from Apple/App/Assets.xcassets/Contents.json rename to Apple/UI/Assets.xcassets/Contents.json diff --git a/Apple/App/Assets.xcassets/HackClub.colorset/Contents.json b/Apple/UI/Assets.xcassets/HackClub.colorset/Contents.json similarity index 100% rename from Apple/App/Assets.xcassets/HackClub.colorset/Contents.json rename to Apple/UI/Assets.xcassets/HackClub.colorset/Contents.json diff --git a/Apple/App/Assets.xcassets/HackClub.imageset/Contents.json b/Apple/UI/Assets.xcassets/HackClub.imageset/Contents.json similarity index 100% rename from Apple/App/Assets.xcassets/HackClub.imageset/Contents.json rename to Apple/UI/Assets.xcassets/HackClub.imageset/Contents.json diff --git a/Apple/App/Assets.xcassets/HackClub.imageset/flag-standalone-wtransparent.pdf b/Apple/UI/Assets.xcassets/HackClub.imageset/flag-standalone-wtransparent.pdf similarity index 100% rename from Apple/App/Assets.xcassets/HackClub.imageset/flag-standalone-wtransparent.pdf rename to Apple/UI/Assets.xcassets/HackClub.imageset/flag-standalone-wtransparent.pdf diff --git a/Apple/App/Assets.xcassets/WireGuard.colorset/Contents.json b/Apple/UI/Assets.xcassets/WireGuard.colorset/Contents.json similarity index 100% rename from Apple/App/Assets.xcassets/WireGuard.colorset/Contents.json rename to Apple/UI/Assets.xcassets/WireGuard.colorset/Contents.json diff --git a/Apple/App/Assets.xcassets/WireGuard.imageset/Contents.json b/Apple/UI/Assets.xcassets/WireGuard.imageset/Contents.json similarity index 100% rename from Apple/App/Assets.xcassets/WireGuard.imageset/Contents.json rename to Apple/UI/Assets.xcassets/WireGuard.imageset/Contents.json diff --git a/Apple/App/Assets.xcassets/WireGuard.imageset/WireGuard.svg b/Apple/UI/Assets.xcassets/WireGuard.imageset/WireGuard.svg similarity index 100% rename from Apple/App/Assets.xcassets/WireGuard.imageset/WireGuard.svg rename to Apple/UI/Assets.xcassets/WireGuard.imageset/WireGuard.svg diff --git a/Apple/App/Assets.xcassets/WireGuardTitle.imageset/Contents.json b/Apple/UI/Assets.xcassets/WireGuardTitle.imageset/Contents.json similarity index 100% rename from Apple/App/Assets.xcassets/WireGuardTitle.imageset/Contents.json rename to Apple/UI/Assets.xcassets/WireGuardTitle.imageset/Contents.json diff --git a/Apple/App/Assets.xcassets/WireGuardTitle.imageset/WireGuardTitle.svg b/Apple/UI/Assets.xcassets/WireGuardTitle.imageset/WireGuardTitle.svg similarity index 100% rename from Apple/App/Assets.xcassets/WireGuardTitle.imageset/WireGuardTitle.svg rename to Apple/UI/Assets.xcassets/WireGuardTitle.imageset/WireGuardTitle.svg diff --git a/Apple/UI/BurrowView.swift b/Apple/UI/BurrowView.swift new file mode 100644 index 0000000..96467c7 --- /dev/null +++ b/Apple/UI/BurrowView.swift @@ -0,0 +1,75 @@ +import AuthenticationServices +import SwiftUI + +#if !os(macOS) +public struct BurrowView: View { + @Environment(\.webAuthenticationSession) + private var webAuthenticationSession + + public var body: some View { + NavigationStack { + VStack { + HStack { + Text("Networks") + .font(.largeTitle) + .fontWeight(.bold) + Spacer() + Menu { + Button("Hack Club", action: addHackClubNetwork) + Button("WireGuard", action: addWireGuardNetwork) + } label: { + Image(systemName: "plus.circle.fill") + .font(.title) + .accessibilityLabel("Add") + } + } + .padding(.top) + NetworkCarouselView() + Spacer() + TunnelStatusView() + TunnelButton() + .padding(.bottom) + } + .padding() + .handleOAuth2Callback() + } + } + + public init() { + } + + private func addHackClubNetwork() { + Task { + try await authenticateWithSlack() + } + } + + private func addWireGuardNetwork() { + } + + private func authenticateWithSlack() async throws { + guard + let authorizationEndpoint = URL(string: "https://slack.com/openid/connect/authorize"), + let tokenEndpoint = URL(string: "https://slack.com/api/openid.connect.token"), + let redirectURI = URL(string: "https://burrow.rs/callback/oauth2") else { return } + let session = OAuth2.Session( + authorizationEndpoint: authorizationEndpoint, + tokenEndpoint: tokenEndpoint, + redirectURI: redirectURI, + scopes: ["openid", "profile"], + clientID: "2210535565.6884042183125", + clientSecret: "2793c8a5255cae38830934c664eeb62d" + ) + let response = try await session.authorize(webAuthenticationSession) + } +} + +#if DEBUG +struct NetworkView_Previews: PreviewProvider { + static var previews: some View { + BurrowView() + .environment(\.tunnel, PreviewTunnel()) + } +} +#endif +#endif diff --git a/Apple/App/FloatingButtonStyle.swift b/Apple/UI/FloatingButtonStyle.swift similarity index 100% rename from Apple/App/FloatingButtonStyle.swift rename to Apple/UI/FloatingButtonStyle.swift diff --git a/Apple/App/MenuItemToggleView.swift b/Apple/UI/MenuItemToggleView.swift similarity index 87% rename from Apple/App/MenuItemToggleView.swift rename to Apple/UI/MenuItemToggleView.swift index 07db51d..ef5e8ee 100644 --- a/Apple/App/MenuItemToggleView.swift +++ b/Apple/UI/MenuItemToggleView.swift @@ -7,11 +7,11 @@ import SwiftUI -struct MenuItemToggleView: View { +public struct MenuItemToggleView: View { @Environment(\.tunnel) var tunnel: Tunnel - var body: some View { + public var body: some View { HStack { VStack(alignment: .leading) { Text("Burrow") @@ -30,10 +30,13 @@ struct MenuItemToggleView: View { .padding(10) .frame(minWidth: 300, minHeight: 32, maxHeight: 32) } + + public init() { + } } extension Tunnel { - fileprivate var toggleDisabled: Bool { + @MainActor fileprivate var toggleDisabled: Bool { switch status { case .disconnected, .permissionRequired, .connected, .disconnecting: false @@ -42,7 +45,7 @@ extension Tunnel { } } - var toggleIsOn: Binding { + @MainActor var toggleIsOn: Binding { Binding { switch status { case .connecting, .reasserting, .connected: diff --git a/Apple/UI/NetworkCarouselView.swift b/Apple/UI/NetworkCarouselView.swift new file mode 100644 index 0000000..f969356 --- /dev/null +++ b/Apple/UI/NetworkCarouselView.swift @@ -0,0 +1,39 @@ +import SwiftUI + +struct NetworkCarouselView: View { + var networks: [any Network] = [ + HackClub(id: 1), + HackClub(id: 2), + WireGuard(id: 4), + HackClub(id: 5) + ] + + var body: some View { + ScrollView(.horizontal) { + LazyHStack { + ForEach(networks, id: \.id) { network in + NetworkView(network: network) + .containerRelativeFrame(.horizontal, count: 10, span: 7, spacing: 0, alignment: .center) + .scrollTransition(.interactive, axis: .horizontal) { content, phase in + content + .scaleEffect(1.0 - abs(phase.value) * 0.1) + } + } + } + } + .scrollTargetLayout() + .scrollClipDisabled() + .scrollIndicators(.hidden) + .defaultScrollAnchor(.center) + .scrollTargetBehavior(.viewAligned) + .containerRelativeFrame(.horizontal) + } +} + +#if DEBUG +struct NetworkCarouselView_Previews: PreviewProvider { + static var previews: some View { + NetworkCarouselView() + } +} +#endif diff --git a/Apple/App/NetworkExtension+Async.swift b/Apple/UI/NetworkExtension+Async.swift similarity index 82% rename from Apple/App/NetworkExtension+Async.swift rename to Apple/UI/NetworkExtension+Async.swift index 4833efb..5820e7f 100644 --- a/Apple/App/NetworkExtension+Async.swift +++ b/Apple/UI/NetworkExtension+Async.swift @@ -1,6 +1,6 @@ import NetworkExtension -extension NEVPNManager { +extension NEVPNManager: @unchecked @retroactive Sendable { func remove() async throws { _ = try await withUnsafeThrowingContinuation { continuation in removeFromPreferences(completionHandler: completion(continuation)) @@ -14,7 +14,7 @@ extension NEVPNManager { } } -extension NETunnelProviderManager { +extension NETunnelProviderManager: @unchecked @retroactive Sendable { class var managers: [NETunnelProviderManager] { get async throws { try await withUnsafeThrowingContinuation { continuation in @@ -34,7 +34,7 @@ private func completion(_ continuation: UnsafeContinuation) -> (Err } } -private func completion(_ continuation: UnsafeContinuation) -> (T?, Error?) -> Void { +private func completion(_ continuation: UnsafeContinuation) -> (T?, Error?) -> Void { return { value, error in if let error { continuation.resume(throwing: error) diff --git a/Apple/App/NetworkExtensionTunnel.swift b/Apple/UI/NetworkExtensionTunnel.swift similarity index 67% rename from Apple/App/NetworkExtensionTunnel.swift rename to Apple/UI/NetworkExtensionTunnel.swift index 08002de..7aaa3b1 100644 --- a/Apple/App/NetworkExtensionTunnel.swift +++ b/Apple/UI/NetworkExtensionTunnel.swift @@ -1,22 +1,23 @@ -import BurrowShared +import BurrowCore import NetworkExtension @Observable -class NetworkExtensionTunnel: Tunnel { - @MainActor private(set) var status: TunnelStatus = .unknown - private var error: NEVPNError? +public final class NetworkExtensionTunnel: Tunnel { + @MainActor public private(set) var status: TunnelStatus = .unknown + @MainActor private var error: NEVPNError? private let logger = Logger.logger(for: Tunnel.self) private let bundleIdentifier: String - private var tasks: [Task] = [] + private let configurationChanged: Task + private let statusChanged: Task // Each manager corresponds to one entry in the Settings app. // Our goal is to maintain a single manager, so we create one if none exist and delete any extra. - private var managers: [NEVPNManager]? { + @MainActor private var managers: [NEVPNManager]? { didSet { Task { await updateStatus() } } } - private var currentStatus: TunnelStatus { + @MainActor private var currentStatus: TunnelStatus { guard let managers = managers else { guard let error = error else { return .unknown @@ -41,35 +42,40 @@ class NetworkExtensionTunnel: Tunnel { return manager.connection.tunnelStatus } - convenience init() { - self.init(Constants.networkExtensionBundleIdentifier) - } - - init(_ bundleIdentifier: String) { + public init(bundleIdentifier: String) { self.bundleIdentifier = bundleIdentifier let center = NotificationCenter.default - let configurationChanged = Task { [weak self] in - for try await _ in center.notifications(named: .NEVPNConfigurationChange).map({ _ in () }) { - await self?.update() + let tunnel: OSAllocatedUnfairLock = .init(initialState: .none) + configurationChanged = Task { + for try await _ in center.notifications(named: .NEVPNConfigurationChange) { + try Task.checkCancellation() + await tunnel.withLock { $0 }?.update() } } - let statusChanged = Task { [weak self] in - for try await _ in center.notifications(named: .NEVPNStatusDidChange).map({ _ in () }) { - await self?.updateStatus() + statusChanged = Task { + for try await _ in center.notifications(named: .NEVPNStatusDidChange) { + try Task.checkCancellation() + await tunnel.withLock { $0 }?.updateStatus() } } - tasks = [configurationChanged, statusChanged] + tunnel.withLock { $0 = self } Task { await update() } } private func update() async { do { - managers = try await NETunnelProviderManager.managers + let result = try await NETunnelProviderManager.managers + await MainActor.run { + managers = result + status = currentStatus + } await self.updateStatus() } catch let vpnError as NEVPNError { - error = vpnError + await MainActor.run { + error = vpnError + } } catch { logger.error("Failed to update VPN configurations: \(error)") } @@ -82,12 +88,7 @@ class NetworkExtensionTunnel: Tunnel { } func configure() async throws { - if managers == nil { - await update() - } - - guard let managers = managers else { return } - + let managers = try await NETunnelProviderManager.managers if managers.count > 1 { try await withThrowingTaskGroup(of: Void.self, returning: Void.self) { group in for manager in managers.suffix(from: 1) { @@ -110,9 +111,9 @@ class NetworkExtensionTunnel: Tunnel { try await manager.save() } - func start() { - guard let manager = managers?.first else { return } + public func start() { Task { + guard let manager = try await NETunnelProviderManager.managers.first else { return } do { if !manager.isEnabled { manager.isEnabled = true @@ -125,12 +126,14 @@ class NetworkExtensionTunnel: Tunnel { } } - func stop() { - guard let manager = managers?.first else { return } - manager.connection.stopVPNTunnel() + public func stop() { + Task { + guard let manager = try await NETunnelProviderManager.managers.first else { return } + manager.connection.stopVPNTunnel() + } } - func enable() { + public func enable() { Task { do { try await configure() @@ -141,7 +144,8 @@ class NetworkExtensionTunnel: Tunnel { } deinit { - tasks.forEach { $0.cancel() } + configurationChanged.cancel() + statusChanged.cancel() } } diff --git a/Apple/UI/NetworkView.swift b/Apple/UI/NetworkView.swift new file mode 100644 index 0000000..b839d65 --- /dev/null +++ b/Apple/UI/NetworkView.swift @@ -0,0 +1,38 @@ +import SwiftUI + +struct NetworkView: View { + var color: Color + var content: () -> Content + + private var gradient: LinearGradient { + LinearGradient( + colors: [ + color.opacity(0.8), + color + ], + startPoint: .init(x: 0.2, y: 0), + endPoint: .init(x: 0.8, y: 1) + ) + } + + var body: some View { + content() + .frame(maxWidth: .infinity, minHeight: 175, maxHeight: 175) + .background( + RoundedRectangle(cornerRadius: 10) + .fill(gradient) + .background( + RoundedRectangle(cornerRadius: 10) + .fill(.white) + ) + ) + .shadow(color: .black.opacity(0.1), radius: 3.0, x: 0, y: 2) + } +} + +extension NetworkView where Content == AnyView { + init(network: any Network) { + color = network.backgroundColor + content = { AnyView(network.label) } + } +} diff --git a/Apple/App/Networks/HackClub.swift b/Apple/UI/Networks/HackClub.swift similarity index 76% rename from Apple/App/Networks/HackClub.swift rename to Apple/UI/Networks/HackClub.swift index f7df674..b1c2023 100644 --- a/Apple/App/Networks/HackClub.swift +++ b/Apple/UI/Networks/HackClub.swift @@ -1,10 +1,14 @@ +import BurrowCore import SwiftUI struct HackClub: Network { - var id: String + typealias NetworkType = Burrow_WireGuardNetwork + static let type: Burrow_NetworkType = .hackClub + + var id: Int32 var backgroundColor: Color { .init("HackClub") } - var label: some View { + @MainActor var label: some View { GeometryReader { reader in VStack(alignment: .leading) { Image("HackClub") diff --git a/Apple/UI/Networks/Network.swift b/Apple/UI/Networks/Network.swift new file mode 100644 index 0000000..c6d5fba --- /dev/null +++ b/Apple/UI/Networks/Network.swift @@ -0,0 +1,36 @@ +import Atomics +import BurrowCore +import SwiftProtobuf +import SwiftUI + +protocol Network { + associatedtype NetworkType: Message + associatedtype Label: View + + static var type: Burrow_NetworkType { get } + + var id: Int32 { get } + var backgroundColor: Color { get } + + @MainActor var label: Label { get } +} + +@Observable +@MainActor +final class NetworkViewModel: Sendable { + private(set) var networks: [Burrow_Network] = [] + + private var task: Task! + + init(socketURL: URL) { + task = Task { [weak self] in + let client = NetworksClient.unix(socketURL: socketURL) + for try await networks in client.networkList(.init()) { + guard let viewModel = self else { continue } + Task { @MainActor in + viewModel.networks = networks.network + } + } + } + } +} diff --git a/Apple/App/Networks/WireGuard.swift b/Apple/UI/Networks/WireGuard.swift similarity index 82% rename from Apple/App/Networks/WireGuard.swift rename to Apple/UI/Networks/WireGuard.swift index 499288a..cba67ef 100644 --- a/Apple/App/Networks/WireGuard.swift +++ b/Apple/UI/Networks/WireGuard.swift @@ -1,10 +1,14 @@ +import BurrowCore import SwiftUI struct WireGuard: Network { - var id: String + typealias NetworkType = Burrow_WireGuardNetwork + static let type: BurrowCore.Burrow_NetworkType = .wireGuard + + var id: Int32 var backgroundColor: Color { .init("WireGuard") } - var label: some View { + @MainActor var label: some View { GeometryReader { reader in VStack(alignment: .leading) { HStack { diff --git a/Apple/UI/OAuth2.swift b/Apple/UI/OAuth2.swift new file mode 100644 index 0000000..0fafc8d --- /dev/null +++ b/Apple/UI/OAuth2.swift @@ -0,0 +1,293 @@ +import AuthenticationServices +import Foundation +import os +import SwiftUI + +enum OAuth2 { + enum Error: Swift.Error { + case unknown + case invalidAuthorizationURL + case invalidCallbackURL + case invalidRedirectURI + } + + struct Credential { + var accessToken: String + var refreshToken: String? + var expirationDate: Date? + } + + struct Session { + var authorizationEndpoint: URL + var tokenEndpoint: URL + var redirectURI: URL + var responseType = OAuth2.ResponseType.code + var scopes: Set + var clientID: String + var clientSecret: String + + fileprivate static let queue: OSAllocatedUnfairLock<[Int: CheckedContinuation]> = { + .init(initialState: [:]) + }() + + fileprivate static func handle(url: URL) { + let continuations = queue.withLock { continuations in + let copy = continuations + continuations.removeAll() + return copy + } + for (_, continuation) in continuations { + continuation.resume(returning: url) + } + } + + init( + authorizationEndpoint: URL, + tokenEndpoint: URL, + redirectURI: URL, + scopes: Set, + clientID: String, + clientSecret: String + ) { + self.authorizationEndpoint = authorizationEndpoint + self.tokenEndpoint = tokenEndpoint + self.redirectURI = redirectURI + self.scopes = scopes + self.clientID = clientID + self.clientSecret = clientSecret + } + + private var authorizationURL: URL { + get throws { + var queryItems: [URLQueryItem] = [ + .init(name: "client_id", value: clientID), + .init(name: "response_type", value: responseType.rawValue), + .init(name: "redirect_uri", value: redirectURI.absoluteString) + ] + if !scopes.isEmpty { + queryItems.append(.init(name: "scope", value: scopes.joined(separator: ","))) + } + guard var components = URLComponents(url: authorizationEndpoint, resolvingAgainstBaseURL: false) else { + throw OAuth2.Error.invalidAuthorizationURL + } + components.queryItems = queryItems + guard let authorizationURL = components.url else { throw OAuth2.Error.invalidAuthorizationURL } + return authorizationURL + } + } + + private func handle(callbackURL: URL) async throws -> OAuth2.AccessTokenResponse { + switch responseType { + case .code: + guard let components = URLComponents(url: callbackURL, resolvingAgainstBaseURL: false) else { + throw OAuth2.Error.invalidCallbackURL + } + return try await handle(response: try components.decode(OAuth2.CodeResponse.self)) + default: + throw OAuth2.Error.invalidCallbackURL + } + } + + private func handle(response: OAuth2.CodeResponse) async throws -> OAuth2.AccessTokenResponse { + var components = URLComponents() + components.queryItems = [ + .init(name: "client_id", value: clientID), + .init(name: "client_secret", value: clientSecret), + .init(name: "grant_type", value: GrantType.authorizationCode.rawValue), + .init(name: "code", value: response.code), + .init(name: "redirect_uri", value: redirectURI.absoluteString) + ] + let httpBody = Data(components.percentEncodedQuery!.utf8) + + var request = URLRequest(url: tokenEndpoint) + request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type") + request.httpMethod = "POST" + request.httpBody = httpBody + + let session = URLSession(configuration: .ephemeral) + let (data, _) = try await session.data(for: request) + return try OAuth2.decoder.decode(OAuth2.AccessTokenResponse.self, from: data) + } + + func authorize(_ session: WebAuthenticationSession) async throws -> Credential { + let authorizationURL = try authorizationURL + let callbackURL = try await session.start( + url: authorizationURL, + redirectURI: redirectURI + ) + return try await handle(callbackURL: callbackURL).credential + } + } + + private struct CodeResponse: Codable { + var code: String + var state: String? + } + + private struct AccessTokenResponse: Codable { + var accessToken: String + var tokenType: TokenType + var expiresIn: Double? + var refreshToken: String? + + var credential: Credential { + .init( + accessToken: accessToken, + refreshToken: refreshToken, + expirationDate: expiresIn.map { Date(timeIntervalSinceNow: $0) } + ) + } + } + + enum TokenType: Codable, RawRepresentable { + case bearer + case unknown(String) + + init(rawValue: String) { + self = switch rawValue.lowercased() { + case "bearer": .bearer + default: .unknown(rawValue) + } + } + + var rawValue: String { + switch self { + case .bearer: "bearer" + case .unknown(let type): type + } + } + } + + enum GrantType: Codable, RawRepresentable { + case authorizationCode + case unknown(String) + + init(rawValue: String) { + self = switch rawValue.lowercased() { + case "authorization_code": .authorizationCode + default: .unknown(rawValue) + } + } + + var rawValue: String { + switch self { + case .authorizationCode: "authorization_code" + case .unknown(let type): type + } + } + } + + enum ResponseType: Codable, RawRepresentable { + case code + case idToken + case unknown(String) + + init(rawValue: String) { + self = switch rawValue.lowercased() { + case "code": .code + case "id_token": .idToken + default: .unknown(rawValue) + } + } + + var rawValue: String { + switch self { + case .code: "code" + case .idToken: "id_token" + case .unknown(let type): type + } + } + } + + fileprivate static var decoder: JSONDecoder { + let decoder = JSONDecoder() + decoder.keyDecodingStrategy = .convertFromSnakeCase + return decoder + } + + fileprivate static var encoder: JSONEncoder { + let encoder = JSONEncoder() + encoder.keyEncodingStrategy = .convertToSnakeCase + return encoder + } +} + +extension WebAuthenticationSession: @unchecked @retroactive Sendable { +} + +extension WebAuthenticationSession { +#if canImport(BrowserEngineKit) + @available(iOS 17.4, macOS 14.4, tvOS 17.4, watchOS 10.4, *) + fileprivate static func callback(for redirectURI: URL) throws -> ASWebAuthenticationSession.Callback { + switch redirectURI.scheme { + case "https": + guard let host = redirectURI.host else { throw OAuth2.Error.invalidRedirectURI } + return .https(host: host, path: redirectURI.path) + case "http": + throw OAuth2.Error.invalidRedirectURI + case .some(let scheme): + return .customScheme(scheme) + case .none: + throw OAuth2.Error.invalidRedirectURI + } + } +#endif + + fileprivate func start(url: URL, redirectURI: URL) async throws -> URL { + #if canImport(BrowserEngineKit) + if #available(iOS 17.4, macOS 14.4, tvOS 17.4, watchOS 10.4, *) { + return try await authenticate( + using: url, + callback: try Self.callback(for: redirectURI), + additionalHeaderFields: [:] + ) + } + #endif + + return try await withThrowingTaskGroup(of: URL.self) { group in + group.addTask { + return try await authenticate(using: url, callbackURLScheme: redirectURI.scheme ?? "") + } + + let id = Int.random(in: 0.. some View { + onOpenURL { url in OAuth2.Session.handle(url: url) } + } +} + +extension URLComponents { + fileprivate func decode(_ type: T.Type) throws -> T { + guard let queryItems else { + throw DecodingError.valueNotFound( + T.self, + .init(codingPath: [], debugDescription: "Missing query items") + ) + } + let data = try OAuth2.encoder.encode(try queryItems.values) + return try OAuth2.decoder.decode(T.self, from: data) + } +} + +extension Sequence where Element == URLQueryItem { + fileprivate var values: [String: String?] { + get throws { + try Dictionary(map { ($0.name, $0.value) }) { _, _ in + throw DecodingError.dataCorrupted(.init(codingPath: [], debugDescription: "Duplicate query items")) + } + } + } +} diff --git a/Apple/UI/Tunnel.swift b/Apple/UI/Tunnel.swift new file mode 100644 index 0000000..4ec9320 --- /dev/null +++ b/Apple/UI/Tunnel.swift @@ -0,0 +1,61 @@ +import BurrowConfiguration +import NetworkExtension +import SwiftUI + +protocol Tunnel: Sendable { + @MainActor var status: TunnelStatus { get } + + func start() + func stop() + func enable() +} + +public enum TunnelStatus: Sendable, Equatable, Hashable { + case unknown + case permissionRequired + case disabled + case connecting + case connected(Date) + case disconnecting + case disconnected + case reasserting + case invalid + case configurationReadWriteFailed +} + +struct TunnelKey: EnvironmentKey { + static var defaultValue: any Tunnel { + NetworkExtensionTunnel(bundleIdentifier: Constants.networkExtensionBundleIdentifier) + } +} + +extension EnvironmentValues { + var tunnel: any Tunnel { + get { self[TunnelKey.self] } + set { self[TunnelKey.self] = newValue } + } +} + +#if DEBUG +@Observable +@MainActor +final class PreviewTunnel: Tunnel { + private(set) var status: TunnelStatus = .permissionRequired + + nonisolated func start() { + set(.connected(.now)) + } + + nonisolated func stop() { + set(.disconnected) + } + + nonisolated func enable() { + set(.disconnected) + } + + nonisolated private func set(_ status: TunnelStatus) { + Task { @MainActor in self.status = status } + } +} +#endif diff --git a/Apple/App/TunnelButton.swift b/Apple/UI/TunnelButton.swift similarity index 67% rename from Apple/App/TunnelButton.swift rename to Apple/UI/TunnelButton.swift index df8d7e6..d0222d4 100644 --- a/Apple/App/TunnelButton.swift +++ b/Apple/UI/TunnelButton.swift @@ -4,21 +4,24 @@ struct TunnelButton: View { @Environment(\.tunnel) var tunnel: any Tunnel + private var action: Action? { tunnel.action } + var body: some View { - if let action = tunnel.action { - Button { + Button { + if let action { tunnel.perform(action) - } label: { - Text(action.description) } - .padding(.horizontal) - .buttonStyle(.floating) + } label: { + Text(action.description) } + .disabled(action.isDisabled) + .padding(.horizontal) + .buttonStyle(.floating) } } extension Tunnel { - fileprivate var action: TunnelButton.Action? { + @MainActor fileprivate var action: TunnelButton.Action? { switch status { case .permissionRequired, .invalid: .enable @@ -40,12 +43,21 @@ extension TunnelButton { } } -extension TunnelButton.Action { +extension TunnelButton.Action? { var description: LocalizedStringKey { switch self { case .enable: "Enable" case .start: "Start" case .stop: "Stop" + case .none: "Start" + } + } + + var isDisabled: Bool { + if case .none = self { + true + } else { + false } } } diff --git a/Apple/App/TunnelStatusView.swift b/Apple/UI/TunnelStatusView.swift similarity index 95% rename from Apple/App/TunnelStatusView.swift rename to Apple/UI/TunnelStatusView.swift index 3593516..15717ec 100644 --- a/Apple/App/TunnelStatusView.swift +++ b/Apple/UI/TunnelStatusView.swift @@ -10,7 +10,7 @@ struct TunnelStatusView: View { } extension TunnelStatus: CustomStringConvertible { - var description: String { + public var description: String { switch self { case .unknown: "Unknown" diff --git a/Apple/UI/UI.xcconfig b/Apple/UI/UI.xcconfig new file mode 100644 index 0000000..b44d676 --- /dev/null +++ b/Apple/UI/UI.xcconfig @@ -0,0 +1,3 @@ +#include "../Configuration/Framework.xcconfig" + +ENABLE_PREVIEWS = YES diff --git a/Cargo.lock b/Cargo.lock index 9689065..a5554fb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "addr2line" -version = "0.21.0" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" dependencies = [ "gimli", ] @@ -17,6 +17,12 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + [[package]] name = "aead" version = "0.5.2" @@ -29,9 +35,9 @@ dependencies = [ [[package]] name = "aes" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac1f845298e95f983ff1944b728ae08b8cebab80d684f0a832ed0fc74dfa27e2" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" dependencies = [ "cfg-if", "cipher", @@ -39,57 +45,70 @@ dependencies = [ ] [[package]] -name = "aho-corasick" -version = "1.1.2" +name = "ahash" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] [[package]] name = "anstream" -version = "0.6.11" +version = "0.6.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e2e1ebcb11de5c03c67de28a7df593d32191b44939c482e97702baaaa6ab6a5" +checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", + "is_terminal_polyfill", "utf8parse", ] [[package]] name = "anstyle" -version = "1.0.4" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" +checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" [[package]] name = "anstyle-parse" -version = "0.2.3" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" +checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.0.2" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" +checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" dependencies = [ "windows-sys 0.52.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.2" +version = "3.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" +checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" dependencies = [ "anstyle", "windows-sys 0.52.0", @@ -97,34 +116,54 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.79" +version = "1.0.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca" +checksum = "10f00e1f6e58a40e807377c75c6a7f97bf9044fab57816f2414e6f5f4499d7b8" [[package]] name = "async-channel" -version = "2.1.1" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ca33f4bc4ed1babef42cad36cc1f51fa88be00420404e5b1e80ab1b18f7678c" +checksum = "89b47800b0be77592da0afd425cc03468052844aff33b84e33cc696f64e77b6a" dependencies = [ "concurrent-queue", - "event-listener", "event-listener-strategy", "futures-core", "pin-project-lite", ] +[[package]] +name = "async-stream" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22068c0c19514942eefcfd4daf8976ef1aad84e61539f95cd200c35202f80af5" +dependencies = [ + "async-stream-impl 0.2.1", + "futures-core", +] + [[package]] name = "async-stream" version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" dependencies = [ - "async-stream-impl", + "async-stream-impl 0.3.5", "futures-core", "pin-project-lite", ] +[[package]] +name = "async-stream-impl" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25f9db3b38af870bf7e5cc649167533b493928e50744e2c30ae350230b414670" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "async-stream-impl" version = "0.3.5" @@ -133,25 +172,31 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.77", ] [[package]] name = "async-trait" -version = "0.1.77" +version = "0.1.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9" +checksum = "a27b8a3a6e1a44fa4c8baf1f653e4172e81486d4941f2237e20dc2d0cf4ddff1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.77", ] [[package]] -name = "autocfg" -version = "1.1.0" +name = "atomic-waker" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "autocfg" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "axum" @@ -160,13 +205,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf" dependencies = [ "async-trait", - "axum-core", + "axum-core 0.3.4", "bitflags 1.3.2", "bytes", "futures-util", - "http", - "http-body", - "hyper", + "http 0.2.12", + "http-body 0.4.6", + "hyper 0.14.30", "itoa", "matchit", "memchr", @@ -175,12 +220,46 @@ dependencies = [ "pin-project-lite", "rustversion", "serde", - "sync_wrapper", + "sync_wrapper 0.1.2", "tower", "tower-layer", "tower-service", ] +[[package]] +name = "axum" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a6c9af12842a67734c9a2e355436e5d03b22383ed60cf13cd0c18fbfe3dcbcf" +dependencies = [ + "async-trait", + "axum-core 0.4.3", + "bytes", + "futures-util", + "http 1.1.0", + "http-body 1.0.1", + "http-body-util", + "hyper 1.4.1", + "hyper-util", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "sync_wrapper 1.0.1", + "tokio", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + [[package]] name = "axum-core" version = "0.3.4" @@ -190,8 +269,8 @@ dependencies = [ "async-trait", "bytes", "futures-util", - "http", - "http-body", + "http 0.2.12", + "http-body 0.4.6", "mime", "rustversion", "tower-layer", @@ -199,16 +278,37 @@ dependencies = [ ] [[package]] -name = "backtrace" -version = "0.3.69" +name = "axum-core" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +checksum = "a15c63fd72d41492dc4f497196f5da1fb04fb7529e631d73630d1b491e47a2e3" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http 1.1.0", + "http-body 1.0.1", + "http-body-util", + "mime", + "pin-project-lite", + "rustversion", + "sync_wrapper 0.1.2", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "backtrace" +version = "0.3.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" dependencies = [ "addr2line", "cc", "cfg-if", "libc", - "miniz_oxide", + "miniz_oxide 0.7.4", "object", "rustc-demangle", ] @@ -219,6 +319,12 @@ version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + [[package]] name = "base64ct" version = "1.6.0" @@ -241,7 +347,7 @@ dependencies = [ "proc-macro2", "quote", "regex", - "rustc-hash", + "rustc-hash 1.1.0", "shlex", "syn 1.0.109", "which", @@ -264,9 +370,9 @@ dependencies = [ "proc-macro2", "quote", "regex", - "rustc-hash", + "rustc-hash 1.1.0", "shlex", - "syn 2.0.48", + "syn 2.0.77", "which", ] @@ -278,9 +384,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.2" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "blake2" @@ -302,9 +408,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.14.0" +version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "burrow" @@ -313,16 +419,20 @@ dependencies = [ "aead", "anyhow", "async-channel", - "base64", + "async-stream 0.2.1", + "axum 0.7.5", + "base64 0.21.7", "blake2", "caps", "chacha20poly1305", "clap", "console", "console-subscriber", + "dotenv", "fehler", "futures", "hmac", + "hyper-util", "insta", "ip_network", "ip_network_table", @@ -331,13 +441,23 @@ dependencies = [ "nix 0.27.1", "once_cell", "parking_lot", + "prost 0.13.2", + "prost-types 0.13.2", "rand", "rand_core", + "reqwest 0.12.7", "ring", + "rusqlite", + "rust-ini", "schemars", "serde", "serde_json", "tokio", + "tokio-stream", + "toml", + "tonic 0.12.2", + "tonic-build", + "tower", "tracing", "tracing-journald", "tracing-log 0.1.4", @@ -355,9 +475,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.5.0" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" +checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" [[package]] name = "bzip2" @@ -392,12 +512,13 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.83" +version = "1.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +checksum = "b62ac837cdb5cb22e10a256099b4fc502b1dfe560cb282963a974d7abd80e476" dependencies = [ "jobserver", "libc", + "shlex", ] [[package]] @@ -452,20 +573,20 @@ dependencies = [ [[package]] name = "clang-sys" -version = "1.7.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67523a3b4be3ce1989d607a828d036249522dd9c1c8de7f4dd2dae43a37369d1" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" dependencies = [ "glob", "libc", - "libloading 0.8.1", + "libloading 0.8.5", ] [[package]] name = "clap" -version = "4.4.18" +version = "4.5.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e578d6ec4194633722ccf9544794b71b1385c3c027efe0c55db226fc880865c" +checksum = "3e5a21b8495e732f1b3c364c9949b201ca7bae518c502c80256c96ad79eaf6ac" dependencies = [ "clap_builder", "clap_derive", @@ -473,9 +594,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.4.18" +version = "4.5.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4df4df40ec50c46000231c914968278b1eb05098cf8f1b3a518a95030e71d1c7" +checksum = "8cf2dd12af7a047ad9d6da2b6b249759a22a7abc0f474c1dae1777afa4b21a73" dependencies = [ "anstream", "anstyle", @@ -485,33 +606,33 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.4.7" +version = "4.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" +checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0" dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.77", ] [[package]] name = "clap_lex" -version = "0.6.0" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" +checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" [[package]] name = "colorchoice" -version = "1.0.0" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" +checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" [[package]] name = "concurrent-queue" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d16048cd947b08fa32c24458a22f5dc5e835264f689f4f5653210c69fd107363" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" dependencies = [ "crossbeam-utils", ] @@ -536,9 +657,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd326812b3fd01da5bb1af7d340d0d555fd3d4b641e7f1dfcf5962a902952787" dependencies = [ "futures-core", - "prost", - "prost-types", - "tonic", + "prost 0.12.6", + "prost-types 0.12.6", + "tonic 0.10.2", "tracing-core", ] @@ -554,18 +675,38 @@ dependencies = [ "futures-task", "hdrhistogram", "humantime", - "prost-types", + "prost-types 0.12.6", "serde", "serde_json", "thread_local", "tokio", "tokio-stream", - "tonic", + "tonic 0.10.2", "tracing", "tracing-core", "tracing-subscriber", ] +[[package]] +name = "const-random" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359" +dependencies = [ + "const-random-macro", +] + +[[package]] +name = "const-random-macro" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" +dependencies = [ + "getrandom", + "once_cell", + "tiny-keccak", +] + [[package]] name = "constant_time_eq" version = "0.1.5" @@ -584,42 +725,48 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "cpufeatures" -version = "0.2.12" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" dependencies = [ "libc", ] [[package]] name = "crc32fast" -version = "1.3.2" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" dependencies = [ "cfg-if", ] [[package]] name = "crossbeam-channel" -version = "0.5.11" +version = "0.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "176dc175b78f56c0f321911d9c8eb2b77a78a4860b9c19db83835fea1a46649b" +checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-utils" -version = "0.8.19" +version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" [[package]] name = "crypto-common" @@ -634,15 +781,14 @@ dependencies = [ [[package]] name = "curve25519-dalek" -version = "4.1.1" +version = "4.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e89b8c6a2e4b1f45971ad09761aafb85514a84744b67a95e32c3cc1352d1f65c" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" dependencies = [ "cfg-if", "cpufeatures", "curve25519-dalek-derive", "fiat-crypto", - "platforms", "rustc_version", "subtle", "zeroize", @@ -656,7 +802,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.77", ] [[package]] @@ -680,16 +826,31 @@ dependencies = [ ] [[package]] -name = "dyn-clone" -version = "1.0.16" +name = "dlv-list" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "545b22097d44f8a9581187cdf93de7a71e4722bf51200cfaba810865b49a495d" +checksum = "442039f5147480ba31067cb00ada1adae6892028e40e45fc5de7b7df6dcc1b5f" +dependencies = [ + "const-random", +] + +[[package]] +name = "dotenv" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" + +[[package]] +name = "dyn-clone" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" [[package]] name = "either" -version = "1.9.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] name = "encode_unicode" @@ -699,9 +860,9 @@ checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" [[package]] name = "encoding_rs" -version = "0.8.33" +version = "0.8.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" +checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" dependencies = [ "cfg-if", ] @@ -714,9 +875,9 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" dependencies = [ "libc", "windows-sys 0.52.0", @@ -724,9 +885,9 @@ dependencies = [ [[package]] name = "event-listener" -version = "4.0.3" +version = "5.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b215c49b2b248c855fb73579eb1f4f26c38ffdc12973e20e07b91d78d5646e" +checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba" dependencies = [ "concurrent-queue", "parking", @@ -735,19 +896,31 @@ dependencies = [ [[package]] name = "event-listener-strategy" -version = "0.4.0" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "958e4d70b6d5e81971bebec42271ec641e7ff4e170a6fa605f2b8a8b65cb97d3" +checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1" dependencies = [ "event-listener", "pin-project-lite", ] [[package]] -name = "fastrand" -version = "2.0.1" +name = "fallible-iterator" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" +checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" + +[[package]] +name = "fallible-streaming-iterator" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" + +[[package]] +name = "fastrand" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" [[package]] name = "fehler" @@ -771,18 +944,24 @@ dependencies = [ [[package]] name = "fiat-crypto" -version = "0.2.5" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27573eac26f4dd11e2b1916c3fe1baa56407c83c71a773a8ba17ec0bca03b6b7" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" + +[[package]] +name = "fixedbitset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "flate2" -version = "1.0.28" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" +checksum = "324a1be68054ef05ad64b861cc9eaf1d623d2d8cb25b4bf2cb9cdd902b4bf253" dependencies = [ "crc32fast", - "miniz_oxide", + "miniz_oxide 0.8.0", ] [[package]] @@ -871,7 +1050,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.77", ] [[package]] @@ -916,9 +1095,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.12" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "libc", @@ -927,9 +1106,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.28.1" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" +checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" [[package]] name = "glob" @@ -939,17 +1118,17 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "h2" -version = "0.3.24" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb2c4422095b67ee78da96fbb51a4cc413b3b25883c7717ff7ca1ab31022c9c9" +checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" dependencies = [ "bytes", "fnv", "futures-core", "futures-sink", "futures-util", - "http", - "indexmap 2.1.0", + "http 0.2.12", + "indexmap 2.5.0", "slab", "tokio", "tokio-util", @@ -957,17 +1136,22 @@ dependencies = [ ] [[package]] -name = "handlebars" -version = "3.5.5" +name = "h2" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4498fc115fa7d34de968184e473529abb40eeb6be8bc5f7faba3d08c316cb3e3" +checksum = "524e8ac6999421f49a846c2d4411f337e53497d8ec55d67753beffa43c5d9205" dependencies = [ - "log", - "pest", - "pest_derive", - "quick-error", - "serde", - "serde_json", + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http 1.1.0", + "indexmap 2.5.0", + "slab", + "tokio", + "tokio-util", + "tracing", ] [[package]] @@ -978,9 +1162,21 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "hashbrown" -version = "0.14.3" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashlink" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af" +dependencies = [ + "hashbrown 0.14.5", +] [[package]] name = "hdrhistogram" @@ -988,7 +1184,7 @@ version = "7.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "765c9198f173dd59ce26ff9f95ef0aafd0a0fe01fb9d72841bc5066a4c06511d" dependencies = [ - "base64", + "base64 0.21.7", "byteorder", "flate2", "nom", @@ -997,15 +1193,15 @@ dependencies = [ [[package]] name = "heck" -version = "0.4.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hermit-abi" -version = "0.3.4" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d3d0e0f38255e7fa3cf31335b3a56f05febd18025f4db5ef7a0cfb4f8da651f" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" [[package]] name = "hex" @@ -1033,9 +1229,20 @@ dependencies = [ [[package]] name = "http" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" dependencies = [ "bytes", "fnv", @@ -1049,15 +1256,38 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" dependencies = [ "bytes", - "http", + "http 0.2.12", + "pin-project-lite", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http 1.1.0", +] + +[[package]] +name = "http-body-util" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" +dependencies = [ + "bytes", + "futures-util", + "http 1.1.0", + "http-body 1.0.1", "pin-project-lite", ] [[package]] name = "httparse" -version = "1.8.0" +version = "1.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" +checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" [[package]] name = "httpdate" @@ -1073,17 +1303,17 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "0.14.28" +version = "0.14.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" +checksum = "a152ddd61dfaec7273fe8419ab357f33aee0d914c5f4efbf0d96fa749eea5ec9" dependencies = [ "bytes", "futures-channel", "futures-core", "futures-util", - "h2", - "http", - "http-body", + "h2 0.3.26", + "http 0.2.12", + "http-body 0.4.6", "httparse", "httpdate", "itoa", @@ -1095,18 +1325,70 @@ dependencies = [ "want", ] +[[package]] +name = "hyper" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "h2 0.4.6", + "http 1.1.0", + "http-body 1.0.1", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333" +dependencies = [ + "futures-util", + "http 1.1.0", + "hyper 1.4.1", + "hyper-util", + "rustls", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tower-service", + "webpki-roots", +] + [[package]] name = "hyper-timeout" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1" dependencies = [ - "hyper", + "hyper 0.14.30", "pin-project-lite", "tokio", "tokio-io-timeout", ] +[[package]] +name = "hyper-timeout" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3203a961e5c83b6f5498933e78b6b263e208c197b63e9c6c53cc82ffd3f63793" +dependencies = [ + "hyper 1.4.1", + "hyper-util", + "pin-project-lite", + "tokio", + "tower-service", +] + [[package]] name = "hyper-tls" version = "0.5.0" @@ -1114,12 +1396,32 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" dependencies = [ "bytes", - "hyper", + "hyper 0.14.30", "native-tls", "tokio", "tokio-native-tls", ] +[[package]] +name = "hyper-util" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cde7055719c54e36e95e8719f95883f22072a48ede39db7fc17a4e1d5281e9b9" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http 1.1.0", + "http-body 1.0.1", + "hyper 1.4.1", + "pin-project-lite", + "socket2", + "tokio", + "tower", + "tower-service", + "tracing", +] + [[package]] name = "idna" version = "0.5.0" @@ -1142,12 +1444,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.1.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" +checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" dependencies = [ "equivalent", - "hashbrown 0.14.3", + "hashbrown 0.14.5", ] [[package]] @@ -1161,32 +1463,15 @@ dependencies = [ [[package]] name = "insta" -version = "1.34.0" +version = "1.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d64600be34b2fcfc267740a243fa7744441bb4947a619ac4e5bb6507f35fbfc" +checksum = "6593a41c7a73841868772495db7dc1e8ecab43bb5c0b6da2059246c4b506ab60" dependencies = [ "console", "lazy_static", "linked-hash-map", "serde", "similar", - "yaml-rust", -] - -[[package]] -name = "interfaces" -version = "0.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb6250a98af259a26fd5a4a6081fccea9ac116e4c3178acf4aeb86d32d2b7715" -dependencies = [ - "bitflags 2.4.2", - "cc", - "handlebars", - "lazy_static", - "libc", - "nix 0.26.4", - "serde", - "serde_derive", ] [[package]] @@ -1213,48 +1498,63 @@ checksum = "8e537132deb99c0eb4b752f0346b6a836200eaaa3516dd7e5514b63930a09e5d" [[package]] name = "ipnet" -version = "2.9.0" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" +checksum = "187674a687eed5fe42285b40c6291f9a01517d415fad1c3cbc6a9f778af7fcd4" + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" [[package]] name = "itertools" -version = "0.11.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" dependencies = [ "either", ] [[package]] name = "itoa" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "jobserver" -version = "0.1.27" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c37f63953c4c63420ed5fd3d6d398c719489b9f872b9fa683262f8edd363c7d" +checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" dependencies = [ "libc", ] [[package]] name = "js-sys" -version = "0.3.67" +version = "0.3.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a1d36f1235bc969acba30b7f5990b864423a6068a10f7c90ae8f0112e3a59d1" +checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" dependencies = [ "wasm-bindgen", ] [[package]] name = "lazy_static" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "lazycell" @@ -1264,9 +1564,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.152" +version = "0.2.158" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7" +checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" [[package]] name = "libloading" @@ -1280,12 +1580,23 @@ dependencies = [ [[package]] name = "libloading" -version = "0.8.1" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c571b676ddfc9a8c12f1f3d3085a7b163966a8fd8098a90640953ce5f6170161" +checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" dependencies = [ "cfg-if", - "windows-sys 0.48.0", + "windows-targets 0.52.6", +] + +[[package]] +name = "libsqlite3-sys" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c10584274047cb335c23d3e61bcef8e323adae7c5c8c760540f73610177fc3f" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", ] [[package]] @@ -1314,15 +1625,15 @@ checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" [[package]] name = "linux-raw-sys" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" [[package]] name = "lock_api" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" dependencies = [ "autocfg", "scopeguard", @@ -1330,9 +1641,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.20" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "matchers" @@ -1351,9 +1662,9 @@ checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" [[package]] name = "memchr" -version = "2.7.1" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "memoffset" @@ -1366,9 +1677,9 @@ dependencies = [ [[package]] name = "memoffset" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" dependencies = [ "autocfg", ] @@ -1393,7 +1704,7 @@ checksum = "49e7bc1560b95a3c4a25d03de42fe76ca718ab92d1a22a55b9b4cf67b3ae635c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.77", ] [[package]] @@ -1410,31 +1721,46 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.7.1" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" dependencies = [ "adler", ] [[package]] -name = "mio" -version = "0.8.10" +name = "miniz_oxide" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" +checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" dependencies = [ - "libc", - "wasi", - "windows-sys 0.48.0", + "adler2", ] [[package]] -name = "native-tls" -version = "0.2.11" +name = "mio" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" +dependencies = [ + "hermit-abi", + "libc", + "wasi", + "windows-sys 0.52.0", +] + +[[package]] +name = "multimap" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "defc4c55412d89136f966bbb339008b474350e5e6e78d2714439c386b3137a03" + +[[package]] +name = "native-tls" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" dependencies = [ - "lazy_static", "libc", "log", "openssl", @@ -1446,43 +1772,6 @@ dependencies = [ "tempfile", ] -[[package]] -name = "netlink-packet-core" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72724faf704479d67b388da142b186f916188505e7e0b26719019c525882eda4" -dependencies = [ - "anyhow", - "byteorder", - "netlink-packet-utils", -] - -[[package]] -name = "netlink-packet-route" -version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74c171cd77b4ee8c7708da746ce392440cb7bcf618d122ec9ecc607b12938bf4" -dependencies = [ - "anyhow", - "byteorder", - "libc", - "log", - "netlink-packet-core", - "netlink-packet-utils", -] - -[[package]] -name = "netlink-packet-utils" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ede8a08c71ad5a95cdd0e4e52facd37190977039a4704eb82a283f713747d34" -dependencies = [ - "anyhow", - "byteorder", - "paste", - "thiserror", -] - [[package]] name = "nix" version = "0.26.4" @@ -1502,10 +1791,10 @@ version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.6.0", "cfg-if", "libc", - "memoffset 0.9.0", + "memoffset 0.9.1", ] [[package]] @@ -1529,29 +1818,25 @@ dependencies = [ ] [[package]] -name = "num-traits" -version = "0.2.17" +name = "num-conv" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", ] -[[package]] -name = "num_cpus" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" -dependencies = [ - "hermit-abi", - "libc", -] - [[package]] name = "object" -version = "0.32.2" +version = "0.36.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +checksum = "084f1a5821ac4c651660a94a7153d27ac9d8a53736203f58b31945ded098070a" dependencies = [ "memchr", ] @@ -1564,17 +1849,17 @@ checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "opaque-debug" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] name = "openssl" -version = "0.10.63" +version = "0.10.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15c9d69dd87a29568d4d017cfe8ec518706046a05184e5aea92d0af890b803c8" +checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.6.0", "cfg-if", "foreign-types", "libc", @@ -1591,7 +1876,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.77", ] [[package]] @@ -1602,9 +1887,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.99" +version = "0.9.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22e1bf214306098e4832460f797824c05d25aacdf896f64a985fb0fd992454ae" +checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6" dependencies = [ "cc", "libc", @@ -1612,6 +1897,16 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "ordered-multimap" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49203cdcae0030493bad186b28da2fa25645fa276a51b6fec8010d281e02ef79" +dependencies = [ + "dlv-list", + "hashbrown 0.14.5", +] + [[package]] name = "overload" version = "0.1.1" @@ -1620,15 +1915,15 @@ checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" [[package]] name = "parking" -version = "2.2.0" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" [[package]] name = "parking_lot" -version = "0.12.1" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" dependencies = [ "lock_api", "parking_lot_core", @@ -1636,15 +1931,15 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.9" +version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", "redox_syscall", "smallvec", - "windows-targets 0.48.5", + "windows-targets 0.52.6", ] [[package]] @@ -1658,12 +1953,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "paste" -version = "1.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" - [[package]] name = "pbkdf2" version = "0.11.0" @@ -1689,75 +1978,40 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] -name = "pest" -version = "2.7.8" +name = "petgraph" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56f8023d0fb78c8e03784ea1c7f3fa36e68a723138990b8d5a47d916b651e7a8" +checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ - "memchr", - "thiserror", - "ucd-trie", -] - -[[package]] -name = "pest_derive" -version = "2.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0d24f72393fd16ab6ac5738bc33cdb6a9aa73f8b902e8fe29cf4e67d7dd1026" -dependencies = [ - "pest", - "pest_generator", -] - -[[package]] -name = "pest_generator" -version = "2.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdc17e2a6c7d0a492f0158d7a4bd66cc17280308bbaff78d5bef566dca35ab80" -dependencies = [ - "pest", - "pest_meta", - "proc-macro2", - "quote", - "syn 2.0.48", -] - -[[package]] -name = "pest_meta" -version = "2.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "934cd7631c050f4674352a6e835d5f6711ffbfb9345c2fc0107155ac495ae293" -dependencies = [ - "once_cell", - "pest", - "sha2", + "fixedbitset", + "indexmap 2.5.0", ] [[package]] name = "pin-project" -version = "1.1.4" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0302c4a0442c456bd56f841aee5c3bfd17967563f6fadc9ceb9f9c23cf3807e0" +checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.4" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "266c042b60c9c76b8d53061e52b2e0d1116abc57cefc8c5cd671619a56ac3690" +checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.77", ] [[package]] name = "pin-project-lite" -version = "0.2.13" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" [[package]] name = "pin-utils" @@ -1767,15 +2021,9 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2900ede94e305130c13ddd391e0ab7cbaeb783945ae07a279c268cb05109c6cb" - -[[package]] -name = "platforms" -version = "3.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "626dec3cac7cc0e1577a2ec3fc496277ec2baa084bebad95bb6fdbfae235f84c" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" [[package]] name = "poly1305" @@ -1796,72 +2044,170 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "ppv-lite86" -version = "0.2.17" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] [[package]] name = "prettyplease" -version = "0.2.16" +version = "0.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a41cf62165e97c7f814d2221421dbb9afcbcdb0a88068e5ea206e19951c2cbb5" +checksum = "479cf940fbbb3426c32c5d5176f62ad57549a0bb84773423ba8be9d089f5faba" dependencies = [ "proc-macro2", - "syn 2.0.48", + "syn 2.0.77", ] [[package]] name = "proc-macro2" -version = "1.0.78" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" dependencies = [ "unicode-ident", ] [[package]] name = "prost" -version = "0.12.3" +version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "146c289cda302b98a28d40c8b3b90498d6e526dd24ac2ecea73e4e491685b94a" +checksum = "deb1435c188b76130da55f17a466d252ff7b1418b2ad3e037d127b94e3411f29" dependencies = [ "bytes", - "prost-derive", + "prost-derive 0.12.6", +] + +[[package]] +name = "prost" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b2ecbe40f08db5c006b5764a2645f7f3f141ce756412ac9e1dd6087e6d32995" +dependencies = [ + "bytes", + "prost-derive 0.13.2", +] + +[[package]] +name = "prost-build" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8650aabb6c35b860610e9cff5dc1af886c9e25073b7b1712a68972af4281302" +dependencies = [ + "bytes", + "heck", + "itertools 0.13.0", + "log", + "multimap", + "once_cell", + "petgraph", + "prettyplease", + "prost 0.13.2", + "prost-types 0.13.2", + "regex", + "syn 2.0.77", + "tempfile", ] [[package]] name = "prost-derive" -version = "0.12.3" +version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efb6c9a1dd1def8e2124d17e83a20af56f1570d6c2d2bd9e266ccb768df3840e" +checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1" dependencies = [ "anyhow", - "itertools", + "itertools 0.12.1", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.77", +] + +[[package]] +name = "prost-derive" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acf0c195eebb4af52c752bec4f52f645da98b6e92077a04110c7f349477ae5ac" +dependencies = [ + "anyhow", + "itertools 0.13.0", + "proc-macro2", + "quote", + "syn 2.0.77", ] [[package]] name = "prost-types" -version = "0.12.3" +version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "193898f59edcf43c26227dcd4c8427f00d99d61e95dcde58dabd49fa291d470e" +checksum = "9091c90b0a32608e984ff2fa4091273cbdd755d54935c51d520887f4a1dbd5b0" dependencies = [ - "prost", + "prost 0.12.6", ] [[package]] -name = "quick-error" -version = "2.0.1" +name = "prost-types" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" +checksum = "60caa6738c7369b940c3d49246a8d1749323674c65cb13010134f5c9bad5b519" +dependencies = [ + "prost 0.13.2", +] + +[[package]] +name = "quinn" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c7c5fdde3cdae7203427dc4f0a68fe0ed09833edc525a03456b153b79828684" +dependencies = [ + "bytes", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash 2.0.0", + "rustls", + "socket2", + "thiserror", + "tokio", + "tracing", +] + +[[package]] +name = "quinn-proto" +version = "0.11.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fadfaed2cd7f389d0161bb73eeb07b7b78f8691047a6f3e73caaeae55310a4a6" +dependencies = [ + "bytes", + "rand", + "ring", + "rustc-hash 2.0.0", + "rustls", + "slab", + "thiserror", + "tinyvec", + "tracing", +] + +[[package]] +name = "quinn-udp" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fe68c2e9e1a1234e218683dbdf9f9dfcb094113c5ac2b938dfcb9bab4c4140b" +dependencies = [ + "libc", + "once_cell", + "socket2", + "tracing", + "windows-sys 0.59.0", +] [[package]] name = "quote" -version = "1.0.35" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ "proc-macro2", ] @@ -1898,23 +2244,23 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.4.1" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.6.0", ] [[package]] name = "regex" -version = "1.10.3" +version = "1.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" +checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.5", - "regex-syntax 0.8.2", + "regex-automata 0.4.7", + "regex-syntax 0.8.4", ] [[package]] @@ -1928,13 +2274,13 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.5" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.2", + "regex-syntax 0.8.4", ] [[package]] @@ -1945,25 +2291,25 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.8.2" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" [[package]] name = "reqwest" -version = "0.11.23" +version = "0.11.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37b1ae8d9ac08420c66222fb9096fc5de435c3c48542bc5336c51892cffafb41" +checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" dependencies = [ - "base64", + "base64 0.21.7", "bytes", "encoding_rs", "futures-core", "futures-util", - "h2", - "http", - "http-body", - "hyper", + "h2 0.3.26", + "http 0.2.12", + "http-body 0.4.6", + "hyper 0.14.30", "hyper-tls", "ipnet", "js-sys", @@ -1973,9 +2319,11 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", + "rustls-pemfile 1.0.4", "serde", "serde_json", "serde_urlencoded", + "sync_wrapper 0.1.2", "system-configuration", "tokio", "tokio-native-tls", @@ -1988,24 +2336,92 @@ dependencies = [ ] [[package]] -name = "ring" -version = "0.17.7" +name = "reqwest" +version = "0.12.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "688c63d65483050968b2a8937f7995f443e27041a0f7700aa59b0822aedebb74" +checksum = "f8f4955649ef5c38cc7f9e8aa41761d48fb9677197daea9984dc54f56aad5e63" +dependencies = [ + "base64 0.22.1", + "bytes", + "futures-core", + "futures-util", + "http 1.1.0", + "http-body 1.0.1", + "http-body-util", + "hyper 1.4.1", + "hyper-rustls", + "hyper-util", + "ipnet", + "js-sys", + "log", + "mime", + "once_cell", + "percent-encoding", + "pin-project-lite", + "quinn", + "rustls", + "rustls-pemfile 2.1.3", + "rustls-pki-types", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper 1.0.1", + "tokio", + "tokio-rustls", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "webpki-roots", + "windows-registry", +] + +[[package]] +name = "ring" +version = "0.17.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" dependencies = [ "cc", + "cfg-if", "getrandom", "libc", "spin", "untrusted", - "windows-sys 0.48.0", + "windows-sys 0.52.0", +] + +[[package]] +name = "rusqlite" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b838eba278d213a8beaf485bd313fd580ca4505a00d5871caeb1457c55322cae" +dependencies = [ + "bitflags 2.6.0", + "fallible-iterator", + "fallible-streaming-iterator", + "hashlink", + "libsqlite3-sys", + "smallvec", +] + +[[package]] +name = "rust-ini" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e310ef0e1b6eeb79169a1171daf9abcb87a2e17c03bee2c4bb100b55c75409f" +dependencies = [ + "cfg-if", + "ordered-multimap", + "trim-in-place", ] [[package]] name = "rustc-demangle" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "rustc-hash" @@ -2014,21 +2430,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] -name = "rustc_version" -version = "0.4.0" +name = "rustc-hash" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152" + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" dependencies = [ "semver", ] [[package]] name = "rustix" -version = "0.38.30" +version = "0.38.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "322394588aaf33c24007e8bb3238ee3e4c5c09c084ab32bc73890b99ff326bca" +checksum = "3f55e80d50763938498dd5ebb18647174e0c76dc38c5505294bb224624f30f36" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.6.0", "errno", "libc", "linux-raw-sys", @@ -2036,31 +2458,81 @@ dependencies = [ ] [[package]] -name = "rustversion" -version = "1.0.14" +name = "rustls" +version = "0.23.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" +checksum = "c58f8c84392efc0a126acce10fa59ff7b3d2ac06ab451a33f2741989b806b044" +dependencies = [ + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +dependencies = [ + "base64 0.21.7", +] + +[[package]] +name = "rustls-pemfile" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "196fe16b00e106300d3e45ecfcb764fa292a535d7326a29a5875c579c7417425" +dependencies = [ + "base64 0.22.1", + "rustls-pki-types", +] + +[[package]] +name = "rustls-pki-types" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc0a2ce646f8655401bb81e7927b812614bd5d91dbc968696be50603510fcaf0" + +[[package]] +name = "rustls-webpki" +version = "0.102.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84678086bd54edf2b415183ed7a94d0efb049f1b646a33e22a36f3794be6ae56" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" [[package]] name = "ryu" -version = "1.0.16" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "schannel" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" +checksum = "e9aaafd5a2b6e3d657ff009d82fbd630b6bd54dd4eb06f21693925cdf80f9b8b" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "schemars" -version = "0.8.16" +version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45a28f4c49489add4ce10783f7911893516f15afe45d015608d41faca6bc4d29" +checksum = "09c024468a378b7e36765cd36702b7a90cc3cba11654f6685c8f233408e89e92" dependencies = [ "dyn-clone", "schemars_derive", @@ -2070,14 +2542,14 @@ dependencies = [ [[package]] name = "schemars_derive" -version = "0.8.16" +version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c767fd6fa65d9ccf9cf026122c1b555f2ef9a4f0cea69da4d7dbc3e258d30967" +checksum = "b1eee588578aff73f856ab961cd2f79e36bc45d7ded33a7562adba4667aecc0e" dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 1.0.109", + "syn 2.0.77", ] [[package]] @@ -2088,11 +2560,11 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "security-framework" -version = "2.9.2" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.6.0", "core-foundation", "core-foundation-sys", "libc", @@ -2101,9 +2573,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.9.1" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" +checksum = "75da29fe9b9b08fe9d6b22b5b4bcbc75d8db3aa31e639aa56bb62e9d46bfceaf" dependencies = [ "core-foundation-sys", "libc", @@ -2111,52 +2583,72 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.21" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" [[package]] name = "serde" -version = "1.0.196" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32" +checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.196" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67" +checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.77", ] [[package]] name = "serde_derive_internals" -version = "0.26.0" +version = "0.29.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c" +checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.77", ] [[package]] name = "serde_json" -version = "1.0.112" +version = "1.0.128" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d1bd37ce2324cf3bf85e5a25f96eb4baf0d5aa6eba43e7ae8958870c4ec48ed" +checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" dependencies = [ "itoa", + "memchr", "ryu", "serde", ] +[[package]] +name = "serde_path_to_error" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af99884400da37c88f5e9146b7f1fd0fbcae8f6eec4e9da38b67d05486f814a6" +dependencies = [ + "itoa", + "serde", +] + +[[package]] +name = "serde_spanned" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb5b1b31579f3811bf615c144393417496f152e12ac8b7663bf664f4a815306d" +dependencies = [ + "serde", +] + [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -2218,10 +2710,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] -name = "similar" -version = "2.4.0" +name = "signal-hook-registry" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32fea41aca09ee824cc9724996433064c89f7777e60762749a4170a14abbfa21" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +dependencies = [ + "libc", +] + +[[package]] +name = "similar" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1de1d4f81173b03af4c0cbed3c898f6bff5b870e4a7f5d6f4057d62a7a4b686e" [[package]] name = "slab" @@ -2234,18 +2735,18 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.13.1" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "socket2" -version = "0.5.5" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" dependencies = [ "libc", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -2260,7 +2761,7 @@ version = "9.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da7a2b3c2bc9693bcb40870c4e9b5bf0d79f9cb46273321bf855ec513e919082" dependencies = [ - "base64", + "base64 0.21.7", "digest", "hex", "miette", @@ -2272,15 +2773,15 @@ dependencies = [ [[package]] name = "strsim" -version = "0.10.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "subtle" -version = "2.5.0" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" @@ -2295,9 +2796,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.48" +version = "2.0.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" +checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" dependencies = [ "proc-macro2", "quote", @@ -2310,6 +2811,15 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" +[[package]] +name = "sync_wrapper" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" +dependencies = [ + "futures-core", +] + [[package]] name = "system-configuration" version = "0.5.1" @@ -2333,42 +2843,42 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.9.0" +version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01ce4141aa927a6d1bd34a041795abd0db1cccba5d5f24b009f694bdf3a1f3fa" +checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64" dependencies = [ "cfg-if", "fastrand", - "redox_syscall", + "once_cell", "rustix", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "thiserror" -version = "1.0.56" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad" +checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.56" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471" +checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.77", ] [[package]] name = "thread_local" -version = "1.1.7" +version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" dependencies = [ "cfg-if", "once_cell", @@ -2376,11 +2886,12 @@ dependencies = [ [[package]] name = "time" -version = "0.3.31" +version = "0.3.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f657ba42c3f86e7680e53c8cd3af8abbe56b5491790b46e22e19c0d57463583e" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" dependencies = [ "deranged", + "num-conv", "powerfmt", "serde", "time-core", @@ -2393,10 +2904,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] -name = "tinyvec" -version = "1.6.0" +name = "tiny-keccak" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + +[[package]] +name = "tinyvec" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" dependencies = [ "tinyvec_macros", ] @@ -2409,20 +2929,20 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.35.1" +version = "1.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c89b4efa943be685f629b149f53829423f8f5531ea21249408e8e2f8671ec104" +checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998" dependencies = [ "backtrace", "bytes", "libc", "mio", - "num_cpus", "pin-project-lite", + "signal-hook-registry", "socket2", "tokio-macros", "tracing", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -2437,13 +2957,13 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.2.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" +checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.77", ] [[package]] @@ -2457,10 +2977,21 @@ dependencies = [ ] [[package]] -name = "tokio-stream" -version = "0.1.14" +name = "tokio-rustls" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" +checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" +dependencies = [ + "rustls", + "rustls-pki-types", + "tokio", +] + +[[package]] +name = "tokio-stream" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f4e6ce100d0eb49a2734f8c0812bcd324cf357d21810932c5df6b96ef2b86f1" dependencies = [ "futures-core", "pin-project-lite", @@ -2469,16 +3000,49 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.10" +version = "0.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" +checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a" dependencies = [ "bytes", "futures-core", "futures-sink", "pin-project-lite", "tokio", - "tracing", +] + +[[package]] +name = "toml" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.22.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d" +dependencies = [ + "indexmap 2.5.0", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", ] [[package]] @@ -2487,19 +3051,19 @@ version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d560933a0de61cf715926b9cac824d4c883c2c43142f787595e48280c40a1d0e" dependencies = [ - "async-stream", + "async-stream 0.3.5", "async-trait", - "axum", - "base64", + "axum 0.6.20", + "base64 0.21.7", "bytes", - "h2", - "http", - "http-body", - "hyper", - "hyper-timeout", + "h2 0.3.26", + "http 0.2.12", + "http-body 0.4.6", + "hyper 0.14.30", + "hyper-timeout 0.4.1", "percent-encoding", "pin-project", - "prost", + "prost 0.12.6", "tokio", "tokio-stream", "tower", @@ -2508,6 +3072,49 @@ dependencies = [ "tracing", ] +[[package]] +name = "tonic" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6f6ba989e4b2c58ae83d862d3a3e27690b6e3ae630d0deb59f3697f32aa88ad" +dependencies = [ + "async-stream 0.3.5", + "async-trait", + "axum 0.7.5", + "base64 0.22.1", + "bytes", + "h2 0.4.6", + "http 1.1.0", + "http-body 1.0.1", + "http-body-util", + "hyper 1.4.1", + "hyper-timeout 0.5.1", + "hyper-util", + "percent-encoding", + "pin-project", + "prost 0.13.2", + "socket2", + "tokio", + "tokio-stream", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tonic-build" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe4ee8877250136bd7e3d2331632810a4df4ea5e004656990d8d66d2f5ee8a67" +dependencies = [ + "prettyplease", + "proc-macro2", + "prost-build", + "quote", + "syn 2.0.77", +] + [[package]] name = "tower" version = "0.4.13" @@ -2530,15 +3137,15 @@ dependencies = [ [[package]] name = "tower-layer" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" [[package]] name = "tower-service" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" @@ -2546,6 +3153,7 @@ version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ + "log", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -2559,7 +3167,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.77", ] [[package]] @@ -2638,6 +3246,12 @@ dependencies = [ "tracing-log 0.2.0", ] +[[package]] +name = "trim-in-place" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "343e926fc669bc8cde4fa3129ab681c63671bae288b1f1081ceee6d9d37904fc" + [[package]] name = "try-lock" version = "0.2.5" @@ -2653,15 +3267,12 @@ dependencies = [ "byteorder", "fehler", "futures", - "interfaces", "lazy_static", "libc", "libloading 0.7.4", "log", - "netlink-packet-core", - "netlink-packet-route", "nix 0.26.4", - "reqwest", + "reqwest 0.11.27", "schemars", "serde", "socket2", @@ -2680,12 +3291,6 @@ version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" -[[package]] -name = "ucd-trie" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" - [[package]] name = "unicode-bidi" version = "0.3.15" @@ -2700,18 +3305,18 @@ checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-normalization" -version = "0.1.22" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" dependencies = [ "tinyvec", ] [[package]] name = "unicode-width" -version = "0.1.11" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" +checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" [[package]] name = "universal-hash" @@ -2731,9 +3336,9 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.5.0" +version = "2.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" dependencies = [ "form_urlencoded", "idna", @@ -2742,15 +3347,15 @@ dependencies = [ [[package]] name = "utf8parse" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.7.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f00cc9702ca12d3c81455259621e676d0f7251cec66a21e98fe2e9a37db93b2a" +checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" dependencies = [ "serde", ] @@ -2769,9 +3374,9 @@ checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" [[package]] name = "version_check" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "want" @@ -2790,34 +3395,35 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.90" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1223296a201415c7fad14792dbefaace9bd52b62d33453ade1c5b5f07555406" +checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" dependencies = [ "cfg-if", + "once_cell", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.90" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcdc935b63408d58a32f8cc9738a0bffd8f05cc7c002086c6ef20b7312ad9dcd" +checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.77", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.40" +version = "0.4.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bde2032aeb86bdfaecc8b261eef3cba735cc426c1f3a3416d1e0791be95fc461" +checksum = "61e9300f63a621e96ed275155c108eb6f843b6a26d053f122ab69724559dc8ed" dependencies = [ "cfg-if", "js-sys", @@ -2827,9 +3433,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.90" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e4c238561b2d428924c49815533a8b9121c664599558a5d9ec51f8a1740a999" +checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2837,33 +3443,42 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.90" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bae1abb6806dc1ad9e560ed242107c0f6c84335f1749dd4e8ddb012ebd5e25a7" +checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.77", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.90" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d91413b1c31d7539ba5ef2451af3f0b833a005eb27a631cec32bc0635a8602b" +checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" [[package]] name = "web-sys" -version = "0.3.67" +version = "0.3.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58cd2333b6e0be7a39605f0e255892fd7418a682d8da8fe042fe25128794d2ed" +checksum = "26fdeaafd9bd129f65e7c031593c24d62186301e0c72c8978fa1678be7d532c0" dependencies = [ "js-sys", "wasm-bindgen", ] +[[package]] +name = "webpki-roots" +version = "0.26.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bd24728e5af82c6c4ec1b66ac4844bdf8156257fccda846ec58b42cd0cdbe6a" +dependencies = [ + "rustls-pki-types", +] + [[package]] name = "which" version = "4.4.2" @@ -2878,9 +3493,9 @@ dependencies = [ [[package]] name = "widestring" -version = "1.0.2" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "653f141f39ec16bba3c5abe400a0c60da7468261cc2cbf36805022876bc721a8" +checksum = "7219d36b6eac893fa81e84ebe06485e7dcbb616177469b142df14f1f4deb1311" [[package]] name = "winapi" @@ -2913,6 +3528,36 @@ dependencies = [ "windows-targets 0.48.5", ] +[[package]] +name = "windows-registry" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" +dependencies = [ + "windows-result", + "windows-strings", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-result" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-strings" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +dependencies = [ + "windows-result", + "windows-targets 0.52.6", +] + [[package]] name = "windows-sys" version = "0.48.0" @@ -2928,7 +3573,16 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.0", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", ] [[package]] @@ -2948,17 +3602,18 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.52.0", - "windows_aarch64_msvc 0.52.0", - "windows_i686_gnu 0.52.0", - "windows_i686_msvc 0.52.0", - "windows_x86_64_gnu 0.52.0", - "windows_x86_64_gnullvm 0.52.0", - "windows_x86_64_msvc 0.52.0", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", ] [[package]] @@ -2969,9 +3624,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" @@ -2981,9 +3636,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" @@ -2993,9 +3648,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" @@ -3005,9 +3666,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" @@ -3017,9 +3678,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" @@ -3029,9 +3690,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" @@ -3041,9 +3702,18 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winnow" +version = "0.6.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f" +dependencies = [ + "memchr", +] [[package]] name = "winreg" @@ -3057,9 +3727,9 @@ dependencies = [ [[package]] name = "x25519-dalek" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb66477291e7e8d2b0ff1bcb900bf29489a9692816d79874bea351e7a8b6de96" +checksum = "c7e468321c81fb07fa7f4c636c3972b9100f0346e5b6a9f2bd0603a52f7ed277" dependencies = [ "curve25519-dalek", "rand_core", @@ -3069,24 +3739,36 @@ dependencies = [ [[package]] name = "xxhash-rust" -version = "0.8.8" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53be06678ed9e83edb1745eb72efc0bbcd7b5c3c35711a860906aed827a13d61" +checksum = "6a5cbf750400958819fb6178eaa83bee5cd9c29a26a40cc241df8c70fdd46984" [[package]] -name = "yaml-rust" -version = "0.4.5" +name = "zerocopy" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ - "linked-hash-map", + "byteorder", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", ] [[package]] name = "zeroize" -version = "1.7.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" dependencies = [ "zeroize_derive", ] @@ -3099,7 +3781,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.77", ] [[package]] @@ -3143,9 +3825,9 @@ dependencies = [ [[package]] name = "zstd-sys" -version = "2.0.9+zstd.1.5.5" +version = "2.0.13+zstd.1.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e16efa8a874a0481a574084d34cc26fdb3b99627480f785888deb6386506656" +checksum = "38ff0f21cfee8f97d94cef41359e0c89aa6113028ab0291aa8ca0038995a95aa" dependencies = [ "cc", "pkg-config", diff --git a/Cargo.toml b/Cargo.toml index 44981a2..362ba2b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,3 +2,8 @@ members = ["burrow", "tun"] resolver = "2" exclude = ["burrow-gtk"] + +[profile.release] +lto = true +panic = "abort" +opt-level = "z" diff --git a/Dockerfile b/Dockerfile index 9f54478..404179b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM docker.io/library/rust:1.76.0-slim-bookworm AS builder +FROM docker.io/library/rust:1.79-slim-bookworm AS builder ARG TARGETPLATFORM ARG LLVM_VERSION=16 @@ -8,11 +8,11 @@ ENV KEYRINGS /etc/apt/keyrings RUN set -eux && \ mkdir -p $KEYRINGS && \ apt-get update && \ - apt-get install --no-install-recommends -y gpg curl musl-dev && \ + apt-get install --no-install-recommends -y gpg curl busybox make musl-dev && \ curl --proto '=https' --tlsv1.2 -sSf https://apt.llvm.org/llvm-snapshot.gpg.key | gpg --dearmor --output $KEYRINGS/llvm.gpg && \ echo "deb [signed-by=$KEYRINGS/llvm.gpg] http://apt.llvm.org/bookworm/ llvm-toolchain-bookworm-$LLVM_VERSION main" > /etc/apt/sources.list.d/llvm.list && \ apt-get update && \ - apt-get install --no-install-recommends -y clang-$LLVM_VERSION llvm-$LLVM_VERSION lld-$LLVM_VERSION && \ + apt-get install --no-install-recommends -y clang-$LLVM_VERSION llvm-$LLVM_VERSION lld-$LLVM_VERSION build-essential sqlite3 libsqlite3-dev musl musl-tools musl-dev protobuf-compiler libprotobuf-dev && \ ln -s clang-$LLVM_VERSION /usr/bin/clang && \ ln -s clang /usr/bin/clang++ && \ ln -s lld-$LLVM_VERSION /usr/bin/ld.lld && \ @@ -25,26 +25,47 @@ RUN set -eux && \ rm -rf /var/lib/apt/lists/* RUN case $TARGETPLATFORM in \ - "linux/arm64") LLVM_TARGET=aarch64-unknown-linux-musl ;; \ - "linux/amd64") LLVM_TARGET=x86_64-unknown-linux-musl ;; \ - *) exit 1 ;; \ + "linux/arm64") LLVM_TARGET=aarch64-unknown-linux-musl ;; \ + "linux/amd64") LLVM_TARGET=x86_64-unknown-linux-musl ;; \ + *) exit 1 ;; \ esac && \ rustup target add $LLVM_TARGET +ARG SQLITE_VERSION=3460000 + +RUN case $TARGETPLATFORM in \ + "linux/arm64") LLVM_TARGET=aarch64-unknown-linux-musl MUSL_TARGET=aarch64-linux-musl ;; \ + "linux/amd64") LLVM_TARGET=x86_64-unknown-linux-musl MUSL_TARGET=x86_64-linux-musl ;; \ + *) exit 1 ;; \ + esac && \ + curl --proto '=https' --tlsv1.2 -sSfO https://www.sqlite.org/2024/sqlite-autoconf-$SQLITE_VERSION.tar.gz && \ + tar xf sqlite-autoconf-$SQLITE_VERSION.tar.gz && \ + cd sqlite-autoconf-$SQLITE_VERSION && \ + ./configure --disable-shared --disable-dependency-tracking \ + CC="clang-$LLVM_VERSION -target $LLVM_TARGET" \ + CFLAGS="-I/usr/local/include -I/usr/include/$MUSL_TARGET" \ + LDFLAGS="-L/usr/local/lib -L/usr/lib/$MUSL_TARGET -L/lib/$MUSL_TARGET" && \ + make && \ + make install && \ + cd .. && \ + rm -rf sqlite-autoconf-$SQLITE_VERSION sqlite-autoconf-$SQLITE_VERSION.tar.gz + ENV CC_x86_64_unknown_linux_musl=clang-$LLVM_VERSION \ AR_x86_64_unknown_linux_musl=llvm-ar-$LLVM_VERSION \ CC_aarch64_unknown_linux_musl=clang-$LLVM_VERSION \ AR_aarch64_unknown_linux_musl=llvm-ar-$LLVM_VERSION \ CARGO_TARGET_X86_64_UNKNOWN_LINUX_MUSL_RUSTFLAGS="-L/usr/lib/x86_64-linux-musl -L/lib/x86_64-linux-musl -C linker=rust-lld" \ CARGO_TARGET_AARCH64_UNKNOWN_LINUX_MUSL_RUSTFLAGS="-L/usr/lib/aarch64-linux-musl -L/lib/aarch64-linux-musl -C linker=rust-lld" \ - CARGO_REGISTRIES_CRATES_IO_PROTOCOL=sparse + SQLITE3_STATIC=1 \ + SQLITE3_INCLUDE_DIR=/usr/local/include \ + SQLITE3_LIB_DIR=/usr/local/lib COPY . . RUN case $TARGETPLATFORM in \ - "linux/arm64") LLVM_TARGET=aarch64-unknown-linux-musl ;; \ - "linux/amd64") LLVM_TARGET=x86_64-unknown-linux-musl ;; \ - *) exit 1 ;; \ + "linux/arm64") LLVM_TARGET=aarch64-unknown-linux-musl ;; \ + "linux/amd64") LLVM_TARGET=x86_64-unknown-linux-musl ;; \ + *) exit 1 ;; \ esac && \ cargo install --path burrow --target $LLVM_TARGET @@ -53,7 +74,8 @@ WORKDIR /tmp/rootfs RUN set -eux && \ mkdir -p ./bin ./etc ./tmp ./data && \ mv /usr/local/cargo/bin/burrow ./bin/burrow && \ - echo 'burrow:x:10001:10001::/tmp:/sbin/nologin' > ./etc/passwd && \ + cp /bin/busybox ./bin/busybox && \ + echo 'burrow:x:10001:10001::/tmp:/bin/busybox' > ./etc/passwd && \ echo 'burrow:x:10001:' > ./etc/group && \ chown -R 10001:10001 ./tmp ./data && \ chmod 0777 ./tmp @@ -72,4 +94,6 @@ USER 10001:10001 COPY --from=builder /tmp/rootfs / WORKDIR /data -ENTRYPOINT ["/bin/burrow"] +EXPOSE 8080 + +CMD ["/bin/burrow", "auth-server"] diff --git a/Makefile b/Makefile index d0c9bd9..6563ab1 100644 --- a/Makefile +++ b/Makefile @@ -20,6 +20,12 @@ start: stop: @$(cargo_norm) stop +status: + @$(cargo_norm) server-status + +tunnel-config: + @$(cargo_norm) tunnel-config + test-dns: @sudo route delete 8.8.8.8 @sudo route add 8.8.8.8 -interface $(tun) diff --git a/Tools/version.sh b/Tools/version.sh new file mode 100755 index 0000000..fcb3f00 --- /dev/null +++ b/Tools/version.sh @@ -0,0 +1,51 @@ +#!/bin/bash + +export PATH="$PATH:/opt/homebrew/bin:/usr/local/bin:/etc/profiles/per-user/$USER/bin" + +set -euo pipefail + +cd "$(dirname "${BASH_SOURCE[0]}")"/.. + +TAG_PREFIX="builds/" + +CURRENT_BUILD=$(git tag --points-at HEAD | tail -n 1) +LATEST_BUILD="$TAG_PREFIX$(git tag -l "builds/[0-9]*" | cut -d'/' -f 2 | sort -n | tail -n 1)" + +CURRENT_BUILD_NUMBER=${CURRENT_BUILD#$TAG_PREFIX} +LATEST_BUILD_NUMBER=${LATEST_BUILD#$TAG_PREFIX} +if [[ -z $LATEST_BUILD_NUMBER ]]; then + LATEST_BUILD_NUMBER="0" +fi + +if [[ ! -z $LATEST_BUILD && $(git merge-base --is-ancestor $LATEST_BUILD HEAD) -ne 0 ]]; then + echo "error: HEAD is not descended from build $LATEST_BUILD_NUMBER" >&2 + exit 1 +fi + +BUILD_NUMBER=$LATEST_BUILD_NUMBER + +if [[ $# -gt 0 && "$1" == "increment" ]]; then + NEW_BUILD_NUMBER=$((LATEST_BUILD_NUMBER + 1)) + NEW_TAG="$TAG_PREFIX$NEW_BUILD_NUMBER" + BUILD_NUMBER=$NEW_BUILD_NUMBER + + git tag $NEW_TAG + git push --quiet origin $NEW_TAG + gh release create "$NEW_TAG" -t "Build $BUILD_NUMBER" --verify-tag --generate-notes >/dev/null +fi + +if [[ -z $(grep $BUILD_NUMBER Apple/Configuration/Version.xcconfig 2>/dev/null) ]]; then + echo "CURRENT_PROJECT_VERSION = $BUILD_NUMBER" > Apple/Configuration/Version.xcconfig + git update-index --assume-unchanged Apple/Configuration/Version.xcconfig +fi + +if [[ $# -gt 0 && "$1" == "status" ]]; then + if [[ $CURRENT_BUILD_NUMBER -eq $LATEST_BUILD_NUMBER ]]; then + echo "clean" + else + echo "dirty" + fi + exit 0 +fi + +echo $BUILD_NUMBER diff --git a/burrow-gtk/build-aux/Dockerfile b/burrow-gtk/build-aux/Dockerfile index df07c4a..834e450 100644 --- a/burrow-gtk/build-aux/Dockerfile +++ b/burrow-gtk/build-aux/Dockerfile @@ -4,7 +4,7 @@ ENV DEBIAN_FRONTEND=noninteractive RUN set -eux && \ dnf update -y && \ - dnf install -y clang ninja-build cmake meson gtk4-devel glib2-devel libadwaita-devel desktop-file-utils libappstream-glib util-linux wget fuse fuse-libs file + dnf install -y clang ninja-build cmake meson gtk4-devel glib2-devel libadwaita-devel desktop-file-utils libappstream-glib util-linux wget fuse fuse-libs file sqlite sqlite-devel protobuf-compiler protobuf-devel RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain stable --profile minimal ENV PATH="/root/.cargo/bin:${PATH}" @@ -12,6 +12,8 @@ ENV PATH="/root/.cargo/bin:${PATH}" WORKDIR /app COPY . /app +ENV SQLITE3_STATIC=1 + RUN cd /app/burrow-gtk/ && \ ./build-aux/build_appimage.sh diff --git a/burrow-gtk/build-aux/build_appimage.sh b/burrow-gtk/build-aux/build_appimage.sh index cd58c17..f054cd9 100755 --- a/burrow-gtk/build-aux/build_appimage.sh +++ b/burrow-gtk/build-aux/build_appimage.sh @@ -22,6 +22,8 @@ elif [ "$ARCHITECTURE" == "aarch64" ]; then chmod a+x /tmp/linuxdeploy fi + +CFLAGS="-I/usr/local/include -I/usr/include/$MUSL_TARGET -fPIE" meson setup $BURROW_GTK_BUILD --bindir bin --prefix /usr --buildtype $BURROW_BUILD_TYPE meson compile -C $BURROW_GTK_BUILD DESTDIR=AppDir meson install -C $BURROW_GTK_BUILD diff --git a/burrow/Cargo.toml b/burrow/Cargo.toml index 4e7688b..d5e56c1 100644 --- a/burrow/Cargo.toml +++ b/burrow/Cargo.toml @@ -10,13 +10,23 @@ crate-type = ["lib", "staticlib"] [dependencies] anyhow = "1.0" -tokio = { version = "1.21", features = ["rt", "macros", "sync", "io-util", "rt-multi-thread", "time", "tracing"] } +tokio = { version = "1.37", features = [ + "rt", + "macros", + "sync", + "io-util", + "rt-multi-thread", + "signal", + "time", + "tracing", + "fs", +] } tun = { version = "0.1", path = "../tun", features = ["serde", "tokio"] } clap = { version = "4.4", features = ["derive"] } tracing = "0.1" tracing-log = "0.1" tracing-oslog = { git = "https://github.com/Stormshield-robinc/tracing-oslog" } -tracing-subscriber = { version = "0.3" , features = ["std", "env-filter"] } +tracing-subscriber = { version = "0.3", features = ["std", "env-filter"] } log = "0.4" serde = { version = "1", features = ["derive"] } serde_json = "1.0" @@ -25,7 +35,10 @@ chacha20poly1305 = "0.10" rand = "0.8" rand_core = "0.6" aead = "0.5" -x25519-dalek = { version = "2.0", features = ["reusable_secrets", "static_secrets"] } +x25519-dalek = { version = "2.0", features = [ + "reusable_secrets", + "static_secrets", +] } ring = "0.17" parking_lot = "0.12" hmac = "0.12" @@ -37,8 +50,24 @@ async-channel = "2.1" schemars = "0.8" futures = "0.3.28" once_cell = "1.19" -console-subscriber = { version = "0.2.0" , optional = true } +console-subscriber = { version = "0.2.0", optional = true } console = "0.15.8" +axum = "0.7.4" +reqwest = { version = "0.12", default-features = false, features = [ + "json", + "rustls-tls", +] } +rusqlite = { version = "0.31.0", features = ["blob"] } +dotenv = "0.15.0" +tonic = "0.12.0" +prost = "0.13.1" +prost-types = "0.13.1" +tokio-stream = "0.1" +async-stream = "0.2" +tower = "0.4.13" +hyper-util = "0.1.6" +toml = "0.8.15" +rust-ini = "0.21.0" [target.'cfg(target_os = "linux")'.dependencies] caps = "0.5" @@ -47,6 +76,7 @@ tracing-journald = "0.3" [target.'cfg(target_vendor = "apple")'.dependencies] nix = { version = "0.27" } +rusqlite = { version = "0.31.0", features = ["bundled", "blob"] } [dev-dependencies] insta = { version = "1.32", features = ["yaml"] } @@ -62,3 +92,8 @@ pre_uninstall_script = "../package/rpm/pre_uninstall" [features] tokio-console = ["dep:console-subscriber"] +bundled = ["rusqlite/bundled"] + + +[build-dependencies] +tonic-build = "0.12.0" diff --git a/burrow/build.rs b/burrow/build.rs new file mode 100644 index 0000000..8eea5dc --- /dev/null +++ b/burrow/build.rs @@ -0,0 +1,4 @@ +fn main() -> Result<(), Box> { + tonic_build::compile_protos("../proto/burrow.proto")?; + Ok(()) +} diff --git a/burrow/src/auth/client.rs b/burrow/src/auth/client.rs new file mode 100644 index 0000000..e9721f3 --- /dev/null +++ b/burrow/src/auth/client.rs @@ -0,0 +1,24 @@ +use std::env::var; + +use anyhow::Result; +use reqwest::Url; + +pub async fn login() -> Result<()> { + let state = "vt :P"; + let nonce = "no"; + + let mut url = Url::parse("https://slack.com/openid/connect/authorize")?; + let mut q = url.query_pairs_mut(); + q.append_pair("response_type", "code"); + q.append_pair("scope", "openid profile email"); + q.append_pair("client_id", &var("CLIENT_ID")?); + q.append_pair("state", state); + q.append_pair("team", &var("SLACK_TEAM_ID")?); + q.append_pair("nonce", nonce); + q.append_pair("redirect_uri", "https://burrow.rs/callback"); + drop(q); + + println!("Continue auth in your browser:\n{}", url.as_str()); + + Ok(()) +} diff --git a/burrow/src/auth/mod.rs b/burrow/src/auth/mod.rs new file mode 100644 index 0000000..c07f47e --- /dev/null +++ b/burrow/src/auth/mod.rs @@ -0,0 +1,2 @@ +pub mod client; +pub mod server; diff --git a/burrow/src/auth/server/db.rs b/burrow/src/auth/server/db.rs new file mode 100644 index 0000000..995e64b --- /dev/null +++ b/burrow/src/auth/server/db.rs @@ -0,0 +1,91 @@ +use anyhow::Result; + +use crate::daemon::rpc::grpc_defs::{Network, NetworkType}; + +pub static PATH: &str = "./server.sqlite3"; + +pub fn init_db() -> Result<()> { + let conn = rusqlite::Connection::open(PATH)?; + + conn.execute( + "CREATE TABLE IF NOT EXISTS user ( + id PRIMARY KEY, + created_at TEXT NOT NULL + )", + (), + )?; + + conn.execute( + "CREATE TABLE IF NOT EXISTS user_connection ( + user_id INTEGER REFERENCES user(id) ON DELETE CASCADE, + openid_provider TEXT NOT NULL, + openid_user_id TEXT NOT NULL, + openid_user_name TEXT NOT NULL, + access_token TEXT NOT NULL, + refresh_token TEXT, + PRIMARY KEY (openid_provider, openid_user_id) + )", + (), + )?; + + conn.execute( + "CREATE TABLE IF NOT EXISTS device ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT, + public_key TEXT NOT NULL, + apns_token TEXT UNIQUE, + user_id INT REFERENCES user(id) ON DELETE CASCADE, + created_at TEXT NOT NULL DEFAULT (datetime('now')) CHECK(created_at IS datetime(created_at)), + ipv4 TEXT NOT NULL UNIQUE, + ipv6 TEXT NOT NULL UNIQUE, + access_token TEXT NOT NULL UNIQUE, + refresh_token TEXT NOT NULL UNIQUE, + expires_at TEXT NOT NULL DEFAULT (datetime('now', '+7 days')) CHECK(expires_at IS datetime(expires_at)) + )", + () + ).unwrap(); + + Ok(()) +} + +pub fn store_connection( + openid_user: super::providers::OpenIdUser, + openid_provider: &str, + access_token: &str, + refresh_token: Option<&str>, +) -> Result<()> { + log::debug!("Storing openid user {:#?}", openid_user); + let conn = rusqlite::Connection::open(PATH)?; + + conn.execute( + "INSERT OR IGNORE INTO user (id, created_at) VALUES (?, datetime('now'))", + (&openid_user.sub,), + )?; + conn.execute( + "INSERT INTO user_connection (user_id, openid_provider, openid_user_id, openid_user_name, access_token, refresh_token) VALUES ( + (SELECT id FROM user WHERE id = ?), + ?, + ?, + ?, + ?, + ? + )", + (&openid_user.sub, &openid_provider, &openid_user.sub, &openid_user.name, access_token, refresh_token), + )?; + + Ok(()) +} + +pub fn store_device( + openid_user: super::providers::OpenIdUser, + openid_provider: &str, + access_token: &str, + refresh_token: Option<&str>, +) -> Result<()> { + log::debug!("Storing openid user {:#?}", openid_user); + let conn = rusqlite::Connection::open(PATH)?; + + // TODO + + Ok(()) +} diff --git a/burrow/src/auth/server/mod.rs b/burrow/src/auth/server/mod.rs new file mode 100644 index 0000000..88b3ff3 --- /dev/null +++ b/burrow/src/auth/server/mod.rs @@ -0,0 +1,62 @@ +pub mod db; +pub mod providers; + +use anyhow::Result; +use axum::{http::StatusCode, routing::post, Router}; +use providers::slack::auth; +use tokio::signal; + +pub async fn serve() -> Result<()> { + db::init_db()?; + + let app = Router::new() + .route("/slack-auth", post(auth)) + .route("/device/new", post(device_new)); + + let listener = tokio::net::TcpListener::bind("0.0.0.0:8080").await.unwrap(); + log::info!("Starting auth server on port 8080"); + axum::serve(listener, app) + .with_graceful_shutdown(shutdown_signal()) + .await + .unwrap(); + + Ok(()) +} + +async fn device_new() -> StatusCode { + StatusCode::OK +} + +async fn shutdown_signal() { + let ctrl_c = async { + signal::ctrl_c() + .await + .expect("failed to install Ctrl+C handler"); + }; + + #[cfg(unix)] + let terminate = async { + signal::unix::signal(signal::unix::SignalKind::terminate()) + .expect("failed to install signal handler") + .recv() + .await; + }; + + #[cfg(not(unix))] + let terminate = std::future::pending::<()>(); + + tokio::select! { + _ = ctrl_c => {}, + _ = terminate => {}, + } +} + +// mod db { +// use rusqlite::{Connection, Result}; + +// #[derive(Debug)] +// struct User { +// id: i32, +// created_at: String, +// } +// } diff --git a/burrow/src/auth/server/providers/mod.rs b/burrow/src/auth/server/providers/mod.rs new file mode 100644 index 0000000..36ff0bd --- /dev/null +++ b/burrow/src/auth/server/providers/mod.rs @@ -0,0 +1,8 @@ +pub mod slack; +pub use super::db; + +#[derive(serde::Deserialize, Default, Debug)] +pub struct OpenIdUser { + pub sub: String, + pub name: String, +} diff --git a/burrow/src/auth/server/providers/slack.rs b/burrow/src/auth/server/providers/slack.rs new file mode 100644 index 0000000..581cd1e --- /dev/null +++ b/burrow/src/auth/server/providers/slack.rs @@ -0,0 +1,102 @@ +use anyhow::Result; +use axum::{ + extract::Json, + http::StatusCode, + routing::{get, post}, +}; +use reqwest::header::AUTHORIZATION; +use serde::Deserialize; + +use super::db::store_connection; + +#[derive(Deserialize)] +pub struct SlackToken { + slack_token: String, +} +pub async fn auth(Json(payload): Json) -> (StatusCode, String) { + let slack_user = match fetch_slack_user(&payload.slack_token).await { + Ok(user) => user, + Err(e) => { + log::error!("Failed to fetch Slack user: {:?}", e); + return (StatusCode::UNAUTHORIZED, String::new()); + } + }; + + log::info!( + "Slack user {} ({}) logged in.", + slack_user.name, + slack_user.sub + ); + + let conn = match store_connection(slack_user, "slack", &payload.slack_token, None) { + Ok(user) => user, + Err(e) => { + log::error!("Failed to fetch Slack user: {:?}", e); + return (StatusCode::UNAUTHORIZED, String::new()); + } + }; + + (StatusCode::OK, String::new()) +} + +async fn fetch_slack_user(access_token: &str) -> Result { + let client = reqwest::Client::new(); + let res = client + .get("https://slack.com/api/openid.connect.userInfo") + .header(AUTHORIZATION, format!("Bearer {}", access_token)) + .send() + .await? + .json::() + .await?; + + let res_ok = res + .get("ok") + .and_then(|v| v.as_bool()) + .ok_or(anyhow::anyhow!("Slack user object not ok!"))?; + + if !res_ok { + return Err(anyhow::anyhow!("Slack user object not ok!")); + } + + Ok(serde_json::from_value(res)?) +} + +// async fn fetch_save_slack_user_data(query: Query) -> anyhow::Result<()> { +// let client = reqwest::Client::new(); +// log::trace!("Code was {}", &query.code); +// let mut url = Url::parse("https://slack.com/api/openid.connect.token")?; + +// { +// let mut q = url.query_pairs_mut(); +// q.append_pair("client_id", &var("CLIENT_ID")?); +// q.append_pair("client_secret", &var("CLIENT_SECRET")?); +// q.append_pair("code", &query.code); +// q.append_pair("grant_type", "authorization_code"); +// q.append_pair("redirect_uri", "https://burrow.rs/callback"); +// } + +// let data = client +// .post(url) +// .send() +// .await? +// .json::() +// .await?; + +// if !data.ok { +// return Err(anyhow::anyhow!("Slack code exchange response not ok!")); +// } + +// if let Some(access_token) = data.access_token { +// log::trace!("Access token is {access_token}"); +// let user = slack::fetch_slack_user(&access_token) +// .await +// .map_err(|err| anyhow::anyhow!("Failed to fetch Slack user info {:#?}", err))?; + +// db::store_user(user, access_token, String::new()) +// .map_err(|_| anyhow::anyhow!("Failed to store user in db"))?; + +// Ok(()) +// } else { +// Err(anyhow::anyhow!("Access token not found in response")) +// } +// } diff --git a/burrow/src/daemon/apple.rs b/burrow/src/daemon/apple.rs index 9460613..c60f131 100644 --- a/burrow/src/daemon/apple.rs +++ b/burrow/src/daemon/apple.rs @@ -18,7 +18,7 @@ static BURROW_NOTIFY: OnceCell> = OnceCell::new(); static BURROW_HANDLE: OnceCell = OnceCell::new(); #[no_mangle] -pub unsafe extern "C" fn spawn_in_process(path: *const c_char) { +pub unsafe extern "C" fn spawn_in_process(path: *const c_char, db_path: *const c_char) { crate::tracing::initialize(); let notify = BURROW_NOTIFY.get_or_init(|| Arc::new(Notify::new())); @@ -28,6 +28,11 @@ pub unsafe extern "C" fn spawn_in_process(path: *const c_char) { } else { Some(PathBuf::from(CStr::from_ptr(path).to_str().unwrap())) }; + let db_path_buf = if db_path.is_null() { + None + } else { + Some(PathBuf::from(CStr::from_ptr(db_path).to_str().unwrap())) + }; let sender = notify.clone(); let (handle_tx, handle_rx) = tokio::sync::oneshot::channel(); @@ -40,7 +45,12 @@ pub unsafe extern "C" fn spawn_in_process(path: *const c_char) { .unwrap(); handle_tx.send(runtime.handle().clone()).unwrap(); runtime.block_on(async { - let result = daemon_main(path_buf.as_deref(), Some(sender.clone())).await; + let result = daemon_main( + path_buf.as_deref(), + db_path_buf.as_deref(), + Some(sender.clone()), + ) + .await; if let Err(error) = result.as_ref() { error!("Burrow thread exited: {}", error); } diff --git a/burrow/src/daemon/instance.rs b/burrow/src/daemon/instance.rs index bb871c6..ce96fa5 100644 --- a/burrow/src/daemon/instance.rs +++ b/burrow/src/daemon/instance.rs @@ -1,114 +1,256 @@ -use std::sync::Arc; - -use anyhow::Result; -use tokio::{sync::RwLock, task::JoinHandle}; -use tracing::{debug, info, warn}; -use tun::tokio::TunInterface; - -use crate::{ - daemon::{ - command::DaemonCommand, - response::{DaemonResponse, DaemonResponseData, ServerConfig, ServerInfo}, - }, - wireguard::Interface, +use std::{ + ops::Deref, + path::{Path, PathBuf}, + sync::Arc, + time::Duration, }; +use anyhow::Result; +use rusqlite::Connection; +use tokio::sync::{mpsc, watch, Notify, RwLock}; +use tokio_stream::wrappers::ReceiverStream; +use tonic::{Request, Response, Status as RspStatus}; +use tracing::{debug, info, warn}; +use tun::{tokio::TunInterface, TunOptions}; + +use super::rpc::grpc_defs::{ + networks_server::Networks, + tunnel_server::Tunnel, + Empty, + Network, + NetworkDeleteRequest, + NetworkListResponse, + NetworkReorderRequest, + State as RPCTunnelState, + TunnelConfigurationResponse, + TunnelStatusResponse, +}; +use crate::{ + daemon::rpc::{ + DaemonCommand, + DaemonNotification, + DaemonResponse, + DaemonResponseData, + ServerConfig, + ServerInfo, + }, + database::{ + add_network, + delete_network, + get_connection, + list_networks, + load_interface, + reorder_network, + }, + wireguard::{Config, Interface}, +}; + +#[derive(Debug, Clone)] enum RunState { - Running(JoinHandle>), + Running, Idle, } -pub struct DaemonInstance { - rx: async_channel::Receiver, - sx: async_channel::Sender, +impl RunState { + pub fn to_rpc(&self) -> RPCTunnelState { + match self { + RunState::Running => RPCTunnelState::Running, + RunState::Idle => RPCTunnelState::Stopped, + } + } +} + +#[derive(Clone)] +pub struct DaemonRPCServer { tun_interface: Arc>>, wg_interface: Arc>, - wg_state: RunState, + config: Arc>, + db_path: Option, + wg_state_chan: (watch::Sender, watch::Receiver), + network_update_chan: (watch::Sender<()>, watch::Receiver<()>), } -impl DaemonInstance { +impl DaemonRPCServer { pub fn new( - rx: async_channel::Receiver, - sx: async_channel::Sender, wg_interface: Arc>, - ) -> Self { - Self { - rx, - sx, - wg_interface, + config: Arc>, + db_path: Option<&Path>, + ) -> Result { + Ok(Self { tun_interface: Arc::new(RwLock::new(None)), - wg_state: RunState::Idle, - } + wg_interface, + config, + db_path: db_path.map(|p| p.to_owned()), + wg_state_chan: watch::channel(RunState::Idle), + network_update_chan: watch::channel(()), + }) } - async fn proc_command(&mut self, command: DaemonCommand) -> Result { - info!("Daemon got command: {:?}", command); - match command { - DaemonCommand::Start(st) => { - match self.wg_state { - RunState::Running(_) => { - warn!("Got start, but tun interface already up."); - } - RunState::Idle => { - let tun_if = st.tun.open()?; - tun_if.set_up(true)?; - - debug!("Setting tun on wg_interface"); - self.wg_interface.read().await.set_tun(tun_if).await; - debug!("tun set on wg_interface"); - - debug!("Setting tun_interface"); - self.tun_interface = self.wg_interface.read().await.get_tun(); - debug!("tun_interface set: {:?}", self.tun_interface); - - debug!("Cloning wg_interface"); - let tmp_wg = self.wg_interface.clone(); - debug!("wg_interface cloned"); - - debug!("Spawning run task"); - let run_task = tokio::spawn(async move { - debug!("Running wg_interface"); - let twlock = tmp_wg.read().await; - debug!("wg_interface read lock acquired"); - twlock.run().await - }); - debug!("Run task spawned: {:?}", run_task); - - debug!("Setting wg_state to Running"); - self.wg_state = RunState::Running(run_task); - debug!("wg_state set to Running"); - - info!("Daemon started tun interface"); - } - } - Ok(DaemonResponseData::None) - } - DaemonCommand::ServerInfo => match &self.tun_interface.read().await.as_ref() { - None => Ok(DaemonResponseData::None), - Some(ti) => { - info!("{:?}", ti); - Ok(DaemonResponseData::ServerInfo(ServerInfo::try_from( - ti.inner.get_ref(), - )?)) - } - }, - DaemonCommand::Stop => { - self.wg_interface.read().await.remove_tun().await; - self.wg_state = RunState::Idle; - Ok(DaemonResponseData::None) - } - DaemonCommand::ServerConfig => { - Ok(DaemonResponseData::ServerConfig(ServerConfig::default())) - } - } + pub fn get_connection(&self) -> Result { + get_connection(self.db_path.as_deref()).map_err(proc_err) } - pub async fn run(&mut self) -> Result<()> { - while let Ok(command) = self.rx.recv().await { - let response = self.proc_command(command).await; - info!("Daemon response: {:?}", response); - self.sx.send(DaemonResponse::new(response)).await?; - } - Ok(()) + async fn set_wg_state(&self, state: RunState) -> Result<(), RspStatus> { + self.wg_state_chan.0.send(state).map_err(proc_err) + } + + async fn get_wg_state(&self) -> RunState { + self.wg_state_chan.1.borrow().to_owned() + } + + async fn notify_network_update(&self) -> Result<(), RspStatus> { + self.network_update_chan.0.send(()).map_err(proc_err) + } +} + +#[tonic::async_trait] +impl Tunnel for DaemonRPCServer { + type TunnelConfigurationStream = ReceiverStream>; + type TunnelStatusStream = ReceiverStream>; + + async fn tunnel_configuration( + &self, + _request: Request, + ) -> Result, RspStatus> { + let (tx, rx) = mpsc::channel(10); + tokio::spawn(async move { + let serv_config = ServerConfig::default(); + tx.send(Ok(TunnelConfigurationResponse { + mtu: serv_config.mtu.unwrap_or(1000), + addresses: serv_config.address, + })) + .await + }); + Ok(Response::new(ReceiverStream::new(rx))) + } + + async fn tunnel_start(&self, _request: Request) -> Result, RspStatus> { + let wg_state = self.get_wg_state().await; + match wg_state { + RunState::Idle => { + let tun_if = TunOptions::new().open()?; + debug!("Setting tun on wg_interface"); + self.tun_interface.write().await.replace(tun_if); + self.wg_interface + .write() + .await + .set_tun_ref(self.tun_interface.clone()) + .await; + debug!("tun set on wg_interface"); + + debug!("Setting tun_interface"); + debug!("tun_interface set: {:?}", self.tun_interface); + + debug!("Cloning wg_interface"); + let tmp_wg = self.wg_interface.clone(); + let run_task = tokio::spawn(async move { + let twlock = tmp_wg.read().await; + twlock.run().await + }); + self.set_wg_state(RunState::Running).await?; + } + + RunState::Running => { + warn!("Got start, but tun interface already up."); + } + } + + return Ok(Response::new(Empty {})); + } + + async fn tunnel_stop(&self, _request: Request) -> Result, RspStatus> { + self.wg_interface.write().await.remove_tun().await; + self.set_wg_state(RunState::Idle).await?; + return Ok(Response::new(Empty {})); + } + + async fn tunnel_status( + &self, + _request: Request, + ) -> Result, RspStatus> { + let (tx, rx) = mpsc::channel(10); + let mut state_rx = self.wg_state_chan.1.clone(); + tokio::spawn(async move { + let cur = state_rx.borrow_and_update().to_owned(); + tx.send(Ok(status_rsp(cur))).await; + loop { + state_rx.changed().await.unwrap(); + let cur = state_rx.borrow().to_owned(); + let res = tx.send(Ok(status_rsp(cur))).await; + if res.is_err() { + eprintln!("Tunnel status channel closed"); + break; + } + } + }); + Ok(Response::new(ReceiverStream::new(rx))) + } +} + +#[tonic::async_trait] +impl Networks for DaemonRPCServer { + type NetworkListStream = ReceiverStream>; + + async fn network_add(&self, request: Request) -> Result, RspStatus> { + let conn = self.get_connection()?; + let network = request.into_inner(); + add_network(&conn, &network).map_err(proc_err)?; + self.notify_network_update().await?; + Ok(Response::new(Empty {})) + } + + async fn network_list( + &self, + _request: Request, + ) -> Result, RspStatus> { + debug!("Mock network_list called"); + let (tx, rx) = mpsc::channel(10); + let conn = self.get_connection()?; + let mut sub = self.network_update_chan.1.clone(); + tokio::spawn(async move { + loop { + let networks = list_networks(&conn) + .map(|res| NetworkListResponse { network: res }) + .map_err(proc_err); + let res = tx.send(networks).await; + if res.is_err() { + eprintln!("Network list channel closed"); + break; + } + sub.changed().await.unwrap(); + } + }); + Ok(Response::new(ReceiverStream::new(rx))) + } + + async fn network_reorder( + &self, + request: Request, + ) -> Result, RspStatus> { + let conn = self.get_connection()?; + reorder_network(&conn, request.into_inner()).map_err(proc_err)?; + self.notify_network_update().await?; + Ok(Response::new(Empty {})) + } + + async fn network_delete( + &self, + request: Request, + ) -> Result, RspStatus> { + let conn = self.get_connection()?; + delete_network(&conn, request.into_inner()).map_err(proc_err)?; + self.notify_network_update().await?; + Ok(Response::new(Empty {})) + } +} + +fn proc_err(err: impl ToString) -> RspStatus { + RspStatus::internal(err.to_string()) +} + +fn status_rsp(state: RunState) -> TunnelStatusResponse { + TunnelStatusResponse { + state: state.to_rpc().into(), + start: None, // TODO: Add timestamp } } diff --git a/burrow/src/daemon/mod.rs b/burrow/src/daemon/mod.rs index 2a971dd..f6b973f 100644 --- a/burrow/src/daemon/mod.rs +++ b/burrow/src/daemon/mod.rs @@ -1,60 +1,63 @@ use std::{path::Path, sync::Arc}; pub mod apple; -mod command; mod instance; mod net; -mod response; +pub mod rpc; -use anyhow::Result; -pub use command::{DaemonCommand, DaemonStartOptions}; -use instance::DaemonInstance; -pub use net::{DaemonClient, Listener}; -pub use response::{DaemonResponse, DaemonResponseData, ServerInfo}; -use tokio::sync::{Notify, RwLock}; +use anyhow::{Error as AhError, Result}; +use instance::DaemonRPCServer; +pub use net::{get_socket_path, DaemonClient}; +pub use rpc::{DaemonCommand, DaemonResponseData, DaemonStartOptions}; +use tokio::{ + net::UnixListener, + sync::{Notify, RwLock}, +}; +use tokio_stream::wrappers::UnixListenerStream; +use tonic::transport::Server; use tracing::{error, info}; -use crate::wireguard::{Config, Interface}; +use crate::{ + daemon::rpc::grpc_defs::{networks_server::NetworksServer, tunnel_server::TunnelServer}, + database::{get_connection, load_interface}, + wireguard::Interface, +}; -pub async fn daemon_main(path: Option<&Path>, notify_ready: Option>) -> Result<()> { - let (commands_tx, commands_rx) = async_channel::unbounded(); - let (response_tx, response_rx) = async_channel::unbounded(); - - let listener = if let Some(path) = path { - info!("Creating listener... {:?}", path); - Listener::new_with_path(commands_tx, response_rx, path) - } else { - info!("Creating listener..."); - Listener::new(commands_tx, response_rx) - }; +pub async fn daemon_main( + socket_path: Option<&Path>, + db_path: Option<&Path>, + notify_ready: Option>, +) -> Result<()> { if let Some(n) = notify_ready { n.notify_one() } - let listener = listener?; - - let config = Config::default(); - let iface: Interface = config.try_into()?; - let mut instance = DaemonInstance::new(commands_rx, response_tx, Arc::new(RwLock::new(iface))); + let conn = get_connection(db_path)?; + let config = load_interface(&conn, "1")?; + let burrow_server = DaemonRPCServer::new( + Arc::new(RwLock::new(config.clone().try_into()?)), + Arc::new(RwLock::new(config)), + db_path.clone(), + )?; + let spp = socket_path.clone(); + let tmp = get_socket_path(); + let sock_path = spp.unwrap_or(Path::new(tmp.as_str())); + if sock_path.exists() { + std::fs::remove_file(sock_path)?; + } + let uds = UnixListener::bind(sock_path)?; + let serve_job = tokio::spawn(async move { + let uds_stream = UnixListenerStream::new(uds); + let _srv = Server::builder() + .add_service(TunnelServer::new(burrow_server.clone())) + .add_service(NetworksServer::new(burrow_server)) + .serve_with_incoming(uds_stream) + .await?; + Ok::<(), AhError>(()) + }); info!("Starting daemon..."); - let main_job = tokio::spawn(async move { - let result = instance.run().await; - if let Err(e) = result.as_ref() { - error!("Instance exited: {}", e); - } - result - }); - - let listener_job = tokio::spawn(async move { - let result = listener.run().await; - if let Err(e) = result.as_ref() { - error!("Listener exited: {}", e); - } - result - }); - - tokio::try_join!(main_job, listener_job) + tokio::try_join!(serve_job) .map(|_| ()) .map_err(|e| e.into()) } diff --git a/burrow/src/daemon/net/mod.rs b/burrow/src/daemon/net/mod.rs index fe35bae..eb45335 100644 --- a/burrow/src/daemon/net/mod.rs +++ b/burrow/src/daemon/net/mod.rs @@ -1,21 +1,11 @@ -use serde::{Deserialize, Serialize}; - -use super::DaemonCommand; - #[cfg(target_family = "unix")] mod unix; #[cfg(target_family = "unix")] -pub use unix::{DaemonClient, Listener}; +pub use unix::{get_socket_path, DaemonClient, Listener}; #[cfg(target_os = "windows")] mod windows; #[cfg(target_os = "windows")] pub use windows::{DaemonClient, Listener}; - -#[derive(Clone, Serialize, Deserialize)] -pub struct DaemonRequest { - pub id: u64, - pub command: DaemonCommand, -} diff --git a/burrow/src/daemon/net/unix.rs b/burrow/src/daemon/net/unix.rs index 26e901d..975c470 100644 --- a/burrow/src/daemon/net/unix.rs +++ b/burrow/src/daemon/net/unix.rs @@ -10,8 +10,14 @@ use tokio::{ }; use tracing::{debug, error, info}; -use super::*; -use crate::daemon::{DaemonCommand, DaemonResponse, DaemonResponseData}; +use crate::daemon::rpc::{ + DaemonCommand, + DaemonMessage, + DaemonNotification, + DaemonRequest, + DaemonResponse, + DaemonResponseData, +}; #[cfg(not(target_vendor = "apple"))] const UNIX_SOCKET_PATH: &str = "/run/burrow.sock"; @@ -19,11 +25,18 @@ const UNIX_SOCKET_PATH: &str = "/run/burrow.sock"; #[cfg(target_vendor = "apple")] const UNIX_SOCKET_PATH: &str = "burrow.sock"; -#[derive(Debug)] +pub fn get_socket_path() -> String { + if std::env::var("BURROW_SOCKET_PATH").is_ok() { + return std::env::var("BURROW_SOCKET_PATH").unwrap(); + } + UNIX_SOCKET_PATH.to_string() +} + pub struct Listener { cmd_tx: async_channel::Sender, rsp_rx: async_channel::Receiver, - inner: UnixListener, + sub_chan: async_channel::Receiver, + pub inner: UnixListener, } impl Listener { @@ -31,9 +44,11 @@ impl Listener { pub fn new( cmd_tx: async_channel::Sender, rsp_rx: async_channel::Receiver, + sub_chan: async_channel::Receiver, ) -> Self { - let path = Path::new(OsStr::new(UNIX_SOCKET_PATH)); - Self::new_with_path(cmd_tx, rsp_rx, path)? + let socket_path = get_socket_path(); + let path = Path::new(OsStr::new(&socket_path)); + Self::new_with_path(cmd_tx, rsp_rx, sub_chan, path)? } #[throws] @@ -41,10 +56,16 @@ impl Listener { pub fn new_with_path( cmd_tx: async_channel::Sender, rsp_rx: async_channel::Receiver, + sub_chan: async_channel::Receiver, path: &Path, ) -> Self { let inner = listener_from_path_or_fd(&path, raw_fd())?; - Self { cmd_tx, rsp_rx, inner } + Self { + cmd_tx, + rsp_rx, + sub_chan, + inner, + } } #[throws] @@ -52,10 +73,16 @@ impl Listener { pub fn new_with_path( cmd_tx: async_channel::Sender, rsp_rx: async_channel::Receiver, + sub_chan: async_channel::Receiver, path: &Path, ) -> Self { let inner = listener_from_path(path)?; - Self { cmd_tx, rsp_rx, inner } + Self { + cmd_tx, + rsp_rx, + inner, + sub_chan, + } } pub async fn run(&self) -> Result<()> { @@ -64,9 +91,10 @@ impl Listener { let (stream, _) = self.inner.accept().await?; let cmd_tx = self.cmd_tx.clone(); let rsp_rxc = self.rsp_rx.clone(); + let sub_chan = self.sub_chan.clone(); tokio::task::spawn(async move { info!("Got connection: {:?}", stream); - Self::stream(stream, cmd_tx, rsp_rxc).await; + Self::stream(stream, cmd_tx, rsp_rxc, sub_chan).await; }); } } @@ -75,34 +103,46 @@ impl Listener { stream: UnixStream, cmd_tx: async_channel::Sender, rsp_rxc: async_channel::Receiver, + sub_chan: async_channel::Receiver, ) { let mut stream = stream; let (mut read_stream, mut write_stream) = stream.split(); let buf_reader = BufReader::new(&mut read_stream); let mut lines = buf_reader.lines(); - while let Ok(Some(line)) = lines.next_line().await { - info!("Line: {}", line); - let mut res: DaemonResponse = DaemonResponseData::None.into(); - let req = match serde_json::from_str::(&line) { - Ok(req) => Some(req), - Err(e) => { - res.result = Err(e.to_string()); - error!("Failed to parse request: {}", e); - None - } - }; - let mut res = serde_json::to_string(&res).unwrap(); - res.push('\n'); + loop { + tokio::select! { + Ok(Some(line)) = lines.next_line() => { + info!("Line: {}", line); + let mut res: DaemonResponse = DaemonResponseData::None.into(); + let req = match serde_json::from_str::(&line) { + Ok(req) => Some(req), + Err(e) => { + res.result = Err(e.to_string()); + error!("Failed to parse request: {}", e); + None + } + }; - if let Some(req) = req { - cmd_tx.send(req.command).await.unwrap(); - let res = rsp_rxc.recv().await.unwrap().with_id(req.id); - let mut retres = serde_json::to_string(&res).unwrap(); - retres.push('\n'); - info!("Sending response: {}", retres); - write_stream.write_all(retres.as_bytes()).await.unwrap(); - } else { - write_stream.write_all(res.as_bytes()).await.unwrap(); + let res = serde_json::to_string(&DaemonMessage::from(res)).unwrap(); + + if let Some(req) = req { + cmd_tx.send(req.command).await.unwrap(); + let res = rsp_rxc.recv().await.unwrap().with_id(req.id); + let mut payload = serde_json::to_string(&DaemonMessage::from(res)).unwrap(); + payload.push('\n'); + info!("Sending response: {}", payload); + write_stream.write_all(payload.as_bytes()).await.unwrap(); + } else { + write_stream.write_all(res.as_bytes()).await.unwrap(); + } + } + Ok(cmd) = sub_chan.recv() => { + info!("Got subscription command: {:?}", cmd); + let msg = DaemonMessage::from(cmd); + let mut payload = serde_json::to_string(&msg).unwrap(); + payload.push('\n'); + write_stream.write_all(payload.as_bytes()).await.unwrap(); + } } } } @@ -176,7 +216,8 @@ pub struct DaemonClient { impl DaemonClient { pub async fn new() -> Result { - let path = Path::new(OsStr::new(UNIX_SOCKET_PATH)); + let socket_path = get_socket_path(); + let path = Path::new(OsStr::new(&socket_path)); Self::new_with_path(path).await } diff --git a/burrow/src/daemon/rpc/client.rs b/burrow/src/daemon/rpc/client.rs new file mode 100644 index 0000000..862e34c --- /dev/null +++ b/burrow/src/daemon/rpc/client.rs @@ -0,0 +1,31 @@ +use anyhow::Result; +use hyper_util::rt::TokioIo; +use tokio::net::UnixStream; +use tonic::transport::{Endpoint, Uri}; +use tower::service_fn; + +use super::grpc_defs::{networks_client::NetworksClient, tunnel_client::TunnelClient}; +use crate::daemon::get_socket_path; + +pub struct BurrowClient { + pub networks_client: NetworksClient, + pub tunnel_client: TunnelClient, +} + +impl BurrowClient { + #[cfg(any(target_os = "linux", target_vendor = "apple"))] + pub async fn from_uds() -> Result { + let channel = Endpoint::try_from("http://[::]:50051")? // NOTE: this is a hack(?) + .connect_with_connector(service_fn(|_: Uri| async { + let sock_path = get_socket_path(); + Ok::<_, std::io::Error>(TokioIo::new(UnixStream::connect(sock_path).await?)) + })) + .await?; + let nw_client = NetworksClient::new(channel.clone()); + let tun_client = TunnelClient::new(channel.clone()); + Ok(BurrowClient { + networks_client: nw_client, + tunnel_client: tun_client, + }) + } +} diff --git a/burrow/src/daemon/rpc/grpc_defs.rs b/burrow/src/daemon/rpc/grpc_defs.rs new file mode 100644 index 0000000..f3085ee --- /dev/null +++ b/burrow/src/daemon/rpc/grpc_defs.rs @@ -0,0 +1,5 @@ +pub use burrowgrpc::*; + +mod burrowgrpc { + tonic::include_proto!("burrow"); +} diff --git a/burrow/src/daemon/rpc/mod.rs b/burrow/src/daemon/rpc/mod.rs new file mode 100644 index 0000000..512662c --- /dev/null +++ b/burrow/src/daemon/rpc/mod.rs @@ -0,0 +1,43 @@ +pub mod client; +pub mod grpc_defs; +pub mod notification; +pub mod request; +pub mod response; + +pub use client::BurrowClient; +pub use notification::DaemonNotification; +pub use request::{DaemonCommand, DaemonRequest, DaemonStartOptions}; +pub use response::{DaemonResponse, DaemonResponseData, ServerConfig, ServerInfo}; +use serde::{Deserialize, Serialize}; + +/// The `Message` object contains either a `DaemonRequest` or a `DaemonResponse` to be serialized / deserialized +/// for our IPC communication. Our IPC protocol is based on jsonrpc (https://www.jsonrpc.org/specification#overview), +/// but deviates from it in a few ways: +/// - We differentiate Notifications from Requests explicitly. +/// - We have a "type" field to differentiate between a request, a response, and a notification. +/// - The params field may receive any json value(such as a string), not just an object or an array. +#[derive(Serialize, Deserialize)] +#[serde(tag = "type")] +pub enum DaemonMessage { + Request(DaemonRequest), + Response(DaemonResponse), + Notification(DaemonNotification), +} + +impl From for DaemonMessage { + fn from(request: DaemonRequest) -> Self { + DaemonMessage::Request(request) + } +} + +impl From for DaemonMessage { + fn from(response: DaemonResponse) -> Self { + DaemonMessage::Response(response) + } +} + +impl From for DaemonMessage { + fn from(notification: DaemonNotification) -> Self { + DaemonMessage::Notification(notification) + } +} diff --git a/burrow/src/daemon/rpc/notification.rs b/burrow/src/daemon/rpc/notification.rs new file mode 100644 index 0000000..135b0e4 --- /dev/null +++ b/burrow/src/daemon/rpc/notification.rs @@ -0,0 +1,11 @@ +use rpc::ServerConfig; +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; + +use crate::daemon::rpc; + +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] +#[serde(tag = "method", content = "params")] +pub enum DaemonNotification { + ConfigChange(ServerConfig), +} diff --git a/burrow/src/daemon/command.rs b/burrow/src/daemon/rpc/request.rs similarity index 82% rename from burrow/src/daemon/command.rs rename to burrow/src/daemon/rpc/request.rs index 53b4108..e9480aa 100644 --- a/burrow/src/daemon/command.rs +++ b/burrow/src/daemon/rpc/request.rs @@ -3,11 +3,13 @@ use serde::{Deserialize, Serialize}; use tun::TunOptions; #[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] +#[serde(tag="method", content="params")] pub enum DaemonCommand { Start(DaemonStartOptions), ServerInfo, ServerConfig, Stop, + ReloadConfig(String), } #[derive(Debug, Clone, Default, Serialize, Deserialize, JsonSchema)] @@ -15,6 +17,13 @@ pub struct DaemonStartOptions { pub tun: TunOptions, } +#[derive(Clone, Serialize, Deserialize)] +pub struct DaemonRequest { + pub id: u64, + #[serde(flatten)] + pub command: DaemonCommand, +} + #[test] fn test_daemoncommand_serialization() { insta::assert_snapshot!(serde_json::to_string(&DaemonCommand::Start( diff --git a/burrow/src/daemon/response.rs b/burrow/src/daemon/rpc/response.rs similarity index 89% rename from burrow/src/daemon/response.rs rename to burrow/src/daemon/rpc/response.rs index 37ee5d9..61c9c50 100644 --- a/burrow/src/daemon/response.rs +++ b/burrow/src/daemon/rpc/response.rs @@ -2,6 +2,8 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use tun::TunInterface; +use crate::wireguard::Config; + #[derive(Clone, Serialize, Deserialize, Debug, JsonSchema)] pub struct DaemonResponse { // Error types can't be serialized, so this is the second best option. @@ -62,6 +64,18 @@ pub struct ServerConfig { pub mtu: Option, } +impl TryFrom<&Config> for ServerConfig { + type Error = anyhow::Error; + + fn try_from(config: &Config) -> anyhow::Result { + Ok(ServerConfig { + address: config.interface.address.clone(), + name: None, + mtu: config.interface.mtu.map(|mtu| mtu as i32), + }) + } +} + impl Default for ServerConfig { fn default() -> Self { Self { @@ -73,6 +87,7 @@ impl Default for ServerConfig { } #[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)] +#[serde(tag = "type")] pub enum DaemonResponseData { ServerInfo(ServerInfo), ServerConfig(ServerConfig), diff --git a/burrow/src/daemon/rpc/snapshots/burrow__daemon__rpc__request__daemoncommand_serialization-2.snap b/burrow/src/daemon/rpc/snapshots/burrow__daemon__rpc__request__daemoncommand_serialization-2.snap new file mode 100644 index 0000000..01ec8a7 --- /dev/null +++ b/burrow/src/daemon/rpc/snapshots/burrow__daemon__rpc__request__daemoncommand_serialization-2.snap @@ -0,0 +1,5 @@ +--- +source: burrow/src/daemon/rpc/request.rs +expression: "serde_json::to_string(&DaemonCommand::Start(DaemonStartOptions {\n tun: TunOptions { ..TunOptions::default() },\n })).unwrap()" +--- +{"method":"Start","params":{"tun":{"name":null,"no_pi":false,"tun_excl":false,"tun_retrieve":false,"address":[]}}} diff --git a/burrow/src/daemon/rpc/snapshots/burrow__daemon__rpc__request__daemoncommand_serialization-3.snap b/burrow/src/daemon/rpc/snapshots/burrow__daemon__rpc__request__daemoncommand_serialization-3.snap new file mode 100644 index 0000000..a6a0466 --- /dev/null +++ b/burrow/src/daemon/rpc/snapshots/burrow__daemon__rpc__request__daemoncommand_serialization-3.snap @@ -0,0 +1,5 @@ +--- +source: burrow/src/daemon/rpc/request.rs +expression: "serde_json::to_string(&DaemonCommand::ServerInfo).unwrap()" +--- +{"method":"ServerInfo"} diff --git a/burrow/src/daemon/rpc/snapshots/burrow__daemon__rpc__request__daemoncommand_serialization-4.snap b/burrow/src/daemon/rpc/snapshots/burrow__daemon__rpc__request__daemoncommand_serialization-4.snap new file mode 100644 index 0000000..f930051 --- /dev/null +++ b/burrow/src/daemon/rpc/snapshots/burrow__daemon__rpc__request__daemoncommand_serialization-4.snap @@ -0,0 +1,5 @@ +--- +source: burrow/src/daemon/rpc/request.rs +expression: "serde_json::to_string(&DaemonCommand::Stop).unwrap()" +--- +{"method":"Stop"} diff --git a/burrow/src/daemon/rpc/snapshots/burrow__daemon__rpc__request__daemoncommand_serialization-5.snap b/burrow/src/daemon/rpc/snapshots/burrow__daemon__rpc__request__daemoncommand_serialization-5.snap new file mode 100644 index 0000000..89dc42c --- /dev/null +++ b/burrow/src/daemon/rpc/snapshots/burrow__daemon__rpc__request__daemoncommand_serialization-5.snap @@ -0,0 +1,5 @@ +--- +source: burrow/src/daemon/rpc/request.rs +expression: "serde_json::to_string(&DaemonCommand::ServerConfig).unwrap()" +--- +{"method":"ServerConfig"} diff --git a/burrow/src/daemon/rpc/snapshots/burrow__daemon__rpc__request__daemoncommand_serialization.snap b/burrow/src/daemon/rpc/snapshots/burrow__daemon__rpc__request__daemoncommand_serialization.snap new file mode 100644 index 0000000..aeca659 --- /dev/null +++ b/burrow/src/daemon/rpc/snapshots/burrow__daemon__rpc__request__daemoncommand_serialization.snap @@ -0,0 +1,5 @@ +--- +source: burrow/src/daemon/rpc/request.rs +expression: "serde_json::to_string(&DaemonCommand::Start(DaemonStartOptions::default())).unwrap()" +--- +{"method":"Start","params":{"tun":{"name":null,"no_pi":false,"tun_excl":false,"tun_retrieve":false,"address":[]}}} diff --git a/burrow/src/daemon/rpc/snapshots/burrow__daemon__rpc__response__response_serialization-2.snap b/burrow/src/daemon/rpc/snapshots/burrow__daemon__rpc__response__response_serialization-2.snap new file mode 100644 index 0000000..d7bd712 --- /dev/null +++ b/burrow/src/daemon/rpc/snapshots/burrow__daemon__rpc__response__response_serialization-2.snap @@ -0,0 +1,5 @@ +--- +source: burrow/src/daemon/rpc/response.rs +expression: "serde_json::to_string(&DaemonResponse::new(Ok::(DaemonResponseData::ServerInfo(ServerInfo {\n name: Some(\"burrow\".to_string()),\n ip: None,\n mtu: Some(1500),\n }))))?" +--- +{"result":{"Ok":{"type":"ServerInfo","name":"burrow","ip":null,"mtu":1500}},"id":0} diff --git a/burrow/src/daemon/rpc/snapshots/burrow__daemon__rpc__response__response_serialization-3.snap b/burrow/src/daemon/rpc/snapshots/burrow__daemon__rpc__response__response_serialization-3.snap new file mode 100644 index 0000000..30068f3 --- /dev/null +++ b/burrow/src/daemon/rpc/snapshots/burrow__daemon__rpc__response__response_serialization-3.snap @@ -0,0 +1,5 @@ +--- +source: burrow/src/daemon/rpc/response.rs +expression: "serde_json::to_string(&DaemonResponse::new(Err::(\"error\".to_string())))?" +--- +{"result":{"Err":"error"},"id":0} diff --git a/burrow/src/daemon/rpc/snapshots/burrow__daemon__rpc__response__response_serialization-4.snap b/burrow/src/daemon/rpc/snapshots/burrow__daemon__rpc__response__response_serialization-4.snap new file mode 100644 index 0000000..c40db25 --- /dev/null +++ b/burrow/src/daemon/rpc/snapshots/burrow__daemon__rpc__response__response_serialization-4.snap @@ -0,0 +1,5 @@ +--- +source: burrow/src/daemon/rpc/response.rs +expression: "serde_json::to_string(&DaemonResponse::new(Ok::(DaemonResponseData::ServerConfig(ServerConfig::default()))))?" +--- +{"result":{"Ok":{"type":"ServerConfig","address":["10.13.13.2"],"name":null,"mtu":null}},"id":0} diff --git a/burrow/src/daemon/rpc/snapshots/burrow__daemon__rpc__response__response_serialization.snap b/burrow/src/daemon/rpc/snapshots/burrow__daemon__rpc__response__response_serialization.snap new file mode 100644 index 0000000..31bd84b --- /dev/null +++ b/burrow/src/daemon/rpc/snapshots/burrow__daemon__rpc__response__response_serialization.snap @@ -0,0 +1,5 @@ +--- +source: burrow/src/daemon/rpc/response.rs +expression: "serde_json::to_string(&DaemonResponse::new(Ok::(DaemonResponseData::None)))?" +--- +{"result":{"Ok":{"type":"None"}},"id":0} diff --git a/burrow/src/database.rs b/burrow/src/database.rs new file mode 100644 index 0000000..9a9aac3 --- /dev/null +++ b/burrow/src/database.rs @@ -0,0 +1,216 @@ +use std::path::Path; + +use anyhow::Result; +use rusqlite::{params, Connection}; + +use crate::{ + daemon::rpc::grpc_defs::{ + Network as RPCNetwork, + NetworkDeleteRequest, + NetworkReorderRequest, + NetworkType, + }, + wireguard::config::{Config, Interface, Peer}, +}; + +#[cfg(target_vendor = "apple")] +const DB_PATH: &str = "burrow.db"; + +#[cfg(not(target_vendor = "apple"))] +const DB_PATH: &str = "/var/lib/burrow/burrow.db"; + +const CREATE_WG_INTERFACE_TABLE: &str = "CREATE TABLE IF NOT EXISTS wg_interface ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT, + listen_port INTEGER, + mtu INTEGER, + private_key TEXT NOT NULL, + address TEXT NOT NULL, + dns TEXT NOT NULL +)"; + +const CREATE_WG_PEER_TABLE: &str = "CREATE TABLE IF NOT EXISTS wg_peer ( + interface_id INT REFERENCES wg_interface(id) ON UPDATE CASCADE, + endpoint TEXT NOT NULL, + public_key TEXT NOT NULL, + allowed_ips TEXT NOT NULL, + preshared_key TEXT +)"; + +const CREATE_NETWORK_TABLE: &str = "CREATE TABLE IF NOT EXISTS network ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + type TEXT NOT NULL, + payload BLOB, + idx INTEGER, + interface_id INT REFERENCES wg_interface(id) ON UPDATE CASCADE +); +CREATE TRIGGER IF NOT EXISTS increment_network_idx +AFTER INSERT ON network +BEGIN + UPDATE network + SET idx = (SELECT COALESCE(MAX(idx), 0) + 1 FROM network) + WHERE id = NEW.id; +END; +"; + +pub fn initialize_tables(conn: &Connection) -> Result<()> { + conn.execute(CREATE_WG_INTERFACE_TABLE, [])?; + conn.execute(CREATE_WG_PEER_TABLE, [])?; + conn.execute(CREATE_NETWORK_TABLE, [])?; + Ok(()) +} + +pub fn load_interface(conn: &Connection, interface_id: &str) -> Result { + let iface = conn.query_row( + "SELECT private_key, dns, address, listen_port, mtu FROM wg_interface WHERE id = ?", + [&interface_id], + |row| { + let dns_rw: String = row.get(1)?; + let dns = parse_lst(&dns_rw); + let address_rw: String = row.get(2)?; + let address = parse_lst(&address_rw); + Ok(Interface { + private_key: row.get(0)?, + dns, + address, + mtu: row.get(4)?, + listen_port: row.get(3)?, + }) + }, + )?; + let mut peers_stmt = conn.prepare("SELECT public_key, preshared_key, allowed_ips, endpoint FROM wg_peer WHERE interface_id = ?")?; + let peers = peers_stmt + .query_map([&interface_id], |row| { + let preshared_key: Option = row.get(1)?; + let allowed_ips_rw: String = row.get(2)?; + let allowed_ips: Vec = + allowed_ips_rw.split(',').map(|s| s.to_string()).collect(); + Ok(Peer { + public_key: row.get(0)?, + preshared_key, + allowed_ips, + endpoint: row.get(3)?, + persistent_keepalive: None, + name: None, + }) + })? + .collect::>>()?; + Ok(Config { interface: iface, peers }) +} + +pub fn dump_interface(conn: &Connection, config: &Config) -> Result<()> { + let mut stmt = conn.prepare("INSERT INTO wg_interface (private_key, dns, address, listen_port, mtu) VALUES (?, ?, ?, ?, ?)")?; + let cif = &config.interface; + stmt.execute(params![ + cif.private_key, + to_lst(&cif.dns), + to_lst(&cif.address), + cif.listen_port.unwrap_or(51820), + cif.mtu + ])?; + let interface_id = conn.last_insert_rowid(); + let mut stmt = conn.prepare("INSERT INTO wg_peer (interface_id, public_key, preshared_key, allowed_ips, endpoint) VALUES (?, ?, ?, ?, ?)")?; + for peer in &config.peers { + stmt.execute(params![ + &interface_id, + &peer.public_key, + &peer.preshared_key, + &peer.allowed_ips.join(","), + &peer.endpoint + ])?; + } + Ok(()) +} + +pub fn get_connection(path: Option<&Path>) -> Result { + let p = path.unwrap_or_else(|| std::path::Path::new(DB_PATH)); + if !p.exists() { + let conn = Connection::open(p)?; + initialize_tables(&conn)?; + dump_interface(&conn, &Config::default())?; + return Ok(conn); + } + Ok(Connection::open(p)?) +} + +pub fn add_network(conn: &Connection, network: &RPCNetwork) -> Result<()> { + let mut stmt = conn.prepare("INSERT INTO network (id, type, payload) VALUES (?, ?, ?)")?; + stmt.execute(params![ + network.id, + network.r#type().as_str_name(), + &network.payload + ])?; + if network.r#type() == NetworkType::WireGuard { + let payload_str = String::from_utf8(network.payload.clone())?; + let wg_config = Config::from_content_fmt(&payload_str, "ini")?; + dump_interface(conn, &wg_config)?; + } + Ok(()) +} + +pub fn list_networks(conn: &Connection) -> Result> { + let mut stmt = conn.prepare("SELECT id, type, payload FROM network ORDER BY idx")?; + let networks: Vec = stmt + .query_map([], |row| { + println!("row: {:?}", row); + let network_id: i32 = row.get(0)?; + let network_type: String = row.get(1)?; + let network_type = NetworkType::from_str_name(network_type.as_str()) + .ok_or(rusqlite::Error::InvalidQuery)?; + let payload: Vec = row.get(2)?; + Ok(RPCNetwork { + id: network_id, + r#type: network_type.into(), + payload: payload.into(), + }) + })? + .collect::, rusqlite::Error>>()?; + Ok(networks) +} + +pub fn reorder_network(conn: &Connection, req: NetworkReorderRequest) -> Result<()> { + let mut stmt = conn.prepare("UPDATE network SET idx = ? WHERE id = ?")?; + let res = stmt.execute(params![req.index, req.id])?; + if res == 0 { + return Err(anyhow::anyhow!("No such network exists")); + } + Ok(()) +} + +pub fn delete_network(conn: &Connection, req: NetworkDeleteRequest) -> Result<()> { + let mut stmt = conn.prepare("DELETE FROM network WHERE id = ?")?; + let res = stmt.execute(params![req.id])?; + if res == 0 { + return Err(anyhow::anyhow!("No such network exists")); + } + Ok(()) +} + +fn parse_lst(s: &str) -> Vec { + if s.is_empty() { + return vec![]; + } + s.split(',').map(|s| s.to_string()).collect() +} + +fn to_lst(v: &Vec) -> String { + v.iter() + .map(|s| s.to_string()) + .collect::>() + .join(",") +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_db() { + let conn = Connection::open_in_memory().unwrap(); + initialize_tables(&conn).unwrap(); + let config = Config::default(); + dump_interface(&conn, &config).unwrap(); + let loaded = load_interface(&conn, "1").unwrap(); + assert_eq!(config, loaded); + } +} diff --git a/burrow/src/lib.rs b/burrow/src/lib.rs index c5406b2..6aae1fb 100644 --- a/burrow/src/lib.rs +++ b/burrow/src/lib.rs @@ -3,16 +3,20 @@ pub mod wireguard; #[cfg(any(target_os = "linux", target_vendor = "apple"))] mod daemon; +#[cfg(any(target_os = "linux", target_vendor = "apple"))] +pub mod database; +#[cfg(any(target_os = "linux", target_vendor = "apple"))] +mod auth; pub(crate) mod tracing; #[cfg(target_vendor = "apple")] pub use daemon::apple::spawn_in_process; #[cfg(any(target_os = "linux", target_vendor = "apple"))] pub use daemon::{ + rpc::DaemonResponse, + rpc::ServerInfo, DaemonClient, DaemonCommand, - DaemonResponse, DaemonResponseData, DaemonStartOptions, - ServerInfo, }; diff --git a/burrow/src/main.rs b/burrow/src/main.rs index 71d1c02..e87b4c9 100644 --- a/burrow/src/main.rs +++ b/burrow/src/main.rs @@ -8,12 +8,20 @@ pub(crate) mod tracing; mod wireguard; #[cfg(any(target_os = "linux", target_vendor = "apple"))] -use daemon::{DaemonClient, DaemonCommand, DaemonStartOptions}; -use tun::TunOptions; +mod auth; + +#[cfg(any(target_os = "linux", target_vendor = "apple"))] +use daemon::{DaemonClient, DaemonCommand}; #[cfg(any(target_os = "linux", target_vendor = "apple"))] use crate::daemon::DaemonResponseData; +#[cfg(any(target_os = "linux", target_vendor = "apple"))] +pub mod database; + +#[cfg(any(target_os = "linux", target_vendor = "apple"))] +use crate::daemon::rpc::{grpc_defs::Empty, BurrowClient}; + #[derive(Parser)] #[command(name = "Burrow")] #[command(author = "Hack Club ")] @@ -42,6 +50,28 @@ enum Commands { ServerInfo, /// Server config ServerConfig, + /// Reload Config + ReloadConfig(ReloadConfigArgs), + /// Authentication server + AuthServer, + /// Server Status + ServerStatus, + /// Tunnel Config + TunnelConfig, + /// Add Network + NetworkAdd(NetworkAddArgs), + /// List Networks + NetworkList, + /// Reorder Network + NetworkReorder(NetworkReorderArgs), + /// Delete Network + NetworkDelete(NetworkDeleteArgs), +} + +#[derive(Args)] +struct ReloadConfigArgs { + #[clap(long, short)] + interface_id: String, } #[derive(Args)] @@ -50,32 +80,138 @@ struct StartArgs {} #[derive(Args)] struct DaemonArgs {} -#[cfg(any(target_os = "linux", target_vendor = "apple"))] -async fn try_start() -> Result<()> { - let mut client = DaemonClient::new().await?; - client - .send_command(DaemonCommand::Start(DaemonStartOptions { - tun: TunOptions::new().address(vec!["10.13.13.2", "::2"]), - })) - .await - .map(|_| ()) +#[derive(Args)] +struct NetworkAddArgs { + id: i32, + network_type: i32, + payload_path: String, +} + +#[derive(Args)] +struct NetworkReorderArgs { + id: i32, + index: i32, +} + +#[derive(Args)] +struct NetworkDeleteArgs { + id: i32, } #[cfg(any(target_os = "linux", target_vendor = "apple"))] -async fn try_stop() -> Result<()> { - let mut client = DaemonClient::new().await?; - client.send_command(DaemonCommand::Stop).await?; +async fn try_start() -> Result<()> { + let mut client = BurrowClient::from_uds().await?; + let res = client.tunnel_client.tunnel_start(Empty {}).await?; + println!("Got results! {:?}", res); Ok(()) } #[cfg(any(target_os = "linux", target_vendor = "apple"))] -async fn try_serverinfo() -> Result<()> { - let mut client = DaemonClient::new().await?; - let res = client.send_command(DaemonCommand::ServerInfo).await?; - match res.result { - Ok(DaemonResponseData::ServerInfo(si)) => { - println!("Got Result! {:?}", si); - } +async fn try_stop() -> Result<()> { + let mut client = BurrowClient::from_uds().await?; + let res = client.tunnel_client.tunnel_stop(Empty {}).await?; + println!("Got results! {:?}", res); + Ok(()) +} + +#[cfg(any(target_os = "linux", target_vendor = "apple"))] +async fn try_serverstatus() -> Result<()> { + let mut client = BurrowClient::from_uds().await?; + let mut res = client + .tunnel_client + .tunnel_status(Empty {}) + .await? + .into_inner(); + if let Some(st) = res.message().await? { + println!("Server Status: {:?}", st); + } else { + println!("Server Status is None"); + } + Ok(()) +} + +#[cfg(any(target_os = "linux", target_vendor = "apple"))] +async fn try_tun_config() -> Result<()> { + let mut client = BurrowClient::from_uds().await?; + let mut res = client + .tunnel_client + .tunnel_configuration(Empty {}) + .await? + .into_inner(); + if let Some(config) = res.message().await? { + println!("Tunnel Config: {:?}", config); + } else { + println!("Tunnel Config is None"); + } + Ok(()) +} + +#[cfg(any(target_os = "linux", target_vendor = "apple"))] +async fn try_network_add(id: i32, network_type: i32, payload_path: &str) -> Result<()> { + use tokio::{fs::File, io::AsyncReadExt}; + + use crate::daemon::rpc::grpc_defs::Network; + + let mut file = File::open(payload_path).await?; + let mut payload = Vec::new(); + file.read_to_end(&mut payload).await?; + + let mut client = BurrowClient::from_uds().await?; + let network = Network { + id, + r#type: network_type, + payload, + }; + let res = client.networks_client.network_add(network).await?; + println!("Network Add Response: {:?}", res); + Ok(()) +} + +#[cfg(any(target_os = "linux", target_vendor = "apple"))] +async fn try_network_list() -> Result<()> { + let mut client = BurrowClient::from_uds().await?; + let mut res = client + .networks_client + .network_list(Empty {}) + .await? + .into_inner(); + while let Some(network_list) = res.message().await? { + println!("Network List: {:?}", network_list); + } + Ok(()) +} + +#[cfg(any(target_os = "linux", target_vendor = "apple"))] +async fn try_network_reorder(id: i32, index: i32) -> Result<()> { + use crate::daemon::rpc::grpc_defs::NetworkReorderRequest; + + let mut client = BurrowClient::from_uds().await?; + let reorder_request = NetworkReorderRequest { id, index }; + let res = client + .networks_client + .network_reorder(reorder_request) + .await?; + println!("Network Reorder Response: {:?}", res); + Ok(()) +} + +#[cfg(any(target_os = "linux", target_vendor = "apple"))] +async fn try_network_delete(id: i32) -> Result<()> { + use crate::daemon::rpc::grpc_defs::NetworkDeleteRequest; + + let mut client = BurrowClient::from_uds().await?; + let delete_request = NetworkDeleteRequest { id }; + let res = client + .networks_client + .network_delete(delete_request) + .await?; + println!("Network Delete Response: {:?}", res); + Ok(()) +} + +#[cfg(any(target_os = "linux", target_vendor = "apple"))] +fn handle_unexpected(res: Result) { + match res { Ok(DaemonResponseData::None) => { println!("Server not started.") } @@ -86,6 +222,17 @@ async fn try_serverinfo() -> Result<()> { println!("Error when retrieving from server: {}", e) } } +} + +#[cfg(any(target_os = "linux", target_vendor = "apple"))] +async fn try_serverinfo() -> Result<()> { + let mut client = DaemonClient::new().await?; + let res = client.send_command(DaemonCommand::ServerInfo).await?; + if let Ok(DaemonResponseData::ServerInfo(si)) = res.result { + println!("Got Result! {:?}", si); + } else { + handle_unexpected(res.result); + } Ok(()) } @@ -93,55 +240,51 @@ async fn try_serverinfo() -> Result<()> { async fn try_serverconfig() -> Result<()> { let mut client = DaemonClient::new().await?; let res = client.send_command(DaemonCommand::ServerConfig).await?; - match res.result { - Ok(DaemonResponseData::ServerConfig(cfig)) => { - println!("Got Result! {:?}", cfig); - } - Ok(DaemonResponseData::None) => { - println!("Server not started.") - } - Ok(res) => { - println!("Unexpected Response: {:?}", res) - } - Err(e) => { - println!("Error when retrieving from server: {}", e) - } + if let Ok(DaemonResponseData::ServerConfig(cfig)) = res.result { + println!("Got Result! {:?}", cfig); + } else { + handle_unexpected(res.result); } Ok(()) } -#[cfg(not(any(target_os = "linux", target_vendor = "apple")))] -async fn try_start() -> Result<()> { - Ok(()) -} - -#[cfg(not(any(target_os = "linux", target_vendor = "apple")))] -async fn try_stop() -> Result<()> { - Ok(()) -} - -#[cfg(not(any(target_os = "linux", target_vendor = "apple")))] -async fn try_serverinfo() -> Result<()> { - Ok(()) -} - -#[cfg(not(any(target_os = "linux", target_vendor = "apple")))] -async fn try_serverconfig() -> Result<()> { +#[cfg(any(target_os = "linux", target_vendor = "apple"))] +async fn try_reloadconfig(interface_id: String) -> Result<()> { + let mut client = DaemonClient::new().await?; + let res = client + .send_command(DaemonCommand::ReloadConfig(interface_id)) + .await?; + if let Ok(DaemonResponseData::ServerConfig(cfig)) = res.result { + println!("Got Result! {:?}", cfig); + } else { + handle_unexpected(res.result); + } Ok(()) } #[cfg(any(target_os = "linux", target_vendor = "apple"))] -#[tokio::main(flavor = "current_thread")] +#[tokio::main] async fn main() -> Result<()> { tracing::initialize(); + dotenv::dotenv().ok(); let cli = Cli::parse(); match &cli.command { Commands::Start(..) => try_start().await?, Commands::Stop => try_stop().await?, - Commands::Daemon(_) => daemon::daemon_main(None, None).await?, + Commands::Daemon(_) => daemon::daemon_main(None, None, None).await?, Commands::ServerInfo => try_serverinfo().await?, Commands::ServerConfig => try_serverconfig().await?, + Commands::ReloadConfig(args) => try_reloadconfig(args.interface_id.clone()).await?, + Commands::AuthServer => crate::auth::server::serve().await?, + Commands::ServerStatus => try_serverstatus().await?, + Commands::TunnelConfig => try_tun_config().await?, + Commands::NetworkAdd(args) => { + try_network_add(args.id, args.network_type, &args.payload_path).await? + } + Commands::NetworkList => try_network_list().await?, + Commands::NetworkReorder(args) => try_network_reorder(args.id, args.index).await?, + Commands::NetworkDelete(args) => try_network_delete(args.id).await?, } Ok(()) @@ -149,5 +292,5 @@ async fn main() -> Result<()> { #[cfg(not(any(target_os = "linux", target_vendor = "apple")))] pub fn main() { - eprintln!("This platform is not supported currently.") + eprintln!("This platform is not supported") } diff --git a/burrow/src/wireguard/config.rs b/burrow/src/wireguard/config.rs index ed7b3cd..5766675 100644 --- a/burrow/src/wireguard/config.rs +++ b/burrow/src/wireguard/config.rs @@ -3,9 +3,12 @@ use std::{net::ToSocketAddrs, str::FromStr}; use anyhow::{anyhow, Error, Result}; use base64::{engine::general_purpose, Engine}; use fehler::throws; +use ini::{Ini, Properties}; use ip_network::IpNetwork; +use serde::{Deserialize, Serialize}; use x25519_dalek::{PublicKey, StaticSecret}; +use super::inifield::IniField; use crate::wireguard::{Interface as WgInterface, Peer as WgPeer}; #[throws] @@ -31,6 +34,7 @@ fn parse_public_key(string: &str) -> PublicKey { /// A raw version of Peer Config that can be used later to reflect configuration files. /// This should be later converted to a `WgPeer`. /// Refers to https://github.com/pirate/wireguard-docs?tab=readme-ov-file#overview +#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] pub struct Peer { pub public_key: String, pub preshared_key: Option, @@ -40,15 +44,18 @@ pub struct Peer { pub name: Option, } +#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] pub struct Interface { pub private_key: String, pub address: Vec, - pub listen_port: u32, + pub listen_port: Option, pub dns: Vec, pub mtu: Option, } +#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] pub struct Config { + #[serde(rename = "Peer")] pub peers: Vec, pub interface: Interface, // Support for multiple interfaces? } @@ -95,7 +102,7 @@ impl Default for Config { interface: Interface { private_key: "OEPVdomeLTxTIBvv3TYsJRge0Hp9NMiY0sIrhT8OWG8=".into(), address: vec!["10.13.13.2/24".into()], - listen_port: 51820, + listen_port: Some(51820), dns: Default::default(), mtu: Default::default(), }, @@ -110,3 +117,83 @@ impl Default for Config { } } } + +fn props_get(props: &Properties, key: &str) -> Result +where + T: TryFrom, +{ + IniField::try_from(props.get(key))?.try_into() +} + +impl TryFrom<&Properties> for Interface { + type Error = anyhow::Error; + + fn try_from(props: &Properties) -> Result { + Ok(Self { + private_key: props_get(props, "PrivateKey")?, + address: props_get(props, "Address")?, + listen_port: props_get(props, "ListenPort")?, + dns: props_get(props, "DNS")?, + mtu: props_get(props, "MTU")?, + }) + } +} + +impl TryFrom<&Properties> for Peer { + type Error = anyhow::Error; + + fn try_from(props: &Properties) -> Result { + Ok(Self { + public_key: props_get(props, "PublicKey")?, + preshared_key: props_get(props, "PresharedKey")?, + allowed_ips: props_get(props, "AllowedIPs")?, + endpoint: props_get(props, "Endpoint")?, + persistent_keepalive: props_get(props, "PersistentKeepalive")?, + name: props_get(props, "Name")?, + }) + } +} + +impl Config { + pub fn from_toml(toml: &str) -> Result { + toml::from_str(toml).map_err(Into::into) + } + + pub fn from_ini(ini: &str) -> Result { + let ini = Ini::load_from_str(ini)?; + let interface = ini + .section(Some("Interface")) + .ok_or(anyhow!("Interface section not found"))?; + let peers = ini.section_all(Some("Peer")); + Ok(Self { + interface: Interface::try_from(interface)?, + peers: peers + .into_iter() + .map(|v| Peer::try_from(v)) + .collect::>>()?, + }) + } + + pub fn from_content_fmt(content: &str, fmt: &str) -> Result { + match fmt { + "toml" => Self::from_toml(content), + "ini" | "conf" => Self::from_ini(content), + _ => Err(anyhow::anyhow!("Unsupported format: {}", fmt)), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn tst_config_toml() { + let cfig = Config::default(); + let toml = toml::to_string(&cfig).unwrap(); + println!("{}", &toml); + insta::assert_snapshot!(toml); + let cfig2: Config = toml::from_str(&toml).unwrap(); + assert_eq!(cfig, cfig2); + } +} diff --git a/burrow/src/wireguard/iface.rs b/burrow/src/wireguard/iface.rs index 6097082..321801b 100755 --- a/burrow/src/wireguard/iface.rs +++ b/burrow/src/wireguard/iface.rs @@ -1,21 +1,26 @@ -use std::{net::IpAddr, sync::Arc}; -use std::ops::Deref; +use std::{net::IpAddr, ops::Deref, sync::Arc}; use anyhow::Error; use fehler::throws; use futures::future::join_all; use ip_network_table::IpNetworkTable; -use tokio::sync::{RwLock, Notify}; +use tokio::sync::{Notify, RwLock}; use tracing::{debug, error}; use tun::tokio::TunInterface; use super::{noise::Tunnel, Peer, PeerPcb}; -struct IndexedPcbs { +pub struct IndexedPcbs { pcbs: Vec>, allowed_ips: IpNetworkTable, } +impl Default for IndexedPcbs { + fn default() -> Self { + Self::new() + } +} + impl IndexedPcbs { pub fn new() -> Self { Self { @@ -49,12 +54,12 @@ impl FromIterator for IndexedPcbs { enum IfaceStatus { Running, - Idle + Idle, } pub struct Interface { - tun: Arc>>, - pcbs: Arc, + pub tun: Arc>>, + pub pcbs: Arc, status: Arc>, stop_notifier: Arc, } @@ -73,7 +78,12 @@ impl Interface { .collect::>()?; let pcbs = Arc::new(pcbs); - Self { pcbs, tun: Arc::new(RwLock::new(None)), status: Arc::new(RwLock::new(IfaceStatus::Idle)), stop_notifier: Arc::new(Notify::new()) } + Self { + pcbs, + tun: Arc::new(RwLock::new(None)), + status: Arc::new(RwLock::new(IfaceStatus::Idle)), + stop_notifier: Arc::new(Notify::new()), + } } pub async fn set_tun(&self, tun: TunInterface) { @@ -83,11 +93,17 @@ impl Interface { *st = IfaceStatus::Running; } + pub async fn set_tun_ref(&mut self, tun: Arc>>) { + self.tun = tun; + let mut st = self.status.write().await; + *st = IfaceStatus::Running; + } + pub fn get_tun(&self) -> Arc>> { self.tun.clone() } - pub async fn remove_tun(&self){ + pub async fn remove_tun(&self) { let mut st = self.status.write().await; self.stop_notifier.notify_waiters(); *st = IfaceStatus::Idle; @@ -95,9 +111,7 @@ impl Interface { pub async fn run(&self) -> anyhow::Result<()> { let pcbs = self.pcbs.clone(); - let tun = self - .tun - .clone(); + let tun = self.tun.clone(); let status = self.status.clone(); let stop_notifier = self.stop_notifier.clone(); log::info!("Starting interface"); @@ -127,7 +141,7 @@ impl Interface { Some(addr) => addr, None => { debug!("No destination found"); - continue + continue; } }; @@ -146,16 +160,14 @@ impl Interface { } Err(e) => { log::error!("Failed to send packet {}", e); - continue + continue; } }; } }; let mut tsks = vec![]; - let tun = self - .tun - .clone(); + let tun = self.tun.clone(); let outgoing = tokio::task::spawn(outgoing); tsks.push(outgoing); debug!("preparing to spawn read tasks"); @@ -169,7 +181,7 @@ impl Interface { let main_tsk = async move { if let Err(e) = pcb.open_if_closed().await { log::error!("failed to open pcb: {}", e); - return + return; } let r2 = pcb.run(tun).await; if let Err(e) = r2 { @@ -189,7 +201,7 @@ impl Interface { Ok(..) => (), Err(e) => { error!("Failed to update timers: {}", e); - return + return; } } } diff --git a/burrow/src/wireguard/inifield.rs b/burrow/src/wireguard/inifield.rs new file mode 100644 index 0000000..946868d --- /dev/null +++ b/burrow/src/wireguard/inifield.rs @@ -0,0 +1,81 @@ +use std::str::FromStr; + +use anyhow::{Error, Result}; + +pub struct IniField(String); + +impl FromStr for IniField { + type Err = Error; + + fn from_str(s: &str) -> Result { + Ok(Self(s.to_string())) + } +} + +impl TryFrom for Vec { + type Error = Error; + + fn try_from(field: IniField) -> Result { + Ok(field.0.split(',').map(|s| s.trim().to_string()).collect()) + } +} + +impl TryFrom for u32 { + type Error = Error; + + fn try_from(value: IniField) -> Result { + value.0.parse().map_err(Error::from) + } +} + +impl TryFrom for Option { + type Error = Error; + + fn try_from(value: IniField) -> Result { + if value.0.is_empty() { + Ok(None) + } else { + value.0.parse().map(Some).map_err(Error::from) + } + } +} + +impl TryFrom for String { + type Error = Error; + + fn try_from(value: IniField) -> Result { + Ok(value.0) + } +} + +impl TryFrom for Option { + type Error = Error; + + fn try_from(value: IniField) -> Result { + if value.0.is_empty() { + Ok(None) + } else { + Ok(Some(value.0)) + } + } +} + +impl TryFrom> for IniField +where + T: ToString, +{ + type Error = Error; + + fn try_from(value: Option) -> Result { + Ok(match value { + Some(v) => Self(v.to_string()), + None => Self(String::new()), + }) + } +} + +impl IniField { + fn new(value: &str) -> Self { + Self(value.to_string()) + } +} diff --git a/burrow/src/wireguard/mod.rs b/burrow/src/wireguard/mod.rs index 15563fb..cfb4585 100755 --- a/burrow/src/wireguard/mod.rs +++ b/burrow/src/wireguard/mod.rs @@ -1,5 +1,6 @@ -mod config; +pub mod config; mod iface; +mod inifield; mod noise; mod pcb; mod peer; diff --git a/burrow/src/wireguard/snapshots/burrow__wireguard__config__tests__tst_config_toml.snap b/burrow/src/wireguard/snapshots/burrow__wireguard__config__tests__tst_config_toml.snap new file mode 100644 index 0000000..3800647 --- /dev/null +++ b/burrow/src/wireguard/snapshots/burrow__wireguard__config__tests__tst_config_toml.snap @@ -0,0 +1,16 @@ +--- +source: burrow/src/wireguard/config.rs +expression: toml +--- +[[Peer]] +public_key = "8GaFjVO6c4luCHG4ONO+1bFG8tO+Zz5/Gy+Geht1USM=" +preshared_key = "ha7j4BjD49sIzyF9SNlbueK0AMHghlj6+u0G3bzC698=" +allowed_ips = ["8.8.8.8/32", "0.0.0.0/0"] +endpoint = "wg.burrow.rs:51820" + +[interface] +private_key = "OEPVdomeLTxTIBvv3TYsJRge0Hp9NMiY0sIrhT8OWG8=" +address = ["10.13.13.2/24"] +listen_port = 51820 +dns = [] + diff --git a/burrow/tmp/conrd.conf b/burrow/tmp/conrd.conf new file mode 100644 index 0000000..52572d1 --- /dev/null +++ b/burrow/tmp/conrd.conf @@ -0,0 +1,8 @@ +[Interface] +PrivateKey = gAaK0KFGOpxY7geGo59XXDufcxeoSNXXNC12mCQmlVs= +Address = 10.1.11.2/32 +DNS = 10.1.11.1 +[Peer] +PublicKey = Ab6V2mgPHiCXaAZfQrNts8ha8RkEzC49VnmMQfe5Yg4= +AllowedIPs = 10.1.11.1/32,10.1.11.2/32,0.0.0.0/0 +Endpoint = 172.251.163.175:51820 \ No newline at end of file diff --git a/proto/burrow.proto b/proto/burrow.proto new file mode 100644 index 0000000..2355b8d --- /dev/null +++ b/proto/burrow.proto @@ -0,0 +1,72 @@ +syntax = "proto3"; +package burrow; + +import "google/protobuf/timestamp.proto"; + +service Tunnel { + rpc TunnelConfiguration (Empty) returns (stream TunnelConfigurationResponse); + rpc TunnelStart (Empty) returns (Empty); + rpc TunnelStop (Empty) returns (Empty); + rpc TunnelStatus (Empty) returns (stream TunnelStatusResponse); +} + +service Networks { + rpc NetworkAdd (Network) returns (Empty); + rpc NetworkList (Empty) returns (stream NetworkListResponse); + rpc NetworkReorder (NetworkReorderRequest) returns (Empty); + rpc NetworkDelete (NetworkDeleteRequest) returns (Empty); +} + +message NetworkReorderRequest { + int32 id = 1; + int32 index = 2; +} + +message WireGuardPeer { + string endpoint = 1; + repeated string subnet = 2; +} + +message WireGuardNetwork { + string address = 1; + string dns = 2; + repeated WireGuardPeer peer = 3; +} + +message NetworkDeleteRequest { + int32 id = 1; +} + +message Network { + int32 id = 1; + NetworkType type = 2; + bytes payload = 3; +} + +enum NetworkType { + WireGuard = 0; + HackClub = 1; +} + +message NetworkListResponse { + repeated Network network = 1; +} + +message Empty { + +} + +enum State { + Stopped = 0; + Running = 1; +} + +message TunnelStatusResponse { + State state = 1; + optional google.protobuf.Timestamp start = 2; +} + +message TunnelConfigurationResponse { + repeated string addresses = 1; + int32 mtu = 2; +} diff --git a/site/.eslintrc.json b/site/.eslintrc.json new file mode 100644 index 0000000..bffb357 --- /dev/null +++ b/site/.eslintrc.json @@ -0,0 +1,3 @@ +{ + "extends": "next/core-web-vitals" +} diff --git a/site/.gitignore b/site/.gitignore new file mode 100644 index 0000000..71b863e --- /dev/null +++ b/site/.gitignore @@ -0,0 +1,5 @@ +/node_modules +/out/ + +/.next/ +next-env.d.ts diff --git a/site/.prettierignore b/site/.prettierignore new file mode 100644 index 0000000..fcac576 --- /dev/null +++ b/site/.prettierignore @@ -0,0 +1,6 @@ +# Ignore artifacts: +build +coverage + +# Ignore all HTML files: +**/*.html \ No newline at end of file diff --git a/site/assets/Bold.woff2 b/site/assets/Bold.woff2 new file mode 100644 index 0000000..8c00084 Binary files /dev/null and b/site/assets/Bold.woff2 differ diff --git a/site/assets/Italic.woff2 b/site/assets/Italic.woff2 new file mode 100644 index 0000000..056909c Binary files /dev/null and b/site/assets/Italic.woff2 differ diff --git a/site/assets/Regular.woff2 b/site/assets/Regular.woff2 new file mode 100644 index 0000000..d7c3d52 Binary files /dev/null and b/site/assets/Regular.woff2 differ diff --git a/site/bun.lockb b/site/bun.lockb new file mode 100755 index 0000000..ea2d137 Binary files /dev/null and b/site/bun.lockb differ diff --git a/site/layout/layout.tsx b/site/layout/layout.tsx new file mode 100644 index 0000000..28ff24d --- /dev/null +++ b/site/layout/layout.tsx @@ -0,0 +1,47 @@ +import { Space_Mono, Poppins } from "next/font/google"; +import localFont from "next/font/local"; + +const space_mono = Space_Mono({ + weight: ["400", "700"], + subsets: ["latin"], + display: "swap", + variable: "--font-space-mono", +}); + +const poppins = Poppins({ + weight: ["400", "500", "600", "700", "800", "900"], + subsets: ["latin"], + display: "swap", + variable: "--font-poppins", +}); + +const phantomSans = localFont({ + src: [ + { + path: "../assets/Regular.woff2", + weight: "400", + style: "normal", + }, + { + path: "../assets/Italic.woff2", + weight: "400", + style: "italic", + }, + { + path: "../assets/Bold.woff2", + weight: "700", + style: "normal", + }, + ], + variable: "--font-phantom-sans", +}); + +export default function Layout({ children }: { children: React.ReactNode }) { + return ( +
+ {children} +
+ ); +} diff --git a/site/next.config.js b/site/next.config.js new file mode 100644 index 0000000..afbb70f --- /dev/null +++ b/site/next.config.js @@ -0,0 +1,13 @@ +/** @type {import('next').NextConfig} */ +const nextConfig = { + headers() { + return [ + { + source: "/.well-known/apple-app-site-association", + headers: [{ key: "Content-Type", value: "application/json" }], + } + ]; + } +}; + +module.exports = nextConfig; diff --git a/site/package.json b/site/package.json new file mode 100644 index 0000000..4fcacc8 --- /dev/null +++ b/site/package.json @@ -0,0 +1,36 @@ +{ + "name": "burrow", + "version": "0.1.0", + "private": true, + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "lint": "next lint" + }, + "dependencies": { + "@fortawesome/fontawesome-free": "^6.4.2", + "@fortawesome/fontawesome-svg-core": "^6.4.2", + "@fortawesome/free-brands-svg-icons": "^6.4.2", + "@fortawesome/free-solid-svg-icons": "^6.4.2", + "@fortawesome/react-fontawesome": "^0.2.0", + "@headlessui/react": "^1.7.17", + "@headlessui/tailwindcss": "^0.2.0", + "@types/node": "20.5.8", + "@types/react": "18.2.21", + "@types/react-dom": "18.2.7", + "autoprefixer": "10.4.15", + "eslint": "8.48.0", + "eslint-config-next": "13.4.19", + "next": "13.4.19", + "postcss": "8.4.29", + "react": "18.2.0", + "react-dom": "18.2.0", + "tailwindcss": "3.3.3", + "typescript": "5.2.2" + }, + "devDependencies": { + "prettier": "^3.0.3", + "prettier-plugin-tailwindcss": "^0.5.4" + } +} diff --git a/site/pages/_app.tsx b/site/pages/_app.tsx new file mode 100644 index 0000000..c0572f6 --- /dev/null +++ b/site/pages/_app.tsx @@ -0,0 +1,14 @@ +import Layout from "@/layout/layout"; +import type { AppProps } from "next/app"; +import { config } from "@fortawesome/fontawesome-svg-core"; +import "@fortawesome/fontawesome-svg-core/styles.css"; +config.autoAddCss = false; +import "static/globals.css"; + +export default function App({ Component, pageProps }: AppProps) { + return ( + + + + ); +} diff --git a/site/pages/_document.tsx b/site/pages/_document.tsx new file mode 100644 index 0000000..ce4b5e1 --- /dev/null +++ b/site/pages/_document.tsx @@ -0,0 +1,13 @@ +import { Html, Head, Main, NextScript } from "next/document"; + +export default function Document() { + return ( + + + +
+ + + + ); +} diff --git a/site/pages/index.tsx b/site/pages/index.tsx new file mode 100644 index 0000000..73fbc33 --- /dev/null +++ b/site/pages/index.tsx @@ -0,0 +1,154 @@ +import { faGithub } from "@fortawesome/free-brands-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import Head from "next/head"; +import { + faChevronDown, + faChevronUp, + faUpRightFromSquare, +} from "@fortawesome/free-solid-svg-icons"; +import { Menu, Transition } from "@headlessui/react"; +import { useState, useRef, useEffect } from "react"; +export default function Page() { + const [chevron, setChevron] = useState(false); + const menuButtonRef = useRef(null); + const toggleDropdown = () => { + setChevron(!chevron); + }; + const handleClickOutside = (event: MouseEvent) => { + if ( + menuButtonRef.current && + !menuButtonRef.current.contains(event.target as Node) + ) { + setChevron(false); + } + }; + useEffect(() => { + document.addEventListener("click", handleClickOutside); + + return () => { + document.removeEventListener("click", handleClickOutside); + }; + }, []); + return ( + <> + + Burrow + + + +
+
+

+ Burrow Through{" "} + Firewalls +

+
+

+ Burrow is an open source tool for burrowing through firewalls, + built by teenagers at{" "} + + + Hack Club. + + {" "} + + burrow + {" "} + is a Rust-based VPN for getting around restrictive Internet + censors. +

+
+
+
+ +
+ toggleDropdown()} + ref={menuButtonRef} + className="w-50 h-12 rounded-2xl bg-hackClubRed px-3 font-SpaceMono hover:scale-105 md:h-12 md:w-auto md:rounded-3xl md:text-xl 2xl:h-16 2xl:text-2xl " + > + Install for Linux + {chevron ? ( + + ) : ( + + )} + +
+ + +
+ + {({ active }) => ( + + Install for Windows + + )} + + + + Install for MacOS + + +
+
+
+
+ + + +
+ +
+ {/* Footer */} + {/* */} +
+
+ + ); +} diff --git a/site/postcss.config.js b/site/postcss.config.js new file mode 100644 index 0000000..12a703d --- /dev/null +++ b/site/postcss.config.js @@ -0,0 +1,6 @@ +module.exports = { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +}; diff --git a/site/prettier.config.js b/site/prettier.config.js new file mode 100644 index 0000000..d573118 --- /dev/null +++ b/site/prettier.config.js @@ -0,0 +1,3 @@ +module.exports = { + plugins: ["prettier-plugin-tailwindcss"], +}; diff --git a/site/public/.well-known/apple-app-site-association b/site/public/.well-known/apple-app-site-association new file mode 100644 index 0000000..63262fb --- /dev/null +++ b/site/public/.well-known/apple-app-site-association @@ -0,0 +1,21 @@ +{ + "applinks": { + "details": [ + { + "appIDs": [ + "P6PV2R9443.com.hackclub.burrow" + ], + "components": [ + { + "/": "/callback/*" + } + ] + } + ] + }, + "webcredentials": { + "apps": [ + "P6PV2R9443.com.hackclub.burrow" + ] + } +} diff --git a/site/public/hackclub.svg b/site/public/hackclub.svg new file mode 100644 index 0000000..38c2a68 --- /dev/null +++ b/site/public/hackclub.svg @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/site/static/globals.css b/site/static/globals.css new file mode 100644 index 0000000..b5c61c9 --- /dev/null +++ b/site/static/globals.css @@ -0,0 +1,3 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; diff --git a/site/tailwind.config.ts b/site/tailwind.config.ts new file mode 100644 index 0000000..3df6f5a --- /dev/null +++ b/site/tailwind.config.ts @@ -0,0 +1,28 @@ +import type { Config } from "tailwindcss"; + +const config: Config = { + content: [ + "./pages/**/*.{js,ts,jsx,tsx,mdx}", + "./components/**/*.{js,ts,jsx,tsx,mdx}", + "./app/**/*.{js,ts,jsx,tsx,mdx}", + ], + theme: { + extend: { + colors: { + backgroundBlack: "#17171D", + hackClubRed: "#EC3750", + hackClubBlueShade: "#32323D", + hackClubBlue: "#338EDA", + burrowStroke: "#595959", + burrowHover: "#3D3D3D", + }, + fontFamily: { + SpaceMono: ["var(--font-space-mono)"], + Poppins: ["var(--font-poppins)"], + PhantomSans: ["var(--font-phantom-sans)"], + }, + }, + }, + plugins: [require("@headlessui/tailwindcss")({ prefix: "ui" })], +}; +export default config; diff --git a/site/tsconfig.json b/site/tsconfig.json new file mode 100644 index 0000000..c714696 --- /dev/null +++ b/site/tsconfig.json @@ -0,0 +1,27 @@ +{ + "compilerOptions": { + "target": "es5", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "incremental": true, + "plugins": [ + { + "name": "next" + } + ], + "paths": { + "@/*": ["./*"] + } + }, + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], + "exclude": ["node_modules"] +} diff --git a/tun/Cargo.toml b/tun/Cargo.toml index 8efd710..1b07833 100644 --- a/tun/Cargo.toml +++ b/tun/Cargo.toml @@ -8,7 +8,7 @@ libc = "0.2" fehler = "1.0" nix = { version = "0.26", features = ["ioctl"] } socket2 = "0.5" -tokio = { version = "1.28", features = [] } +tokio = { version = "1.37", default-features = false, optional = true } byteorder = "1.4" tracing = "0.1" log = "0.4" @@ -16,19 +16,10 @@ serde = { version = "1", features = ["derive"], optional = true } schemars = { version = "0.8", optional = true } futures = { version = "0.3.28", optional = true } -netlink-packet-route = "0.19.0" -netlink-packet-core = "0.7.0" - [features] serde = ["dep:serde", "dep:schemars"] -tokio = ["tokio/net", "dep:futures"] - -[target.'cfg(feature = "tokio")'.dev-dependencies] -tokio = { features = ["rt", "macros"] } - -[target.'cfg(target_os = "linux")'.dependencies] -interfaces = "0.0.9" +tokio = ["tokio/net", "dep:tokio", "dep:futures"] [target.'cfg(windows)'.dependencies] lazy_static = "1.4" @@ -43,7 +34,7 @@ windows = { version = "0.48", features = [ [target.'cfg(windows)'.build-dependencies] anyhow = "1.0" bindgen = "0.65" -reqwest = { version = "0.11", features = ["native-tls"] } +reqwest = { version = "0.11" } ssri = { version = "9.0", default-features = false } tokio = { version = "1.28", features = ["rt", "macros"] } zip = { version = "0.6", features = ["deflate"] } diff --git a/tun/src/tokio/mod.rs b/tun/src/tokio/mod.rs index 30badda..bd27109 100644 --- a/tun/src/tokio/mod.rs +++ b/tun/src/tokio/mod.rs @@ -1,6 +1,6 @@ use std::io; -use tokio::io::unix::{AsyncFd, TryIoError}; +use tokio::io::unix::AsyncFd; use tracing::instrument; #[derive(Debug)] @@ -15,11 +15,6 @@ impl TunInterface { Ok(Self { inner: AsyncFd::new(tun)? }) } - #[instrument] - pub fn set_up(&self, up: bool) -> io::Result<()> { - self.inner.get_ref().set_up(up) - } - #[instrument] pub async fn send(&self, buf: &[u8]) -> io::Result { loop { diff --git a/tun/src/unix/apple/mod.rs b/tun/src/unix/apple/mod.rs index 82e3f71..74e93eb 100644 --- a/tun/src/unix/apple/mod.rs +++ b/tun/src/unix/apple/mod.rs @@ -1,11 +1,13 @@ -use std::{io::{Error, IoSlice}, mem, net::{Ipv4Addr, SocketAddrV4}, os::fd::{AsRawFd, FromRawFd, RawFd}, ptr}; -use std::net::{IpAddr, Ipv6Addr, SocketAddrV6}; -use std::ptr::addr_of; +use std::{ + io::{Error, IoSlice}, + mem, + net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddrV4}, + os::fd::{AsRawFd, FromRawFd, RawFd}, +}; use byteorder::{ByteOrder, NetworkEndian}; use fehler::throws; -use libc::{c_char, iovec, writev, AF_INET, AF_INET6, sockaddr_in6}; -use nix::sys::socket::SockaddrIn6; +use libc::{c_char, iovec, writev, AF_INET, AF_INET6}; use socket2::{Domain, SockAddr, Socket, Type}; use tracing::{self, instrument}; @@ -69,11 +71,11 @@ impl TunInterface { #[throws] fn configure(&self, options: TunOptions) { - for addr in options.address{ + for addr in options.address { if let Ok(addr) = addr.parse::() { match addr { - IpAddr::V4(addr) => {self.set_ipv4_addr(addr)?} - IpAddr::V6(addr) => {self.set_ipv6_addr(addr)?} + IpAddr::V4(addr) => self.set_ipv4_addr(addr)?, + IpAddr::V6(addr) => self.set_ipv6_addr(addr)?, } } } @@ -146,7 +148,7 @@ impl TunInterface { } #[throws] - pub fn set_ipv6_addr(&self, addr: Ipv6Addr) { + pub fn set_ipv6_addr(&self, _addr: Ipv6Addr) { // let addr = SockAddr::from(SocketAddrV6::new(addr, 0, 0, 0)); // println!("addr: {:?}", addr); // let mut iff = self.in6_ifreq()?; @@ -247,10 +249,4 @@ impl TunInterface { .try_into() .map_err(|_| Error::new(ErrorKind::Other, "Conversion error"))? } - - #[throws] - #[instrument] - pub fn set_up(&self, up: bool) { - tracing::warn!("Setting tun up is not supported yet on apple."); - } } diff --git a/tun/src/unix/linux/mod.rs b/tun/src/unix/linux/mod.rs index cf042da..60d6341 100644 --- a/tun/src/unix/linux/mod.rs +++ b/tun/src/unix/linux/mod.rs @@ -95,24 +95,6 @@ impl TunInterface { unsafe { iff.ifr_ifru.ifru_ifindex } } - #[throws] - #[instrument] - pub fn set_up(&self, up: bool) { - let mut inter = interfaces::Interface::get_by_name(&self.name()?) - .unwrap() - .unwrap(); - inter.set_up(up).unwrap(); - } - - #[throws] - #[instrument] - pub fn is_up(&self) -> bool { - let inter = interfaces::Interface::get_by_name(&self.name()?) - .unwrap() - .unwrap(); - inter.is_up() - } - #[throws] #[instrument] pub fn set_ipv4_addr(&self, addr: Ipv4Addr) { diff --git a/tun/tests/configure.rs b/tun/tests/configure.rs index 8b466c3..e7e2c6d 100644 --- a/tun/tests/configure.rs +++ b/tun/tests/configure.rs @@ -24,21 +24,6 @@ fn test_set_get_broadcast_addr() { assert_eq!(broadcast_addr, result); } -#[test] -#[throws] -#[cfg(not(any(target_os = "windows", target_vendor = "apple")))] -fn test_set_get_up() { - let tun = TunInterface::new()?; - let addr = Ipv4Addr::new(10, 0, 0, 1); - tun.set_ipv4_addr(addr)?; - - let broadcast_addr = Ipv4Addr::new(255, 255, 255, 0); - tun.set_broadcast_addr(broadcast_addr)?; - tun.set_up(true)?; - - assert!(tun.is_up()?); -} - #[test] #[throws] #[cfg(not(target_os = "windows"))]