diff --git a/Apple/Configuration/Compiler.xcconfig b/Apple/Configuration/Compiler.xcconfig index 6b071f1..a5f4339 100644 --- a/Apple/Configuration/Compiler.xcconfig +++ b/Apple/Configuration/Compiler.xcconfig @@ -40,5 +40,4 @@ APP_GROUP_IDENTIFIER = group.$(APP_BUNDLE_IDENTIFIER) APP_GROUP_IDENTIFIER[sdk=macosx*] = $(DEVELOPMENT_TEAM).$(APP_BUNDLE_IDENTIFIER) NETWORK_EXTENSION_BUNDLE_IDENTIFIER = $(APP_BUNDLE_IDENTIFIER).network -// https://github.com/grpc/grpc-swift/issues/683#issuecomment-1130118953 -OTHER_SWIFT_FLAGS = $(inherited) -Xcc -fmodule-map-file=$(GENERATED_MODULEMAP_DIR)/CNIOAtomics.modulemap -Xcc -fmodule-map-file=$(GENERATED_MODULEMAP_DIR)/CNIODarwin.modulemap -Xcc -fmodule-map-file=$(GENERATED_MODULEMAP_DIR)/CGRPCZlib.modulemap +OTHER_SWIFT_FLAGS = $(inherited) diff --git a/Apple/Configuration/Constants/Constants.swift b/Apple/Configuration/Constants/Constants.swift index 3f8ae95..8844564 100644 --- a/Apple/Configuration/Constants/Constants.swift +++ b/Apple/Configuration/Constants/Constants.swift @@ -1,4 +1,5 @@ @_implementationOnly import CConstants +import Foundation import OSLog public enum Constants { @@ -27,9 +28,30 @@ public enum Constants { private static let _groupContainerURL: Result = { switch FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: appGroupIdentifier) { case .some(let url): .success(url) - case .none: .failure(.invalidAppGroupIdentifier) + case .none: + fallbackContainerURL().mapError { _ in .invalidAppGroupIdentifier } } }() + + private static func fallbackContainerURL() -> Result { +#if targetEnvironment(simulator) + Result { + let baseURL = try FileManager.default.url( + for: .applicationSupportDirectory, + in: .userDomainMask, + appropriateFor: nil, + create: true + ) + let url = baseURL + .appending(component: bundleIdentifier, directoryHint: .isDirectory) + .appending(component: "SimulatorFallback", directoryHint: .isDirectory) + try FileManager.default.createDirectory(at: url, withIntermediateDirectories: true) + return url + } +#else + .failure(Error.invalidAppGroupIdentifier) +#endif + } } extension Logger { diff --git a/Apple/Core/Client/google/protobuf/timestamp.proto b/Apple/Core/Client/google/protobuf/timestamp.proto new file mode 100644 index 0000000..7db2f6a --- /dev/null +++ b/Apple/Core/Client/google/protobuf/timestamp.proto @@ -0,0 +1,64 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +syntax = "proto3"; + +package google.protobuf; + +option cc_enable_arenas = true; +option go_package = "google.golang.org/protobuf/types/known/timestamppb"; +option java_package = "com.google.protobuf"; +option java_outer_classname = "TimestampProto"; +option java_multiple_files = true; +option objc_class_prefix = "GPB"; +option csharp_namespace = "Google.Protobuf.WellKnownTypes"; + +// A Timestamp represents a point in time independent of any time zone or local +// calendar, encoded as a count of seconds and fractions of seconds at +// nanosecond resolution. The count is relative to an epoch at UTC midnight on +// January 1, 1970, in the proleptic Gregorian calendar which extends the +// Gregorian calendar backwards to year one. +// +// All minutes are 60 seconds long. Leap seconds are "smeared" so that no leap +// second table is needed for interpretation, using a 24-hour linear smear. +// +// The range is from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z. By +// restricting to that range, we ensure that we can convert to and from RFC +// 3339 date strings. +message Timestamp { + // Represents seconds of UTC time since Unix epoch 1970-01-01T00:00:00Z. + // Must be from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59Z inclusive. + int64 seconds = 1; + + // Non-negative fractions of a second at nanosecond resolution. Negative + // second values with fractions must still have non-negative nanos values + // that count forward in time. Must be from 0 to 999,999,999 inclusive. + int32 nanos = 2; +} diff --git a/Apple/NetworkExtension/PacketTunnelProvider.swift b/Apple/NetworkExtension/PacketTunnelProvider.swift index a8e42e0..4f29543 100644 --- a/Apple/NetworkExtension/PacketTunnelProvider.swift +++ b/Apple/NetworkExtension/PacketTunnelProvider.swift @@ -5,7 +5,15 @@ import libburrow import NetworkExtension import os -class PacketTunnelProvider: NEPacketTunnelProvider { +private final class SendableCallbackBox: @unchecked Sendable { + let callback: Callback + + init(_ callback: Callback) { + self.callback = callback + } +} + +final class PacketTunnelProvider: NEPacketTunnelProvider, @unchecked Sendable { enum Error: Swift.Error { case missingTunnelConfiguration } @@ -30,27 +38,41 @@ class PacketTunnelProvider: NEPacketTunnelProvider { } } - override func startTunnel(options: [String: NSObject]? = nil) async throws { - do { - let configuration = try await Array(client.tunnelConfiguration(.init()).prefix(1)).first - guard let settings = configuration?.settings else { - throw Error.missingTunnelConfiguration + override func startTunnel( + options: [String: NSObject]?, + completionHandler: @escaping (Swift.Error?) -> Void + ) { + let completion = SendableCallbackBox(completionHandler) + Task { + do { + let configuration = try await Array(client.tunnelConfiguration(.init()).prefix(1)).first + guard let settings = configuration?.settings else { + throw Error.missingTunnelConfiguration + } + try await setTunnelNetworkSettings(settings) + _ = try await client.tunnelStart(.init()) + logger.log("Started tunnel with network settings: \(settings)") + completion.callback(nil) + } catch { + logger.error("Failed to start tunnel: \(error)") + completion.callback(error) } - try await setTunnelNetworkSettings(settings) - _ = try await client.tunnelStart(.init()) - logger.log("Started tunnel with network settings: \(settings)") - } catch { - logger.error("Failed to start tunnel: \(error)") - throw error } } - override func stopTunnel(with reason: NEProviderStopReason) async { - do { - _ = try await client.tunnelStop(.init()) - logger.log("Stopped client") - } catch { - logger.error("Failed to stop tunnel: \(error)") + override func stopTunnel( + with reason: NEProviderStopReason, + completionHandler: @escaping () -> Void + ) { + let completion = SendableCallbackBox(completionHandler) + Task { + do { + _ = try await client.tunnelStop(.init()) + logger.log("Stopped client") + } catch { + logger.error("Failed to stop tunnel: \(error)") + } + completion.callback() } } } diff --git a/Apple/NetworkExtension/libburrow/build-rust.sh b/Apple/NetworkExtension/libburrow/build-rust.sh index 6f455a9..5db2a2b 100755 --- a/Apple/NetworkExtension/libburrow/build-rust.sh +++ b/Apple/NetworkExtension/libburrow/build-rust.sh @@ -73,7 +73,21 @@ 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" 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[@]}" +CARGO_ENV=( + "PATH=$CARGO_PATH" + "PROTOC=$PROTOC" + "CARGO_TARGET_DIR=${CONFIGURATION_TEMP_DIR}/target" +) + +if [[ -n "$IPHONEOS_DEPLOYMENT_TARGET" ]]; then + CARGO_ENV+=("IPHONEOS_DEPLOYMENT_TARGET=$IPHONEOS_DEPLOYMENT_TARGET") +fi + +if [[ -n "$MACOSX_DEPLOYMENT_TARGET" ]]; then + CARGO_ENV+=("MACOSX_DEPLOYMENT_TARGET=$MACOSX_DEPLOYMENT_TARGET") +fi + +env -i "${CARGO_ENV[@]}" cargo build "${CARGO_ARGS[@]}" mkdir -p "${BUILT_PRODUCTS_DIR}" diff --git a/burrow/Cargo.toml b/burrow/Cargo.toml index c3cfb75..22f3d25 100644 --- a/burrow/Cargo.toml +++ b/burrow/Cargo.toml @@ -15,6 +15,8 @@ tokio = { version = "1.37", features = [ "macros", "sync", "io-util", + "net", + "process", "rt-multi-thread", "signal", "time", @@ -25,7 +27,6 @@ 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"] } log = "0.4" serde = { version = "1", features = ["derive"] } @@ -47,13 +48,14 @@ base64 = "0.21" fehler = "1.0" ip_network_table = "0.2" ip_network = "0.4" -ipnetwork = "0.21" +ipnetwork = { version = "0.21", features = ["serde"] } async-channel = "2.1" schemars = "0.8" futures = "0.3.28" once_cell = "1.19" arti-client = "0.40.0" hickory-proto = "0.25.2" +netstack-smoltcp = "0.2.1" tokio-util = { version = "0.7.18", features = ["compat"] } tor-rtcompat = "0.40.0" console-subscriber = { version = "0.2.0", optional = true } @@ -65,7 +67,6 @@ reqwest = { version = "0.12", default-features = false, features = [ "rustls-tls", ] } rusqlite = { version = "0.38.0", features = ["blob"] } -iroh = "0.94.0" dotenv = "0.15.0" tonic = "0.12.0" prost = "0.13.1" @@ -82,12 +83,16 @@ subtle = "2.6" caps = "0.5" libc = "0.2" libsystemd = "0.7" +nix = { version = "0.27", features = ["fs", "socket", "uio"] } tracing-journald = "0.3" [target.'cfg(target_vendor = "apple")'.dependencies] nix = { version = "0.27" } rusqlite = { version = "0.38.0", features = ["bundled", "blob"] } +[target.'cfg(target_os = "macos")'.dependencies] +tracing-oslog = { git = "https://github.com/Stormshield-robinc/tracing-oslog" } + [dev-dependencies] insta = { version = "1.32", features = ["yaml"] } tempfile = "3.13" diff --git a/burrow/src/tracing.rs b/burrow/src/tracing.rs index 861b41f..21e16ae 100644 --- a/burrow/src/tracing.rs +++ b/burrow/src/tracing.rs @@ -3,8 +3,7 @@ use std::sync::Once; use tracing::{error, info}; use tracing_subscriber::{ layer::{Layer, SubscriberExt}, - EnvFilter, - Registry, + EnvFilter, Registry, }; static TRACING: Once = Once::new(); @@ -15,36 +14,49 @@ pub fn initialize() { error!("Failed to initialize LogTracer: {}", e); } - #[cfg(target_os = "windows")] - let system_log = Some(tracing_subscriber::fmt::layer()); - - #[cfg(target_os = "linux")] - let system_log = match tracing_journald::layer() { - Ok(layer) => Some(layer), - Err(e) => { - if e.kind() != std::io::ErrorKind::NotFound { - error!("Failed to initialize journald: {}", e); - } - None - } - }; - - #[cfg(target_vendor = "apple")] - let system_log = Some(tracing_oslog::OsLogger::new( - "com.hackclub.burrow", - "tracing", - )); - - let stderr = (console::user_attended_stderr() || system_log.is_none()).then(|| { + let make_stderr = || { tracing_subscriber::fmt::layer() .with_level(true) .with_writer(std::io::stderr) .with_line_number(true) .compact() .with_filter(EnvFilter::from_default_env()) - }); + }; - let subscriber = Registry::default().with(stderr).with(system_log); + #[cfg(target_os = "windows")] + let subscriber = { + let system_log = Some(tracing_subscriber::fmt::layer()); + let stderr = (console::user_attended_stderr() || system_log.is_none()).then(make_stderr); + Registry::default().with(stderr).with(system_log) + }; + + #[cfg(target_os = "linux")] + let subscriber = { + let system_log = match tracing_journald::layer() { + Ok(layer) => Some(layer), + Err(e) => { + if e.kind() != std::io::ErrorKind::NotFound { + error!("Failed to initialize journald: {}", e); + } + None + } + }; + let stderr = (console::user_attended_stderr() || system_log.is_none()).then(make_stderr); + Registry::default().with(stderr).with(system_log) + }; + + #[cfg(target_os = "macos")] + let subscriber = { + let system_log = Some(tracing_oslog::OsLogger::new( + "com.hackclub.burrow", + "tracing", + )); + let stderr = (console::user_attended_stderr() || system_log.is_none()).then(make_stderr); + Registry::default().with(stderr).with(system_log) + }; + + #[cfg(not(any(target_os = "windows", target_os = "linux", target_os = "macos")))] + let subscriber = Registry::default().with(Some(make_stderr())); #[cfg(feature = "tokio-console")] let subscriber = subscriber.with(