diff --git a/Apple/App/App-macOS.entitlements b/Apple/App/App-macOS.entitlements
index 53fcbb7..595fe08 100644
--- a/Apple/App/App-macOS.entitlements
+++ b/Apple/App/App-macOS.entitlements
@@ -15,5 +15,7 @@
$(APP_GROUP_IDENTIFIER)
+ com.apple.security.network.client
+
diff --git a/Apple/App/App.xcconfig b/Apple/App/App.xcconfig
index 4e42ddc..bfdd25d 100644
--- a/Apple/App/App.xcconfig
+++ b/Apple/App/App.xcconfig
@@ -15,9 +15,10 @@ EXCLUDED_SOURCE_FILE_NAMES = MainMenu.xib
EXCLUDED_SOURCE_FILE_NAMES[sdk=macosx*] =
INFOPLIST_KEY_LSUIElement[sdk=macosx*] = YES
-INFOPLIST_KEY_NSMainNibFile[sdk=macosx*] = MainMenu
INFOPLIST_KEY_NSPrincipalClass[sdk=macosx*] = NSApplication
INFOPLIST_KEY_LSApplicationCategoryType[sdk=macosx*] = public.app-category.utilities
CODE_SIGN_ENTITLEMENTS = App/App-iOS.entitlements
CODE_SIGN_ENTITLEMENTS[sdk=macosx*] = App/App-macOS.entitlements
+
+SWIFT_INCLUDE_PATHS = $(inherited) $(PROJECT_DIR)/NetworkExtension
diff --git a/Apple/App/AppDelegate.swift b/Apple/App/AppDelegate.swift
index c3cb4cb..a322943 100644
--- a/Apple/App/AppDelegate.swift
+++ b/Apple/App/AppDelegate.swift
@@ -1,23 +1,17 @@
#if os(macOS)
import AppKit
+import BurrowConfiguration
+import BurrowCore
import BurrowUI
import SwiftUI
+import libburrow
-@main
@MainActor
class AppDelegate: NSObject, NSApplicationDelegate {
private var windowController: NSWindowController?
+ private let logger = Logger.logger(for: AppDelegate.self)
- private let quitItem: NSMenuItem = {
- let quitItem = NSMenuItem(
- title: "Quit Burrow",
- action: #selector(NSApplication.terminate(_:)),
- keyEquivalent: "q"
- )
- quitItem.target = NSApplication.shared
- quitItem.keyEquivalentModifierMask = .command
- return quitItem
- }()
+ private let quitItem = AppDelegate.makeQuitItem()
private lazy var openItem: NSMenuItem = {
let item = NSMenuItem(
@@ -61,9 +55,85 @@ class AppDelegate: NSObject, NSApplicationDelegate {
}()
func applicationDidFinishLaunching(_ notification: Notification) {
+ installMainMenu()
+ startDaemon()
statusItem.menu = menu
}
+ private func startDaemon() {
+ do {
+ libburrow.spawnInProcess(
+ socketPath: try Constants.socketURL.path(percentEncoded: false),
+ databasePath: try Constants.databaseURL.path(percentEncoded: false)
+ )
+ } catch {
+ logger.error("Failed to spawn daemon thread: \(error)")
+ }
+ }
+
+ private func installMainMenu() {
+ let mainMenu = NSMenu(title: "Burrow")
+
+ let appItem = NSMenuItem(title: "Burrow", action: nil, keyEquivalent: "")
+ let appMenu = NSMenu(title: "Burrow")
+
+ let aboutItem = NSMenuItem(
+ title: "About Burrow",
+ action: #selector(NSApplication.orderFrontStandardAboutPanel(_:)),
+ keyEquivalent: ""
+ )
+ aboutItem.target = NSApplication.shared
+ appMenu.addItem(aboutItem)
+ appMenu.addItem(.separator())
+ appMenu.addItem(Self.makeQuitItem())
+
+ appItem.submenu = appMenu
+ mainMenu.addItem(appItem)
+
+ let editItem = NSMenuItem(title: "Edit", action: nil, keyEquivalent: "")
+ editItem.submenu = makeEditMenu()
+ mainMenu.addItem(editItem)
+
+ NSApplication.shared.mainMenu = mainMenu
+ }
+
+ private static func makeQuitItem() -> NSMenuItem {
+ let quitItem = NSMenuItem(
+ title: "Quit Burrow",
+ action: #selector(NSApplication.terminate(_:)),
+ keyEquivalent: "q"
+ )
+ quitItem.target = NSApplication.shared
+ quitItem.keyEquivalentModifierMask = .command
+ return quitItem
+ }
+
+ private func makeEditMenu() -> NSMenu {
+ let editMenu = NSMenu(title: "Edit")
+
+ editMenu.addItem(NSMenuItem(title: "Undo", action: Selector(("undo:")), keyEquivalent: "z"))
+ editMenu.addItem(NSMenuItem(title: "Redo", action: Selector(("redo:")), keyEquivalent: "Z"))
+ editMenu.addItem(.separator())
+
+ editMenu.addItem(NSMenuItem(title: "Cut", action: #selector(NSText.cut(_:)), keyEquivalent: "x"))
+ editMenu.addItem(NSMenuItem(title: "Copy", action: #selector(NSText.copy(_:)), keyEquivalent: "c"))
+ editMenu.addItem(NSMenuItem(title: "Paste", action: #selector(NSText.paste(_:)), keyEquivalent: "v"))
+
+ let pastePlainItem = NSMenuItem(
+ title: "Paste and Match Style",
+ action: #selector(NSTextView.pasteAsPlainText(_:)),
+ keyEquivalent: "V"
+ )
+ pastePlainItem.keyEquivalentModifierMask = [.command, .option, .shift]
+ editMenu.addItem(pastePlainItem)
+
+ editMenu.addItem(NSMenuItem(title: "Delete", action: #selector(NSText.delete(_:)), keyEquivalent: ""))
+ editMenu.addItem(.separator())
+ editMenu.addItem(NSMenuItem(title: "Select All", action: #selector(NSText.selectAll(_:)), keyEquivalent: "a"))
+
+ return editMenu
+ }
+
@objc
private func openWindow() {
if let window = windowController?.window {
@@ -74,9 +144,13 @@ class AppDelegate: NSObject, NSApplicationDelegate {
let contentView = BurrowView()
let hostingController = NSHostingController(rootView: contentView)
+ if #available(macOS 13.0, *) {
+ hostingController.sizingOptions = []
+ }
let window = NSWindow(contentViewController: hostingController)
window.title = "Burrow"
window.setContentSize(NSSize(width: 820, height: 720))
+ window.contentMinSize = NSSize(width: 640, height: 560)
window.styleMask.insert([.titled, .closable, .miniaturizable, .resizable])
window.center()
@@ -87,4 +161,19 @@ class AppDelegate: NSObject, NSApplicationDelegate {
NSApplication.shared.activate(ignoringOtherApps: true)
}
}
+
+@main
+enum BurrowApplication {
+ @MainActor
+ private static let delegate = AppDelegate()
+
+ @MainActor
+ static func main() {
+ let application = NSApplication.shared
+ application.delegate = delegate
+ application.setActivationPolicy(.accessory)
+ application.finishLaunching()
+ application.run()
+ }
+}
#endif
diff --git a/Apple/App/MainMenu.xib b/Apple/App/MainMenu.xib
index 50ba431..5c7bc1e 100644
--- a/Apple/App/MainMenu.xib
+++ b/Apple/App/MainMenu.xib
@@ -1,7 +1,8 @@
-
+
-
+
+
diff --git a/Apple/Burrow.xcodeproj/project.pbxproj b/Apple/Burrow.xcodeproj/project.pbxproj
index 83d32e0..6c64803 100644
--- a/Apple/Burrow.xcodeproj/project.pbxproj
+++ b/Apple/Burrow.xcodeproj/project.pbxproj
@@ -8,7 +8,6 @@
/* Begin PBXBuildFile section */
D00AA8972A4669BC005C8102 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D00AA8962A4669BC005C8102 /* AppDelegate.swift */; };
- D11000012F70000100112233 /* BurrowUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D11000042F70000100112233 /* BurrowUITests.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, ); }; };
D03383AD2C8E67E300F7C44E /* SwiftProtobuf in Frameworks */ = {isa = PBXBuildFile; productRef = D078F7E22C8DA375008A8CEC /* SwiftProtobuf */; };
@@ -19,6 +18,7 @@
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 */; };
+ D0BCC60C2A09A0C200AD070D /* libburrow.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D0BCC6032A09535900AD070D /* libburrow.a */; };
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, ); }; };
@@ -43,20 +43,14 @@
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 */; };
- D0FA10012D10200100112233 /* burrow.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0FA10032D10200100112233 /* burrow.pb.swift */; };
- D0FA10022D10200100112233 /* burrow.grpc.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0FA10042D10200100112233 /* burrow.grpc.swift */; };
D0F7597E2C8DB30500126CF3 /* CGRPCZlib in Frameworks */ = {isa = PBXBuildFile; productRef = D0F7597D2C8DB30500126CF3 /* CGRPCZlib */; };
D0F7598D2C8DB3DA00126CF3 /* Client.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0D4E4992C8D921A007F820A /* Client.swift */; };
+ D0FA10012D10200100112233 /* Generated/burrow.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0FA10032D10200100112233 /* Generated/burrow.pb.swift */; };
+ D0FA10022D10200100112233 /* Generated/burrow.grpc.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0FA10042D10200100112233 /* Generated/burrow.grpc.swift */; };
+ D11000012F70000100112233 /* BurrowUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D11000042F70000100112233 /* BurrowUITests.swift */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
- D11000022F70000100112233 /* PBXContainerItemProxy */ = {
- isa = PBXContainerItemProxy;
- containerPortal = D05B9F6A29E39EEC008CB1F9 /* Project object */;
- proxyType = 1;
- remoteGlobalIDString = D05B9F7129E39EEC008CB1F9;
- remoteInfo = App;
- };
D020F65B29E4A697002790F6 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = D05B9F6A29E39EEC008CB1F9 /* Project object */;
@@ -106,6 +100,13 @@
remoteGlobalIDString = D0D4E5302C8D996F007F820A;
remoteInfo = Core;
};
+ D11000022F70000100112233 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = D05B9F6A29E39EEC008CB1F9 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = D05B9F7129E39EEC008CB1F9;
+ remoteInfo = App;
+ };
/* End PBXContainerItemProxy section */
/* Begin PBXCopyFilesBuildPhase section */
@@ -138,9 +139,6 @@
/* Begin PBXFileReference section */
D00117422B30348D00D87C25 /* Configuration.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Configuration.xcconfig; sourceTree = ""; };
D00AA8962A4669BC005C8102 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
- D11000032F70000100112233 /* BurrowUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = BurrowUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
- D11000042F70000100112233 /* BurrowUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BurrowUITests.swift; sourceTree = ""; };
- D11000052F70000100112233 /* UITests.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = UITests.xcconfig; 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 = ""; };
@@ -188,18 +186,14 @@
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 = ""; };
- D0FA10032D10200100112233 /* burrow.pb.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Generated/burrow.pb.swift; sourceTree = ""; };
- D0FA10042D10200100112233 /* burrow.grpc.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Generated/burrow.grpc.swift; sourceTree = ""; };
+ D0FA10032D10200100112233 /* Generated/burrow.pb.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Generated/burrow.pb.swift; sourceTree = ""; };
+ D0FA10042D10200100112233 /* Generated/burrow.grpc.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Generated/burrow.grpc.swift; sourceTree = ""; };
+ D11000032F70000100112233 /* BurrowUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = BurrowUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
+ D11000042F70000100112233 /* BurrowUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BurrowUITests.swift; sourceTree = ""; };
+ D11000052F70000100112233 /* UITests.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = UITests.xcconfig; sourceTree = ""; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
- D11000062F70000100112233 /* Frameworks */ = {
- isa = PBXFrameworksBuildPhase;
- buildActionMask = 2147483647;
- files = (
- );
- runOnlyForDeploymentPostprocessing = 0;
- };
D020F65029E4A697002790F6 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
@@ -218,6 +212,7 @@
D0BF09552C8E66FD000D8DEC /* BurrowConfiguration.framework in Frameworks */,
D0F4FAD32C8DC79C0068730A /* BurrowCore.framework in Frameworks */,
D0D4E5892C8D9C94007F820A /* BurrowUI.framework in Frameworks */,
+ D0BCC60C2A09A0C200AD070D /* libburrow.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -242,6 +237,13 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
+ D11000062F70000100112233 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
@@ -324,14 +326,6 @@
path = App;
sourceTree = "";
};
- D11000072F70000100112233 /* AppUITests */ = {
- isa = PBXGroup;
- children = (
- D11000042F70000100112233 /* BurrowUITests.swift */,
- );
- path = AppUITests;
- sourceTree = "";
- };
D0B98FD729FDDB57004E7149 /* libburrow */ = {
isa = PBXGroup;
children = (
@@ -346,8 +340,8 @@
isa = PBXGroup;
children = (
D0D4E4952C8D921A007F820A /* burrow.proto */,
- D0FA10032D10200100112233 /* burrow.pb.swift */,
- D0FA10042D10200100112233 /* burrow.grpc.swift */,
+ D0FA10032D10200100112233 /* Generated/burrow.pb.swift */,
+ D0FA10042D10200100112233 /* Generated/burrow.grpc.swift */,
);
path = Client;
sourceTree = "";
@@ -401,27 +395,17 @@
path = Constants;
sourceTree = "";
};
+ D11000072F70000100112233 /* AppUITests */ = {
+ isa = PBXGroup;
+ children = (
+ D11000042F70000100112233 /* BurrowUITests.swift */,
+ );
+ path = AppUITests;
+ sourceTree = "";
+ };
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
- D11000082F70000100112233 /* BurrowUITests */ = {
- isa = PBXNativeTarget;
- buildConfigurationList = D110000E2F70000100112233 /* Build configuration list for PBXNativeTarget "BurrowUITests" */;
- buildPhases = (
- D110000A2F70000100112233 /* Sources */,
- D11000062F70000100112233 /* Frameworks */,
- D11000092F70000100112233 /* Resources */,
- );
- buildRules = (
- );
- dependencies = (
- D110000B2F70000100112233 /* PBXTargetDependency */,
- );
- name = BurrowUITests;
- productName = BurrowUITests;
- productReference = D11000032F70000100112233 /* BurrowUITests.xctest */;
- productType = "com.apple.product-type.bundle.ui-testing";
- };
D020F65229E4A697002790F6 /* NetworkExtension */ = {
isa = PBXNativeTarget;
buildConfigurationList = D020F65E29E4A697002790F6 /* Build configuration list for PBXNativeTarget "NetworkExtension" */;
@@ -527,6 +511,24 @@
productReference = D0D4E5622C8D9BF4007F820A /* BurrowConfiguration.framework */;
productType = "com.apple.product-type.framework";
};
+ D11000082F70000100112233 /* BurrowUITests */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = D110000E2F70000100112233 /* Build configuration list for PBXNativeTarget "BurrowUITests" */;
+ buildPhases = (
+ D110000A2F70000100112233 /* Sources */,
+ D11000062F70000100112233 /* Frameworks */,
+ D11000092F70000100112233 /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ D110000B2F70000100112233 /* PBXTargetDependency */,
+ );
+ name = BurrowUITests;
+ productName = BurrowUITests;
+ productReference = D11000032F70000100112233 /* BurrowUITests.xctest */;
+ productType = "com.apple.product-type.bundle.ui-testing";
+ };
/* End PBXNativeTarget section */
/* Begin PBXProject section */
@@ -537,19 +539,54 @@
LastSwiftUpdateCheck = 1600;
LastUpgradeCheck = 1520;
TargetAttributes = {
- D11000082F70000100112233 = {
- CreatedOnToolsVersion = 16.0;
- TestTargetID = D05B9F7129E39EEC008CB1F9;
- };
D020F65229E4A697002790F6 = {
CreatedOnToolsVersion = 14.3;
+ DevelopmentTeam = 2KPBQHRJ2D;
+ ProvisioningStyle = Automatic;
+ SystemCapabilities = {
+ com.apple.ApplicationGroups.Mac = {
+ enabled = 1;
+ };
+ com.apple.NetworkExtensions = {
+ enabled = 1;
+ };
+ com.apple.Sandbox = {
+ enabled = 1;
+ };
+ };
};
D05B9F7129E39EEC008CB1F9 = {
CreatedOnToolsVersion = 14.3;
+ DevelopmentTeam = 2KPBQHRJ2D;
+ ProvisioningStyle = Automatic;
+ SystemCapabilities = {
+ com.apple.ApplicationGroups.Mac = {
+ enabled = 1;
+ };
+ com.apple.ApplicationGroups.iOS = {
+ enabled = 1;
+ };
+ com.apple.AssociatedDomains = {
+ enabled = 1;
+ };
+ com.apple.NetworkExtensions = {
+ enabled = 1;
+ };
+ com.apple.NetworkExtensions.iOS = {
+ enabled = 1;
+ };
+ com.apple.Sandbox = {
+ enabled = 1;
+ };
+ };
};
D0D4E5302C8D996F007F820A = {
CreatedOnToolsVersion = 16.0;
};
+ D11000082F70000100112233 = {
+ CreatedOnToolsVersion = 16.0;
+ TestTargetID = D05B9F7129E39EEC008CB1F9;
+ };
};
};
buildConfigurationList = D05B9F6D29E39EEC008CB1F9 /* Build configuration list for PBXProject "Burrow" */;
@@ -583,13 +620,6 @@
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
- D11000092F70000100112233 /* Resources */ = {
- isa = PBXResourcesBuildPhase;
- buildActionMask = 2147483647;
- files = (
- );
- runOnlyForDeploymentPostprocessing = 0;
- };
D05B9F7029E39EEC008CB1F9 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
@@ -605,6 +635,13 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
+ D11000092F70000100112233 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
@@ -653,14 +690,6 @@
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
- D110000A2F70000100112233 /* Sources */ = {
- isa = PBXSourcesBuildPhase;
- buildActionMask = 2147483647;
- files = (
- D11000012F70000100112233 /* BurrowUITests.swift in Sources */,
- );
- runOnlyForDeploymentPostprocessing = 0;
- };
D020F64F29E4A697002790F6 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
@@ -682,8 +711,8 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
- D0FA10012D10200100112233 /* burrow.pb.swift in Sources */,
- D0FA10022D10200100112233 /* burrow.grpc.swift in Sources */,
+ D0FA10012D10200100112233 /* Generated/burrow.pb.swift in Sources */,
+ D0FA10022D10200100112233 /* Generated/burrow.grpc.swift in Sources */,
D0F7598D2C8DB3DA00126CF3 /* Client.swift in Sources */,
D0D4E56B2C8D9C2F007F820A /* Logging.swift in Sources */,
);
@@ -716,14 +745,17 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
+ D110000A2F70000100112233 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ D11000012F70000100112233 /* BurrowUITests.swift in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
- D110000B2F70000100112233 /* PBXTargetDependency */ = {
- isa = PBXTargetDependency;
- target = D05B9F7129E39EEC008CB1F9 /* App */;
- targetProxy = D11000022F70000100112233 /* PBXContainerItemProxy */;
- };
D020F65C29E4A697002790F6 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = D020F65229E4A697002790F6 /* NetworkExtension */;
@@ -763,27 +795,19 @@
isa = PBXTargetDependency;
productRef = D0F759892C8DB34200126CF3 /* GRPC */;
};
+ D110000B2F70000100112233 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = D05B9F7129E39EEC008CB1F9 /* App */;
+ targetProxy = D11000022F70000100112233 /* PBXContainerItemProxy */;
+ };
/* End PBXTargetDependency section */
/* Begin XCBuildConfiguration section */
- D110000C2F70000100112233 /* Debug */ = {
- isa = XCBuildConfiguration;
- baseConfigurationReference = D11000052F70000100112233 /* UITests.xcconfig */;
- buildSettings = {
- };
- name = Debug;
- };
- D110000D2F70000100112233 /* Release */ = {
- isa = XCBuildConfiguration;
- baseConfigurationReference = D11000052F70000100112233 /* UITests.xcconfig */;
- buildSettings = {
- };
- name = Release;
- };
D020F65F29E4A697002790F6 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = D020F66229E4A6E5002790F6 /* NetworkExtension.xcconfig */;
buildSettings = {
+ DEVELOPMENT_TEAM = 2KPBQHRJ2D;
};
name = Debug;
};
@@ -791,6 +815,7 @@
isa = XCBuildConfiguration;
baseConfigurationReference = D020F66229E4A6E5002790F6 /* NetworkExtension.xcconfig */;
buildSettings = {
+ DEVELOPMENT_TEAM = 2KPBQHRJ2D;
};
name = Release;
};
@@ -798,6 +823,7 @@
isa = XCBuildConfiguration;
baseConfigurationReference = D020F64029E4A1FF002790F6 /* Compiler.xcconfig */;
buildSettings = {
+ DEVELOPMENT_TEAM = 2KPBQHRJ2D;
};
name = Debug;
};
@@ -805,6 +831,7 @@
isa = XCBuildConfiguration;
baseConfigurationReference = D020F64029E4A1FF002790F6 /* Compiler.xcconfig */;
buildSettings = {
+ DEVELOPMENT_TEAM = 2KPBQHRJ2D;
};
name = Release;
};
@@ -812,6 +839,10 @@
isa = XCBuildConfiguration;
baseConfigurationReference = D020F64929E4A34B002790F6 /* App.xcconfig */;
buildSettings = {
+ CODE_SIGN_IDENTITY = "Apple Development";
+ CODE_SIGN_STYLE = Automatic;
+ DEVELOPMENT_TEAM = 2KPBQHRJ2D;
+ PROVISIONING_PROFILE_SPECIFIER = "";
};
name = Debug;
};
@@ -819,6 +850,10 @@
isa = XCBuildConfiguration;
baseConfigurationReference = D020F64929E4A34B002790F6 /* App.xcconfig */;
buildSettings = {
+ CODE_SIGN_IDENTITY = "Apple Development";
+ CODE_SIGN_STYLE = Automatic;
+ DEVELOPMENT_TEAM = 2KPBQHRJ2D;
+ PROVISIONING_PROFILE_SPECIFIER = "";
};
name = Release;
};
@@ -864,18 +899,23 @@
};
name = Release;
};
+ D110000C2F70000100112233 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = D11000052F70000100112233 /* UITests.xcconfig */;
+ buildSettings = {
+ };
+ name = Debug;
+ };
+ D110000D2F70000100112233 /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = D11000052F70000100112233 /* UITests.xcconfig */;
+ buildSettings = {
+ };
+ name = Release;
+ };
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
- D110000E2F70000100112233 /* Build configuration list for PBXNativeTarget "BurrowUITests" */ = {
- isa = XCConfigurationList;
- buildConfigurations = (
- D110000C2F70000100112233 /* Debug */,
- D110000D2F70000100112233 /* Release */,
- );
- defaultConfigurationIsVisible = 0;
- defaultConfigurationName = Release;
- };
D020F65E29E4A697002790F6 /* Build configuration list for PBXNativeTarget "NetworkExtension" */ = {
isa = XCConfigurationList;
buildConfigurations = (
@@ -930,6 +970,15 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
+ D110000E2F70000100112233 /* Build configuration list for PBXNativeTarget "BurrowUITests" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ D110000C2F70000100112233 /* Debug */,
+ D110000D2F70000100112233 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
/* End XCConfigurationList section */
/* Begin XCRemoteSwiftPackageReference section */
diff --git a/Apple/Configuration/Constants/Constants.swift b/Apple/Configuration/Constants/Constants.swift
index 95d8c78..648ded2 100644
--- a/Apple/Configuration/Constants/Constants.swift
+++ b/Apple/Configuration/Constants/Constants.swift
@@ -16,6 +16,13 @@ public enum Constants {
try groupContainerURL.appending(component: "burrow.sock", directoryHint: .notDirectory)
}
}
+
+ public static var packetTunnelSocketURL: URL {
+ get throws {
+ try groupContainerURL.appending(component: "burrow-packet-tunnel.sock", directoryHint: .notDirectory)
+ }
+ }
+
public static var databaseURL: URL {
get throws {
try groupContainerURL.appending(component: "burrow.db", directoryHint: .notDirectory)
diff --git a/Apple/Configuration/Identity.xcconfig b/Apple/Configuration/Identity.xcconfig
index e9ca78d..7318401 100644
--- a/Apple/Configuration/Identity.xcconfig
+++ b/Apple/Configuration/Identity.xcconfig
@@ -1,2 +1,2 @@
-DEVELOPMENT_TEAM = P6PV2R9443
-APP_BUNDLE_IDENTIFIER = com.hackclub.burrow
+DEVELOPMENT_TEAM = 2KPBQHRJ2D
+APP_BUNDLE_IDENTIFIER = com.tianruichen.burrow
diff --git a/Apple/Core/Client.swift b/Apple/Core/Client.swift
index 7d4cfc7..559238f 100644
--- a/Apple/Core/Client.swift
+++ b/Apple/Core/Client.swift
@@ -108,6 +108,83 @@ public struct Burrow_TailnetLoginStatusResponse: Sendable {
public init() {}
}
+public struct Burrow_ProxySubscriptionImportRequest: Sendable {
+ public var url: String = ""
+ public var unknownFields = SwiftProtobuf.UnknownStorage()
+
+ public init() {}
+}
+
+public struct Burrow_ProxySubscriptionNodePreview: Sendable {
+ public var ordinal: Int32 = 0
+ public var name: String = ""
+ public var `protocol`: String = ""
+ public var server: String = ""
+ public var port: Int32 = 0
+ public var warnings: [String] = []
+ public var runtimeSupported: Bool = false
+ public var unknownFields = SwiftProtobuf.UnknownStorage()
+
+ public init() {}
+}
+
+public struct Burrow_ProxySubscriptionRejectedEntry: Sendable {
+ public var ordinal: Int32 = 0
+ public var reason: String = ""
+ public var scheme: String = ""
+ public var unknownFields = SwiftProtobuf.UnknownStorage()
+
+ public init() {}
+}
+
+public struct Burrow_ProxySubscriptionPreviewResponse: Sendable {
+ public var suggestedName: String = ""
+ public var detectedFormat: String = ""
+ public var compatibleCount: Int32 = 0
+ public var rejectedCount: Int32 = 0
+ public var node: [Burrow_ProxySubscriptionNodePreview] = []
+ public var rejected: [Burrow_ProxySubscriptionRejectedEntry] = []
+ public var warnings: [String] = []
+ public var unknownFields = SwiftProtobuf.UnknownStorage()
+
+ public init() {}
+}
+
+public struct Burrow_ProxySubscriptionApplyRequest: Sendable {
+ public var id: Int32 = 0
+ public var url: String = ""
+ public var name: String = ""
+ public var selectedOrdinal: Int32 = 0
+ public var unknownFields = SwiftProtobuf.UnknownStorage()
+
+ public init() {}
+}
+
+public struct Burrow_ProxySubscriptionApplyResponse: Sendable {
+ public var id: Int32 = 0
+ public var importedCount: Int32 = 0
+ public var selectedOrdinal: Int32 = 0
+ public var unknownFields = SwiftProtobuf.UnknownStorage()
+
+ public init() {}
+}
+
+public struct Burrow_ProxySubscriptionSelectRequest: Sendable {
+ public var id: Int32 = 0
+ public var selectedOrdinal: Int32 = 0
+ public var unknownFields = SwiftProtobuf.UnknownStorage()
+
+ public init() {}
+}
+
+public struct Burrow_ProxySubscriptionSelectResponse: Sendable {
+ public var id: Int32 = 0
+ public var selectedOrdinal: Int32 = 0
+ public var unknownFields = SwiftProtobuf.UnknownStorage()
+
+ public init() {}
+}
+
public struct Burrow_TunnelPacket: Sendable {
public var payload = Data()
public var unknownFields = SwiftProtobuf.UnknownStorage()
@@ -417,6 +494,239 @@ extension Burrow_TunnelPacket: SwiftProtobuf.Message, SwiftProtobuf._MessageImpl
}
}
+extension Burrow_ProxySubscriptionImportRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
+ public static let protoMessageName: String = "burrow.ProxySubscriptionImportRequest"
+ public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
+ 1: .same(proto: "url")
+ ]
+
+ public mutating func decodeMessage(decoder: inout D) throws {
+ while let fieldNumber = try decoder.nextFieldNumber() {
+ switch fieldNumber {
+ case 1: try decoder.decodeSingularStringField(value: &self.url)
+ default: break
+ }
+ }
+ }
+
+ public func traverse(visitor: inout V) throws {
+ if !self.url.isEmpty {
+ try visitor.visitSingularStringField(value: self.url, fieldNumber: 1)
+ }
+ try unknownFields.traverse(visitor: &visitor)
+ }
+}
+
+extension Burrow_ProxySubscriptionNodePreview: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
+ public static let protoMessageName: String = "burrow.ProxySubscriptionNodePreview"
+ public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
+ 1: .same(proto: "ordinal"),
+ 2: .same(proto: "name"),
+ 3: .same(proto: "protocol"),
+ 4: .same(proto: "server"),
+ 5: .same(proto: "port"),
+ 6: .same(proto: "warnings"),
+ 7: .standard(proto: "runtime_supported"),
+ ]
+
+ public mutating func decodeMessage(decoder: inout D) throws {
+ while let fieldNumber = try decoder.nextFieldNumber() {
+ switch fieldNumber {
+ case 1: try decoder.decodeSingularInt32Field(value: &self.ordinal)
+ case 2: try decoder.decodeSingularStringField(value: &self.name)
+ case 3: try decoder.decodeSingularStringField(value: &self.`protocol`)
+ case 4: try decoder.decodeSingularStringField(value: &self.server)
+ case 5: try decoder.decodeSingularInt32Field(value: &self.port)
+ case 6: try decoder.decodeRepeatedStringField(value: &self.warnings)
+ case 7: try decoder.decodeSingularBoolField(value: &self.runtimeSupported)
+ default: break
+ }
+ }
+ }
+
+ public func traverse(visitor: inout V) throws {
+ if self.ordinal != 0 { try visitor.visitSingularInt32Field(value: self.ordinal, fieldNumber: 1) }
+ if !self.name.isEmpty { try visitor.visitSingularStringField(value: self.name, fieldNumber: 2) }
+ if !self.`protocol`.isEmpty { try visitor.visitSingularStringField(value: self.`protocol`, fieldNumber: 3) }
+ if !self.server.isEmpty { try visitor.visitSingularStringField(value: self.server, fieldNumber: 4) }
+ if self.port != 0 { try visitor.visitSingularInt32Field(value: self.port, fieldNumber: 5) }
+ if !self.warnings.isEmpty { try visitor.visitRepeatedStringField(value: self.warnings, fieldNumber: 6) }
+ if self.runtimeSupported { try visitor.visitSingularBoolField(value: self.runtimeSupported, fieldNumber: 7) }
+ try unknownFields.traverse(visitor: &visitor)
+ }
+}
+
+extension Burrow_ProxySubscriptionRejectedEntry: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
+ public static let protoMessageName: String = "burrow.ProxySubscriptionRejectedEntry"
+ public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
+ 1: .same(proto: "ordinal"),
+ 2: .same(proto: "reason"),
+ 3: .same(proto: "scheme"),
+ ]
+
+ public mutating func decodeMessage(decoder: inout D) throws {
+ while let fieldNumber = try decoder.nextFieldNumber() {
+ switch fieldNumber {
+ case 1: try decoder.decodeSingularInt32Field(value: &self.ordinal)
+ case 2: try decoder.decodeSingularStringField(value: &self.reason)
+ case 3: try decoder.decodeSingularStringField(value: &self.scheme)
+ default: break
+ }
+ }
+ }
+
+ public func traverse(visitor: inout V) throws {
+ if self.ordinal != 0 { try visitor.visitSingularInt32Field(value: self.ordinal, fieldNumber: 1) }
+ if !self.reason.isEmpty { try visitor.visitSingularStringField(value: self.reason, fieldNumber: 2) }
+ if !self.scheme.isEmpty { try visitor.visitSingularStringField(value: self.scheme, fieldNumber: 3) }
+ try unknownFields.traverse(visitor: &visitor)
+ }
+}
+
+extension Burrow_ProxySubscriptionPreviewResponse: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
+ public static let protoMessageName: String = "burrow.ProxySubscriptionPreviewResponse"
+ public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
+ 1: .standard(proto: "suggested_name"),
+ 2: .standard(proto: "detected_format"),
+ 3: .standard(proto: "compatible_count"),
+ 4: .standard(proto: "rejected_count"),
+ 5: .same(proto: "node"),
+ 6: .same(proto: "rejected"),
+ 7: .same(proto: "warnings"),
+ ]
+
+ public mutating func decodeMessage(decoder: inout D) throws {
+ while let fieldNumber = try decoder.nextFieldNumber() {
+ switch fieldNumber {
+ case 1: try decoder.decodeSingularStringField(value: &self.suggestedName)
+ case 2: try decoder.decodeSingularStringField(value: &self.detectedFormat)
+ case 3: try decoder.decodeSingularInt32Field(value: &self.compatibleCount)
+ case 4: try decoder.decodeSingularInt32Field(value: &self.rejectedCount)
+ case 5: try decoder.decodeRepeatedMessageField(value: &self.node)
+ case 6: try decoder.decodeRepeatedMessageField(value: &self.rejected)
+ case 7: try decoder.decodeRepeatedStringField(value: &self.warnings)
+ default: break
+ }
+ }
+ }
+
+ public func traverse(visitor: inout V) throws {
+ if !self.suggestedName.isEmpty { try visitor.visitSingularStringField(value: self.suggestedName, fieldNumber: 1) }
+ if !self.detectedFormat.isEmpty { try visitor.visitSingularStringField(value: self.detectedFormat, fieldNumber: 2) }
+ if self.compatibleCount != 0 { try visitor.visitSingularInt32Field(value: self.compatibleCount, fieldNumber: 3) }
+ if self.rejectedCount != 0 { try visitor.visitSingularInt32Field(value: self.rejectedCount, fieldNumber: 4) }
+ if !self.node.isEmpty { try visitor.visitRepeatedMessageField(value: self.node, fieldNumber: 5) }
+ if !self.rejected.isEmpty { try visitor.visitRepeatedMessageField(value: self.rejected, fieldNumber: 6) }
+ if !self.warnings.isEmpty { try visitor.visitRepeatedStringField(value: self.warnings, fieldNumber: 7) }
+ try unknownFields.traverse(visitor: &visitor)
+ }
+}
+
+extension Burrow_ProxySubscriptionApplyRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
+ public static let protoMessageName: String = "burrow.ProxySubscriptionApplyRequest"
+ public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
+ 1: .same(proto: "id"),
+ 2: .same(proto: "url"),
+ 3: .same(proto: "name"),
+ 4: .standard(proto: "selected_ordinal"),
+ ]
+
+ public mutating func decodeMessage(decoder: inout D) throws {
+ while let fieldNumber = try decoder.nextFieldNumber() {
+ switch fieldNumber {
+ case 1: try decoder.decodeSingularInt32Field(value: &self.id)
+ case 2: try decoder.decodeSingularStringField(value: &self.url)
+ case 3: try decoder.decodeSingularStringField(value: &self.name)
+ case 4: try decoder.decodeSingularInt32Field(value: &self.selectedOrdinal)
+ default: break
+ }
+ }
+ }
+
+ public func traverse(visitor: inout V) throws {
+ if self.id != 0 { try visitor.visitSingularInt32Field(value: self.id, fieldNumber: 1) }
+ if !self.url.isEmpty { try visitor.visitSingularStringField(value: self.url, fieldNumber: 2) }
+ if !self.name.isEmpty { try visitor.visitSingularStringField(value: self.name, fieldNumber: 3) }
+ if self.selectedOrdinal != 0 { try visitor.visitSingularInt32Field(value: self.selectedOrdinal, fieldNumber: 4) }
+ try unknownFields.traverse(visitor: &visitor)
+ }
+}
+
+extension Burrow_ProxySubscriptionApplyResponse: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
+ public static let protoMessageName: String = "burrow.ProxySubscriptionApplyResponse"
+ public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
+ 1: .same(proto: "id"),
+ 2: .standard(proto: "imported_count"),
+ 3: .standard(proto: "selected_ordinal"),
+ ]
+
+ public mutating func decodeMessage(decoder: inout D) throws {
+ while let fieldNumber = try decoder.nextFieldNumber() {
+ switch fieldNumber {
+ case 1: try decoder.decodeSingularInt32Field(value: &self.id)
+ case 2: try decoder.decodeSingularInt32Field(value: &self.importedCount)
+ case 3: try decoder.decodeSingularInt32Field(value: &self.selectedOrdinal)
+ default: break
+ }
+ }
+ }
+
+ public func traverse(visitor: inout V) throws {
+ if self.id != 0 { try visitor.visitSingularInt32Field(value: self.id, fieldNumber: 1) }
+ if self.importedCount != 0 { try visitor.visitSingularInt32Field(value: self.importedCount, fieldNumber: 2) }
+ if self.selectedOrdinal != 0 { try visitor.visitSingularInt32Field(value: self.selectedOrdinal, fieldNumber: 3) }
+ try unknownFields.traverse(visitor: &visitor)
+ }
+}
+
+extension Burrow_ProxySubscriptionSelectRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
+ public static let protoMessageName: String = "burrow.ProxySubscriptionSelectRequest"
+ public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
+ 1: .same(proto: "id"),
+ 2: .standard(proto: "selected_ordinal"),
+ ]
+
+ public mutating func decodeMessage(decoder: inout D) throws {
+ while let fieldNumber = try decoder.nextFieldNumber() {
+ switch fieldNumber {
+ case 1: try decoder.decodeSingularInt32Field(value: &self.id)
+ case 2: try decoder.decodeSingularInt32Field(value: &self.selectedOrdinal)
+ default: break
+ }
+ }
+ }
+
+ public func traverse(visitor: inout V) throws {
+ if self.id != 0 { try visitor.visitSingularInt32Field(value: self.id, fieldNumber: 1) }
+ if self.selectedOrdinal != 0 { try visitor.visitSingularInt32Field(value: self.selectedOrdinal, fieldNumber: 2) }
+ try unknownFields.traverse(visitor: &visitor)
+ }
+}
+
+extension Burrow_ProxySubscriptionSelectResponse: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
+ public static let protoMessageName: String = "burrow.ProxySubscriptionSelectResponse"
+ public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
+ 1: .same(proto: "id"),
+ 2: .standard(proto: "selected_ordinal"),
+ ]
+
+ public mutating func decodeMessage(decoder: inout D) throws {
+ while let fieldNumber = try decoder.nextFieldNumber() {
+ switch fieldNumber {
+ case 1: try decoder.decodeSingularInt32Field(value: &self.id)
+ case 2: try decoder.decodeSingularInt32Field(value: &self.selectedOrdinal)
+ default: break
+ }
+ }
+ }
+
+ public func traverse(visitor: inout V) throws {
+ if self.id != 0 { try visitor.visitSingularInt32Field(value: self.id, fieldNumber: 1) }
+ if self.selectedOrdinal != 0 { try visitor.visitSingularInt32Field(value: self.selectedOrdinal, fieldNumber: 2) }
+ try unknownFields.traverse(visitor: &visitor)
+ }
+}
+
public struct TailnetClient: Client, GRPCClient {
public let channel: GRPCChannel
public var defaultCallOptions: CallOptions
@@ -487,6 +797,52 @@ public struct TailnetClient: Client, GRPCClient {
}
}
+public struct ProxySubscriptionClient: Client, GRPCClient {
+ public let channel: GRPCChannel
+ public var defaultCallOptions: CallOptions
+
+ public init(channel: any GRPCChannel) {
+ self.channel = channel
+ self.defaultCallOptions = .init()
+ }
+
+ public func previewImport(
+ _ request: Burrow_ProxySubscriptionImportRequest,
+ callOptions: CallOptions? = nil
+ ) async throws -> Burrow_ProxySubscriptionPreviewResponse {
+ try await self.performAsyncUnaryCall(
+ path: "/burrow.ProxySubscriptions/PreviewImport",
+ request: request,
+ callOptions: callOptions ?? self.defaultCallOptions,
+ interceptors: []
+ )
+ }
+
+ public func applyImport(
+ _ request: Burrow_ProxySubscriptionApplyRequest,
+ callOptions: CallOptions? = nil
+ ) async throws -> Burrow_ProxySubscriptionApplyResponse {
+ try await self.performAsyncUnaryCall(
+ path: "/burrow.ProxySubscriptions/ApplyImport",
+ request: request,
+ callOptions: callOptions ?? self.defaultCallOptions,
+ interceptors: []
+ )
+ }
+
+ public func selectNode(
+ _ request: Burrow_ProxySubscriptionSelectRequest,
+ callOptions: CallOptions? = nil
+ ) async throws -> Burrow_ProxySubscriptionSelectResponse {
+ try await self.performAsyncUnaryCall(
+ path: "/burrow.ProxySubscriptions/SelectNode",
+ request: request,
+ callOptions: callOptions ?? self.defaultCallOptions,
+ interceptors: []
+ )
+ }
+}
+
public struct TunnelPacketClient: Client, GRPCClient {
public let channel: GRPCChannel
public var defaultCallOptions: CallOptions
diff --git a/Apple/Core/Client/Generated/burrow.pb.swift b/Apple/Core/Client/Generated/burrow.pb.swift
index fccd769..df6603b 100644
--- a/Apple/Core/Client/Generated/burrow.pb.swift
+++ b/Apple/Core/Client/Generated/burrow.pb.swift
@@ -25,6 +25,8 @@ public enum Burrow_NetworkType: SwiftProtobuf.Enum, Swift.CaseIterable {
public typealias RawValue = Int
case wireGuard // = 0
case tailnet // = 1
+ case trojan // = 2
+ case proxySubscription // = 3
case UNRECOGNIZED(Int)
public init() {
@@ -35,6 +37,8 @@ public enum Burrow_NetworkType: SwiftProtobuf.Enum, Swift.CaseIterable {
switch rawValue {
case 0: self = .wireGuard
case 1: self = .tailnet
+ case 2: self = .trojan
+ case 3: self = .proxySubscription
default: self = .UNRECOGNIZED(rawValue)
}
}
@@ -43,6 +47,8 @@ public enum Burrow_NetworkType: SwiftProtobuf.Enum, Swift.CaseIterable {
switch self {
case .wireGuard: return 0
case .tailnet: return 1
+ case .trojan: return 2
+ case .proxySubscription: return 3
case .UNRECOGNIZED(let i): return i
}
}
@@ -51,6 +57,8 @@ public enum Burrow_NetworkType: SwiftProtobuf.Enum, Swift.CaseIterable {
public static let allCases: [Burrow_NetworkType] = [
.wireGuard,
.tailnet,
+ .trojan,
+ .proxySubscription,
]
}
@@ -217,6 +225,8 @@ public struct Burrow_TunnelConfigurationResponse: Sendable {
public var routes: [String] = []
+ public var excludedRoutes: [String] = []
+
public var dnsServers: [String] = []
public var searchDomains: [String] = []
@@ -236,6 +246,8 @@ extension Burrow_NetworkType: SwiftProtobuf._ProtoNameProviding {
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
0: .same(proto: "WireGuard"),
1: .same(proto: "Tailnet"),
+ 2: .same(proto: "Trojan"),
+ 3: .same(proto: "ProxySubscription"),
]
}
@@ -544,6 +556,7 @@ extension Burrow_TunnelConfigurationResponse: SwiftProtobuf.Message, SwiftProtob
4: .standard(proto: "dns_servers"),
5: .standard(proto: "search_domains"),
6: .standard(proto: "include_default_route"),
+ 7: .standard(proto: "excluded_routes"),
]
public mutating func decodeMessage(decoder: inout D) throws {
@@ -558,6 +571,7 @@ extension Burrow_TunnelConfigurationResponse: SwiftProtobuf.Message, SwiftProtob
case 4: try { try decoder.decodeRepeatedStringField(value: &self.dnsServers) }()
case 5: try { try decoder.decodeRepeatedStringField(value: &self.searchDomains) }()
case 6: try { try decoder.decodeSingularBoolField(value: &self.includeDefaultRoute) }()
+ case 7: try { try decoder.decodeRepeatedStringField(value: &self.excludedRoutes) }()
default: break
}
}
@@ -573,6 +587,9 @@ extension Burrow_TunnelConfigurationResponse: SwiftProtobuf.Message, SwiftProtob
if !self.routes.isEmpty {
try visitor.visitRepeatedStringField(value: self.routes, fieldNumber: 3)
}
+ if !self.excludedRoutes.isEmpty {
+ try visitor.visitRepeatedStringField(value: self.excludedRoutes, fieldNumber: 7)
+ }
if !self.dnsServers.isEmpty {
try visitor.visitRepeatedStringField(value: self.dnsServers, fieldNumber: 4)
}
@@ -589,6 +606,7 @@ extension Burrow_TunnelConfigurationResponse: SwiftProtobuf.Message, SwiftProtob
if lhs.addresses != rhs.addresses {return false}
if lhs.mtu != rhs.mtu {return false}
if lhs.routes != rhs.routes {return false}
+ if lhs.excludedRoutes != rhs.excludedRoutes {return false}
if lhs.dnsServers != rhs.dnsServers {return false}
if lhs.searchDomains != rhs.searchDomains {return false}
if lhs.includeDefaultRoute != rhs.includeDefaultRoute {return false}
diff --git a/Apple/NetworkExtension/NetworkExtension.xcconfig b/Apple/NetworkExtension/NetworkExtension.xcconfig
index 35c7198..a6a2635 100644
--- a/Apple/NetworkExtension/NetworkExtension.xcconfig
+++ b/Apple/NetworkExtension/NetworkExtension.xcconfig
@@ -9,3 +9,5 @@ CODE_SIGN_ENTITLEMENTS = NetworkExtension/NetworkExtension-iOS.entitlements
CODE_SIGN_ENTITLEMENTS[sdk=macosx*] = NetworkExtension/NetworkExtension-macOS.entitlements
SWIFT_INCLUDE_PATHS = $(inherited) $(PROJECT_DIR)/NetworkExtension
+
+OTHER_LDFLAGS = $(inherited) -framework SystemConfiguration
diff --git a/Apple/NetworkExtension/PacketTunnelProvider.swift b/Apple/NetworkExtension/PacketTunnelProvider.swift
index 3f3d8b4..12f0cd0 100644
--- a/Apple/NetworkExtension/PacketTunnelProvider.swift
+++ b/Apple/NetworkExtension/PacketTunnelProvider.swift
@@ -28,13 +28,13 @@ final class PacketTunnelProvider: NEPacketTunnelProvider, @unchecked Sendable {
get throws { try _client.get() }
}
private let _client: Result = Result {
- try TunnelClient.unix(socketURL: Constants.socketURL)
+ try TunnelClient.unix(socketURL: Constants.packetTunnelSocketURL)
}
override init() {
do {
libburrow.spawnInProcess(
- socketPath: try Constants.socketURL.path(percentEncoded: false),
+ socketPath: try Constants.packetTunnelSocketURL.path(percentEncoded: false),
databasePath: try Constants.databaseURL.path(percentEncoded: false)
)
} catch {
@@ -54,6 +54,11 @@ final class PacketTunnelProvider: NEPacketTunnelProvider, @unchecked Sendable {
guard let settings = configuration?.settings else {
throw Error.missingTunnelConfiguration
}
+ if let configuration {
+ logger.log(
+ "Applying tunnel configuration addresses=\(configuration.addresses.joined(separator: ","), privacy: .public) routes=\(configuration.routes.joined(separator: ","), privacy: .public) excludedRoutes=\(configuration.excludedRoutes.joined(separator: ","), privacy: .public) dns=\(configuration.dnsServers.joined(separator: ","), privacy: .public) includeDefaultRoute=\(configuration.includeDefaultRoute, privacy: .public)"
+ )
+ }
try await setTunnelNetworkSettings(settings)
try startPacketBridge()
logger.log("Started tunnel with network settings: \(settings)")
@@ -88,15 +93,22 @@ extension PacketTunnelProvider {
private func startPacketBridge() throws {
stopPacketBridge()
- let packetClient = TunnelPacketClient.unix(socketURL: try Constants.socketURL)
+ let packetClient = TunnelPacketClient.unix(socketURL: try Constants.packetTunnelSocketURL)
let call = packetClient.makeTunnelPacketsCall()
self.packetCall = call
inboundPacketTask = Task { [weak self] in
guard let self else { return }
+ var packetCount = 0
+ var byteCount = 0
do {
for try await packet in call.responseStream {
let payload = packet.payload
+ packetCount += 1
+ byteCount += payload.count
+ if packetCount == 1 || packetCount % 1024 == 0 {
+ self.logger.log("Tunnel packet receive progress packets=\(packetCount, privacy: .public) bytes=\(byteCount, privacy: .public) lastBytes=\(payload.count, privacy: .public)")
+ }
self.packetFlow.writePackets(
[payload],
withProtocols: [Self.protocolNumber(for: payload)]
@@ -111,10 +123,17 @@ extension PacketTunnelProvider {
outboundPacketTask = Task { [weak self] in
guard let self else { return }
defer { call.requestStream.finish() }
+ var packetCount = 0
+ var byteCount = 0
do {
while !Task.isCancelled {
let packets = await self.readPacketsBatch()
for (payload, _) in packets {
+ packetCount += 1
+ byteCount += payload.count
+ if packetCount == 1 || packetCount % 1024 == 0 {
+ self.logger.log("Tunnel packet send progress packets=\(packetCount, privacy: .public) bytes=\(byteCount, privacy: .public) lastBytes=\(payload.count, privacy: .public)")
+ }
var packet = Burrow_TunnelPacket()
packet.payload = payload
try await call.requestStream.send(packet)
@@ -128,6 +147,7 @@ extension PacketTunnelProvider {
}
private func stopPacketBridge() {
+ logger.log("Stopping tunnel packet bridge")
inboundPacketTask?.cancel()
inboundPacketTask = nil
outboundPacketTask?.cancel()
@@ -165,6 +185,9 @@ extension Burrow_TunnelConfigurationResponse {
let parsedRoutes = routes.compactMap(ParsedTunnelRoute.init(rawValue:))
var ipv4Routes = parsedRoutes.compactMap(\.ipv4Route)
var ipv6Routes = parsedRoutes.compactMap(\.ipv6Route)
+ let parsedExcludedRoutes = excludedRoutes.compactMap(ParsedTunnelRoute.init(rawValue:))
+ let excludedIPv4Routes = parsedExcludedRoutes.compactMap(\.ipv4Route)
+ let excludedIPv6Routes = parsedExcludedRoutes.compactMap(\.ipv6Route)
if includeDefaultRoute {
ipv4Routes.append(.default())
ipv6Routes.append(.default())
@@ -180,6 +203,9 @@ extension Burrow_TunnelConfigurationResponse {
if !ipv4Routes.isEmpty {
ipv4Settings.includedRoutes = ipv4Routes
}
+ if !excludedIPv4Routes.isEmpty {
+ ipv4Settings.excludedRoutes = excludedIPv4Routes
+ }
settings.ipv4Settings = ipv4Settings
}
if !ipv6Addresses.isEmpty {
@@ -190,6 +216,9 @@ extension Burrow_TunnelConfigurationResponse {
if !ipv6Routes.isEmpty {
ipv6Settings.includedRoutes = ipv6Routes
}
+ if !excludedIPv6Routes.isEmpty {
+ ipv6Settings.excludedRoutes = excludedIPv6Routes
+ }
settings.ipv6Settings = ipv6Settings
}
if !dnsServers.isEmpty {
diff --git a/Apple/NetworkExtension/libburrow/build-rust.sh b/Apple/NetworkExtension/libburrow/build-rust.sh
index 5db2a2b..79a45d6 100755
--- a/Apple/NetworkExtension/libburrow/build-rust.sh
+++ b/Apple/NetworkExtension/libburrow/build-rust.sh
@@ -3,7 +3,7 @@
# This is a build script. It is run by Xcode as a build step.
# The type of build is described in various environment variables set by Xcode.
-export PATH="${PATH}:${HOME}/.cargo/bin:/opt/homebrew/bin:/usr/local/bin:/etc/profiles/per-user/${USER}/bin"
+export PATH="${PATH}:${HOME}/.cargo/bin:${HOME}/.nix-profile/bin:/opt/homebrew/bin:/usr/local/bin:/etc/profiles/per-user/${USER}/bin:/run/current-system/sw/bin:/nix/var/nix/profiles/default/bin"
if ! [[ -x "$(command -v cargo)" ]]; then
echo 'error: Unable to find cargo'
@@ -63,13 +63,13 @@ else
fi
if [[ -x "$(command -v rustup)" ]]; then
- CARGO_PATH="$(dirname $(rustup which cargo)):/usr/bin"
+ CARGO_PATH="$(dirname "$(rustup which cargo)"):/usr/bin"
else
- CARGO_PATH="$(dirname $(readlink -f $(which cargo))):/usr/bin"
+ CARGO_PATH="$(dirname "$(readlink -f "$(command -v cargo)")"):/usr/bin"
fi
-PROTOC=$(readlink -f $(which protoc))
-CARGO_PATH="$(dirname $PROTOC):$CARGO_PATH"
+PROTOC="$(readlink -f "$(command -v 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.
diff --git a/Apple/UI/BurrowView.swift b/Apple/UI/BurrowView.swift
index e15d3f7..bc9e361 100644
--- a/Apple/UI/BurrowView.swift
+++ b/Apple/UI/BurrowView.swift
@@ -1,5 +1,6 @@
import BurrowConfiguration
import Foundation
+import GRPC
import SwiftUI
#if canImport(AuthenticationServices)
import AuthenticationServices
@@ -15,6 +16,7 @@ public struct BurrowView: View {
@State private var accountStore = NetworkAccountStore()
@State private var activeSheet: ConfigurationSheet?
@State private var didRunAutomation = false
+ @State private var expandedProxySubscriptionID: Int32?
public var body: some View {
NavigationStack {
@@ -37,6 +39,9 @@ public struct BurrowView: View {
Button("Add WireGuard Network") {
activeSheet = .wireGuard
}
+ Button("Import Proxy Subscription") {
+ activeSheet = .proxySubscription
+ }
Button("Save Tor Account") {
activeSheet = .tor
}
@@ -67,8 +72,22 @@ public struct BurrowView: View {
Text(connectionError)
.font(.footnote)
.foregroundStyle(.secondary)
+ .lineLimit(4)
+ .truncationMode(.middle)
+ }
+ NetworkCarouselView(
+ networks: networkViewModel.cards,
+ selectedNetworkID: expandedProxySubscriptionID
+ ) { network in
+ guard networkViewModel.proxySubscriptions.contains(where: { $0.id == network.id }) else {
+ return
+ }
+ withAnimation(.snappy) {
+ expandedProxySubscriptionID = expandedProxySubscriptionID == network.id
+ ? nil
+ : network.id
+ }
}
- NetworkCarouselView(networks: networkViewModel.cards)
}
if showsAccountsSection {
@@ -119,6 +138,15 @@ public struct BurrowView: View {
accountStore: accountStore
)
}
+ .sheet(isPresented: proxySubscriptionSheetBinding) {
+ ProxySubscriptionManagerView(
+ networks: networkViewModel.proxySubscriptions,
+ networkViewModel: networkViewModel,
+ selectedNetworkID: $expandedProxySubscriptionID
+ )
+ .frame(width: 820, height: 600)
+ .padding(20)
+ }
.onAppear {
runAutomationIfNeeded()
}
@@ -157,15 +185,29 @@ public struct BurrowView: View {
}
}
+ private var proxySubscriptionSheetBinding: Binding {
+ Binding(
+ get: { expandedProxySubscriptionID != nil },
+ set: { isPresented in
+ guard !isPresented else { return }
+ expandedProxySubscriptionID = nil
+ }
+ )
+ }
+
@ViewBuilder
private func sectionHeader(title: String, detail: String?) -> some View {
VStack(alignment: .leading, spacing: 4) {
Text(title)
.font(.title2.weight(.semibold))
+ .lineLimit(1)
+ .truncationMode(.tail)
if let detail, !detail.isEmpty {
Text(detail)
.font(.subheadline)
.foregroundStyle(.secondary)
+ .lineLimit(3)
+ .truncationMode(.tail)
}
}
}
@@ -190,13 +232,14 @@ public struct BurrowView: View {
#if os(iOS)
!accountStore.accounts.isEmpty
#else
- true
+ !accountStore.accounts.isEmpty || networkViewModel.networks.isEmpty
#endif
}
}
private enum ConfigurationSheet: String, CaseIterable, Identifiable {
case wireGuard
+ case proxySubscription
case tor
case tailnet
@@ -205,6 +248,7 @@ private enum ConfigurationSheet: String, CaseIterable, Identifiable {
var kind: AccountNetworkKind {
switch self {
case .wireGuard: .wireGuard
+ case .proxySubscription: .proxySubscription
case .tor: .tor
case .tailnet: .tailnet
}
@@ -214,6 +258,8 @@ private enum ConfigurationSheet: String, CaseIterable, Identifiable {
switch self {
case .wireGuard:
"wave.3.right"
+ case .proxySubscription:
+ "link.badge.plus"
case .tor:
"shield.lefthalf.filled.badge.checkmark"
case .tailnet:
@@ -225,6 +271,8 @@ private enum ConfigurationSheet: String, CaseIterable, Identifiable {
switch self {
case .wireGuard:
"WireGuard"
+ case .proxySubscription:
+ "Proxy Subscription"
case .tor:
"Tor"
case .tailnet:
@@ -236,6 +284,8 @@ private enum ConfigurationSheet: String, CaseIterable, Identifiable {
switch self {
case .wireGuard:
"Import a tunnel"
+ case .proxySubscription:
+ "Import Trojan or Shadowsocks"
case .tor:
"Save an Arti profile"
case .tailnet:
@@ -247,6 +297,8 @@ private enum ConfigurationSheet: String, CaseIterable, Identifiable {
switch self {
case .wireGuard:
.blue
+ case .proxySubscription:
+ .teal
case .tor, .tailnet:
kind.accentColor
}
@@ -286,6 +338,8 @@ private struct AccountDraft {
var accountName = ""
var identityName = ""
var wireGuardConfig = ""
+ var proxySubscriptionURL = ""
+ var proxySubscriptionName = ""
var discoveryEmail = ""
var authority = ""
@@ -304,6 +358,8 @@ private struct AccountDraft {
switch sheet {
case .wireGuard:
break
+ case .proxySubscription:
+ title = "Proxy Subscription"
case .tor:
title = "Default Tor"
accountName = "default"
@@ -338,6 +394,11 @@ private struct ConfigurationSheetView: View {
@State private var tailnetLoginError: String?
@State private var tailnetLoginSessionID: String?
@State private var isStartingTailnetLogin = false
+ @State private var proxySubscriptionPreview: ProxySubscriptionPreview?
+ @State private var proxySubscriptionPreviewError: String?
+ @State private var isPreviewingProxySubscription = false
+ @State private var selectedProxySubscriptionOrdinal: Int32?
+ @State private var proxySubscriptionNodeFilter = ""
@State private var tailnetPresentedAuthURL: URL?
@State private var preserveTailnetLoginSession = false
@State private var usesCustomTailnetAuthority = false
@@ -374,31 +435,11 @@ private struct ConfigurationSheetView: View {
}
}
- switch sheet {
- case .wireGuard:
- Section("WireGuard Configuration") {
- TextEditor(text: $draft.wireGuardConfig)
- .font(.body.monospaced())
- .frame(minHeight: wireGuardEditorHeight)
- .contextMenu {
- wireGuardContextActions
- }
- }
- case .tor:
- Section("Tor Preferences") {
- TextField("Virtual Addresses", text: $draft.torAddresses)
- TextField("DNS Resolvers", text: $draft.torDNS)
- TextField("MTU", text: $draft.torMTU)
- TextField("Transparent Listener", text: $draft.torListen)
- }
- case .tailnet:
- tailnetSections
- }
+ configurationSections
if let errorMessage {
Section {
- Text(errorMessage)
- .foregroundStyle(.red)
+ feedbackText(errorMessage, color: .red)
}
}
}
@@ -445,7 +486,8 @@ private struct ConfigurationSheetView: View {
}
}
#if os(macOS)
- .frame(minWidth: 520, minHeight: 620)
+ .frame(width: 520)
+ .frame(minHeight: 620)
#endif
.safeAreaInset(edge: .bottom) {
if showsBottomActionButton {
@@ -473,6 +515,12 @@ private struct ConfigurationSheetView: View {
await cancelTailnetLoginIfNeeded()
}
}
+ .onChange(of: draft.proxySubscriptionURL) { _, _ in
+ proxySubscriptionPreview = nil
+ proxySubscriptionPreviewError = nil
+ selectedProxySubscriptionOrdinal = nil
+ proxySubscriptionNodeFilter = ""
+ }
.onDisappear {
tailnetLoginPollTask?.cancel()
tailnetDiscoveryTask?.cancel()
@@ -486,6 +534,71 @@ private struct ConfigurationSheetView: View {
}
}
+ @ViewBuilder
+ private var configurationSections: some View {
+ switch sheet {
+ case .wireGuard:
+ wireGuardConfigurationSection
+ case .proxySubscription:
+ proxySubscriptionSections
+ case .tor:
+ torPreferenceSection
+ case .tailnet:
+ tailnetSections
+ }
+ }
+
+ private var wireGuardConfigurationSection: some View {
+ Section("WireGuard Configuration") {
+ TextEditor(text: $draft.wireGuardConfig)
+ .font(.body.monospaced())
+ .frame(minHeight: wireGuardEditorHeight)
+ .contextMenu {
+ wireGuardContextActions
+ }
+ }
+ }
+
+ @ViewBuilder
+ private var proxySubscriptionSections: some View {
+ Section("Subscription") {
+ TextField("Subscription URL", text: $draft.proxySubscriptionURL)
+ .autocorrectionDisabled()
+ TextField("Name", text: $draft.proxySubscriptionName)
+ }
+
+ Section("Preview") {
+ proxySubscriptionPreviewButton
+
+ if let proxySubscriptionPreview {
+ proxySubscriptionPreviewView(proxySubscriptionPreview)
+ } else if let proxySubscriptionPreviewError {
+ feedbackText(proxySubscriptionPreviewError, color: .red)
+ }
+ }
+ }
+
+ private var proxySubscriptionPreviewButton: some View {
+ Button {
+ previewProxySubscription()
+ } label: {
+ Label(
+ isPreviewingProxySubscription ? "Previewing" : "Preview",
+ systemImage: isPreviewingProxySubscription ? "hourglass" : "eye"
+ )
+ }
+ .disabled(isPreviewingProxySubscription || normalizedOptional(draft.proxySubscriptionURL) == nil)
+ }
+
+ private var torPreferenceSection: some View {
+ Section("Tor Preferences") {
+ TextField("Virtual Addresses", text: $draft.torAddresses)
+ TextField("DNS Resolvers", text: $draft.torDNS)
+ TextField("MTU", text: $draft.torMTU)
+ TextField("Transparent Listener", text: $draft.torListen)
+ }
+ }
+
@ViewBuilder
private var identityFields: some View {
TextField("Title", text: $draft.title)
@@ -792,6 +905,143 @@ private struct ConfigurationSheetView: View {
)
}
+ private func proxySubscriptionPreviewView(_ preview: ProxySubscriptionPreview) -> some View {
+ VStack(alignment: .leading, spacing: 8) {
+ labeledValue("Format", preview.detectedFormat)
+ labeledValue("Compatible", "\(preview.compatibleCount)")
+ labeledValue("Runtime Supported", "\(preview.nodes.filter { $0.runtimeSupported }.count)")
+ labeledValue("Rejected", "\(preview.rejectedCount)")
+
+ let selectableNodes = supportedProxySubscriptionNodes(in: preview)
+ if !selectableNodes.isEmpty {
+ TextField("Filter nodes", text: $proxySubscriptionNodeFilter)
+ .autocorrectionDisabled()
+
+ let filteredNodes = filteredProxySubscriptionNodes(selectableNodes)
+ proxySubscriptionNodeList(nodes: filteredNodes, preview: preview)
+
+ if !proxySubscriptionNodeFilter.isEmpty && filteredNodes.isEmpty {
+ feedbackText("No nodes match the current filter.", color: .orange)
+ }
+ } else if !preview.nodes.isEmpty {
+ Text("No previewed nodes are supported by the packet runtime.")
+ .font(.caption)
+ .foregroundStyle(.orange)
+ }
+
+ ForEach(preview.warnings, id: \.self) { warning in
+ feedbackText(warning, color: .orange)
+ }
+ }
+ }
+
+ private func proxySubscriptionNodeList(
+ nodes: [ProxySubscriptionNodePreview],
+ preview: ProxySubscriptionPreview
+ ) -> some View {
+ ScrollView {
+ LazyVStack(alignment: .leading, spacing: 6) {
+ ForEach(nodes) { node in
+ proxySubscriptionNodeRow(
+ node: node,
+ isSelected: selectedProxySubscriptionNode(in: preview)?.ordinal == node.ordinal
+ )
+ }
+ }
+ .padding(.vertical, 2)
+ }
+ .frame(maxHeight: 260)
+ }
+
+ private func proxySubscriptionNodeRow(
+ node: ProxySubscriptionNodePreview,
+ isSelected: Bool
+ ) -> some View {
+ Button {
+ selectedProxySubscriptionOrdinal = node.ordinal
+ } label: {
+ HStack(spacing: 8) {
+ Image(systemName: isSelected ? "checkmark.circle.fill" : "circle")
+ .foregroundStyle(isSelected ? Color.accentColor : Color.secondary)
+ .imageScale(.small)
+ .frame(width: 16)
+
+ VStack(alignment: .leading, spacing: 2) {
+ HStack(spacing: 6) {
+ Text(node.name)
+ .font(.footnote.weight(isSelected ? .semibold : .regular))
+ .lineLimit(1)
+ .truncationMode(.tail)
+ Text(node.proxyProtocol.uppercased())
+ .font(.caption2.weight(.medium))
+ .foregroundStyle(.secondary)
+ .padding(.horizontal, 5)
+ .padding(.vertical, 2)
+ .background(
+ Capsule()
+ .fill(.secondary.opacity(0.12))
+ )
+ }
+
+ Text("\(node.server):\(node.port)")
+ .font(.caption)
+ .foregroundStyle(.secondary)
+ .lineLimit(1)
+ .truncationMode(.middle)
+
+ }
+
+ Spacer(minLength: 0)
+ }
+ .padding(.horizontal, 10)
+ .padding(.vertical, 8)
+ .frame(maxWidth: .infinity, alignment: .leading)
+ .background(
+ RoundedRectangle(cornerRadius: 8)
+ .fill(isSelected ? Color.accentColor.opacity(0.14) : Color.secondary.opacity(0.08))
+ )
+ .overlay(
+ RoundedRectangle(cornerRadius: 8)
+ .stroke(isSelected ? Color.accentColor.opacity(0.45) : Color.clear, lineWidth: 1)
+ )
+ }
+ .buttonStyle(.plain)
+ }
+
+ private func supportedProxySubscriptionNodes(
+ in preview: ProxySubscriptionPreview
+ ) -> [ProxySubscriptionNodePreview] {
+ preview.nodes.filter { $0.runtimeSupported }
+ }
+
+ private func filteredProxySubscriptionNodes(
+ _ nodes: [ProxySubscriptionNodePreview]
+ ) -> [ProxySubscriptionNodePreview] {
+ let query = proxySubscriptionNodeFilter.trimmingCharacters(in: .whitespacesAndNewlines)
+ guard !query.isEmpty else { return nodes }
+ return nodes.filter { node in
+ node.name.localizedCaseInsensitiveContains(query)
+ || node.server.localizedCaseInsensitiveContains(query)
+ || node.proxyProtocol.localizedCaseInsensitiveContains(query)
+ }
+ }
+
+ private func selectedProxySubscriptionNode(in preview: ProxySubscriptionPreview) -> ProxySubscriptionNodePreview? {
+ let ordinal = selectedProxySubscriptionOrdinal
+ ?? preview.nodes.first(where: { $0.runtimeSupported })?.ordinal
+ return preview.nodes.first { $0.ordinal == ordinal }
+ }
+
+ private func feedbackText(_ message: String, color: Color) -> some View {
+ Text(message)
+ .font(.caption)
+ .foregroundStyle(color)
+ .lineLimit(6)
+ .truncationMode(.middle)
+ .frame(maxWidth: .infinity, alignment: .leading)
+ .fixedSize(horizontal: false, vertical: true)
+ }
+
@ViewBuilder
private var bottomActionBar: some View {
VStack(spacing: 0) {
@@ -827,6 +1077,12 @@ private struct ConfigurationSheetView: View {
}
.disabled(draft.wireGuardConfig.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty)
+ case .proxySubscription:
+ Button("Preview Subscription") {
+ previewProxySubscription()
+ }
+ .disabled(normalizedOptional(draft.proxySubscriptionURL) == nil)
+
case .tor:
Menu("Presets") {
Button("Recommended Tor Defaults") {
@@ -883,6 +1139,8 @@ private struct ConfigurationSheetView: View {
switch sheet {
case .wireGuard:
.blue
+ case .proxySubscription:
+ .teal
case .tor, .tailnet:
sheet.kind.accentColor
}
@@ -892,6 +1150,8 @@ private struct ConfigurationSheetView: View {
switch sheet {
case .wireGuard:
"Import WireGuard"
+ case .proxySubscription:
+ "Import Proxy Subscription"
case .tor:
"Configure Tor"
case .tailnet:
@@ -911,6 +1171,8 @@ private struct ConfigurationSheetView: View {
switch sheet {
case .wireGuard, .tor:
return true
+ case .proxySubscription:
+ return false
case .tailnet:
return showsAdvancedTailnetSettings
}
@@ -928,6 +1190,8 @@ private struct ConfigurationSheetView: View {
switch sheet {
case .wireGuard:
return "Add Network"
+ case .proxySubscription:
+ return "Import"
case .tor:
return "Save Account"
case .tailnet:
@@ -942,7 +1206,7 @@ private struct ConfigurationSheetView: View {
return normalizedOptional(draft.authority) == nil
}
return false
- case .wireGuard, .tor:
+ case .wireGuard, .tor, .proxySubscription:
return true
}
}
@@ -951,6 +1215,9 @@ private struct ConfigurationSheetView: View {
switch sheet {
case .wireGuard:
return draft.wireGuardConfig.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty
+ case .proxySubscription:
+ return normalizedOptional(draft.proxySubscriptionURL) == nil
+ || proxySubscriptionPreview?.nodes.contains(where: { $0.runtimeSupported }) != true
case .tor:
return normalizedOptional(draft.accountName) == nil || normalizedOptional(draft.identityName) == nil
case .tailnet:
@@ -1025,6 +1292,9 @@ private struct ConfigurationSheetView: View {
case .wireGuard:
try await submitWireGuard()
dismiss()
+ case .proxySubscription:
+ try await submitProxySubscription()
+ dismiss()
case .tor:
try submitTor()
dismiss()
@@ -1032,7 +1302,7 @@ private struct ConfigurationSheetView: View {
try await submitTailnet()
}
} catch {
- errorMessage = error.localizedDescription
+ errorMessage = displayError(error)
}
}
}
@@ -1062,6 +1332,44 @@ private struct ConfigurationSheetView: View {
try accountStore.upsert(record, secret: nil)
}
+ private func submitProxySubscription() async throws {
+ let url = normalized(draft.proxySubscriptionURL, fallback: "")
+ let name = normalized(draft.proxySubscriptionName, fallback: "Proxy Subscription")
+ let selectedOrdinal = selectedProxySubscriptionOrdinal
+ ?? proxySubscriptionPreview?.nodes.first(where: { $0.runtimeSupported })?.ordinal
+ _ = try await networkViewModel.importProxySubscription(
+ url: url,
+ name: name,
+ selectedOrdinal: selectedOrdinal
+ )
+ }
+
+ private func previewProxySubscription() {
+ guard let url = normalizedOptional(draft.proxySubscriptionURL) else {
+ proxySubscriptionPreviewError = "Enter a subscription URL before previewing."
+ return
+ }
+ isPreviewingProxySubscription = true
+ proxySubscriptionPreviewError = nil
+ Task { @MainActor in
+ defer { isPreviewingProxySubscription = false }
+ do {
+ let preview = try await networkViewModel.previewProxySubscription(url: url)
+ proxySubscriptionPreview = preview
+ selectedProxySubscriptionOrdinal = preview.nodes.first(where: { $0.runtimeSupported })?.ordinal
+ proxySubscriptionNodeFilter = ""
+ if draft.proxySubscriptionName.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
+ draft.proxySubscriptionName = preview.suggestedName
+ }
+ } catch {
+ proxySubscriptionPreview = nil
+ selectedProxySubscriptionOrdinal = nil
+ proxySubscriptionNodeFilter = ""
+ proxySubscriptionPreviewError = displayError(error)
+ }
+ }
+ }
+
private func submitTor() throws {
let title = titleOrFallback("Tor \(normalized(draft.identityName, fallback: "apple"))")
let note = [
@@ -1529,6 +1837,8 @@ private struct ConfigurationSheetView: View {
.foregroundStyle(.secondary)
Text(value)
.font(.body.monospaced())
+ .lineLimit(3)
+ .truncationMode(.middle)
}
}
}
@@ -1543,9 +1853,11 @@ private struct AccountRowView: View {
VStack(alignment: .leading, spacing: 4) {
Text(account.title)
.font(.headline)
+ .lineLimit(1)
+ .truncationMode(.tail)
Text(account.kind.title)
- .font(.subheadline)
- .foregroundStyle(account.kind.accentColor)
+ .font(.subheadline)
+ .foregroundStyle(account.kind.accentColor)
}
Spacer()
if hasSecret {
@@ -1578,6 +1890,8 @@ private struct AccountRowView: View {
Text(note)
.font(.footnote)
.foregroundStyle(.secondary)
+ .lineLimit(3)
+ .truncationMode(.middle)
}
}
.padding()
@@ -1596,11 +1910,378 @@ private struct AccountRowView: View {
.foregroundStyle(.secondary)
Text(value)
.font(.body.monospaced())
+ .lineLimit(3)
+ .truncationMode(.middle)
+ }
+ }
+}
+
+private struct ProxySubscriptionManagerView: View {
+ @Environment(\.tunnel)
+ private var tunnel: any Tunnel
+
+ let networks: [ProxySubscriptionNetwork]
+ let networkViewModel: NetworkViewModel
+ @Binding var selectedNetworkID: Int32?
+
+ @State private var nodeFilter = ""
+ @State private var operationID: String?
+ @State private var feedback: String?
+ @State private var errorMessage: String?
+ @State private var pendingDelete: ProxySubscriptionNetwork?
+
+ var body: some View {
+ if let network = selectedNetwork {
+ VStack(alignment: .leading, spacing: 16) {
+ HStack(alignment: .top, spacing: 12) {
+ VStack(alignment: .leading, spacing: 4) {
+ Text("Proxy Subscription")
+ .font(.caption.weight(.medium))
+ .foregroundStyle(.secondary)
+ Text(network.name)
+ .font(.title3.weight(.semibold))
+ .lineLimit(1)
+ .truncationMode(.tail)
+ if let selectedNode = network.selectedNode {
+ Text("\(selectedNode.proxyProtocol.uppercased()) \(selectedNode.server):\(selectedNode.port)")
+ .font(.caption)
+ .foregroundStyle(.secondary)
+ .lineLimit(1)
+ .truncationMode(.middle)
+ }
+ }
+
+ Spacer(minLength: 0)
+
+ HStack(spacing: 8) {
+ Button {
+ refresh(network)
+ } label: {
+ Label("Refresh", systemImage: "arrow.clockwise")
+ }
+ .disabled(isBusy)
+
+ Button(role: .destructive) {
+ pendingDelete = network
+ } label: {
+ Label("Remove", systemImage: "trash")
+ }
+ .disabled(isBusy)
+ }
+ .burrowGlassControl()
+ .controlSize(.small)
+
+ Button {
+ withAnimation(.snappy) {
+ selectedNetworkID = nil
+ }
+ } label: {
+ Image(systemName: "xmark.circle.fill")
+ .font(.title3)
+ .foregroundStyle(.secondary)
+ }
+ .buttonStyle(.plain)
+ .accessibilityLabel("Close proxy subscription picker")
+ }
+
+ if networks.count > 1 {
+ Picker("Subscription", selection: selectedNetworkIDBinding) {
+ ForEach(networks) { network in
+ Text(network.name).tag(network.id)
+ }
+ }
+ .pickerStyle(.menu)
+ }
+
+ HStack(spacing: 16) {
+ Label(network.detectedFormat, systemImage: "doc.text")
+ Label("\(network.runtimeSupportedNodes.count) usable of \(network.nodes.count) nodes", systemImage: "circle.grid.2x1")
+ }
+ .font(.caption)
+ .foregroundStyle(.secondary)
+
+ TextField("Filter nodes", text: $nodeFilter)
+ .textFieldStyle(.roundedBorder)
+ .autocorrectionDisabled()
+
+ HStack {
+ Text("Nodes")
+ .font(.subheadline.weight(.semibold))
+ Spacer()
+ Text("\(filteredNodes(for: network).count) shown")
+ .font(.caption)
+ .foregroundStyle(.secondary)
+ }
+
+ ScrollView {
+ LazyVGrid(columns: nodeColumns, alignment: .leading, spacing: 10) {
+ ForEach(filteredNodes(for: network)) { node in
+ nodeTile(node, in: network)
+ }
+ }
+ .padding(.vertical, 2)
+ }
+ .frame(maxHeight: 410)
+ .scrollIndicators(.visible)
+
+ if let feedback {
+ Text(feedback)
+ .font(.caption)
+ .foregroundStyle(.secondary)
+ .lineLimit(2)
+ .truncationMode(.tail)
+ }
+
+ if let errorMessage {
+ Text(errorMessage)
+ .font(.caption)
+ .foregroundStyle(.red)
+ .lineLimit(3)
+ .truncationMode(.middle)
+ }
+ }
+ .frame(maxWidth: .infinity, alignment: .leading)
+ .onAppear {
+ ensureSelectedNetwork()
+ }
+ .onChange(of: networks) { _, _ in
+ ensureSelectedNetwork()
+ }
+ .confirmationDialog(
+ "Remove proxy subscription?",
+ isPresented: Binding(
+ get: { pendingDelete != nil },
+ set: { if !$0 { pendingDelete = nil } }
+ ),
+ presenting: pendingDelete
+ ) { network in
+ Button("Remove \(network.name)", role: .destructive) {
+ delete(network)
+ }
+ }
+ }
+ }
+
+ private var selectedNetwork: ProxySubscriptionNetwork? {
+ guard let selectedNetworkID else {
+ return nil
+ }
+ return networks.first(where: { $0.id == selectedNetworkID })
+ }
+
+ private var selectedNetworkIDBinding: Binding {
+ Binding(
+ get: { selectedNetwork?.id ?? networks.first?.id ?? 0 },
+ set: { selectedNetworkID = $0 }
+ )
+ }
+
+ private var isBusy: Bool {
+ operationID != nil
+ }
+
+ private var nodeColumns: [GridItem] {
+ [
+ GridItem(
+ .adaptive(minimum: 245, maximum: 320),
+ spacing: 10,
+ alignment: .top
+ )
+ ]
+ }
+
+ private func ensureSelectedNetwork() {
+ guard let selectedNetworkID else {
+ return
+ }
+ if !networks.contains(where: { $0.id == selectedNetworkID }) {
+ self.selectedNetworkID = nil
+ }
+ }
+
+ private func filteredNodes(for network: ProxySubscriptionNetwork) -> [ProxySubscriptionNetworkNode] {
+ let query = nodeFilter.trimmingCharacters(in: .whitespacesAndNewlines).lowercased()
+ guard !query.isEmpty else {
+ return network.nodes
+ }
+ return network.nodes.filter { node in
+ node.name.lowercased().contains(query)
+ || node.server.lowercased().contains(query)
+ || node.proxyProtocol.lowercased().contains(query)
+ }
+ }
+
+ private func nodeTile(
+ _ node: ProxySubscriptionNetworkNode,
+ in network: ProxySubscriptionNetwork
+ ) -> some View {
+ let isSelected = network.selectedNode?.ordinal == node.ordinal
+ return Button {
+ select(node, in: network)
+ } label: {
+ VStack(alignment: .leading, spacing: 10) {
+ HStack(alignment: .top, spacing: 8) {
+ Image(systemName: isSelected ? "checkmark.circle.fill" : "circle")
+ .foregroundStyle(isSelected ? Color.accentColor : Color.secondary)
+ .frame(width: 18)
+
+ VStack(alignment: .leading, spacing: 4) {
+ Text(node.name)
+ .font(.footnote.weight(isSelected ? .semibold : .regular))
+ .lineLimit(1)
+ .truncationMode(.tail)
+
+ HStack(spacing: 6) {
+ Text(node.proxyProtocol.uppercased())
+ .font(.caption2.weight(.medium))
+ .foregroundStyle(.secondary)
+ .padding(.horizontal, 5)
+ .padding(.vertical, 2)
+ .background(Capsule().fill(.secondary.opacity(0.12)))
+ if !node.runtimeSupported {
+ Text("UNSUPPORTED")
+ .font(.caption2.weight(.medium))
+ .foregroundStyle(.orange)
+ }
+ }
+ }
+
+ Spacer(minLength: 0)
+ }
+
+ Text("\(node.server):\(node.port)")
+ .font(.caption.monospaced())
+ .foregroundStyle(.secondary)
+ .lineLimit(1)
+ .truncationMode(.middle)
+
+ if operationID == operationIdentifier(networkID: network.id, ordinal: node.ordinal) {
+ ProgressView()
+ .controlSize(.small)
+ .frame(maxWidth: .infinity, alignment: .trailing)
+ }
+ }
+ .padding(12)
+ .frame(minHeight: 94, alignment: .topLeading)
+ .frame(maxWidth: .infinity, alignment: .leading)
+ .background(.thinMaterial, in: RoundedRectangle(cornerRadius: 12))
+ .background(
+ RoundedRectangle(cornerRadius: 12)
+ .fill(isSelected ? Color.accentColor.opacity(0.10) : Color.clear)
+ )
+ .overlay(
+ RoundedRectangle(cornerRadius: 12)
+ .stroke(isSelected ? Color.accentColor.opacity(0.65) : Color.secondary.opacity(0.16), lineWidth: 1)
+ )
+ }
+ .buttonStyle(.plain)
+ .disabled(isBusy || !node.runtimeSupported)
+ }
+
+ private func select(
+ _ node: ProxySubscriptionNetworkNode,
+ in network: ProxySubscriptionNetwork
+ ) {
+ let operationIdentifier = operationIdentifier(networkID: network.id, ordinal: node.ordinal)
+ operationID = operationIdentifier
+ feedback = nil
+ errorMessage = nil
+ Task { @MainActor in
+ defer { operationID = nil }
+ do {
+ _ = try await networkViewModel.updateProxySubscriptionSelection(
+ network: network,
+ selectedOrdinal: node.ordinal
+ )
+ if isTunnelRunning {
+ _ = try await networkViewModel.updateActiveProxySubscriptionSelection(
+ network: network,
+ selectedOrdinal: node.ordinal
+ )
+ feedback = "Selected \(node.name) for the active tunnel"
+ } else {
+ feedback = "Selected \(node.name)"
+ }
+ } catch {
+ errorMessage = displayError(error)
+ }
+ }
+ }
+
+ private func refresh(_ network: ProxySubscriptionNetwork) {
+ operationID = "refresh-\(network.id)"
+ feedback = nil
+ errorMessage = nil
+ Task { @MainActor in
+ defer { operationID = nil }
+ do {
+ _ = try await networkViewModel.refreshProxySubscription(network: network)
+ if isTunnelRunning {
+ _ = try await networkViewModel.refreshActiveProxySubscription(network: network)
+ feedback = "Refreshed \(network.name) for the active tunnel"
+ } else {
+ feedback = "Refreshed \(network.name)"
+ }
+ } catch {
+ errorMessage = displayError(error)
+ }
+ }
+ }
+
+ private func delete(_ network: ProxySubscriptionNetwork) {
+ operationID = "delete-\(network.id)"
+ feedback = nil
+ errorMessage = nil
+ Task { @MainActor in
+ defer { operationID = nil }
+ do {
+ try await networkViewModel.deleteNetwork(id: network.id)
+ pendingDelete = nil
+ selectedNetworkID = nil
+ feedback = nil
+ } catch {
+ errorMessage = displayError(error)
+ }
+ }
+ }
+
+ private func operationIdentifier(networkID: Int32, ordinal: Int32) -> String {
+ "select-\(networkID)-\(ordinal)"
+ }
+
+ @MainActor
+ private var isTunnelRunning: Bool {
+ switch tunnel.status {
+ case .connected, .reasserting:
+ true
+ default:
+ false
+ }
+ }
+
+ private func labeledValue(_ label: String, _ value: String) -> some View {
+ VStack(alignment: .leading, spacing: 2) {
+ Text(label)
+ .font(.caption)
+ .foregroundStyle(.secondary)
+ Text(value)
+ .font(.caption.monospaced())
+ .lineLimit(1)
+ .truncationMode(.tail)
}
}
}
private extension View {
+ @ViewBuilder
+ func burrowGlassControl() -> some View {
+ if #available(macOS 26.0, iOS 26.0, *) {
+ self.buttonStyle(.glass)
+ } else {
+ self.buttonStyle(.bordered)
+ }
+ }
+
@ViewBuilder
func burrowLoginField() -> some View {
#if os(iOS)
@@ -1674,6 +2355,13 @@ private final class TailnetBrowserAuthenticator {
}
#endif
+private func displayError(_ error: Error) -> String {
+ if let status = error as? GRPCStatus {
+ return status.message ?? status.description
+ }
+ return error.localizedDescription
+}
+
private struct BurrowAutomationConfig {
enum Action: String {
case tailnetLogin = "tailnet-login"
diff --git a/Apple/UI/NetworkCarouselView.swift b/Apple/UI/NetworkCarouselView.swift
index e7368db..d109672 100644
--- a/Apple/UI/NetworkCarouselView.swift
+++ b/Apple/UI/NetworkCarouselView.swift
@@ -2,6 +2,18 @@ import SwiftUI
struct NetworkCarouselView: View {
var networks: [NetworkCardModel]
+ var selectedNetworkID: Int32?
+ var onSelect: ((NetworkCardModel) -> Void)?
+
+ init(
+ networks: [NetworkCardModel],
+ selectedNetworkID: Int32? = nil,
+ onSelect: ((NetworkCardModel) -> Void)? = nil
+ ) {
+ self.networks = networks
+ self.selectedNetworkID = selectedNetworkID
+ self.onSelect = onSelect
+ }
var body: some View {
Group {
@@ -29,10 +41,19 @@ struct NetworkCarouselView: View {
.frame(maxWidth: .infinity, minHeight: 175)
#endif
} else {
+ #if os(macOS)
+ LazyVStack(alignment: .leading, spacing: 12) {
+ ForEach(networks) { network in
+ networkCard(network)
+ .frame(maxWidth: 560)
+ }
+ }
+ .frame(maxWidth: .infinity, alignment: .leading)
+ #else
ScrollView(.horizontal) {
LazyHStack {
ForEach(networks) { network in
- NetworkView(network: network)
+ networkCard(network)
.containerRelativeFrame(.horizontal, count: 10, span: 7, spacing: 0, alignment: .center)
.scrollTransition(.interactive, axis: .horizontal) { content, phase in
content
@@ -47,9 +68,33 @@ struct NetworkCarouselView: View {
.defaultScrollAnchor(.center)
.scrollTargetBehavior(.viewAligned)
.containerRelativeFrame(.horizontal)
+ #endif
}
}
}
+
+ @ViewBuilder
+ private func networkCard(_ network: NetworkCardModel) -> some View {
+ if let onSelect {
+ Button {
+ onSelect(network)
+ } label: {
+ NetworkView(network: network)
+ .overlay(selectionOverlay(for: network))
+ }
+ .buttonStyle(.plain)
+ } else {
+ NetworkView(network: network)
+ }
+ }
+
+ private func selectionOverlay(for network: NetworkCardModel) -> some View {
+ RoundedRectangle(cornerRadius: 10)
+ .stroke(
+ selectedNetworkID == network.id ? Color.accentColor : Color.clear,
+ lineWidth: 3
+ )
+ }
}
#if DEBUG
diff --git a/Apple/UI/NetworkExtensionTunnel.swift b/Apple/UI/NetworkExtensionTunnel.swift
index 23559f3..151e683 100644
--- a/Apple/UI/NetworkExtensionTunnel.swift
+++ b/Apple/UI/NetworkExtensionTunnel.swift
@@ -98,23 +98,33 @@ public final class NetworkExtensionTunnel: Tunnel {
}
}
- guard managers.isEmpty else { return }
-
- let manager = NETunnelProviderManager()
- manager.localizedDescription = "Burrow"
+ let manager = managers.first ?? NETunnelProviderManager()
+ var needsSave = managers.isEmpty
+ if manager.localizedDescription != "Burrow" {
+ manager.localizedDescription = "Burrow"
+ needsSave = true
+ }
let proto = NETunnelProviderProtocol()
proto.providerBundleIdentifier = bundleIdentifier
proto.serverAddress = "burrow.rs"
+ proto.proxySettings = nil
- manager.protocolConfiguration = proto
- try await manager.save()
+ if !manager.protocolConfiguration.isBurrowEquivalent(to: proto) {
+ manager.protocolConfiguration = proto
+ needsSave = true
+ }
+
+ if needsSave {
+ try await manager.save()
+ }
}
public func start() {
Task {
- guard let manager = try await NETunnelProviderManager.managers.first else { return }
do {
+ try await configure()
+ guard let manager = try await NETunnelProviderManager.managers.first else { return }
if !manager.isEnabled {
manager.isEnabled = true
try await manager.save()
@@ -149,6 +159,17 @@ public final class NetworkExtensionTunnel: Tunnel {
}
}
+private extension Optional where Wrapped == NEVPNProtocol {
+ func isBurrowEquivalent(to expected: NETunnelProviderProtocol) -> Bool {
+ guard let current = self as? NETunnelProviderProtocol else {
+ return false
+ }
+ return current.providerBundleIdentifier == expected.providerBundleIdentifier
+ && current.serverAddress == expected.serverAddress
+ && current.proxySettings == nil
+ }
+}
+
extension NEVPNConnection {
fileprivate var tunnelStatus: TunnelStatus {
switch status {
diff --git a/Apple/UI/Networks/Network.swift b/Apple/UI/Networks/Network.swift
index 35bd0e1..0f46406 100644
--- a/Apple/UI/Networks/Network.swift
+++ b/Apple/UI/Networks/Network.swift
@@ -53,6 +53,218 @@ struct TailnetLoginStatus: Sendable {
var health: [String]
}
+struct ProxySubscriptionNodePreview: Sendable, Identifiable {
+ var id: Int32 { ordinal }
+ var ordinal: Int32
+ var name: String
+ var proxyProtocol: String
+ var server: String
+ var port: Int32
+ var warnings: [String]
+ var runtimeSupported: Bool
+}
+
+struct ProxySubscriptionPreview: Sendable {
+ var suggestedName: String
+ var detectedFormat: String
+ var compatibleCount: Int32
+ var rejectedCount: Int32
+ var nodes: [ProxySubscriptionNodePreview]
+ var warnings: [String]
+}
+
+struct ProxySubscriptionNetwork: Sendable, Identifiable, Hashable {
+ var id: Int32
+ var name: String
+ var sourceURL: String
+ var detectedFormat: String
+ var selectedOrdinal: Int32?
+ var selectedName: String?
+ var nodes: [ProxySubscriptionNetworkNode]
+ var warnings: [String]
+
+ var selectedNode: ProxySubscriptionNetworkNode? {
+ selectedName
+ .flatMap { name in nodes.first { $0.name == name && $0.runtimeSupported } }
+ ?? selectedOrdinal
+ .flatMap { ordinal in nodes.first { $0.ordinal == ordinal && $0.runtimeSupported } }
+ ?? nodes.first { $0.runtimeSupported }
+ ?? nodes.first
+ }
+
+ var runtimeSupportedNodes: [ProxySubscriptionNetworkNode] {
+ nodes.filter(\.runtimeSupported)
+ }
+
+ init?(network: Burrow_Network) {
+ guard network.type == .proxySubscription,
+ let payload = try? JSONDecoder().decode(StoredProxySubscriptionPayload.self, from: network.payload)
+ else {
+ return nil
+ }
+ id = network.id
+ name = payload.name
+ sourceURL = payload.source.url
+ detectedFormat = payload.detectedFormat
+ selectedOrdinal = payload.selectedOrdinal.map(Int32.init)
+ selectedName = payload.selectedName
+ nodes = payload.nodes.map(ProxySubscriptionNetworkNode.init)
+ warnings = payload.warnings
+ }
+}
+
+struct ProxySubscriptionNetworkNode: Sendable, Identifiable, Hashable {
+ var id: Int32 { ordinal }
+ var ordinal: Int32
+ var name: String
+ var proxyProtocol: String
+ var server: String
+ var port: Int32
+ var warnings: [String]
+ var runtimeSupported: Bool
+
+ fileprivate init(stored: StoredProxySubscriptionNode) {
+ ordinal = Int32(stored.ordinal)
+ name = stored.name
+ proxyProtocol = stored.proxyProtocol
+ server = stored.server
+ port = Int32(stored.port)
+ warnings = stored.warnings
+ runtimeSupported = stored.config.runtimeSupported
+ }
+}
+
+private struct StoredProxySubscriptionPayload: Decodable {
+ var name: String
+ var source: StoredProxySubscriptionSource
+ var detectedFormat: String
+ var selectedOrdinal: Int?
+ var selectedName: String?
+ var nodes: [StoredProxySubscriptionNode]
+ var warnings: [String]
+
+ enum CodingKeys: String, CodingKey {
+ case name
+ case source
+ case detectedFormat = "detected_format"
+ case selectedOrdinal = "selected_ordinal"
+ case selectedName = "selected_name"
+ case nodes
+ case warnings
+ }
+}
+
+private struct StoredProxySubscriptionSource: Decodable {
+ var url: String
+}
+
+private struct StoredProxySubscriptionNode: Decodable {
+ var ordinal: Int
+ var name: String
+ var proxyProtocol: String
+ var server: String
+ var port: Int
+ var warnings: [String]
+ var config: StoredProxySubscriptionNodeConfig
+
+ enum CodingKeys: String, CodingKey {
+ case ordinal
+ case name
+ case proxyProtocol = "protocol"
+ case server
+ case port
+ case warnings
+ case config
+ }
+}
+
+private struct StoredProxySubscriptionNodeConfig: Decodable {
+ var type: String
+ var network: StoredProxySubscriptionNetworkTransport?
+ var cipher: String?
+ var plugin: StoredJSONValue?
+ var udpOverTCP: Bool?
+
+ var runtimeSupported: Bool {
+ switch type {
+ case "trojan":
+ return network?.isTrojanTCP ?? true
+ case "shadowsocks":
+ return plugin == nil
+ && udpOverTCP != true
+ && Self.supportedShadowsocksCiphers.contains(cipher?.lowercased() ?? "")
+ default:
+ return false
+ }
+ }
+
+ enum CodingKeys: String, CodingKey {
+ case type
+ case network
+ case cipher
+ case plugin
+ case udpOverTCP = "udp_over_tcp"
+ }
+
+ private static let supportedShadowsocksCiphers: Set = [
+ "aes-128-gcm",
+ "aead_aes_128_gcm",
+ "aes-256-gcm",
+ "aead_aes_256_gcm",
+ "chacha20-ietf-poly1305",
+ "chacha20-poly1305",
+ "aead_chacha20_poly1305",
+ ]
+}
+
+private enum StoredProxySubscriptionNetworkTransport: Decodable, Hashable {
+ case named(String)
+ case keyed(String)
+
+ var isTrojanTCP: Bool {
+ switch self {
+ case .named(let value), .keyed(let value):
+ return value == "tcp"
+ }
+ }
+
+ init(from decoder: Swift.Decoder) throws {
+ let container = try decoder.singleValueContainer()
+ if let value = try? container.decode(String.self) {
+ self = .named(value)
+ return
+ }
+ let object = try container.decode([String: StoredJSONValue].self)
+ self = .keyed(object.keys.first ?? "")
+ }
+}
+
+private enum StoredJSONValue: Decodable, Hashable {
+ case string(String)
+ case number(Double)
+ case bool(Bool)
+ case object([String: StoredJSONValue])
+ case array([StoredJSONValue])
+ case null
+
+ init(from decoder: Swift.Decoder) throws {
+ let container = try decoder.singleValueContainer()
+ if container.decodeNil() {
+ self = .null
+ } else if let value = try? container.decode(Bool.self) {
+ self = .bool(value)
+ } else if let value = try? container.decode(Double.self) {
+ self = .number(value)
+ } else if let value = try? container.decode(String.self) {
+ self = .string(value)
+ } else if let value = try? container.decode([StoredJSONValue].self) {
+ self = .array(value)
+ } else {
+ self = .object(try container.decode([String: StoredJSONValue].self))
+ }
+ }
+}
+
enum TailnetDiscoveryClient {
static func discover(email: String, socketURL: URL) async throws -> TailnetDiscoveryResponse {
var request = Burrow_TailnetDiscoverRequest()
@@ -139,6 +351,76 @@ enum TailnetLoginClient {
}
}
+enum ProxySubscriptionImportClient {
+ static func preview(url: String, socketURL: URL) async throws -> ProxySubscriptionPreview {
+ var request = Burrow_ProxySubscriptionImportRequest()
+ request.url = url
+
+ let response = try await ProxySubscriptionClient.unix(socketURL: socketURL)
+ .previewImport(request)
+ return ProxySubscriptionPreview(
+ suggestedName: response.suggestedName,
+ detectedFormat: response.detectedFormat,
+ compatibleCount: response.compatibleCount,
+ rejectedCount: response.rejectedCount,
+ nodes: response.node.map { node in
+ ProxySubscriptionNodePreview(
+ ordinal: node.ordinal,
+ name: node.name,
+ proxyProtocol: node.`protocol`,
+ server: node.server,
+ port: node.port,
+ warnings: node.warnings,
+ runtimeSupported: node.runtimeSupported
+ )
+ },
+ warnings: response.warnings
+ )
+ }
+
+ static func apply(
+ id: Int32,
+ url: String,
+ name: String,
+ selectedOrdinal: Int32?,
+ socketURL: URL
+ ) async throws -> Int32 {
+ var request = Burrow_ProxySubscriptionApplyRequest()
+ request.id = id
+ request.url = url
+ request.name = name
+ request.selectedOrdinal = selectedOrdinal ?? -1
+ let response = try await ProxySubscriptionClient.unix(socketURL: socketURL)
+ .applyImport(request)
+ return response.id
+ }
+
+ static func selectNode(
+ id: Int32,
+ selectedOrdinal: Int32,
+ socketURL: URL
+ ) async throws -> Int32 {
+ var request = Burrow_ProxySubscriptionSelectRequest()
+ request.id = id
+ request.selectedOrdinal = selectedOrdinal
+ let response = try await ProxySubscriptionClient.unix(socketURL: socketURL)
+ .selectNode(request)
+ return response.selectedOrdinal
+ }
+}
+
+private struct DaemonUnavailableError: LocalizedError {
+ var socketPath: String
+
+ var errorDescription: String? {
+ "Burrow daemon is not reachable yet. Enable the tunnel or start the daemon, then try again."
+ }
+
+ var failureReason: String? {
+ "The daemon socket does not exist at \(socketPath)."
+ }
+}
+
@Observable
@MainActor
final class NetworkViewModel: Sendable {
@@ -161,6 +443,16 @@ final class NetworkViewModel: Sendable {
networks.map(Self.makeCard(for:))
}
+ var nonProxyCards: [NetworkCardModel] {
+ networks
+ .filter { $0.type != .proxySubscription }
+ .map(Self.makeCard(for:))
+ }
+
+ var proxySubscriptions: [ProxySubscriptionNetwork] {
+ networks.compactMap(ProxySubscriptionNetwork.init)
+ }
+
var nextNetworkID: Int32 {
(networks.map(\.id).max() ?? 0) + 1
}
@@ -173,13 +465,83 @@ final class NetworkViewModel: Sendable {
try await addNetwork(type: .tailnet, payload: payload.encoded())
}
+ func previewProxySubscription(url: String) async throws -> ProxySubscriptionPreview {
+ let socketURL = try daemonSocketURL()
+ return try await ProxySubscriptionImportClient.preview(url: url, socketURL: socketURL)
+ }
+
+ func importProxySubscription(url: String, name: String, selectedOrdinal: Int32?) async throws -> Int32 {
+ let socketURL = try daemonSocketURL()
+ let networkID = nextNetworkID
+ return try await ProxySubscriptionImportClient.apply(
+ id: networkID,
+ url: url,
+ name: name,
+ selectedOrdinal: selectedOrdinal,
+ socketURL: socketURL
+ )
+ }
+
+ func updateProxySubscriptionSelection(
+ network: ProxySubscriptionNetwork,
+ selectedOrdinal: Int32
+ ) async throws -> Int32 {
+ let socketURL = try daemonSocketURL()
+ return try await ProxySubscriptionImportClient.selectNode(
+ id: network.id,
+ selectedOrdinal: selectedOrdinal,
+ socketURL: socketURL
+ )
+ }
+
+ func updateActiveProxySubscriptionSelection(
+ network: ProxySubscriptionNetwork,
+ selectedOrdinal: Int32
+ ) async throws -> Int32 {
+ let socketURL = try Constants.packetTunnelSocketURL
+ return try await ProxySubscriptionImportClient.selectNode(
+ id: network.id,
+ selectedOrdinal: selectedOrdinal,
+ socketURL: socketURL
+ )
+ }
+
+ func refreshProxySubscription(network: ProxySubscriptionNetwork) async throws -> Int32 {
+ let socketURL = try daemonSocketURL()
+ return try await ProxySubscriptionImportClient.apply(
+ id: network.id,
+ url: network.sourceURL,
+ name: network.name,
+ selectedOrdinal: network.selectedNode?.ordinal,
+ socketURL: socketURL
+ )
+ }
+
+ func refreshActiveProxySubscription(network: ProxySubscriptionNetwork) async throws -> Int32 {
+ let socketURL = try Constants.packetTunnelSocketURL
+ return try await ProxySubscriptionImportClient.apply(
+ id: network.id,
+ url: network.sourceURL,
+ name: network.name,
+ selectedOrdinal: network.selectedNode?.ordinal,
+ socketURL: socketURL
+ )
+ }
+
+ func deleteNetwork(id: Int32) async throws {
+ let socketURL = try daemonSocketURL()
+ var request = Burrow_NetworkDeleteRequest()
+ request.id = id
+ _ = try await NetworksClient.unix(socketURL: socketURL).networkDelete(request)
+ }
+
func discoverTailnet(email: String) async throws -> TailnetDiscoveryResponse {
- let socketURL = try socketURLResult.get()
+ let socketURL = try daemonSocketURL()
return try await TailnetDiscoveryClient.discover(email: email, socketURL: socketURL)
}
func probeTailnetAuthority(_ authority: String) async throws -> TailnetAuthorityProbeStatus {
- let socketURL = try socketURLResult.get()
+ let socketURL = try daemonSocketURL()
return try await TailnetAuthorityProbeClient.probe(authority: authority, socketURL: socketURL)
}
@@ -189,7 +551,7 @@ final class NetworkViewModel: Sendable {
hostname: String?,
authority: String
) async throws -> TailnetLoginStatus {
- let socketURL = try socketURLResult.get()
+ let socketURL = try daemonSocketURL()
return try await TailnetLoginClient.start(
accountName: accountName,
identityName: identityName,
@@ -200,17 +562,17 @@ final class NetworkViewModel: Sendable {
}
func tailnetLoginStatus(sessionID: String) async throws -> TailnetLoginStatus {
- let socketURL = try socketURLResult.get()
+ let socketURL = try daemonSocketURL()
return try await TailnetLoginClient.status(sessionID: sessionID, socketURL: socketURL)
}
func cancelTailnetLogin(sessionID: String) async throws {
- let socketURL = try socketURLResult.get()
+ let socketURL = try daemonSocketURL()
try await TailnetLoginClient.cancel(sessionID: sessionID, socketURL: socketURL)
}
private func addNetwork(type: Burrow_NetworkType, payload: Data) async throws -> Int32 {
- let socketURL = try socketURLResult.get()
+ let socketURL = try daemonSocketURL()
let networkID = nextNetworkID
let request = Burrow_Network.with {
$0.id = networkID
@@ -223,12 +585,25 @@ final class NetworkViewModel: Sendable {
return networkID
}
+ private func daemonSocketURL() throws -> URL {
+ try Self.daemonSocketURL(from: socketURLResult)
+ }
+
+ private static func daemonSocketURL(from result: Result) throws -> URL {
+ let socketURL = try result.get()
+ let socketPath = socketURL.path(percentEncoded: false)
+ guard FileManager.default.fileExists(atPath: socketPath) else {
+ throw DaemonUnavailableError(socketPath: socketPath)
+ }
+ return socketURL
+ }
+
private func startStreaming() {
task?.cancel()
let socketURLResult = self.socketURLResult
task = Task { [weak self] in
do {
- let socketURL = try socketURLResult.get()
+ let socketURL = try Self.daemonSocketURL(from: socketURLResult)
let client = NetworksClient.unix(socketURL: socketURL)
for try await response in client.networkList(.init()) {
guard !Task.isCancelled else { return }
@@ -254,6 +629,14 @@ final class NetworkViewModel: Sendable {
WireGuardCard(network: network).card
case .tailnet:
TailnetCard(network: network).card
+ case .trojan:
+ unsupportedCard(
+ id: network.id,
+ title: "Legacy Trojan Proxy",
+ detail: "Unsupported SOCKS-era profile. Import a proxy subscription instead."
+ )
+ case .proxySubscription:
+ proxySubscriptionCard(network: network)
case .UNRECOGNIZED(let rawValue):
unsupportedCard(
id: network.id,
@@ -269,6 +652,76 @@ final class NetworkViewModel: Sendable {
}
}
+ private static func proxySubscriptionCard(network: Burrow_Network) -> NetworkCardModel {
+ guard let subscription = ProxySubscriptionNetwork(network: network) else {
+ return unsupportedCard(
+ id: network.id,
+ title: "Proxy Subscription",
+ detail: "Imported proxy subscription."
+ )
+ }
+ return NetworkCardModel(
+ id: subscription.id,
+ backgroundColor: .teal,
+ label: AnyView(
+ VStack(alignment: .leading, spacing: 10) {
+ HStack(alignment: .top) {
+ VStack(alignment: .leading, spacing: 4) {
+ Text("Proxy Subscription")
+ .font(.headline)
+ .foregroundStyle(.white.opacity(0.85))
+ Text(subscription.name)
+ .font(.title3.weight(.semibold))
+ .foregroundStyle(.white)
+ .lineLimit(1)
+ .truncationMode(.tail)
+ }
+ Spacer()
+ }
+ Spacer()
+ if let selectedNode = subscription.selectedNode {
+ VStack(alignment: .leading, spacing: 6) {
+ Text("Selected node")
+ .font(.caption.weight(.medium))
+ .foregroundStyle(.white.opacity(0.72))
+
+ HStack(spacing: 8) {
+ Text(selectedNode.name)
+ .font(.headline.weight(.semibold))
+ .foregroundStyle(.white)
+ .lineLimit(1)
+ .truncationMode(.tail)
+
+ Text(selectedNode.proxyProtocol.uppercased())
+ .font(.caption2.weight(.bold))
+ .foregroundStyle(.white.opacity(0.78))
+ .padding(.horizontal, 7)
+ .padding(.vertical, 3)
+ .background(
+ Capsule()
+ .fill(.white.opacity(0.18))
+ )
+ }
+
+ Text("\(selectedNode.server):\(selectedNode.port)")
+ .font(.caption.monospaced())
+ .foregroundStyle(.white.opacity(0.78))
+ .lineLimit(1)
+ .truncationMode(.middle)
+ }
+ }
+ Text("\(subscription.runtimeSupportedNodes.count) usable of \(subscription.nodes.count) nodes · \(subscription.detectedFormat)")
+ .font(.footnote)
+ .foregroundStyle(.white.opacity(0.78))
+ .lineLimit(1)
+ .truncationMode(.tail)
+ }
+ .padding()
+ .frame(maxWidth: .infinity, alignment: .leading)
+ )
+ )
+ }
+
private static func unsupportedCard(id: Int32, title: String, detail: String) -> NetworkCardModel {
NetworkCardModel(
id: id,
@@ -278,9 +731,13 @@ final class NetworkViewModel: Sendable {
Text(title)
.font(.title3.weight(.semibold))
.foregroundStyle(.white)
+ .lineLimit(2)
+ .truncationMode(.tail)
Text(detail)
.font(.body)
.foregroundStyle(.white.opacity(0.9))
+ .lineLimit(4)
+ .truncationMode(.tail)
Spacer()
Text("Network #\(id)")
.font(.footnote.monospaced())
@@ -358,6 +815,7 @@ enum TailnetProvider: String, CaseIterable, Codable, Identifiable, Sendable {
enum AccountNetworkKind: String, CaseIterable, Codable, Identifiable, Sendable {
case wireGuard
+ case proxySubscription
case tor
case tailnet
@@ -366,6 +824,7 @@ enum AccountNetworkKind: String, CaseIterable, Codable, Identifiable, Sendable {
var title: String {
switch self {
case .wireGuard: "WireGuard"
+ case .proxySubscription: "Proxy Subscription"
case .tor: "Tor"
case .tailnet: "Tailnet"
}
@@ -374,6 +833,7 @@ enum AccountNetworkKind: String, CaseIterable, Codable, Identifiable, Sendable {
var subtitle: String {
switch self {
case .wireGuard: "Import a tunnel and optional account metadata."
+ case .proxySubscription: "Import Trojan and Shadowsocks subscription nodes."
case .tor: "Store Arti account and identity preferences."
case .tailnet: "Save Tailnet authority, identity defaults, and login material."
}
@@ -382,6 +842,7 @@ enum AccountNetworkKind: String, CaseIterable, Codable, Identifiable, Sendable {
var accentColor: Color {
switch self {
case .wireGuard: .init("WireGuard")
+ case .proxySubscription: .teal
case .tor: .orange
case .tailnet: .mint
}
@@ -390,6 +851,7 @@ enum AccountNetworkKind: String, CaseIterable, Codable, Identifiable, Sendable {
var actionTitle: String {
switch self {
case .wireGuard: "Add Network"
+ case .proxySubscription: "Import"
case .tor: "Save Account"
case .tailnet: "Save Account"
}
@@ -397,7 +859,7 @@ enum AccountNetworkKind: String, CaseIterable, Codable, Identifiable, Sendable {
var availabilityNote: String? {
switch self {
- case .wireGuard:
+ case .wireGuard, .proxySubscription:
nil
case .tor:
"Tor account preferences are stored on Apple now. The managed Tor runtime is not wired on Apple in this branch yet."
diff --git a/Cargo.lock b/Cargo.lock
index 2950701..53e4faf 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -437,6 +437,28 @@ version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
+[[package]]
+name = "aws-lc-rs"
+version = "1.16.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ec6fb3fe69024a75fa7e1bfb48aa6cf59706a101658ea01bfd33b2b248a038f"
+dependencies = [
+ "aws-lc-sys",
+ "zeroize",
+]
+
+[[package]]
+name = "aws-lc-sys"
+version = "0.40.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f50037ee5e1e41e7b8f9d161680a725bd1626cb6f8c7e901f91f942850852fe7"
+dependencies = [
+ "cc",
+ "cmake",
+ "dunce",
+ "fs_extra",
+]
+
[[package]]
name = "axum"
version = "0.6.20"
@@ -733,6 +755,8 @@ dependencies = [
"libc",
"libsystemd",
"log",
+ "md-5",
+ "native-tls",
"netstack-smoltcp",
"nix 0.27.1",
"once_cell",
@@ -745,12 +769,17 @@ dependencies = [
"ring",
"rusqlite",
"rust-ini",
+ "rustls",
"schemars 0.8.22",
"serde",
"serde_json",
+ "serde_yaml",
+ "sha2",
"subtle",
"tempfile",
"tokio",
+ "tokio-native-tls",
+ "tokio-rustls",
"tokio-stream",
"tokio-util",
"toml 0.8.23",
@@ -764,6 +793,7 @@ dependencies = [
"tracing-oslog",
"tracing-subscriber",
"tun",
+ "webpki-roots 0.26.11",
"x25519-dalek",
]
@@ -835,9 +865,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
[[package]]
name = "cc"
-version = "1.2.38"
+version = "1.2.61"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "80f41ae168f955c12fb8960b057d70d0ca153fb83182b57d86380443527be7e9"
+checksum = "d16d90359e986641506914ba71350897565610e87ce0ad9e6f28569db3dd5c6d"
dependencies = [
"find-msvc-tools",
"jobserver",
@@ -991,6 +1021,15 @@ version = "0.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675"
+[[package]]
+name = "cmake"
+version = "0.1.58"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c0f78a02292a74a88ac736019ab962ece0bc380e3f977bf72e376c5d78ff0678"
+dependencies = [
+ "cc",
+]
+
[[package]]
name = "coarsetime"
version = "0.1.37"
@@ -1669,6 +1708,12 @@ version = "2.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "117240f60069e65410b3ae1bb213295bd828f707b5bec6596a1afc8793ce0cbc"
+[[package]]
+name = "dunce"
+version = "1.0.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813"
+
[[package]]
name = "dyn-clone"
version = "1.0.20"
@@ -1951,9 +1996,9 @@ dependencies = [
[[package]]
name = "find-msvc-tools"
-version = "0.1.2"
+version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1ced73b1dacfc750a6db6c0a0c3a3853c8b41997e2e2c563dc90804ae6867959"
+checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
[[package]]
name = "fixedbitset"
@@ -2034,6 +2079,12 @@ dependencies = [
"walkdir",
]
+[[package]]
+name = "fs_extra"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c"
+
[[package]]
name = "fslock"
version = "0.2.1"
@@ -2566,7 +2617,7 @@ dependencies = [
"tokio",
"tokio-rustls",
"tower-service",
- "webpki-roots",
+ "webpki-roots 1.0.3",
]
[[package]]
@@ -3170,6 +3221,16 @@ version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94"
+[[package]]
+name = "md-5"
+version = "0.10.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf"
+dependencies = [
+ "cfg-if",
+ "digest",
+]
+
[[package]]
name = "memchr"
version = "2.7.5"
@@ -4484,7 +4545,7 @@ dependencies = [
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
- "webpki-roots",
+ "webpki-roots 1.0.3",
]
[[package]]
@@ -4645,6 +4706,8 @@ version = "0.23.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a9586e9ee2b4f8fab52a0048ca7334d7024eef48e2cb9407e3497bb7cab7fa7"
dependencies = [
+ "aws-lc-rs",
+ "log",
"once_cell",
"ring",
"rustls-pki-types",
@@ -4678,6 +4741,7 @@ version = "0.103.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2ffdfa2f5286e2247234e03f680868ac2815974dc39e00ea15adc445d0aafe52"
dependencies = [
+ "aws-lc-rs",
"ring",
"rustls-pki-types",
"untrusted",
@@ -4984,6 +5048,19 @@ dependencies = [
"syn 2.0.106",
]
+[[package]]
+name = "serde_yaml"
+version = "0.9.34+deprecated"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47"
+dependencies = [
+ "indexmap 2.11.4",
+ "itoa",
+ "ryu",
+ "serde",
+ "unsafe-libyaml",
+]
+
[[package]]
name = "sha-1"
version = "0.10.1"
@@ -6954,9 +7031,9 @@ dependencies = [
[[package]]
name = "typenum"
-version = "1.18.0"
+version = "1.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f"
+checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de"
[[package]]
name = "uncased"
@@ -7007,6 +7084,12 @@ dependencies = [
"subtle",
]
+[[package]]
+name = "unsafe-libyaml"
+version = "0.2.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861"
+
[[package]]
name = "untrusted"
version = "0.9.0"
@@ -7265,6 +7348,15 @@ dependencies = [
"wasm-bindgen",
]
+[[package]]
+name = "webpki-roots"
+version = "0.26.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9"
+dependencies = [
+ "webpki-roots 1.0.3",
+]
+
[[package]]
name = "webpki-roots"
version = "1.0.3"
diff --git a/Scripts/burrow-doctor b/Scripts/burrow-doctor
new file mode 100755
index 0000000..a364e59
--- /dev/null
+++ b/Scripts/burrow-doctor
@@ -0,0 +1,434 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+usage() {
+ cat <<'EOF'
+Usage: Scripts/burrow-doctor [--last 10m]
+
+Print a single stdout diagnostics report for the CURRENT macOS networking state.
+
+Run this while Burrow is the active VPN but routing/websites are broken, then
+redirect stdout to a file before switching to a working VPN:
+
+ Scripts/burrow-doctor --last 10m > burrow-broken.txt
+
+The report focuses on live VPN/proxy/DNS/route state, Burrow NetworkExtension
+state, socket ownership, the selected Burrow node, and recent Burrow logs.
+It intentionally does not redact local output, because broken proxy state often
+depends on exact subscription and node configuration.
+EOF
+}
+
+last="10m"
+while [[ $# -gt 0 ]]; do
+ case "$1" in
+ --last)
+ [[ $# -ge 2 ]] || { echo "--last requires a value" >&2; exit 2; }
+ last="$2"
+ shift 2
+ ;;
+ -h|--help)
+ usage
+ exit 0
+ ;;
+ *)
+ echo "unknown argument: $1" >&2
+ usage >&2
+ exit 2
+ ;;
+ esac
+done
+
+repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
+group_id="${BURROW_APP_GROUP_ID:-2KPBQHRJ2D.com.tianruichen.burrow}"
+group_dir="${BURROW_APP_GROUP_DIR:-$HOME/Library/Group Containers/$group_id}"
+db_path="${BURROW_DB_PATH:-$group_dir/burrow.db}"
+runtime_log="${BURROW_RUNTIME_LOG:-$group_dir/burrow-runtime.log}"
+bundle_id="${BURROW_BUNDLE_ID:-com.tianruichen.burrow}"
+extension_process="${BURROW_EXTENSION_PROCESS:-BurrowNetworkExtension}"
+
+section() {
+ printf '\n## %s\n\n' "$1"
+}
+
+cmd() {
+ printf '$'
+ printf ' %q' "$@"
+ printf '\n'
+}
+
+run() {
+ local title="$1"
+ shift
+ section "$title"
+ cmd "$@"
+ printf '\n'
+ "$@" 2>&1 || printf '\n[burrow-doctor] command exited with status %s\n' "$?"
+}
+
+run_zsh() {
+ local title="$1"
+ local script="$2"
+ section "$title"
+ printf '$ %s\n\n' "$script"
+ /bin/zsh -lc "$script" 2>&1 || printf '\n[burrow-doctor] command exited with status %s\n' "$?"
+}
+
+run_python() {
+ if command -v uv >/dev/null 2>&1; then
+ uv run python "$@"
+ elif command -v python3 >/dev/null 2>&1; then
+ python3 "$@"
+ else
+ return 127
+ fi
+}
+
+capture() {
+ "$@" 2>&1 || true
+}
+
+nc_list="$(capture scutil --nc list)"
+proxy_state="$(capture scutil --proxy)"
+dns_state="$(capture scutil --dns)"
+inet_routes="$(capture netstat -rn -f inet)"
+proxy_listeners="$(capture /bin/zsh -lc "lsof -nP -iTCP:6152 -iTCP:6153 -iTCP:7890 -iTCP:7891 -iTCP:7897 -iTCP:1080 -iTCP:1087 -sTCP:LISTEN")"
+
+cat < dict | None:
+ nodes = payload.get("nodes") or []
+ selected_name = payload.get("selected_name")
+ selected_ordinal = payload.get("selected_ordinal")
+ for node in nodes:
+ if selected_name is not None and node.get("name") == selected_name:
+ return node
+ if selected_ordinal is not None and node.get("ordinal") == selected_ordinal:
+ return node
+ return nodes[0] if nodes else None
+
+
+conn = sqlite3.connect(db_path)
+conn.row_factory = sqlite3.Row
+row = conn.execute(
+ "select payload from network where type = 'ProxySubscription' order by idx limit 1"
+).fetchone()
+if row is None:
+ print("No ProxySubscription network in database.")
+ raise SystemExit
+
+payload_blob = row["payload"]
+payload_text = payload_blob.decode("utf-8", errors="replace") if isinstance(payload_blob, bytes) else payload_blob
+payload = json.loads(payload_text)
+node = choose_node(payload)
+if not node:
+ print("ProxySubscription has no nodes.")
+ raise SystemExit
+
+server = node.get("server")
+port = int(node.get("port"))
+print(f"selected_proxy_server={server}:{port}")
+
+addresses = []
+try:
+ for family, socktype, proto, canonname, sockaddr in socket.getaddrinfo(server, port, type=socket.SOCK_STREAM):
+ host, resolved_port = sockaddr[:2]
+ key = (host, resolved_port)
+ if key not in addresses:
+ addresses.append(key)
+except Exception as exc:
+ print(f"resolve_error={type(exc).__name__}: {exc}")
+ raise SystemExit
+
+print("resolved_addresses=" + ",".join(f"{host}:{resolved_port}" for host, resolved_port in addresses))
+
+for host, _ in addresses:
+ print(f"\n$ route -n get {host}")
+ try:
+ result = subprocess.run(
+ ["route", "-n", "get", host],
+ text=True,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT,
+ timeout=5,
+ )
+ print(result.stdout.rstrip())
+ print(f"[exit={result.returncode}]")
+ except Exception as exc:
+ print(f"route_probe_error={type(exc).__name__}: {exc}")
+
+for host, resolved_port in addresses:
+ started = time.monotonic()
+ try:
+ with socket.create_connection((host, resolved_port), timeout=5):
+ elapsed_ms = (time.monotonic() - started) * 1000
+ print(f"tcp_connect_ok address={host}:{resolved_port} elapsed_ms={elapsed_ms:.1f}")
+ except Exception as exc:
+ elapsed_ms = (time.monotonic() - started) * 1000
+ print(f"tcp_connect_error address={host}:{resolved_port} elapsed_ms={elapsed_ms:.1f} error={type(exc).__name__}: {exc}")
+PY
+else
+ echo "DB not found: $db_path"
+fi
+
+run_zsh "Route Probes" "for host in 1.1.1.1 8.8.8.8 google.com; do echo; echo \"\$ route -n get \$host\"; route -n get \"\$host\" 2>&1 || true; done"
+
+run_zsh "DNS Probes" "if command -v dig >/dev/null 2>&1; then dig +time=3 +tries=1 google.com A; echo; dig +time=3 +tries=1 @1.1.1.1 google.com A; else dscacheutil -q host -a name google.com; fi"
+
+run_zsh "Scoped Physical DNS Probes" "if command -v dig >/dev/null 2>&1; then servers=\$(scutil --dns | awk 'BEGIN { scoped=0; current=\"\" } /^DNS configuration \\(for scoped queries\\)/ { scoped=1; next } scoped && /^resolver #/ { current=\"\"; next } scoped && /nameserver\\[[0-9]+\\]/ { current=\$3 } scoped && /if_index/ && \$0 !~ /utun/ && current != \"\" { print current; current=\"\" }'); if [[ -z \"\$servers\" ]]; then echo 'No scoped non-utun DNS servers found.'; else for server in \$servers; do echo; echo \"\$ dig +time=3 +tries=1 @\$server google.com A\"; dig +time=3 +tries=1 @\"\$server\" google.com A; done; fi; else echo 'dig not found'; fi"
+
+run_zsh "TCP Probes" "for target in '1.1.1.1 53' '1.1.1.1 443' 'google.com 443'; do set -- \$target; echo; echo \"\$ nc -vz -w 5 \$1 \$2\"; nc -vz -w 5 \"\$1\" \"\$2\" 2>&1 || true; done"
+
+run_zsh "HTTP Probes" "for url in 'http://www.gstatic.com/generate_204' 'https://www.google.com/generate_204' 'https://www.cloudflare.com/cdn-cgi/trace'; do echo; echo \"\$ curl -4 -I --connect-timeout 5 --max-time 10 \$url\"; curl -4 -I --connect-timeout 5 --max-time 10 \"\$url\" 2>&1 || true; done"
+
+run_zsh "Burrow Controlled Proxy Self-Test" "if [[ -x '$repo_root/Scripts/burrow-proxy-selftest' ]]; then '$repo_root/Scripts/burrow-proxy-selftest'; else echo 'Scripts/burrow-proxy-selftest not found or not executable'; fi"
+
+run_zsh "Burrow Direct Provider Proxy Ladder" "if [[ -x '$repo_root/Scripts/burrow-proxy-ladder' ]]; then '$repo_root/Scripts/burrow-proxy-ladder' --db '$db_path' --resolve-server doh-all --target google.com:80 --http-host google.com --skip-udp --timeout 8; else echo 'Scripts/burrow-proxy-ladder not found or not executable'; fi"
+
+run_zsh "Burrow Daemon Packet Proxy Probe" "if [[ -x '$repo_root/target/debug/burrow' ]]; then BURROW_SOCKET_PATH=\"\${BURROW_SOCKET_PATH:-burrow.sock}\" '$repo_root/target/debug/burrow' proxy-tcp-probe --dns-name google.com --remote 1.1.1.1:80 --timeout-ms 12000; else echo 'target/debug/burrow not found; run cargo build -p burrow first'; fi"
+
+run_zsh "Burrow Daemon HTTPS Packet Proxy Probe" "if [[ -x '$repo_root/target/debug/burrow' ]]; then BURROW_SOCKET_PATH=\"\${BURROW_SOCKET_PATH:-burrow.sock}\" '$repo_root/target/debug/burrow' proxy-https-probe --dns-name news.ycombinator.com --remote 1.1.1.1:443 --timeout-ms 20000; else echo 'target/debug/burrow not found; run cargo build -p burrow first'; fi"
+
+section "Proxy Subscription Endpoint Status"
+if [[ -f "$db_path" ]]; then
+ run_python - "$db_path" <<'PY'
+from __future__ import annotations
+
+import json
+import sqlite3
+import sys
+import urllib.error
+import urllib.request
+
+db_path = sys.argv[1]
+
+conn = sqlite3.connect(db_path)
+conn.row_factory = sqlite3.Row
+row = conn.execute(
+ "select payload from network where type = 'ProxySubscription' order by idx limit 1"
+).fetchone()
+if row is None:
+ print("No ProxySubscription network in database.")
+ raise SystemExit
+
+payload_blob = row["payload"]
+payload_text = payload_blob.decode("utf-8", errors="replace") if isinstance(payload_blob, bytes) else payload_blob
+payload = json.loads(payload_text)
+url = (payload.get("source") or {}).get("url")
+if not url:
+ print("ProxySubscription has no source URL.")
+ raise SystemExit
+
+print("source_url_redacted=yes")
+request = urllib.request.Request(url, headers={"User-Agent": "mihomo/1.18.3"})
+try:
+ with urllib.request.urlopen(request, timeout=20) as response:
+ body = response.read(1024 * 1024)
+ print(f"status={response.status}")
+ for key, value in response.headers.items():
+ lower = key.lower()
+ if (
+ lower in {"subscription-userinfo", "profile-update-interval", "profile-title", "content-type"}
+ or "expire" in lower
+ or "subscription" in lower
+ ):
+ print(f"header_{key}={value}")
+ print(f"body_bytes={len(body)}")
+except urllib.error.HTTPError as exc:
+ print(f"status={exc.code}")
+ print(f"reason={exc.reason}")
+ for key, value in exc.headers.items():
+ lower = key.lower()
+ if (
+ lower in {"subscription-userinfo", "profile-update-interval", "profile-title", "content-type"}
+ or "expire" in lower
+ or "subscription" in lower
+ ):
+ print(f"header_{key}={value}")
+ body = exc.read(1000).decode("utf-8", errors="replace").strip()
+ if body:
+ print(f"body_prefix={body[:200]}")
+except Exception as exc:
+ print(f"fetch_error={type(exc).__name__}: {exc}")
+PY
+else
+ echo "DB not found: $db_path"
+fi
+
+section "Burrow Runtime Log File"
+if [[ -f "$runtime_log" ]]; then
+ cmd tail -400 "$runtime_log"
+ printf '\n'
+ tail -400 "$runtime_log" 2>&1 || printf '\n[burrow-doctor] command exited with status %s\n' "$?"
+else
+ echo "Runtime log not found: $runtime_log"
+fi
+
+section "Recent Burrow NetworkExtension Logs"
+cmd /usr/bin/log show --last "$last" --style compact --info --debug --predicate "process == \"nesessionmanager\" AND eventMessage CONTAINS[c] \"Burrow\""
+printf '\n'
+{ /usr/bin/log show --last "$last" --style compact --info --debug --predicate "process == \"nesessionmanager\" AND eventMessage CONTAINS[c] \"Burrow\"" 2>&1 \
+ || printf '\n[burrow-doctor] log command exited with status %s\n' "$?"; }
+
+section "Recent Burrow Extension Logs"
+cmd /usr/bin/log show --last "$last" --style compact --info --debug --predicate "process == \"$extension_process\" OR eventMessage CONTAINS[c] \"$bundle_id.network\""
+printf '\n'
+{ /usr/bin/log show --last "$last" --style compact --info --debug --predicate "process == \"$extension_process\" OR eventMessage CONTAINS[c] \"$bundle_id.network\"" 2>&1 \
+ || printf '\n[burrow-doctor] log command exited with status %s\n' "$?"; }
+
+section "Recent Burrow Errors"
+cmd /usr/bin/log show --last "$last" --style compact --info --debug --predicate "((process CONTAINS[c] \"Burrow\") OR (eventMessage CONTAINS[c] \"$bundle_id\") OR (eventMessage CONTAINS[c] \"Trojan\") OR (eventMessage CONTAINS[c] \"proxy\")) AND (messageType == error OR messageType == fault OR eventMessage CONTAINS[c] \"failed\" OR eventMessage CONTAINS[c] \"error\" OR eventMessage CONTAINS[c] \"panic\")"
+printf '\n'
+{ /usr/bin/log show --last "$last" --style compact --info --debug --predicate "((process CONTAINS[c] \"Burrow\") OR (eventMessage CONTAINS[c] \"$bundle_id\") OR (eventMessage CONTAINS[c] \"Trojan\") OR (eventMessage CONTAINS[c] \"proxy\")) AND (messageType == error OR messageType == fault OR eventMessage CONTAINS[c] \"failed\" OR eventMessage CONTAINS[c] \"error\" OR eventMessage CONTAINS[c] \"panic\")" 2>&1 \
+ || printf '\n[burrow-doctor] log command exited with status %s\n' "$?"; }
diff --git a/Scripts/burrow-proxy-ladder b/Scripts/burrow-proxy-ladder
new file mode 100755
index 0000000..9c1da3d
--- /dev/null
+++ b/Scripts/burrow-proxy-ladder
@@ -0,0 +1,576 @@
+#!/usr/bin/env python3
+"""Probe a Burrow proxy subscription node without enabling system proxying.
+
+This script intentionally bypasses the Burrow daemon, Network Extension,
+routes, DNS settings, and browsers. It loads the selected proxy node from the
+Burrow SQLite database and speaks the provider protocol directly.
+"""
+
+from __future__ import annotations
+
+import argparse
+import hashlib
+import ipaddress
+import json
+import os
+import socket
+import sqlite3
+import ssl
+import struct
+import sys
+import time
+import urllib.parse
+import urllib.request
+from dataclasses import dataclass
+from pathlib import Path
+from typing import Any
+
+
+DEFAULT_APP_GROUP = "2KPBQHRJ2D.com.tianruichen.burrow"
+DEFAULT_DB = (
+ Path.home()
+ / "Library"
+ / "Group Containers"
+ / DEFAULT_APP_GROUP
+ / "burrow.db"
+)
+DEFAULT_TARGET = "1.1.1.1:80"
+DEFAULT_HTTP_HOST = "one.one.one.one"
+DEFAULT_DNS_SERVER = "1.1.1.1:53"
+DEFAULT_DNS_NAME = "google.com"
+DEFAULT_TIMEOUT = 5.0
+DOH_PROVIDERS = {
+ "doh-google": "https://dns.google/resolve",
+ "doh-cloudflare": "https://cloudflare-dns.com/dns-query",
+}
+
+TROJAN_CMD_CONNECT = 0x01
+TROJAN_CMD_UDP_ASSOCIATE = 0x03
+
+
+@dataclass(frozen=True)
+class Endpoint:
+ host: str
+ port: int
+
+
+@dataclass(frozen=True)
+class ProbeResult:
+ ok: bool
+ detail: str
+
+
+def parse_endpoint(value: str, default_port: int | None = None) -> Endpoint:
+ text = value.strip()
+ if not text:
+ raise ValueError("endpoint must not be empty")
+
+ if text.startswith("["):
+ end = text.find("]")
+ if end == -1:
+ raise ValueError(f"invalid bracketed IPv6 endpoint: {value}")
+ host = text[1:end]
+ rest = text[end + 1 :]
+ if rest.startswith(":"):
+ port = int(rest[1:])
+ elif default_port is not None:
+ port = default_port
+ else:
+ raise ValueError(f"endpoint is missing a port: {value}")
+ return Endpoint(host, port)
+
+ if text.count(":") == 1:
+ host, port_text = text.rsplit(":", 1)
+ return Endpoint(host, int(port_text))
+
+ try:
+ ipaddress.ip_address(text)
+ except ValueError:
+ if ":" in text:
+ if default_port is None:
+ raise ValueError(f"IPv6 endpoint is missing a port: {value}")
+ return Endpoint(text, default_port)
+ if default_port is None:
+ raise ValueError(f"endpoint is missing a port: {value}")
+ return Endpoint(text, default_port)
+
+ if default_port is None:
+ raise ValueError(f"endpoint is missing a port: {value}")
+ return Endpoint(text, default_port)
+
+
+def encode_socks_addr(endpoint: Endpoint) -> bytes:
+ try:
+ ip = ipaddress.ip_address(endpoint.host)
+ except ValueError:
+ host = endpoint.host.encode("idna")
+ if len(host) > 255:
+ raise ValueError(f"SOCKS domain is too long: {endpoint.host}")
+ return bytes([3, len(host)]) + host + struct.pack("!H", endpoint.port)
+
+ if ip.version == 4:
+ return bytes([1]) + ip.packed + struct.pack("!H", endpoint.port)
+ return bytes([4]) + ip.packed + struct.pack("!H", endpoint.port)
+
+
+def read_exact(sock: ssl.SSLSocket, length: int) -> bytes:
+ chunks: list[bytes] = []
+ remaining = length
+ while remaining:
+ chunk = sock.recv(remaining)
+ if not chunk:
+ raise EOFError(f"expected {remaining} more bytes")
+ chunks.append(chunk)
+ remaining -= len(chunk)
+ return b"".join(chunks)
+
+
+def read_socks_addr(sock: ssl.SSLSocket) -> Endpoint:
+ atyp = read_exact(sock, 1)[0]
+ if atyp == 1:
+ host = str(ipaddress.IPv4Address(read_exact(sock, 4)))
+ elif atyp == 4:
+ host = str(ipaddress.IPv6Address(read_exact(sock, 16)))
+ elif atyp == 3:
+ length = read_exact(sock, 1)[0]
+ host = read_exact(sock, length).decode("idna")
+ else:
+ raise ValueError(f"unsupported SOCKS address type in response: {atyp}")
+ port = struct.unpack("!H", read_exact(sock, 2))[0]
+ return Endpoint(host, port)
+
+
+def trojan_password_hash(password: str) -> bytes:
+ return hashlib.sha224(password.encode()).hexdigest().encode()
+
+
+def trojan_header(password: str, command: int, target: Endpoint) -> bytes:
+ return (
+ trojan_password_hash(password)
+ + b"\r\n"
+ + bytes([command])
+ + encode_socks_addr(target)
+ + b"\r\n"
+ )
+
+
+def trojan_udp_frame(target: Endpoint, payload: bytes) -> bytes:
+ return encode_socks_addr(target) + struct.pack("!H", len(payload)) + b"\r\n" + payload
+
+
+def build_dns_query(name: str) -> tuple[int, bytes]:
+ query_id = int(time.time() * 1000) & 0xFFFF
+ labels = [label for label in name.rstrip(".").split(".") if label]
+ qname = b"".join(bytes([len(label)]) + label.encode("ascii") for label in labels) + b"\x00"
+ header = struct.pack("!HHHHHH", query_id, 0x0100, 1, 0, 0, 0)
+ question = qname + struct.pack("!HH", 1, 1)
+ return query_id, header + question
+
+
+def parse_dns_response(query_id: int, payload: bytes) -> str:
+ if len(payload) < 12:
+ return f"truncated DNS response: {len(payload)} bytes"
+ response_id, flags, questions, answers, authority, additional = struct.unpack(
+ "!HHHHHH", payload[:12]
+ )
+ rcode = flags & 0x000F
+ qr = bool(flags & 0x8000)
+ id_note = "matching id" if response_id == query_id else f"id mismatch {response_id:#06x}"
+ return (
+ f"{len(payload)} bytes, qr={qr}, rcode={rcode}, answers={answers}, "
+ f"authority={authority}, additional={additional}, questions={questions}, {id_note}"
+ )
+
+
+def load_payload(db_path: Path, network_id: int | None) -> tuple[int, dict[str, Any]]:
+ if not db_path.exists():
+ raise FileNotFoundError(f"Burrow database not found: {db_path}")
+ conn = sqlite3.connect(str(db_path))
+ conn.row_factory = sqlite3.Row
+ try:
+ if network_id is None:
+ row = conn.execute(
+ "select id,payload from network where type = 'ProxySubscription' order by idx limit 1"
+ ).fetchone()
+ else:
+ row = conn.execute(
+ "select id,payload from network where id = ? and type = 'ProxySubscription'",
+ (network_id,),
+ ).fetchone()
+ finally:
+ conn.close()
+ if row is None:
+ selector = "first ProxySubscription" if network_id is None else f"ProxySubscription id={network_id}"
+ raise LookupError(f"could not find {selector} in {db_path}")
+ payload_blob = row["payload"]
+ if isinstance(payload_blob, bytes):
+ payload_text = payload_blob.decode("utf-8", errors="replace")
+ else:
+ payload_text = payload_blob or "{}"
+ return int(row["id"]), json.loads(payload_text)
+
+
+def choose_node(
+ payload: dict[str, Any],
+ node_name: str | None = None,
+ node_ordinal: int | None = None,
+) -> dict[str, Any]:
+ nodes = payload.get("nodes") or []
+ if node_name is not None:
+ for node in nodes:
+ if node.get("name") == node_name:
+ return node
+ raise LookupError(f"ProxySubscription payload has no node named {node_name!r}")
+ if node_ordinal is not None:
+ for node in nodes:
+ if node.get("ordinal") == node_ordinal:
+ return node
+ raise LookupError(f"ProxySubscription payload has no node with ordinal {node_ordinal}")
+
+ selected_name = payload.get("selected_name")
+ selected_ordinal = payload.get("selected_ordinal")
+ for node in nodes:
+ if selected_name is not None and node.get("name") == selected_name:
+ return node
+ if selected_ordinal is not None and node.get("ordinal") == selected_ordinal:
+ return node
+ if nodes:
+ return nodes[0]
+ raise LookupError("ProxySubscription payload has no compatible nodes")
+
+
+def resolved_stream_addresses(endpoint: Endpoint) -> list[tuple[int, tuple[Any, ...]]]:
+ addresses: list[tuple[int, tuple[Any, ...]]] = []
+ seen: set[tuple[Any, ...]] = set()
+ for family, _, _, _, sockaddr in socket.getaddrinfo(
+ endpoint.host, endpoint.port, type=socket.SOCK_STREAM
+ ):
+ key = (family, sockaddr)
+ if key not in seen:
+ seen.add(key)
+ addresses.append((family, sockaddr))
+ return addresses
+
+
+def resolve_doh(host: str, port: int, provider: str, timeout: float) -> list[Endpoint]:
+ base = DOH_PROVIDERS[provider]
+ url = base + "?" + urllib.parse.urlencode({"name": host, "type": "A"})
+ request = urllib.request.Request(
+ url,
+ headers={"Accept": "application/dns-json", "User-Agent": "burrow-proxy-ladder"},
+ )
+ with urllib.request.urlopen(request, timeout=timeout) as response:
+ body = json.load(response)
+ endpoints: list[Endpoint] = []
+ for answer in body.get("Answer") or []:
+ if answer.get("type") != 1:
+ continue
+ address = str(answer.get("data") or "")
+ try:
+ ipaddress.IPv4Address(address)
+ except ValueError:
+ continue
+ endpoint = Endpoint(address, port)
+ if endpoint not in endpoints:
+ endpoints.append(endpoint)
+ return endpoints
+
+
+def resolve_doh_all(host: str, port: int, timeout: float) -> list[Endpoint]:
+ endpoints: list[Endpoint] = []
+ errors: list[str] = []
+ for provider in DOH_PROVIDERS:
+ try:
+ provider_endpoints = resolve_doh(host, port, provider, timeout)
+ except BaseException as exc:
+ errors.append(f"{provider}={type(exc).__name__}: {exc}")
+ continue
+ print(
+ f"{provider}_answers="
+ + ",".join(f"{endpoint.host}:{endpoint.port}" for endpoint in provider_endpoints)
+ )
+ for endpoint in provider_endpoints:
+ if endpoint not in endpoints:
+ endpoints.append(endpoint)
+ if errors:
+ print("doh_warnings=" + "; ".join(errors))
+ return endpoints
+
+
+def is_fake_ip(host: str) -> bool:
+ try:
+ ip = ipaddress.ip_address(host)
+ except ValueError:
+ return False
+ return ip in ipaddress.ip_network("198.18.0.0/15")
+
+
+def server_hostname(server: str, sni: str | None) -> str | None:
+ value = sni or server
+ if not value:
+ return None
+ return value
+
+
+def tls_context(config: dict[str, Any]) -> ssl.SSLContext:
+ skip_verify = bool(config.get("skip_cert_verify"))
+ if skip_verify:
+ context = ssl._create_unverified_context()
+ else:
+ context = ssl.create_default_context()
+
+ alpn_present = bool(config.get("alpn_present"))
+ alpn = config.get("alpn") or []
+ if not alpn_present:
+ alpn = ["h2", "http/1.1"]
+ if alpn:
+ context.set_alpn_protocols([str(value) for value in alpn])
+ return context
+
+
+def connect_tls(
+ connect_endpoint: Endpoint,
+ tls_server_name: str,
+ node: dict[str, Any],
+ timeout: float,
+) -> ssl.SSLSocket:
+ config = node["config"]
+ context = tls_context(config)
+ sni = server_hostname(tls_server_name, config.get("sni"))
+ last_error: BaseException | None = None
+ for _family, sockaddr in resolved_stream_addresses(connect_endpoint):
+ started = time.monotonic()
+ raw_sock: socket.socket | None = None
+ try:
+ raw_sock = socket.create_connection(sockaddr, timeout=timeout)
+ raw_sock.settimeout(timeout)
+ tls_sock = context.wrap_socket(raw_sock, server_hostname=sni)
+ tls_sock.settimeout(timeout)
+ elapsed_ms = (time.monotonic() - started) * 1000
+ peer = sockaddr[0] if isinstance(sockaddr, tuple) else str(sockaddr)
+ print(
+ f" tls_connect_ok address={peer}:{connect_endpoint.port} "
+ f"elapsed_ms={elapsed_ms:.1f} alpn={tls_sock.selected_alpn_protocol()!r}"
+ )
+ return tls_sock
+ except BaseException as exc:
+ last_error = exc
+ elapsed_ms = (time.monotonic() - started) * 1000
+ peer = sockaddr[0] if isinstance(sockaddr, tuple) else str(sockaddr)
+ print(
+ f" tls_connect_error address={peer}:{connect_endpoint.port} "
+ f"elapsed_ms={elapsed_ms:.1f} error={type(exc).__name__}: {exc}"
+ )
+ if raw_sock is not None:
+ raw_sock.close()
+ assert last_error is not None
+ raise last_error
+
+
+def probe_trojan_tcp(
+ connect_endpoint: Endpoint,
+ tls_server_name: str,
+ node: dict[str, Any],
+ target: Endpoint,
+ http_host: str,
+ timeout: float,
+) -> ProbeResult:
+ request = (
+ f"GET / HTTP/1.1\r\n"
+ f"Host: {http_host}\r\n"
+ f"Connection: close\r\n"
+ f"User-Agent: burrow-proxy-ladder\r\n\r\n"
+ ).encode()
+ try:
+ with connect_tls(connect_endpoint, tls_server_name, node, timeout) as sock:
+ sock.sendall(trojan_header(node["config"]["password"], TROJAN_CMD_CONNECT, target))
+ sock.sendall(request)
+ data = sock.recv(2048)
+ except BaseException as exc:
+ return ProbeResult(False, f"{type(exc).__name__}: {exc}")
+ if not data:
+ return ProbeResult(False, "proxy returned EOF before any HTTP response bytes")
+ first_line = data.splitlines()[0].decode("utf-8", errors="replace")
+ return ProbeResult(True, f"received {len(data)} bytes; first_line={first_line!r}")
+
+
+def probe_trojan_udp_dns(
+ connect_endpoint: Endpoint,
+ tls_server_name: str,
+ node: dict[str, Any],
+ dns_server: Endpoint,
+ dns_name: str,
+ timeout: float,
+) -> ProbeResult:
+ if node["config"].get("udp") is False:
+ return ProbeResult(False, "selected Trojan node has udp=false")
+ query_id, query = build_dns_query(dns_name)
+ try:
+ with connect_tls(connect_endpoint, tls_server_name, node, timeout) as sock:
+ sock.sendall(
+ trojan_header(node["config"]["password"], TROJAN_CMD_UDP_ASSOCIATE, dns_server)
+ )
+ sock.sendall(trojan_udp_frame(dns_server, query))
+ source = read_socks_addr(sock)
+ payload_len = struct.unpack("!H", read_exact(sock, 2))[0]
+ delimiter = read_exact(sock, 2)
+ if delimiter != b"\r\n":
+ return ProbeResult(False, f"invalid Trojan UDP delimiter: {delimiter!r}")
+ payload = read_exact(sock, payload_len)
+ except BaseException as exc:
+ return ProbeResult(False, f"{type(exc).__name__}: {exc}")
+ dns_summary = parse_dns_response(query_id, payload)
+ return ProbeResult(
+ True,
+ f"received UDP frame from {source.host}:{source.port}; DNS response {dns_summary}",
+ )
+
+
+def print_node_summary(network_id: int, payload: dict[str, Any], node: dict[str, Any]) -> None:
+ config = node.get("config") or {}
+ print(f"network_id={network_id}")
+ print(f"subscription={payload.get('name')!r}")
+ print(f"selected_node={node.get('name')!r}")
+ print(f"protocol={node.get('protocol')!r}")
+ print(f"server={node.get('server')}:{node.get('port')}")
+ print(f"sni={config.get('sni')!r}")
+ print(f"skip_cert_verify={bool(config.get('skip_cert_verify'))}")
+ print(f"alpn_present={bool(config.get('alpn_present'))} alpn={config.get('alpn') or []!r}")
+ print(f"udp={config.get('udp', True)!r}")
+
+
+def main() -> int:
+ parser = argparse.ArgumentParser(
+ description=(
+ "Directly probe the selected Burrow proxy subscription node without "
+ "using the daemon, Network Extension, system proxy, routes, or DNS settings."
+ )
+ )
+ parser.add_argument("--db", type=Path, default=Path(os.environ.get("BURROW_DB", DEFAULT_DB)))
+ parser.add_argument("--network-id", type=int)
+ parser.add_argument("--node-name", help="Probe this subscription node instead of the selected node.")
+ parser.add_argument("--node-ordinal", type=int, help="Probe this subscription ordinal instead of the selected node.")
+ parser.add_argument(
+ "--server-address",
+ help=(
+ "Override the proxy connect address, as host or host:port. "
+ "This bypasses system DNS for the proxy hostname while preserving SNI."
+ ),
+ )
+ parser.add_argument(
+ "--resolve-server",
+ choices=["system", "doh-google", "doh-cloudflare", "doh-all"],
+ default="system",
+ help="How to resolve the proxy server when --server-address is not provided.",
+ )
+ parser.add_argument(
+ "--all-server-addresses",
+ action="store_true",
+ help="Probe every resolved proxy server address and pass if any address works.",
+ )
+ parser.add_argument("--target", default=DEFAULT_TARGET, help="TCP HTTP target, host:port")
+ parser.add_argument("--http-host", default=DEFAULT_HTTP_HOST)
+ parser.add_argument("--dns-server", default=DEFAULT_DNS_SERVER, help="UDP DNS target, host:port")
+ parser.add_argument("--dns-name", default=DEFAULT_DNS_NAME)
+ parser.add_argument("--timeout", type=float, default=DEFAULT_TIMEOUT)
+ parser.add_argument("--skip-udp", action="store_true")
+ parser.add_argument("--require-udp", action="store_true")
+ args = parser.parse_args()
+
+ print("Burrow proxy ladder: direct provider/protocol probe")
+ print("bypasses=daemon,NetworkExtension,system_proxy,routes,system_dns,browser")
+ print(f"db={args.db}")
+
+ try:
+ if args.node_name is not None and args.node_ordinal is not None:
+ raise ValueError("--node-name and --node-ordinal are mutually exclusive")
+ network_id, payload = load_payload(args.db, args.network_id)
+ node = choose_node(payload, args.node_name, args.node_ordinal)
+ print_node_summary(network_id, payload, node)
+ if node.get("protocol") != "trojan":
+ print(f"unsupported protocol for this direct probe: {node.get('protocol')!r}")
+ return 2
+ server = Endpoint(str(node["server"]), int(node["port"]))
+ if args.server_address:
+ connect_endpoints = [parse_endpoint(args.server_address, default_port=server.port)]
+ elif args.resolve_server == "system":
+ system_addresses = resolved_stream_addresses(server)
+ connect_endpoints = [
+ Endpoint(str(sockaddr[0]), int(sockaddr[1])) for _family, sockaddr in system_addresses
+ ]
+ if not args.all_server_addresses and connect_endpoints:
+ connect_endpoints = connect_endpoints[:1]
+ if not connect_endpoints:
+ connect_endpoints = [server]
+ elif args.resolve_server == "doh-all":
+ connect_endpoints = resolve_doh_all(server.host, server.port, args.timeout)
+ if not args.all_server_addresses and connect_endpoints:
+ connect_endpoints = connect_endpoints[:1]
+ else:
+ connect_endpoints = resolve_doh(server.host, server.port, args.resolve_server, args.timeout)
+ print(
+ f"{args.resolve_server}_answers="
+ + ",".join(f"{endpoint.host}:{endpoint.port}" for endpoint in connect_endpoints)
+ )
+ if not args.all_server_addresses and connect_endpoints:
+ connect_endpoints = connect_endpoints[:1]
+ if not connect_endpoints:
+ raise LookupError("proxy server resolution returned no usable A records")
+ target = parse_endpoint(args.target)
+ dns_server = parse_endpoint(args.dns_server)
+ except BaseException as exc:
+ print(f"setup_error={type(exc).__name__}: {exc}", file=sys.stderr)
+ return 2
+
+ print(
+ "connect_endpoints="
+ + ",".join(f"{endpoint.host}:{endpoint.port}" for endpoint in connect_endpoints)
+ )
+ if any(endpoint != server for endpoint in connect_endpoints):
+ print(f"tls_server_name_source={server.host!r} sni_preserved=True")
+ if any(is_fake_ip(endpoint.host) for endpoint in connect_endpoints):
+ print(
+ "warning=proxy hostname resolved into 198.18.0.0/15; "
+ "rerun with --resolve-server doh-all or --server-address REAL_IP to avoid fake-IP DNS"
+ )
+
+ any_tcp_ok = False
+ any_required_udp_ok = False
+ for idx, connect_endpoint in enumerate(connect_endpoints, start=1):
+ if len(connect_endpoints) > 1:
+ print(f"\n=== proxy server candidate {idx}/{len(connect_endpoints)} ===")
+ print(f"connect_endpoint={connect_endpoint.host}:{connect_endpoint.port}")
+
+ print("\n[1/2] Trojan TCP CONNECT probe")
+ print(f"target={target.host}:{target.port} http_host={args.http_host!r}")
+ tcp_result = probe_trojan_tcp(
+ connect_endpoint, server.host, node, target, args.http_host, args.timeout
+ )
+ print(("ok: " if tcp_result.ok else "fail: ") + tcp_result.detail)
+ any_tcp_ok = any_tcp_ok or tcp_result.ok
+
+ udp_result = ProbeResult(True, "skipped")
+ if not args.skip_udp:
+ print("\n[2/2] Trojan UDP DNS probe")
+ print(f"dns_server={dns_server.host}:{dns_server.port} dns_name={args.dns_name!r}")
+ udp_result = probe_trojan_udp_dns(
+ connect_endpoint, server.host, node, dns_server, args.dns_name, args.timeout
+ )
+ print(("ok: " if udp_result.ok else "fail: ") + udp_result.detail)
+ else:
+ print("\n[2/2] Trojan UDP DNS probe")
+ print("skipped by --skip-udp")
+ any_required_udp_ok = any_required_udp_ok or udp_result.ok
+
+ if tcp_result.ok and (not args.require_udp or udp_result.ok):
+ break
+
+ if not any_tcp_ok:
+ return 1
+ if args.require_udp and not any_required_udp_ok:
+ return 1
+ return 0
+
+
+if __name__ == "__main__":
+ raise SystemExit(main())
diff --git a/Scripts/burrow-proxy-selftest b/Scripts/burrow-proxy-selftest
new file mode 100755
index 0000000..bc980c9
--- /dev/null
+++ b/Scripts/burrow-proxy-selftest
@@ -0,0 +1,272 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+usage() {
+ cat <<'EOF'
+Usage: Scripts/burrow-proxy-selftest
+
+Run a controlled Burrow proxy packet-runtime test without changing system proxy
+settings or using the user's Burrow database. The script starts a local Trojan
+TLS server, starts a Burrow daemon against a temporary database/socket, imports a
+temporary ProxySubscription pointed at that server, then runs the packet-level
+proxy-tcp-probe through daemon DNS and TCP packet streaming.
+EOF
+}
+
+if [[ "${1:-}" == "-h" || "${1:-}" == "--help" ]]; then
+ usage
+ exit 0
+fi
+
+repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
+burrow_bin="${BURROW_BIN:-$repo_root/target/debug/burrow}"
+
+if [[ ! -x "$burrow_bin" ]]; then
+ (cd "$repo_root" && cargo build -p burrow)
+fi
+
+if ! command -v openssl >/dev/null 2>&1; then
+ echo "openssl is required for the local TLS server certificate" >&2
+ exit 2
+fi
+
+tmpdir="$(mktemp -d "${TMPDIR:-/tmp}/burrow-proxy-selftest.XXXXXX")"
+daemon_pid=""
+server_pid=""
+status=0
+
+cleanup() {
+ if [[ -n "$server_pid" ]]; then
+ kill "$server_pid" >/dev/null 2>&1 || true
+ fi
+ if [[ -n "$daemon_pid" ]]; then
+ kill "$daemon_pid" >/dev/null 2>&1 || true
+ fi
+ if [[ "${BURROW_SELFTEST_KEEP:-}" == "1" || "$status" -ne 0 ]]; then
+ echo "preserving self-test artifacts: $tmpdir" >&2
+ else
+ rm -rf "$tmpdir"
+ fi
+}
+trap 'status=$?; cleanup' EXIT
+
+cert="$tmpdir/cert.pem"
+key="$tmpdir/key.pem"
+socket="$tmpdir/burrow.sock"
+port_file="$tmpdir/server.port"
+server_result="$tmpdir/server-result.json"
+payload="$tmpdir/proxy-payload.json"
+daemon_log="$tmpdir/daemon.log"
+server_log="$tmpdir/server.log"
+
+openssl req \
+ -x509 \
+ -newkey rsa:2048 \
+ -nodes \
+ -subj /CN=localhost \
+ -keyout "$key" \
+ -out "$cert" \
+ -days 1 >/dev/null 2>&1
+
+python3 - "$port_file" "$server_result" "$cert" "$key" >"$server_log" 2>&1 <<'PY' &
+from __future__ import annotations
+
+import hashlib
+import json
+import socket
+import ssl
+import struct
+import sys
+
+port_file, result_file, cert_file, key_file = sys.argv[1:]
+password = "secret"
+
+
+def read_exact(sock: ssl.SSLSocket, length: int) -> bytes:
+ chunks: list[bytes] = []
+ remaining = length
+ while remaining:
+ chunk = sock.recv(remaining)
+ if not chunk:
+ raise EOFError(f"expected {remaining} more bytes")
+ chunks.append(chunk)
+ remaining -= len(chunk)
+ return b"".join(chunks)
+
+
+def read_socks_addr(sock: ssl.SSLSocket) -> dict[str, object]:
+ atyp = read_exact(sock, 1)[0]
+ if atyp == 1:
+ host = socket.inet_ntop(socket.AF_INET, read_exact(sock, 4))
+ elif atyp == 4:
+ host = socket.inet_ntop(socket.AF_INET6, read_exact(sock, 16))
+ elif atyp == 3:
+ length = read_exact(sock, 1)[0]
+ host = read_exact(sock, length).decode("idna")
+ else:
+ raise ValueError(f"unsupported SOCKS address type {atyp}")
+ port = struct.unpack("!H", read_exact(sock, 2))[0]
+ return {"atyp": atyp, "host": host, "port": port}
+
+
+def read_http_headers(sock: ssl.SSLSocket) -> bytes:
+ data = bytearray()
+ while b"\r\n\r\n" not in data:
+ chunk = sock.recv(4096)
+ if not chunk:
+ break
+ data.extend(chunk)
+ if len(data) > 65535:
+ raise ValueError("HTTP request headers exceeded 64 KiB")
+ return bytes(data)
+
+
+ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
+ctx.load_cert_chain(cert_file, key_file)
+ctx.set_alpn_protocols(["h2", "http/1.1"])
+
+listener = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+listener.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+listener.bind(("127.0.0.1", 0))
+listener.listen(1)
+port = listener.getsockname()[1]
+with open(port_file, "w", encoding="utf-8") as f:
+ f.write(str(port))
+
+raw, peer = listener.accept()
+raw.settimeout(10)
+result: dict[str, object] = {"peer": str(peer)}
+try:
+ with ctx.wrap_socket(raw, server_side=True) as tls:
+ tls.settimeout(10)
+ result["alpn"] = tls.selected_alpn_protocol()
+ got_hash = read_exact(tls, 56)
+ expected_hash = hashlib.sha224(password.encode()).hexdigest().encode()
+ result["password_hash_ok"] = got_hash == expected_hash
+ if not result["password_hash_ok"]:
+ raise ValueError("unexpected Trojan password hash")
+ if read_exact(tls, 2) != b"\r\n":
+ raise ValueError("missing Trojan password delimiter")
+ command = read_exact(tls, 1)[0]
+ result["command"] = command
+ target = read_socks_addr(tls)
+ result["target"] = target
+ if read_exact(tls, 2) != b"\r\n":
+ raise ValueError("missing Trojan header delimiter")
+ request = read_http_headers(tls)
+ result["request_first_line"] = request.splitlines()[0].decode("utf-8", "replace")
+ body = json.dumps(
+ {
+ "ok": True,
+ "target": target,
+ "request_first_line": result["request_first_line"],
+ },
+ sort_keys=True,
+ ).encode()
+ tls.sendall(
+ b"HTTP/1.1 200 OK\r\n"
+ + f"Content-Length: {len(body)}\r\n".encode()
+ + b"Connection: close\r\n"
+ + b"Content-Type: application/json\r\n\r\n"
+ + body
+ )
+finally:
+ with open(result_file, "w", encoding="utf-8") as f:
+ json.dump(result, f, ensure_ascii=False, indent=2, sort_keys=True)
+ listener.close()
+PY
+server_pid=$!
+
+for _ in {1..100}; do
+ if [[ -s "$port_file" ]]; then
+ break
+ fi
+ if ! kill -0 "$server_pid" >/dev/null 2>&1; then
+ echo "local Trojan server exited before listening" >&2
+ cat "$server_log" >&2 || true
+ exit 1
+ fi
+ sleep 0.05
+done
+
+if [[ ! -s "$port_file" ]]; then
+ echo "timed out waiting for local Trojan server" >&2
+ cat "$server_log" >&2 || true
+ exit 1
+fi
+server_port="$(cat "$port_file")"
+
+cat >"$payload" <"$daemon_log" 2>&1
+) &
+daemon_pid=$!
+
+for _ in {1..100}; do
+ if [[ -S "$socket" ]]; then
+ break
+ fi
+ if ! kill -0 "$daemon_pid" >/dev/null 2>&1; then
+ echo "Burrow daemon exited before creating socket" >&2
+ cat "$daemon_log" >&2 || true
+ exit 1
+ fi
+ sleep 0.05
+done
+
+if [[ ! -S "$socket" ]]; then
+ echo "timed out waiting for Burrow daemon socket" >&2
+ cat "$daemon_log" >&2 || true
+ exit 1
+fi
+
+(
+ cd "$tmpdir"
+ BURROW_SOCKET_PATH="$socket" "$burrow_bin" network-add 1 3 "$payload"
+ BURROW_SOCKET_PATH="$socket" "$burrow_bin" tunnel-config
+ BURROW_SOCKET_PATH="$socket" "$burrow_bin" proxy-tcp-probe \
+ --dns-name selftest.invalid \
+ --remote 1.1.1.1:80 \
+ --timeout-ms 8000
+)
+
+wait "$server_pid"
+server_pid=""
+
+echo
+echo "Local Trojan server observed:"
+cat "$server_result"
+echo
diff --git a/burrow-gtk/Cargo.lock b/burrow-gtk/Cargo.lock
index a1a9ebd..f64b325 100644
--- a/burrow-gtk/Cargo.lock
+++ b/burrow-gtk/Cargo.lock
@@ -36,18 +36,7 @@ dependencies = [
"cfg-if",
"cipher",
"cpufeatures",
-]
-
-[[package]]
-name = "ahash"
-version = "0.8.12"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75"
-dependencies = [
- "cfg-if",
- "once_cell",
- "version_check",
- "zerocopy",
+ "zeroize",
]
[[package]]
@@ -59,6 +48,76 @@ dependencies = [
"memchr",
]
+[[package]]
+name = "alloca"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e5a7d05ea6aea7e9e64d25b9156ba2fee3fdd659e34e41063cd2fc7cd020d7f4"
+dependencies = [
+ "cc",
+]
+
+[[package]]
+name = "amplify"
+version = "4.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f7fb4ac7c881e54a8e7015e399b6112a2a5bc958b6c89ac510840ff20273b31"
+dependencies = [
+ "amplify_derive",
+ "amplify_num",
+ "ascii",
+ "getrandom 0.2.12",
+ "getrandom 0.3.3",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "amplify_derive"
+version = "4.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2a6309e6b8d89b36b9f959b7a8fa093583b94922a0f6438a24fb08936de4d428"
+dependencies = [
+ "amplify_syn",
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "amplify_num"
+version = "0.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "afed304556696656d2d71495e1e5f2c4b524a3fb6eb0f2f3778ffc482a40b8a8"
+dependencies = [
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "amplify_syn"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7736fb8d473c0d83098b5bac44df6a561e20470375cd8bcae30516dc889fd62a"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "android_system_properties"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "anes"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299"
+
[[package]]
name = "anstream"
version = "0.6.11"
@@ -75,9 +134,9 @@ dependencies = [
[[package]]
name = "anstyle"
-version = "1.0.4"
+version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87"
+checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000"
[[package]]
name = "anstyle-parse"
@@ -113,6 +172,123 @@ version = "1.0.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca"
+[[package]]
+name = "argon2"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3c3610892ee6e0cbce8ae2700349fcf8f98adb0dbfbee85aec3c9179d29cc072"
+dependencies = [
+ "base64ct",
+ "blake2",
+ "cpufeatures",
+ "password-hash 0.5.0",
+]
+
+[[package]]
+name = "arrayvec"
+version = "0.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
+
+[[package]]
+name = "arti-client"
+version = "0.40.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e89842cae6e3bda0fd128a5c66eb3392ed412065dc698c77d9fcc4b77e4159f2"
+dependencies = [
+ "async-trait",
+ "cfg-if",
+ "derive-deftly",
+ "derive_builder_fork_arti",
+ "derive_more",
+ "educe",
+ "fs-mistrust",
+ "futures",
+ "hostname-validator",
+ "humantime",
+ "humantime-serde",
+ "libc",
+ "once_cell",
+ "postage",
+ "rand 0.9.2",
+ "safelog",
+ "serde",
+ "thiserror 2.0.16",
+ "time",
+ "tor-async-utils",
+ "tor-basic-utils",
+ "tor-chanmgr",
+ "tor-circmgr",
+ "tor-config",
+ "tor-config-path",
+ "tor-dircommon",
+ "tor-dirmgr",
+ "tor-error",
+ "tor-guardmgr",
+ "tor-keymgr",
+ "tor-linkspec",
+ "tor-llcrypto",
+ "tor-memquota",
+ "tor-netdir",
+ "tor-netdoc",
+ "tor-persist",
+ "tor-proto",
+ "tor-protover",
+ "tor-rtcompat",
+ "tracing",
+ "void",
+]
+
+[[package]]
+name = "ascii"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d92bec98840b8f03a5ff5413de5293bfcd8bf96467cf5452609f939ec6f5de16"
+
+[[package]]
+name = "asn1-rs"
+version = "0.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b7f43a50ac4fdca5df8e885c21b835997f0a1cdee65494a6847694a98652d9d8"
+dependencies = [
+ "asn1-rs-derive",
+ "asn1-rs-impl",
+ "displaydoc",
+ "nom",
+ "num-traits",
+ "rusticata-macros",
+ "thiserror 2.0.16",
+]
+
+[[package]]
+name = "asn1-rs-derive"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3109e49b1e4909e9db6515a30c633684d68cdeaa252f215214cb4fa1a5bfee2c"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.106",
+ "synstructure",
+]
+
+[[package]]
+name = "asn1-rs-impl"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.106",
+]
+
+[[package]]
+name = "assert_matches"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9"
+
[[package]]
name = "async-channel"
version = "2.1.1"
@@ -120,12 +296,40 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ca33f4bc4ed1babef42cad36cc1f51fa88be00420404e5b1e80ab1b18f7678c"
dependencies = [
"concurrent-queue",
- "event-listener",
+ "event-listener 4.0.3",
"event-listener-strategy",
"futures-core",
"pin-project-lite",
]
+[[package]]
+name = "async-compression"
+version = "0.4.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cd066d0b4ef8ecb03a55319dc13aa6910616d0f44008a045bb1835af830abff5"
+dependencies = [
+ "flate2",
+ "futures-core",
+ "futures-io",
+ "memchr",
+ "pin-project-lite",
+ "xz2",
+ "zstd 0.13.0",
+ "zstd-safe 7.0.0",
+]
+
+[[package]]
+name = "async-native-tls"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9343dc5acf07e79ff82d0c37899f079db3534d99f189a1837c8e549c99405bec"
+dependencies = [
+ "futures-util",
+ "native-tls",
+ "thiserror 1.0.56",
+ "url",
+]
+
[[package]]
name = "async-stream"
version = "0.2.1"
@@ -180,6 +384,49 @@ dependencies = [
"syn 2.0.106",
]
+[[package]]
+name = "async_executors"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a982d2f86de6137cc05c9db9a915a19886c97911f9790d04f174cede74be01a5"
+dependencies = [
+ "blanket",
+ "futures-core",
+ "futures-task",
+ "futures-util",
+ "pin-project",
+ "rustc_version",
+ "tokio",
+]
+
+[[package]]
+name = "asynchronous-codec"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a860072022177f903e59730004fb5dc13db9275b79bb2aef7ba8ce831956c233"
+dependencies = [
+ "bytes",
+ "futures-sink",
+ "futures-util",
+ "memchr",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "atomic"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c59bdb34bc650a32731b31bd8f0829cc15d24a708ee31559e0bb34f2bc320cba"
+
+[[package]]
+name = "atomic"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a89cbf775b137e9b968e67227ef7f775587cde3fd31b0d8599dbd0f598a48340"
+dependencies = [
+ "bytemuck",
+]
+
[[package]]
name = "atomic-waker"
version = "1.1.2"
@@ -192,6 +439,29 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
+[[package]]
+name = "aws-lc-rs"
+version = "1.13.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c953fe1ba023e6b7730c0d4b031d06f267f23a46167dcbd40316644b10a17ba"
+dependencies = [
+ "aws-lc-sys",
+ "zeroize",
+]
+
+[[package]]
+name = "aws-lc-sys"
+version = "0.30.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dbfd150b5dbdb988bcc8fb1fe787eb6b7ee6180ca24da683b61ea5405f3d43ff"
+dependencies = [
+ "bindgen 0.69.5",
+ "cc",
+ "cmake",
+ "dunce",
+ "fs_extra",
+]
+
[[package]]
name = "axum"
version = "0.7.5"
@@ -262,6 +532,12 @@ dependencies = [
"rustc-demangle",
]
+[[package]]
+name = "base16ct"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf"
+
[[package]]
name = "base64"
version = "0.21.7"
@@ -276,9 +552,19 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
[[package]]
name = "base64ct"
-version = "1.6.0"
+version = "1.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
+checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06"
+
+[[package]]
+name = "bincode"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "36eaf5d7b090263e8150820482d5d93cd964a81e4019913c972f4edcc6edb740"
+dependencies = [
+ "serde",
+ "unty",
+]
[[package]]
name = "bindgen"
@@ -325,6 +611,29 @@ dependencies = [
"which",
]
+[[package]]
+name = "bindgen"
+version = "0.69.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088"
+dependencies = [
+ "bitflags 2.11.1",
+ "cexpr",
+ "clang-sys",
+ "itertools 0.12.1",
+ "lazy_static",
+ "lazycell",
+ "log",
+ "prettyplease",
+ "proc-macro2",
+ "quote",
+ "regex",
+ "rustc-hash 1.1.0",
+ "shlex",
+ "syn 2.0.106",
+ "which",
+]
+
[[package]]
name = "bitflags"
version = "1.3.2"
@@ -333,9 +642,21 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
-version = "2.4.2"
+version = "2.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf"
+checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3"
+
+[[package]]
+name = "bitvec"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c"
+dependencies = [
+ "funty",
+ "radium",
+ "tap",
+ "wyz",
+]
[[package]]
name = "blake2"
@@ -346,6 +667,17 @@ dependencies = [
"digest",
]
+[[package]]
+name = "blanket"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e0b121a9fe0df916e362fb3271088d071159cdf11db0e4182d02152850756eff"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.106",
+]
+
[[package]]
name = "block"
version = "0.1.6"
@@ -361,6 +693,26 @@ dependencies = [
"generic-array",
]
+[[package]]
+name = "bs58"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4"
+dependencies = [
+ "tinyvec",
+]
+
+[[package]]
+name = "bstr"
+version = "1.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "63044e1ae8e69f3b5a92c736ca6269b8d12fa7efe39bf34ddb06d102cf0e2cab"
+dependencies = [
+ "memchr",
+ "regex-automata 0.4.14",
+ "serde",
+]
+
[[package]]
name = "bumpalo"
version = "3.14.0"
@@ -373,11 +725,14 @@ version = "0.1.0"
dependencies = [
"aead",
"anyhow",
+ "argon2",
+ "arti-client",
"async-channel",
"async-stream 0.2.1",
"axum",
"base64 0.21.7",
"blake2",
+ "bytes",
"caps",
"chacha20poly1305",
"clap",
@@ -385,12 +740,17 @@ dependencies = [
"dotenv",
"fehler",
"futures",
+ "hickory-proto",
"hmac",
"hyper-util",
"ip_network",
"ip_network_table",
+ "ipnetwork",
+ "libc",
"libsystemd",
"log",
+ "md-5",
+ "netstack-smoltcp",
"nix 0.27.1",
"once_cell",
"parking_lot",
@@ -402,14 +762,21 @@ dependencies = [
"ring",
"rusqlite",
"rust-ini",
- "schemars",
+ "rustls",
+ "schemars 0.8.16",
"serde",
"serde_json",
+ "serde_yaml",
+ "sha2",
+ "subtle",
"tokio",
+ "tokio-rustls",
"tokio-stream",
- "toml",
+ "tokio-util",
+ "toml 0.8.23",
"tonic",
"tonic-build",
+ "tor-rtcompat",
"tower",
"tracing",
"tracing-journald",
@@ -417,6 +784,7 @@ dependencies = [
"tracing-oslog",
"tracing-subscriber",
"tun",
+ "webpki-roots 0.26.11",
"x25519-dalek",
]
@@ -429,9 +797,23 @@ dependencies = [
"gettext-rs",
"glib-build-tools",
"relm4",
+ "serde",
+ "serde_json",
"tokio",
]
+[[package]]
+name = "by_address"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "64fa3c856b712db6612c019f14756e64e4bcea13337a6b33b696333a9eaa2d06"
+
+[[package]]
+name = "bytemuck"
+version = "1.25.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec"
+
[[package]]
name = "byteorder"
version = "1.5.0"
@@ -501,13 +883,27 @@ dependencies = [
]
[[package]]
-name = "cc"
-version = "1.0.83"
+name = "caret"
+version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
+checksum = "beae2cb9f60bc3f21effaaf9c64e51f6627edd54eedc9199ba07f519ef2a2101"
+
+[[package]]
+name = "cast"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
+
+[[package]]
+name = "cc"
+version = "1.2.62"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1dce859f0832a7d088c4f1119888ab94ef4b5d6795d1ce05afb7fe159d79f98"
dependencies = [
+ "find-msvc-tools",
"jobserver",
"libc",
+ "shlex",
]
[[package]]
@@ -565,6 +961,45 @@ dependencies = [
"zeroize",
]
+[[package]]
+name = "chrono"
+version = "0.4.44"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0"
+dependencies = [
+ "iana-time-zone",
+ "num-traits",
+ "serde",
+ "windows-link 0.2.1",
+]
+
+[[package]]
+name = "ciborium"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e"
+dependencies = [
+ "ciborium-io",
+ "ciborium-ll",
+ "serde",
+]
+
+[[package]]
+name = "ciborium-io"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757"
+
+[[package]]
+name = "ciborium-ll"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9"
+dependencies = [
+ "ciborium-io",
+ "half",
+]
+
[[package]]
name = "cipher"
version = "0.4.4"
@@ -589,9 +1024,9 @@ dependencies = [
[[package]]
name = "clap"
-version = "4.4.18"
+version = "4.5.60"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e578d6ec4194633722ccf9544794b71b1385c3c027efe0c55db226fc880865c"
+checksum = "2797f34da339ce31042b27d23607e051786132987f595b02ba4f6a6dffb7030a"
dependencies = [
"clap_builder",
"clap_derive",
@@ -599,23 +1034,23 @@ dependencies = [
[[package]]
name = "clap_builder"
-version = "4.4.18"
+version = "4.5.60"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4df4df40ec50c46000231c914968278b1eb05098cf8f1b3a518a95030e71d1c7"
+checksum = "24a241312cea5059b13574bb9b3861cabf758b879c15190b37b6d6fd63ab6876"
dependencies = [
"anstream",
"anstyle",
"clap_lex",
- "strsim",
+ "strsim 0.11.1",
]
[[package]]
name = "clap_derive"
-version = "4.4.7"
+version = "4.5.55"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442"
+checksum = "a92793da1a46a5f2a02a6f4c46c6496b28c43638adea8306fcb0caa1634f24e5"
dependencies = [
- "heck",
+ "heck 0.5.0",
"proc-macro2",
"quote",
"syn 2.0.106",
@@ -623,9 +1058,29 @@ dependencies = [
[[package]]
name = "clap_lex"
-version = "0.6.0"
+version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1"
+checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9"
+
+[[package]]
+name = "cmake"
+version = "0.1.58"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c0f78a02292a74a88ac736019ab962ece0bc380e3f977bf72e376c5d78ff0678"
+dependencies = [
+ "cc",
+]
+
+[[package]]
+name = "coarsetime"
+version = "0.1.37"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e58eb270476aa4fc7843849f8a35063e8743b4dbcdf6dd0f8ea0886980c204c2"
+dependencies = [
+ "libc",
+ "wasix",
+ "wasm-bindgen",
+]
[[package]]
name = "colorchoice"
@@ -655,6 +1110,12 @@ dependencies = [
"windows-sys 0.52.0",
]
+[[package]]
+name = "const-oid"
+version = "0.9.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
+
[[package]]
name = "const-random"
version = "0.1.18"
@@ -681,6 +1142,24 @@ version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc"
+[[package]]
+name = "convert_case"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9"
+dependencies = [
+ "unicode-segmentation",
+]
+
+[[package]]
+name = "cookie-factory"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9885fa71e26b8ab7855e2ec7cae6e9b380edff76cd052e07c683a0319d51b3a2"
+dependencies = [
+ "futures",
+]
+
[[package]]
name = "core-foundation"
version = "0.9.4"
@@ -715,6 +1194,85 @@ dependencies = [
"cfg-if",
]
+[[package]]
+name = "criterion"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "950046b2aa2492f9a536f5f4f9a3de7b9e2476e575e05bd6c333371add4d98f3"
+dependencies = [
+ "alloca",
+ "anes",
+ "cast",
+ "ciborium",
+ "clap",
+ "criterion-plot",
+ "itertools 0.13.0",
+ "num-traits",
+ "oorandom",
+ "page_size",
+ "plotters",
+ "rayon",
+ "regex",
+ "serde",
+ "serde_json",
+ "tinytemplate",
+ "walkdir",
+]
+
+[[package]]
+name = "criterion-cycles-per-byte"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5396de42a52e9e5d8f67ef0702dae30451f310a9ba1c3094dcf228f0be0e54bc"
+dependencies = [
+ "cfg-if",
+ "criterion",
+]
+
+[[package]]
+name = "criterion-plot"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d8d80a2f4f5b554395e47b5d8305bc3d27813bacb73493eb1001e8f76dae29ea"
+dependencies = [
+ "cast",
+ "itertools 0.13.0",
+]
+
+[[package]]
+name = "critical-section"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b"
+
+[[package]]
+name = "crossbeam-deque"
+version = "0.8.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51"
+dependencies = [
+ "crossbeam-epoch",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-epoch"
+version = "0.9.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
+dependencies = [
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-queue"
+version = "0.3.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115"
+dependencies = [
+ "crossbeam-utils",
+]
+
[[package]]
name = "crossbeam-utils"
version = "0.8.19"
@@ -727,6 +1285,18 @@ version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5"
+[[package]]
+name = "crypto-bigint"
+version = "0.5.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76"
+dependencies = [
+ "generic-array",
+ "rand_core 0.6.4",
+ "subtle",
+ "zeroize",
+]
+
[[package]]
name = "crypto-common"
version = "0.1.6"
@@ -738,6 +1308,15 @@ dependencies = [
"typenum",
]
+[[package]]
+name = "ctr"
+version = "0.9.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835"
+dependencies = [
+ "cipher",
+]
+
[[package]]
name = "curve25519-dalek"
version = "4.1.1"
@@ -747,6 +1326,7 @@ dependencies = [
"cfg-if",
"cpufeatures",
"curve25519-dalek-derive",
+ "digest",
"fiat-crypto",
"platforms",
"rustc_version",
@@ -766,12 +1346,270 @@ dependencies = [
]
[[package]]
-name = "deranged"
-version = "0.3.11"
+name = "darling"
+version = "0.14.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4"
+checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850"
+dependencies = [
+ "darling_core 0.14.4",
+ "darling_macro 0.14.4",
+]
+
+[[package]]
+name = "darling"
+version = "0.21.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9cdf337090841a411e2a7f3deb9187445851f91b309c0c0a29e05f74a00a48c0"
+dependencies = [
+ "darling_core 0.21.3",
+ "darling_macro 0.21.3",
+]
+
+[[package]]
+name = "darling"
+version = "0.23.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d"
+dependencies = [
+ "darling_core 0.23.0",
+ "darling_macro 0.23.0",
+]
+
+[[package]]
+name = "darling_core"
+version = "0.14.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0"
+dependencies = [
+ "fnv",
+ "ident_case",
+ "proc-macro2",
+ "quote",
+ "strsim 0.10.0",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "darling_core"
+version = "0.21.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1247195ecd7e3c85f83c8d2a366e4210d588e802133e1e355180a9870b517ea4"
+dependencies = [
+ "fnv",
+ "ident_case",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.106",
+]
+
+[[package]]
+name = "darling_core"
+version = "0.23.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9865a50f7c335f53564bb694ef660825eb8610e0a53d3e11bf1b0d3df31e03b0"
+dependencies = [
+ "ident_case",
+ "proc-macro2",
+ "quote",
+ "strsim 0.11.1",
+ "syn 2.0.106",
+]
+
+[[package]]
+name = "darling_macro"
+version = "0.14.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e"
+dependencies = [
+ "darling_core 0.14.4",
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "darling_macro"
+version = "0.21.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81"
+dependencies = [
+ "darling_core 0.21.3",
+ "quote",
+ "syn 2.0.106",
+]
+
+[[package]]
+name = "darling_macro"
+version = "0.23.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d"
+dependencies = [
+ "darling_core 0.23.0",
+ "quote",
+ "syn 2.0.106",
+]
+
+[[package]]
+name = "data-encoding"
+version = "2.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a4ae5f15dda3c708c0ade84bfee31ccab44a3da4f88015ed22f63732abe300c8"
+
+[[package]]
+name = "defmt"
+version = "0.3.100"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f0963443817029b2024136fc4dd07a5107eb8f977eaf18fcd1fdeb11306b64ad"
+dependencies = [
+ "defmt 1.1.0",
+]
+
+[[package]]
+name = "defmt"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a6e524506490a1953d237cb87b1cfc1e46f88c18f10a22dfe0f507dc6bfc7f7f"
+dependencies = [
+ "bitflags 1.3.2",
+ "defmt-macros",
+]
+
+[[package]]
+name = "defmt-macros"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f0a27770e9c8f719a79d8b638281f4d828f77d8fd61e0bd94451b9b85e576a0b"
+dependencies = [
+ "defmt-parser",
+ "proc-macro-error2",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.106",
+]
+
+[[package]]
+name = "defmt-parser"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "10d60334b3b2e7c9d91ef8150abfb6fa4c1c39ebbcf4a81c2e346aad939fee3e"
+dependencies = [
+ "thiserror 2.0.16",
+]
+
+[[package]]
+name = "der"
+version = "0.7.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb"
+dependencies = [
+ "const-oid",
+ "pem-rfc7468",
+ "zeroize",
+]
+
+[[package]]
+name = "der-parser"
+version = "10.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "07da5016415d5a3c4dd39b11ed26f915f52fc4e0dc197d87908bc916e51bc1a6"
+dependencies = [
+ "asn1-rs",
+ "cookie-factory",
+ "displaydoc",
+ "nom",
+ "num-traits",
+ "rusticata-macros",
+]
+
+[[package]]
+name = "deranged"
+version = "0.5.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c"
dependencies = [
"powerfmt",
+ "serde_core",
+]
+
+[[package]]
+name = "derive-deftly"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "284db66a66f03c3dafbe17360d959eb76b83f77cfe191677e2a7899c0da291f3"
+dependencies = [
+ "derive-deftly-macros",
+ "heck 0.5.0",
+]
+
+[[package]]
+name = "derive-deftly-macros"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "caef6056a5788d05d173cdc3c562ac28ae093828f851f69378b74e4e3d578e41"
+dependencies = [
+ "heck 0.5.0",
+ "indexmap 2.11.4",
+ "itertools 0.14.0",
+ "proc-macro-crate",
+ "proc-macro2",
+ "quote",
+ "sha3",
+ "strum",
+ "syn 2.0.106",
+ "void",
+]
+
+[[package]]
+name = "derive_builder_core_fork_arti"
+version = "0.11.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "24c1b715c79be6328caa9a5e1a387a196ea503740f0722ec3dd8f67a9e72314d"
+dependencies = [
+ "darling 0.14.4",
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "derive_builder_fork_arti"
+version = "0.11.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c3eae24d595f4d0ecc90a9a5a6d11c2bd8dafe2375ec4a1ec63250e5ade7d228"
+dependencies = [
+ "derive_builder_macro_fork_arti",
+]
+
+[[package]]
+name = "derive_builder_macro_fork_arti"
+version = "0.11.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "69887769a2489cd946bf782eb2b1bb2cb7bc88551440c94a765d4f040c08ebf3"
+dependencies = [
+ "derive_builder_core_fork_arti",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "derive_more"
+version = "2.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134"
+dependencies = [
+ "derive_more-impl",
+]
+
+[[package]]
+name = "derive_more-impl"
+version = "2.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb"
+dependencies = [
+ "convert_case",
+ "proc-macro2",
+ "quote",
+ "rustc_version",
+ "syn 2.0.106",
+ "unicode-xid",
]
[[package]]
@@ -781,10 +1619,52 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [
"block-buffer",
+ "const-oid",
"crypto-common",
"subtle",
]
+[[package]]
+name = "directories"
+version = "6.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "16f5094c54661b38d03bd7e50df373292118db60b585c08a411c6d840017fe7d"
+dependencies = [
+ "dirs-sys",
+]
+
+[[package]]
+name = "dirs"
+version = "6.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e"
+dependencies = [
+ "dirs-sys",
+]
+
+[[package]]
+name = "dirs-sys"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab"
+dependencies = [
+ "libc",
+ "option-ext",
+ "redox_users",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "displaydoc"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.106",
+]
+
[[package]]
name = "dlv-list"
version = "0.5.2"
@@ -801,10 +1681,74 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f"
[[package]]
-name = "dyn-clone"
-version = "1.0.16"
+name = "downcast-rs"
+version = "2.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "545b22097d44f8a9581187cdf93de7a71e4722bf51200cfaba810865b49a495d"
+checksum = "117240f60069e65410b3ae1bb213295bd828f707b5bec6596a1afc8793ce0cbc"
+
+[[package]]
+name = "dunce"
+version = "1.0.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813"
+
+[[package]]
+name = "dyn-clone"
+version = "1.0.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555"
+
+[[package]]
+name = "ecdsa"
+version = "0.16.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca"
+dependencies = [
+ "der",
+ "digest",
+ "elliptic-curve",
+ "rfc6979",
+ "signature",
+ "spki",
+]
+
+[[package]]
+name = "ed25519"
+version = "2.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53"
+dependencies = [
+ "pkcs8",
+ "signature",
+]
+
+[[package]]
+name = "ed25519-dalek"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9"
+dependencies = [
+ "curve25519-dalek",
+ "ed25519",
+ "merlin",
+ "rand_core 0.6.4",
+ "serde",
+ "sha2",
+ "subtle",
+ "zeroize",
+]
+
+[[package]]
+name = "educe"
+version = "0.4.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0f0042ff8246a363dbe77d2ceedb073339e85a804b9a47636c6e016a9a32c05f"
+dependencies = [
+ "enum-ordinalize",
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
[[package]]
name = "either"
@@ -812,6 +1756,25 @@ version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
+[[package]]
+name = "elliptic-curve"
+version = "0.13.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47"
+dependencies = [
+ "base16ct",
+ "crypto-bigint",
+ "digest",
+ "ff",
+ "generic-array",
+ "group",
+ "pkcs8",
+ "rand_core 0.6.4",
+ "sec1",
+ "subtle",
+ "zeroize",
+]
+
[[package]]
name = "encode_unicode"
version = "0.3.6"
@@ -827,6 +1790,64 @@ dependencies = [
"cfg-if",
]
+[[package]]
+name = "enum-as-inner"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1e6a265c649f3f5979b601d26f1d05ada116434c87741c9493cb56218f76cbc"
+dependencies = [
+ "heck 0.5.0",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.106",
+]
+
+[[package]]
+name = "enum-ordinalize"
+version = "3.1.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1bf1fa3f06bbff1ea5b1a9c7b14aa992a39657db60a2759457328d7e058f49ee"
+dependencies = [
+ "num-bigint",
+ "num-traits",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.106",
+]
+
+[[package]]
+name = "enum_dispatch"
+version = "0.3.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aa18ce2bc66555b3218614519ac839ddb759a7d6720732f979ef8d13be147ecd"
+dependencies = [
+ "once_cell",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.106",
+]
+
+[[package]]
+name = "enumset"
+version = "1.1.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "839c4174b41e75c8f7306110b2c51996a293b8d1d850edd529011841d9fede7d"
+dependencies = [
+ "enumset_derive",
+]
+
+[[package]]
+name = "enumset_derive"
+version = "0.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4bd536557b58c682b217b8fb199afdff47cd3eff260623f19e77074eb073d63a"
+dependencies = [
+ "darling 0.21.3",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.106",
+]
+
[[package]]
name = "equivalent"
version = "1.0.1"
@@ -843,6 +1864,15 @@ dependencies = [
"windows-sys 0.52.0",
]
+[[package]]
+name = "etherparse"
+version = "0.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b8d8a704b617484e9d867a0423cd45f7577f008c4068e2e33378f8d3860a6d73"
+dependencies = [
+ "arrayvec",
+]
+
[[package]]
name = "event-listener"
version = "4.0.3"
@@ -854,13 +1884,24 @@ dependencies = [
"pin-project-lite",
]
+[[package]]
+name = "event-listener"
+version = "5.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab"
+dependencies = [
+ "concurrent-queue",
+ "parking",
+ "pin-project-lite",
+]
+
[[package]]
name = "event-listener-strategy"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "958e4d70b6d5e81971bebec42271ec641e7ff4e170a6fa605f2b8a8b65cb97d3"
dependencies = [
- "event-listener",
+ "event-listener 4.0.3",
"pin-project-lite",
]
@@ -878,9 +1919,9 @@ checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
[[package]]
name = "fastrand"
-version = "2.0.1"
+version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5"
+checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6"
[[package]]
name = "fehler"
@@ -902,6 +1943,16 @@ dependencies = [
"syn 1.0.109",
]
+[[package]]
+name = "ff"
+version = "0.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393"
+dependencies = [
+ "rand_core 0.6.4",
+ "subtle",
+]
+
[[package]]
name = "fiat-crypto"
version = "0.2.5"
@@ -918,6 +1969,35 @@ dependencies = [
"rustc_version",
]
+[[package]]
+name = "figment"
+version = "0.10.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8cb01cd46b0cf372153850f4c6c272d9cbea2da513e07538405148f95bd789f3"
+dependencies = [
+ "atomic 0.6.1",
+ "serde",
+ "toml 0.8.23",
+ "uncased",
+ "version_check",
+]
+
+[[package]]
+name = "filetime"
+version = "0.2.29"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c287a33c7f0a620c38e641e7f60827713987b3c0f26e8ddc9462cc69cf75759"
+dependencies = [
+ "cfg-if",
+ "libc",
+]
+
+[[package]]
+name = "find-msvc-tools"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
+
[[package]]
name = "fixedbitset"
version = "0.5.7"
@@ -934,6 +2014,12 @@ dependencies = [
"miniz_oxide",
]
+[[package]]
+name = "fluid-let"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "749cff877dc1af878a0b31a41dd221a753634401ea0ef2f87b62d3171522485a"
+
[[package]]
name = "flume"
version = "0.10.14"
@@ -944,7 +2030,7 @@ dependencies = [
"futures-sink",
"nanorand",
"pin-project",
- "spin",
+ "spin 0.9.8",
]
[[package]]
@@ -953,6 +2039,18 @@ version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
+[[package]]
+name = "foldhash"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
+
+[[package]]
+name = "foldhash"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb"
+
[[package]]
name = "foreign-types"
version = "0.3.2"
@@ -970,9 +2068,9 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
[[package]]
name = "form_urlencoded"
-version = "1.2.1"
+version = "1.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456"
+checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf"
dependencies = [
"percent-encoding",
]
@@ -983,6 +2081,43 @@ version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa"
+[[package]]
+name = "fs-mistrust"
+version = "0.14.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9f5ac9f88fd18733e0f9ce1f4a95c40eb1d4f83131bf1472e81d1f128fefb7c2"
+dependencies = [
+ "derive_builder_fork_arti",
+ "dirs",
+ "libc",
+ "pwd-grp",
+ "serde",
+ "thiserror 2.0.16",
+ "walkdir",
+]
+
+[[package]]
+name = "fs_extra"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c"
+
+[[package]]
+name = "fslock"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "04412b8935272e3a9bae6f48c7bfff74c2911f60525404edfdd28e49884c3bfb"
+dependencies = [
+ "libc",
+ "winapi",
+]
+
+[[package]]
+name = "funty"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c"
+
[[package]]
name = "futures"
version = "0.3.30"
@@ -1000,9 +2135,9 @@ dependencies = [
[[package]]
name = "futures-channel"
-version = "0.3.30"
+version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78"
+checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d"
dependencies = [
"futures-core",
"futures-sink",
@@ -1010,9 +2145,9 @@ dependencies = [
[[package]]
name = "futures-core"
-version = "0.3.30"
+version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d"
+checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d"
[[package]]
name = "futures-executor"
@@ -1027,15 +2162,15 @@ dependencies = [
[[package]]
name = "futures-io"
-version = "0.3.30"
+version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1"
+checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718"
[[package]]
name = "futures-macro"
-version = "0.3.30"
+version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
+checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b"
dependencies = [
"proc-macro2",
"quote",
@@ -1044,21 +2179,21 @@ dependencies = [
[[package]]
name = "futures-sink"
-version = "0.3.30"
+version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5"
+checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893"
[[package]]
name = "futures-task"
-version = "0.3.30"
+version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004"
+checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393"
[[package]]
name = "futures-util"
-version = "0.3.30"
+version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
+checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6"
dependencies = [
"futures-channel",
"futures-core",
@@ -1068,7 +2203,6 @@ dependencies = [
"futures-task",
"memchr",
"pin-project-lite",
- "pin-utils",
"slab",
]
@@ -1140,6 +2274,7 @@ checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
dependencies = [
"typenum",
"version_check",
+ "zeroize",
]
[[package]]
@@ -1164,11 +2299,36 @@ dependencies = [
"cfg-if",
"js-sys",
"libc",
- "r-efi",
+ "r-efi 5.3.0",
"wasi 0.14.7+wasi-0.2.4",
"wasm-bindgen",
]
+[[package]]
+name = "getrandom"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "r-efi 6.0.0",
+ "wasip2",
+ "wasip3",
+]
+
+[[package]]
+name = "getset"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9cf0fc11e47561d47397154977bc219f4cf809b2974facc3ccb3b89e2436f912"
+dependencies = [
+ "proc-macro-error2",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.106",
+]
+
[[package]]
name = "gettext-rs"
version = "0.7.0"
@@ -1264,7 +2424,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eca5c79337338391f1ab8058d6698125034ce8ef31b72a442437fa6c8580de26"
dependencies = [
"anyhow",
- "heck",
+ "heck 0.4.1",
"proc-macro-crate",
"proc-macro-error",
"proc-macro2",
@@ -1288,6 +2448,12 @@ version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
+[[package]]
+name = "glob-match"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9985c9503b412198aa4197559e9a318524ebc4519c229bfa05a535828c950b9d"
+
[[package]]
name = "gobject-sys"
version = "0.17.10"
@@ -1322,6 +2488,17 @@ dependencies = [
"system-deps",
]
+[[package]]
+name = "group"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63"
+dependencies = [
+ "ff",
+ "rand_core 0.6.4",
+ "subtle",
+]
+
[[package]]
name = "gsk4"
version = "0.6.3"
@@ -1448,6 +2625,26 @@ dependencies = [
"tracing",
]
+[[package]]
+name = "half"
+version = "2.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b"
+dependencies = [
+ "cfg-if",
+ "crunchy",
+ "zerocopy",
+]
+
+[[package]]
+name = "hash32"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606"
+dependencies = [
+ "byteorder",
+]
+
[[package]]
name = "hashbrown"
version = "0.12.3"
@@ -1459,23 +2656,42 @@ name = "hashbrown"
version = "0.14.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
+
+[[package]]
+name = "hashbrown"
+version = "0.15.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
dependencies = [
- "ahash",
+ "foldhash 0.1.5",
]
[[package]]
name = "hashbrown"
-version = "0.16.0"
+version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d"
+checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
+dependencies = [
+ "foldhash 0.2.0",
+]
[[package]]
name = "hashlink"
-version = "0.9.1"
+version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af"
+checksum = "ea0b22561a9c04a7cb1a302c013e0259cd3b4bb619f145b32f72b8b4bcbed230"
dependencies = [
- "hashbrown 0.14.3",
+ "hashbrown 0.16.1",
+]
+
+[[package]]
+name = "heapless"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad"
+dependencies = [
+ "hash32",
+ "stable_deref_trait",
]
[[package]]
@@ -1484,12 +2700,52 @@ version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
+[[package]]
+name = "heck"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
+
[[package]]
name = "hex"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
+[[package]]
+name = "hickory-proto"
+version = "0.25.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f8a6fe56c0038198998a6f217ca4e7ef3a5e51f46163bd6dd60b5c71ca6c6502"
+dependencies = [
+ "async-trait",
+ "cfg-if",
+ "data-encoding",
+ "enum-as-inner",
+ "futures-channel",
+ "futures-io",
+ "futures-util",
+ "idna",
+ "ipnet",
+ "once_cell",
+ "rand 0.9.2",
+ "ring",
+ "thiserror 2.0.16",
+ "tinyvec",
+ "tokio",
+ "tracing",
+ "url",
+]
+
+[[package]]
+name = "hkdf"
+version = "0.12.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7"
+dependencies = [
+ "hmac",
+]
+
[[package]]
name = "hmac"
version = "0.12.1"
@@ -1508,6 +2764,12 @@ dependencies = [
"windows-sys 0.52.0",
]
+[[package]]
+name = "hostname-validator"
+version = "1.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f558a64ac9af88b5ba400d99b579451af0d39c6d360980045b91aac966d705e2"
+
[[package]]
name = "http"
version = "0.2.11"
@@ -1576,6 +2838,22 @@ version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
+[[package]]
+name = "humantime"
+version = "2.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "135b12329e5e3ce057a9f972339ea52bc954fe1e9358ef27f95e89716fbc5424"
+
+[[package]]
+name = "humantime-serde"
+version = "1.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "57a3db5ea5923d99402c94e9feb261dc5ee9b4efa158b0315f788cf549cc200c"
+dependencies = [
+ "humantime",
+ "serde",
+]
+
[[package]]
name = "hyper"
version = "0.14.28"
@@ -1593,7 +2871,7 @@ dependencies = [
"httpdate",
"itoa",
"pin-project-lite",
- "socket2 0.4.10",
+ "socket2 0.5.10",
"tokio",
"tower-service",
"tracing",
@@ -1679,20 +2957,149 @@ dependencies = [
"hyper 1.6.0",
"libc",
"pin-project-lite",
- "socket2 0.5.10",
+ "socket2 0.6.3",
"tokio",
"tower-service",
"tracing",
]
[[package]]
-name = "idna"
-version = "0.5.0"
+name = "iana-time-zone"
+version = "0.1.65"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6"
+checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470"
dependencies = [
- "unicode-bidi",
- "unicode-normalization",
+ "android_system_properties",
+ "core-foundation-sys",
+ "iana-time-zone-haiku",
+ "js-sys",
+ "log",
+ "wasm-bindgen",
+ "windows-core 0.62.2",
+]
+
+[[package]]
+name = "iana-time-zone-haiku"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
+dependencies = [
+ "cc",
+]
+
+[[package]]
+name = "icu_collections"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c"
+dependencies = [
+ "displaydoc",
+ "potential_utf",
+ "utf8_iter",
+ "yoke",
+ "zerofrom",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_locale_core"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29"
+dependencies = [
+ "displaydoc",
+ "litemap",
+ "tinystr",
+ "writeable",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_normalizer"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4"
+dependencies = [
+ "icu_collections",
+ "icu_normalizer_data",
+ "icu_properties",
+ "icu_provider",
+ "smallvec",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_normalizer_data"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38"
+
+[[package]]
+name = "icu_properties"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de"
+dependencies = [
+ "icu_collections",
+ "icu_locale_core",
+ "icu_properties_data",
+ "icu_provider",
+ "zerotrie",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_properties_data"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14"
+
+[[package]]
+name = "icu_provider"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421"
+dependencies = [
+ "displaydoc",
+ "icu_locale_core",
+ "writeable",
+ "yoke",
+ "zerofrom",
+ "zerotrie",
+ "zerovec",
+]
+
+[[package]]
+name = "id-arena"
+version = "2.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954"
+
+[[package]]
+name = "ident_case"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
+
+[[package]]
+name = "idna"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de"
+dependencies = [
+ "idna_adapter",
+ "smallvec",
+ "utf8_iter",
+]
+
+[[package]]
+name = "idna_adapter"
+version = "1.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714"
+dependencies = [
+ "icu_normalizer",
+ "icu_properties",
]
[[package]]
@@ -1703,6 +3110,7 @@ checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
dependencies = [
"autocfg",
"hashbrown 0.12.3",
+ "serde",
]
[[package]]
@@ -1712,7 +3120,29 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b0f83760fb341a774ed326568e19f5a863af4a952def8c39f9ab92fd95b88e5"
dependencies = [
"equivalent",
- "hashbrown 0.16.0",
+ "hashbrown 0.16.1",
+ "serde",
+ "serde_core",
+]
+
+[[package]]
+name = "inotify"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bd5b3eaf1a28b758ac0faa5a4254e8ab2705605496f1b1f3fbbc3988ad73d199"
+dependencies = [
+ "bitflags 2.11.1",
+ "inotify-sys",
+ "libc",
+]
+
+[[package]]
+name = "inotify-sys"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb"
+dependencies = [
+ "libc",
]
[[package]]
@@ -1724,13 +3154,22 @@ dependencies = [
"generic-array",
]
+[[package]]
+name = "inventory"
+version = "0.3.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a4f0c30c76f2f4ccee3fe55a2435f691ca00c0e4bd87abe4f4a851b1d4dac39b"
+dependencies = [
+ "rustversion",
+]
+
[[package]]
name = "io-uring"
version = "0.7.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "046fa2d4d00aea763528b4950358d0ead425372445dc8ff86312b3c69ff7727b"
dependencies = [
- "bitflags 2.4.2",
+ "bitflags 2.11.1",
"cfg-if",
"libc",
]
@@ -1763,6 +3202,33 @@ version = "2.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3"
+[[package]]
+name = "ipnetwork"
+version = "0.21.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf370abdafd54d13e54a620e8c3e1145f28e46cc9d704bc6d94414559df41763"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "itertools"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+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 = "itertools"
version = "0.14.0"
@@ -1780,28 +3246,63 @@ checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
[[package]]
name = "jobserver"
-version = "0.1.27"
+version = "0.1.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8c37f63953c4c63420ed5fd3d6d398c719489b9f872b9fa683262f8edd363c7d"
+checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33"
dependencies = [
+ "getrandom 0.3.3",
"libc",
]
[[package]]
name = "js-sys"
-version = "0.3.80"
+version = "0.3.99"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "852f13bec5eba4ba9afbeb93fd7c13fe56147f055939ae21c43a29a0ecb2702e"
+checksum = "142bc4740e452c1e57ade0cbc129f139c9093e354346f0872ef985f4f5cf5f11"
dependencies = [
+ "cfg-if",
+ "futures-util",
"once_cell",
"wasm-bindgen",
]
+[[package]]
+name = "keccak"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cb26cec98cce3a3d96cbb7bced3c4b16e3d13f27ec56dbd62cbc8f39cfb9d653"
+dependencies = [
+ "cpufeatures",
+]
+
+[[package]]
+name = "kqueue"
+version = "1.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eac30106d7dce88daf4a3fcb4879ea939476d5074a9b7ddd0fb97fa4bed5596a"
+dependencies = [
+ "kqueue-sys",
+ "libc",
+]
+
+[[package]]
+name = "kqueue-sys"
+version = "1.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "07293a4e297ac234359b510362495713f75ea345d5307140414f20c69ffeb087"
+dependencies = [
+ "bitflags 2.11.1",
+ "libc",
+]
+
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+dependencies = [
+ "spin 0.5.2",
+]
[[package]]
name = "lazycell"
@@ -1809,6 +3310,12 @@ version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
+[[package]]
+name = "leb128fmt"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
+
[[package]]
name = "libadwaita"
version = "0.4.4"
@@ -1869,10 +3376,25 @@ dependencies = [
]
[[package]]
-name = "libsqlite3-sys"
-version = "0.28.0"
+name = "libm"
+version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0c10584274047cb335c23d3e61bcef8e323adae7c5c8c760540f73610177fc3f"
+checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981"
+
+[[package]]
+name = "libredox"
+version = "0.1.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f02ab6bace2054fb888a3c16f990117b579d14a3088e472d63c6011fa185c9d3"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "libsqlite3-sys"
+version = "0.36.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95b4103cffefa72eb8428cb6b47d6627161e51c2739fc5e3b734584157bc642a"
dependencies = [
"cc",
"pkg-config",
@@ -1903,6 +3425,12 @@ version = "0.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c"
+[[package]]
+name = "litemap"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0"
+
[[package]]
name = "locale_config"
version = "0.3.0"
@@ -1938,6 +3466,17 @@ version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154"
+[[package]]
+name = "lzma-sys"
+version = "0.1.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5fda04ab3764e6cde78b9974eec4f779acaba7c4e84b36eca3cf77c581b85d27"
+dependencies = [
+ "cc",
+ "libc",
+ "pkg-config",
+]
+
[[package]]
name = "malloc_buf"
version = "0.0.6"
@@ -1947,6 +3486,12 @@ dependencies = [
"libc",
]
+[[package]]
+name = "managed"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ca88d725a0a943b096803bd34e73a4437208b6077654cc4ecb2947a5f91618d"
+
[[package]]
name = "matchers"
version = "0.1.0"
@@ -1962,12 +3507,31 @@ version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94"
+[[package]]
+name = "md-5"
+version = "0.10.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf"
+dependencies = [
+ "cfg-if",
+ "digest",
+]
+
[[package]]
name = "memchr"
version = "2.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149"
+[[package]]
+name = "memmap2"
+version = "0.9.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "714098028fe011992e1c3962653c96b2d578c4b4bce9036e15ff220319b1e0e3"
+dependencies = [
+ "libc",
+]
+
[[package]]
name = "memoffset"
version = "0.7.1"
@@ -1986,6 +3550,18 @@ dependencies = [
"autocfg",
]
+[[package]]
+name = "merlin"
+version = "3.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "58c38e2799fc0978b65dfff8023ec7843e2330bb462f19198840b34b6582397d"
+dependencies = [
+ "byteorder",
+ "keccak",
+ "rand_core 0.6.4",
+ "zeroize",
+]
+
[[package]]
name = "miette"
version = "5.10.0"
@@ -2037,6 +3613,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd"
dependencies = [
"libc",
+ "log",
"wasi 0.11.0+wasi-snapshot-preview1",
"windows-sys 0.52.0",
]
@@ -2074,6 +3651,22 @@ dependencies = [
"tempfile",
]
+[[package]]
+name = "netstack-smoltcp"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "398691cef792b89eb5d29e6ea6b3999def706b908d355e29815ba8101cf5c4c8"
+dependencies = [
+ "etherparse",
+ "futures",
+ "rand 0.8.5",
+ "smoltcp",
+ "spin 0.9.8",
+ "tokio",
+ "tokio-util",
+ "tracing",
+]
+
[[package]]
name = "nix"
version = "0.26.4"
@@ -2093,7 +3686,7 @@ version = "0.27.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053"
dependencies = [
- "bitflags 2.4.2",
+ "bitflags 2.11.1",
"cfg-if",
"libc",
"memoffset 0.9.0",
@@ -2109,6 +3702,47 @@ dependencies = [
"minimal-lexical",
]
+[[package]]
+name = "nonany"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f6b8866ec53810a9a4b3d434a29801e78c707430a9ae11c2db4b8b62bb9675a0"
+
+[[package]]
+name = "notify"
+version = "8.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4d3d07927151ff8575b7087f245456e549fea62edf0ec4e565a5ee50c8402bc3"
+dependencies = [
+ "bitflags 2.11.1",
+ "inotify",
+ "kqueue",
+ "libc",
+ "log",
+ "mio",
+ "notify-types",
+ "walkdir",
+ "windows-sys 0.60.2",
+]
+
+[[package]]
+name = "notify-types"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "42b8cfee0e339a0337359f3c88165702ac6e600dc01c0cc9579a92d62b08477a"
+dependencies = [
+ "bitflags 2.11.1",
+]
+
+[[package]]
+name = "ntapi"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c3b335231dfd352ffb0f8017f3b6027a4917f7df785ea2143d8af2adc66980ae"
+dependencies = [
+ "winapi",
+]
+
[[package]]
name = "nu-ansi-term"
version = "0.46.0"
@@ -2119,6 +3753,90 @@ dependencies = [
"winapi",
]
+[[package]]
+name = "num-bigint"
+version = "0.4.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9"
+dependencies = [
+ "num-integer",
+ "num-traits",
+]
+
+[[package]]
+name = "num-bigint-dig"
+version = "0.8.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e661dda6640fad38e827a6d4a310ff4763082116fe217f279885c97f511bb0b7"
+dependencies = [
+ "lazy_static",
+ "libm",
+ "num-integer",
+ "num-iter",
+ "num-traits",
+ "rand 0.8.5",
+ "smallvec",
+ "zeroize",
+]
+
+[[package]]
+name = "num-conv"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441"
+
+[[package]]
+name = "num-integer"
+version = "0.1.46"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f"
+dependencies = [
+ "num-traits",
+]
+
+[[package]]
+name = "num-iter"
+version = "0.1.45"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf"
+dependencies = [
+ "autocfg",
+ "num-integer",
+ "num-traits",
+]
+
+[[package]]
+name = "num-traits"
+version = "0.2.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
+dependencies = [
+ "autocfg",
+ "libm",
+]
+
+[[package]]
+name = "num_enum"
+version = "0.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5d0bca838442ec211fa11de3a8b0e0e8f3a4522575b5c4c06ed722e005036f26"
+dependencies = [
+ "num_enum_derive",
+ "rustversion",
+]
+
+[[package]]
+name = "num_enum_derive"
+version = "0.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "680998035259dcfcafe653688bf2aa6d3e2dc05e98be6ab46afb089dc84f1df8"
+dependencies = [
+ "proc-macro-crate",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.106",
+]
+
[[package]]
name = "objc"
version = "0.2.7"
@@ -2139,6 +3857,25 @@ dependencies = [
"objc_id",
]
+[[package]]
+name = "objc2-core-foundation"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536"
+dependencies = [
+ "bitflags 2.11.1",
+]
+
+[[package]]
+name = "objc2-io-kit"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "33fafba39597d6dc1fb709123dfa8289d39406734be322956a69f0931c73bb15"
+dependencies = [
+ "libc",
+ "objc2-core-foundation",
+]
+
[[package]]
name = "objc_id"
version = "0.1.1"
@@ -2159,9 +3896,28 @@ dependencies = [
[[package]]
name = "once_cell"
-version = "1.19.0"
+version = "1.21.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
+checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
+dependencies = [
+ "critical-section",
+ "portable-atomic",
+]
+
+[[package]]
+name = "oneshot-fused-workaround"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e17b52d0e4a06a4c7eb8d2943c0015fa628cf4ccc409429cebc0f5bed6d33a82"
+dependencies = [
+ "futures",
+]
+
+[[package]]
+name = "oorandom"
+version = "11.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e"
[[package]]
name = "opaque-debug"
@@ -2175,7 +3931,7 @@ version = "0.10.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "15c9d69dd87a29568d4d017cfe8ec518706046a05184e5aea92d0af890b803c8"
dependencies = [
- "bitflags 2.4.2",
+ "bitflags 2.11.1",
"cfg-if",
"foreign-types",
"libc",
@@ -2213,6 +3969,21 @@ dependencies = [
"vcpkg",
]
+[[package]]
+name = "option-ext"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
+
+[[package]]
+name = "ordered-float"
+version = "2.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c"
+dependencies = [
+ "num-traits",
+]
+
[[package]]
name = "ordered-multimap"
version = "0.7.3"
@@ -2223,12 +3994,69 @@ dependencies = [
"hashbrown 0.14.3",
]
+[[package]]
+name = "os_str_bytes"
+version = "6.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2355d85b9a3786f481747ced0e0ff2ba35213a1f9bd406ed906554d7af805a1"
+dependencies = [
+ "memchr",
+]
+
[[package]]
name = "overload"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
+[[package]]
+name = "p256"
+version = "0.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b"
+dependencies = [
+ "ecdsa",
+ "elliptic-curve",
+ "primeorder",
+ "sha2",
+]
+
+[[package]]
+name = "p384"
+version = "0.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fe42f1670a52a47d448f14b6a5c61dd78fce51856e68edaa38f7ae3a46b8d6b6"
+dependencies = [
+ "ecdsa",
+ "elliptic-curve",
+ "primeorder",
+ "sha2",
+]
+
+[[package]]
+name = "p521"
+version = "0.13.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fc9e2161f1f215afdfce23677034ae137bbd45016a880c2eb3ba8eb95f085b2"
+dependencies = [
+ "base16ct",
+ "ecdsa",
+ "elliptic-curve",
+ "primeorder",
+ "rand_core 0.6.4",
+ "sha2",
+]
+
+[[package]]
+name = "page_size"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "30d5b2194ed13191c1999ae0704b7839fb18384fa22e49b57eeaa97d79ce40da"
+dependencies = [
+ "libc",
+ "winapi",
+]
+
[[package]]
name = "pango"
version = "0.17.10"
@@ -2295,6 +4123,23 @@ dependencies = [
"subtle",
]
+[[package]]
+name = "password-hash"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166"
+dependencies = [
+ "base64ct",
+ "rand_core 0.6.4",
+ "subtle",
+]
+
+[[package]]
+name = "paste"
+version = "1.0.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
+
[[package]]
name = "pbkdf2"
version = "0.11.0"
@@ -2303,7 +4148,7 @@ checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917"
dependencies = [
"digest",
"hmac",
- "password-hash",
+ "password-hash 0.4.2",
"sha2",
]
@@ -2314,10 +4159,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
[[package]]
-name = "percent-encoding"
-version = "2.3.1"
+name = "pem-rfc7468"
+version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
+checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412"
+dependencies = [
+ "base64ct",
+]
+
+[[package]]
+name = "percent-encoding"
+version = "2.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
[[package]]
name = "petgraph"
@@ -2329,6 +4183,49 @@ dependencies = [
"indexmap 2.11.4",
]
+[[package]]
+name = "phf"
+version = "0.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c1562dc717473dbaa4c1f85a36410e03c047b2e7df7f45ee938fbef64ae7fadf"
+dependencies = [
+ "phf_macros",
+ "phf_shared",
+ "serde",
+]
+
+[[package]]
+name = "phf_generator"
+version = "0.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "135ace3a761e564ec88c03a77317a7c6b80bb7f7135ef2544dbe054243b89737"
+dependencies = [
+ "fastrand",
+ "phf_shared",
+]
+
+[[package]]
+name = "phf_macros"
+version = "0.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "812f032b54b1e759ccd5f8b6677695d5268c588701effba24601f6932f8269ef"
+dependencies = [
+ "phf_generator",
+ "phf_shared",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.106",
+]
+
+[[package]]
+name = "phf_shared"
+version = "0.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e57fef6bc5981e38c2ce2d63bfa546861309f875b8a75f092d1d54ae2d64f266"
+dependencies = [
+ "siphasher",
+]
+
[[package]]
name = "pin-project"
version = "1.1.4"
@@ -2361,6 +4258,27 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
+[[package]]
+name = "pkcs1"
+version = "0.7.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f"
+dependencies = [
+ "der",
+ "pkcs8",
+ "spki",
+]
+
+[[package]]
+name = "pkcs8"
+version = "0.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7"
+dependencies = [
+ "der",
+ "spki",
+]
+
[[package]]
name = "pkg-config"
version = "0.3.29"
@@ -2373,6 +4291,34 @@ version = "3.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "626dec3cac7cc0e1577a2ec3fc496277ec2baa084bebad95bb6fdbfae235f84c"
+[[package]]
+name = "plotters"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747"
+dependencies = [
+ "num-traits",
+ "plotters-backend",
+ "plotters-svg",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "plotters-backend"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a"
+
+[[package]]
+name = "plotters-svg"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670"
+dependencies = [
+ "plotters-backend",
+]
+
[[package]]
name = "poly1305"
version = "0.8.0"
@@ -2384,6 +4330,36 @@ dependencies = [
"universal-hash",
]
+[[package]]
+name = "portable-atomic"
+version = "1.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49"
+
+[[package]]
+name = "postage"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af3fb618632874fb76937c2361a7f22afd393c982a2165595407edc75b06d3c1"
+dependencies = [
+ "atomic 0.5.3",
+ "crossbeam-queue",
+ "futures",
+ "parking_lot",
+ "pin-project",
+ "static_assertions",
+ "thiserror 1.0.56",
+]
+
+[[package]]
+name = "potential_utf"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564"
+dependencies = [
+ "zerovec",
+]
+
[[package]]
name = "powerfmt"
version = "0.2.0"
@@ -2398,14 +4374,34 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[package]]
name = "prettyplease"
-version = "0.2.16"
+version = "0.2.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a41cf62165e97c7f814d2221421dbb9afcbcdb0a88068e5ea206e19951c2cbb5"
+checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b"
dependencies = [
"proc-macro2",
"syn 2.0.106",
]
+[[package]]
+name = "primeorder"
+version = "0.13.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6"
+dependencies = [
+ "elliptic-curve",
+]
+
+[[package]]
+name = "priority-queue"
+version = "2.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93980406f12d9f8140ed5abe7155acb10bb1e69ea55c88960b9c2f117445ef96"
+dependencies = [
+ "equivalent",
+ "indexmap 2.11.4",
+ "serde",
+]
+
[[package]]
name = "proc-macro-crate"
version = "1.3.1"
@@ -2440,6 +4436,28 @@ dependencies = [
"version_check",
]
+[[package]]
+name = "proc-macro-error-attr2"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5"
+dependencies = [
+ "proc-macro2",
+ "quote",
+]
+
+[[package]]
+name = "proc-macro-error2"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802"
+dependencies = [
+ "proc-macro-error-attr2",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.106",
+]
+
[[package]]
name = "proc-macro2"
version = "1.0.101"
@@ -2465,8 +4483,8 @@ version = "0.13.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be769465445e8c1474e9c5dac2018218498557af32d9ed057325ec9a41ae81bf"
dependencies = [
- "heck",
- "itertools",
+ "heck 0.5.0",
+ "itertools 0.14.0",
"log",
"multimap",
"once_cell",
@@ -2486,7 +4504,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d"
dependencies = [
"anyhow",
- "itertools",
+ "itertools 0.14.0",
"proc-macro2",
"quote",
"syn 2.0.106",
@@ -2501,6 +4519,18 @@ dependencies = [
"prost",
]
+[[package]]
+name = "pwd-grp"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0e2023f41b5fcb7c30eb5300a5733edfaa9e0e0d502d51b586f65633fd39e40c"
+dependencies = [
+ "derive-deftly",
+ "libc",
+ "paste",
+ "thiserror 2.0.16",
+]
+
[[package]]
name = "quinn"
version = "0.11.9"
@@ -2514,7 +4544,7 @@ dependencies = [
"quinn-udp",
"rustc-hash 2.1.1",
"rustls",
- "socket2 0.5.10",
+ "socket2 0.6.3",
"thiserror 2.0.16",
"tokio",
"tracing",
@@ -2551,16 +4581,16 @@ dependencies = [
"cfg_aliases",
"libc",
"once_cell",
- "socket2 0.5.10",
+ "socket2 0.6.3",
"tracing",
- "windows-sys 0.52.0",
+ "windows-sys 0.60.2",
]
[[package]]
name = "quote"
-version = "1.0.35"
+version = "1.0.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
+checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
dependencies = [
"proc-macro2",
]
@@ -2571,6 +4601,18 @@ version = "5.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
+[[package]]
+name = "r-efi"
+version = "6.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
+
+[[package]]
+name = "radium"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09"
+
[[package]]
name = "rand"
version = "0.8.5"
@@ -2630,6 +4672,46 @@ dependencies = [
"getrandom 0.3.3",
]
+[[package]]
+name = "rand_jitter"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b16df48f071248e67b8fc5e866d9448d45c08ad8b672baaaf796e2f15e606ff0"
+dependencies = [
+ "libc",
+ "rand_core 0.9.3",
+ "winapi",
+]
+
+[[package]]
+name = "rayon"
+version = "1.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fb39b166781f92d482534ef4b4b1b2568f42613b53e5b6c160e24cfbfa30926d"
+dependencies = [
+ "either",
+ "rayon-core",
+]
+
+[[package]]
+name = "rayon-core"
+version = "1.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91"
+dependencies = [
+ "crossbeam-deque",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "rdrand"
+version = "0.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d92195228612ac8eed47adbc2ed0f04e513a4ccb98175b6f2bd04d963b533655"
+dependencies = [
+ "rand_core 0.6.4",
+]
+
[[package]]
name = "redox_syscall"
version = "0.4.1"
@@ -2640,15 +4722,46 @@ dependencies = [
]
[[package]]
-name = "regex"
-version = "1.10.3"
+name = "redox_users"
+version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15"
+checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac"
+dependencies = [
+ "getrandom 0.2.12",
+ "libredox",
+ "thiserror 2.0.16",
+]
+
+[[package]]
+name = "ref-cast"
+version = "1.0.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d"
+dependencies = [
+ "ref-cast-impl",
+]
+
+[[package]]
+name = "ref-cast-impl"
+version = "1.0.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.106",
+]
+
+[[package]]
+name = "regex"
+version = "1.12.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276"
dependencies = [
"aho-corasick",
"memchr",
- "regex-automata 0.4.4",
- "regex-syntax 0.8.2",
+ "regex-automata 0.4.14",
+ "regex-syntax 0.8.10",
]
[[package]]
@@ -2662,13 +4775,13 @@ dependencies = [
[[package]]
name = "regex-automata"
-version = "0.4.4"
+version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3b7fa1134405e2ec9353fd416b17f8dacd46c473d7d3fd1cf202706a14eb792a"
+checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f"
dependencies = [
"aho-corasick",
"memchr",
- "regex-syntax 0.8.2",
+ "regex-syntax 0.8.10",
]
[[package]]
@@ -2679,9 +4792,9 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
[[package]]
name = "regex-syntax"
-version = "0.8.2"
+version = "0.8.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
+checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a"
[[package]]
name = "relm4"
@@ -2792,6 +4905,25 @@ dependencies = [
"winreg 0.52.0",
]
+[[package]]
+name = "retry-error"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c322ea521636c5a3f13685a6266055b2dda7e54e2be35214d7c2a5d0672a5db"
+dependencies = [
+ "humantime",
+]
+
+[[package]]
+name = "rfc6979"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2"
+dependencies = [
+ "hmac",
+ "subtle",
+]
+
[[package]]
name = "ring"
version = "0.17.7"
@@ -2801,23 +4933,56 @@ dependencies = [
"cc",
"getrandom 0.2.12",
"libc",
- "spin",
+ "spin 0.9.8",
"untrusted",
"windows-sys 0.48.0",
]
[[package]]
-name = "rusqlite"
-version = "0.31.0"
+name = "rsa"
+version = "0.9.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b838eba278d213a8beaf485bd313fd580ca4505a00d5871caeb1457c55322cae"
+checksum = "b8573f03f5883dcaebdfcf4725caa1ecb9c15b2ef50c43a07b816e06799bb12d"
dependencies = [
- "bitflags 2.4.2",
+ "const-oid",
+ "digest",
+ "num-bigint-dig",
+ "num-integer",
+ "num-traits",
+ "pkcs1",
+ "pkcs8",
+ "rand_core 0.6.4",
+ "sha2",
+ "signature",
+ "spki",
+ "subtle",
+ "zeroize",
+]
+
+[[package]]
+name = "rsqlite-vfs"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c51c9ae4df8a7fba42103df5c621fa3c37eccf3a3c650879e90fc48b11cc192c"
+dependencies = [
+ "hashbrown 0.16.1",
+ "thiserror 2.0.16",
+]
+
+[[package]]
+name = "rusqlite"
+version = "0.38.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f1c93dd1c9683b438c392c492109cb702b8090b2bfc8fed6f6e4eb4523f17af3"
+dependencies = [
+ "bitflags 2.11.1",
"fallible-iterator",
"fallible-streaming-iterator",
"hashlink",
"libsqlite3-sys",
"smallvec",
+ "sqlite-wasm-rs",
+ "time",
]
[[package]]
@@ -2857,13 +5022,22 @@ dependencies = [
"semver",
]
+[[package]]
+name = "rusticata-macros"
+version = "4.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632"
+dependencies = [
+ "nom",
+]
+
[[package]]
name = "rustix"
version = "0.38.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "322394588aaf33c24007e8bb3238ee3e4c5c09c084ab32bc73890b99ff326bca"
dependencies = [
- "bitflags 2.4.2",
+ "bitflags 2.11.1",
"errno",
"libc",
"linux-raw-sys",
@@ -2876,6 +5050,8 @@ version = "0.23.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0ebcbd2f03de0fc1122ad9bb24b127a5a6cd51d72604a3f3c50ac459762b6cc"
dependencies = [
+ "aws-lc-rs",
+ "log",
"once_cell",
"ring",
"rustls-pki-types",
@@ -2905,10 +5081,11 @@ dependencies = [
[[package]]
name = "rustls-webpki"
-version = "0.103.6"
+version = "0.103.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8572f3c2cb9934231157b45499fc41e1f58c589fdfb81a844ba873265e80f8eb"
+checksum = "0a17884ae0c1b773f1ccd2bd4a8c72f16da897310a98b0e84bf349ad5ead92fc"
dependencies = [
+ "aws-lc-rs",
"ring",
"rustls-pki-types",
"untrusted",
@@ -2926,6 +5103,43 @@ version = "1.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c"
+[[package]]
+name = "safelog"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "97a907e0d82c61b1b06a2030c968eb313dcf432686b77801a26bc4b206f96573"
+dependencies = [
+ "derive_more",
+ "educe",
+ "either",
+ "fluid-let",
+ "thiserror 2.0.16",
+]
+
+[[package]]
+name = "same-file"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
+name = "sanitize-filename"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bc984f4f9ceb736a7bb755c3e3bd17dc56370af2600c9780dcc48c66453da34d"
+dependencies = [
+ "regex",
+]
+
+[[package]]
+name = "saturating-time"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b63583a1dd0647d1484228529ab4ecaa874048d2956f117362aa5f5826456230"
+
[[package]]
name = "schannel"
version = "0.1.23"
@@ -2947,6 +5161,30 @@ dependencies = [
"serde_json",
]
+[[package]]
+name = "schemars"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4cd191f9397d57d581cddd31014772520aa448f65ef991055d7f61582c65165f"
+dependencies = [
+ "dyn-clone",
+ "ref-cast",
+ "serde",
+ "serde_json",
+]
+
+[[package]]
+name = "schemars"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2b42f36aa1cd011945615b92222f6bf73c599a102a300334cd7f8dbeec726cc"
+dependencies = [
+ "dyn-clone",
+ "ref-cast",
+ "serde",
+ "serde_json",
+]
+
[[package]]
name = "schemars_derive"
version = "0.8.16"
@@ -2965,6 +5203,20 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
+[[package]]
+name = "sec1"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc"
+dependencies = [
+ "base16ct",
+ "der",
+ "generic-array",
+ "pkcs8",
+ "subtle",
+ "zeroize",
+]
+
[[package]]
name = "security-framework"
version = "2.9.2"
@@ -2996,28 +5248,38 @@ checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0"
[[package]]
name = "serde"
-version = "1.0.226"
+version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0dca6411025b24b60bfa7ec1fe1f8e710ac09782dca409ee8237ba74b51295fd"
+checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
dependencies = [
"serde_core",
"serde_derive",
]
[[package]]
-name = "serde_core"
-version = "1.0.226"
+name = "serde-value"
+version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ba2ba63999edb9dac981fb34b3e5c0d111a69b0924e253ed29d83f7c99e966a4"
+checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c"
+dependencies = [
+ "ordered-float",
+ "serde",
+]
+
+[[package]]
+name = "serde_core"
+version = "1.0.228"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
-version = "1.0.226"
+version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8db53ae22f34573731bafa1db20f04027b2d25e02d8205921b569171699cdb33"
+checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
dependencies = [
"proc-macro2",
"quote",
@@ -3036,14 +5298,26 @@ dependencies = [
]
[[package]]
-name = "serde_json"
-version = "1.0.111"
+name = "serde_ignored"
+version = "0.1.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "176e46fa42316f18edd598015a5166857fc835ec732f5215eac6b7bdbf0a84f4"
+checksum = "115dffd5f3853e06e746965a20dcbae6ee747ae30b543d91b0e089668bb07798"
+dependencies = [
+ "serde",
+ "serde_core",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.150"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9"
dependencies = [
"itoa",
- "ryu",
+ "memchr",
"serde",
+ "serde_core",
+ "zmij",
]
[[package]]
@@ -3066,6 +5340,15 @@ dependencies = [
"serde",
]
+[[package]]
+name = "serde_spanned"
+version = "1.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26"
+dependencies = [
+ "serde_core",
+]
+
[[package]]
name = "serde_urlencoded"
version = "0.7.1"
@@ -3078,6 +5361,51 @@ dependencies = [
"serde",
]
+[[package]]
+name = "serde_with"
+version = "3.20.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e72c1c2cb7b223fafb600a619537a871c2818583d619401b785e7c0b746ccde2"
+dependencies = [
+ "base64 0.22.1",
+ "bs58",
+ "chrono",
+ "hex",
+ "indexmap 1.9.3",
+ "indexmap 2.11.4",
+ "schemars 0.9.0",
+ "schemars 1.2.1",
+ "serde_core",
+ "serde_json",
+ "serde_with_macros",
+ "time",
+]
+
+[[package]]
+name = "serde_with_macros"
+version = "3.20.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b90c488738ecb4fb0262f41f43bc40efc5868d9fb744319ddf5f5317f417bfac"
+dependencies = [
+ "darling 0.23.0",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.106",
+]
+
+[[package]]
+name = "serde_yaml"
+version = "0.9.34+deprecated"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47"
+dependencies = [
+ "indexmap 2.11.4",
+ "itoa",
+ "ryu",
+ "serde",
+ "unsafe-libyaml",
+]
+
[[package]]
name = "sha-1"
version = "0.10.1"
@@ -3111,6 +5439,16 @@ dependencies = [
"digest",
]
+[[package]]
+name = "sha3"
+version = "0.10.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77fd7028345d415a4034cf8777cd4f8ab1851274233b45f84e3d955502d93874"
+dependencies = [
+ "digest",
+ "keccak",
+]
+
[[package]]
name = "sharded-slab"
version = "0.1.7"
@@ -3120,6 +5458,17 @@ dependencies = [
"lazy_static",
]
+[[package]]
+name = "shellexpand"
+version = "3.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32824fab5e16e6c4d86dc1ba84489390419a39f97699852b66480bb87d297ed8"
+dependencies = [
+ "bstr",
+ "dirs",
+ "os_str_bytes",
+]
+
[[package]]
name = "shlex"
version = "1.3.0"
@@ -3135,6 +5484,22 @@ dependencies = [
"libc",
]
+[[package]]
+name = "signature"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de"
+dependencies = [
+ "digest",
+ "rand_core 0.6.4",
+]
+
+[[package]]
+name = "siphasher"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ee5873ec9cce0195efcb7a4e9507a04cd49aec9c83d0389df45b1ef7ba2e649"
+
[[package]]
name = "slab"
version = "0.4.9"
@@ -3144,6 +5509,29 @@ dependencies = [
"autocfg",
]
+[[package]]
+name = "slotmap"
+version = "1.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bdd58c3c93c3d278ca835519292445cb4b0d4dc59ccfdf7ceadaab3f8aeb4038"
+dependencies = [
+ "serde",
+ "version_check",
+]
+
+[[package]]
+name = "slotmap-careful"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed92816c1fbb29891a525b92d5fa95757c9dee47044f76c8e06ceb1e052a8d64"
+dependencies = [
+ "paste",
+ "serde",
+ "slotmap",
+ "thiserror 2.0.16",
+ "void",
+]
+
[[package]]
name = "smallvec"
version = "1.13.1"
@@ -3151,13 +5539,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7"
[[package]]
-name = "socket2"
-version = "0.4.10"
+name = "smoltcp"
+version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d"
+checksum = "dad095989c1533c1c266d9b1e8d70a1329dd3723c3edac6d03bbd67e7bf6f4bb"
dependencies = [
- "libc",
- "winapi",
+ "bitflags 1.3.2",
+ "byteorder",
+ "cfg-if",
+ "defmt 0.3.100",
+ "heapless",
+ "log",
+ "managed",
]
[[package]]
@@ -3170,6 +5563,22 @@ dependencies = [
"windows-sys 0.52.0",
]
+[[package]]
+name = "socket2"
+version = "0.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e"
+dependencies = [
+ "libc",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "spin"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
+
[[package]]
name = "spin"
version = "0.9.8"
@@ -3179,6 +5588,70 @@ dependencies = [
"lock_api",
]
+[[package]]
+name = "spki"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d"
+dependencies = [
+ "base64ct",
+ "der",
+]
+
+[[package]]
+name = "sqlite-wasm-rs"
+version = "0.5.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc3efc0da82635d7e1ced0053bbbfa8c7ab9645d0bf36ceb4f7127bb85315d75"
+dependencies = [
+ "cc",
+ "js-sys",
+ "rsqlite-vfs",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "ssh-cipher"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "caac132742f0d33c3af65bfcde7f6aa8f62f0e991d80db99149eb9d44708784f"
+dependencies = [
+ "cipher",
+ "ssh-encoding",
+]
+
+[[package]]
+name = "ssh-encoding"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eb9242b9ef4108a78e8cd1a2c98e193ef372437f8c22be363075233321dd4a15"
+dependencies = [
+ "base64ct",
+ "pem-rfc7468",
+ "sha2",
+]
+
+[[package]]
+name = "ssh-key"
+version = "0.6.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3b86f5297f0f04d08cabaa0f6bff7cb6aec4d9c3b49d87990d63da9d9156a8c3"
+dependencies = [
+ "num-bigint-dig",
+ "p256",
+ "p384",
+ "p521",
+ "rand_core 0.6.4",
+ "rsa",
+ "sec1",
+ "sha2",
+ "signature",
+ "ssh-cipher",
+ "ssh-encoding",
+ "subtle",
+ "zeroize",
+]
+
[[package]]
name = "ssri"
version = "9.2.0"
@@ -3195,6 +5668,18 @@ dependencies = [
"xxhash-rust",
]
+[[package]]
+name = "stable_deref_trait"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
+
+[[package]]
+name = "static_assertions"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
+
[[package]]
name = "strsim"
version = "0.10.0"
@@ -3202,10 +5687,37 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
-name = "subtle"
-version = "2.5.0"
+name = "strsim"
+version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
+checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
+
+[[package]]
+name = "strum"
+version = "0.27.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf"
+dependencies = [
+ "strum_macros",
+]
+
+[[package]]
+name = "strum_macros"
+version = "0.27.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7"
+dependencies = [
+ "heck 0.5.0",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.106",
+]
+
+[[package]]
+name = "subtle"
+version = "2.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
[[package]]
name = "syn"
@@ -3235,6 +5747,31 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263"
+[[package]]
+name = "synstructure"
+version = "0.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.106",
+]
+
+[[package]]
+name = "sysinfo"
+version = "0.36.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "252800745060e7b9ffb7b2badbd8b31cfa4aa2e61af879d0a3bf2a317c20217d"
+dependencies = [
+ "libc",
+ "memchr",
+ "ntapi",
+ "objc2-core-foundation",
+ "objc2-io-kit",
+ "windows 0.61.3",
+]
+
[[package]]
name = "system-configuration"
version = "0.5.1"
@@ -3263,12 +5800,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a2d580ff6a20c55dfb86be5f9c238f67835d0e81cbdea8bf5680e0897320331"
dependencies = [
"cfg-expr",
- "heck",
+ "heck 0.4.1",
"pkg-config",
- "toml",
+ "toml 0.8.23",
"version-compare",
]
+[[package]]
+name = "tap"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
+
[[package]]
name = "target-lexicon"
version = "0.12.13"
@@ -3346,21 +5889,35 @@ dependencies = [
[[package]]
name = "time"
-version = "0.3.31"
+version = "0.3.47"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f657ba42c3f86e7680e53c8cd3af8abbe56b5491790b46e22e19c0d57463583e"
+checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c"
dependencies = [
"deranged",
+ "itoa",
+ "js-sys",
+ "num-conv",
"powerfmt",
- "serde",
+ "serde_core",
"time-core",
+ "time-macros",
]
[[package]]
name = "time-core"
-version = "0.1.2"
+version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
+checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca"
+
+[[package]]
+name = "time-macros"
+version = "0.2.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215"
+dependencies = [
+ "num-conv",
+ "time-core",
+]
[[package]]
name = "tiny-keccak"
@@ -3371,6 +5928,27 @@ dependencies = [
"crunchy",
]
+[[package]]
+name = "tinystr"
+version = "0.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d"
+dependencies = [
+ "displaydoc",
+ "serde_core",
+ "zerovec",
+]
+
+[[package]]
+name = "tinytemplate"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc"
+dependencies = [
+ "serde",
+ "serde_json",
+]
+
[[package]]
name = "tinyvec"
version = "1.6.0"
@@ -3450,16 +6028,16 @@ dependencies = [
[[package]]
name = "tokio-util"
-version = "0.7.10"
+version = "0.7.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15"
+checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098"
dependencies = [
"bytes",
"futures-core",
+ "futures-io",
"futures-sink",
"pin-project-lite",
"tokio",
- "tracing",
]
[[package]]
@@ -3469,11 +6047,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362"
dependencies = [
"serde",
- "serde_spanned",
- "toml_datetime",
+ "serde_spanned 0.6.9",
+ "toml_datetime 0.6.11",
"toml_edit 0.22.27",
]
+[[package]]
+name = "toml"
+version = "0.9.12+spec-1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf92845e79fc2e2def6a5d828f0801e29a2f8acc037becc5ab08595c7d5e9863"
+dependencies = [
+ "indexmap 2.11.4",
+ "serde_core",
+ "serde_spanned 1.1.1",
+ "toml_datetime 0.7.5+spec-1.1.0",
+ "toml_parser",
+ "toml_writer",
+ "winnow 0.7.13",
+]
+
[[package]]
name = "toml_datetime"
version = "0.6.11"
@@ -3483,6 +6076,15 @@ dependencies = [
"serde",
]
+[[package]]
+name = "toml_datetime"
+version = "0.7.5+spec-1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347"
+dependencies = [
+ "serde_core",
+]
+
[[package]]
name = "toml_edit"
version = "0.19.15"
@@ -3490,7 +6092,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421"
dependencies = [
"indexmap 2.11.4",
- "toml_datetime",
+ "toml_datetime 0.6.11",
"winnow 0.5.34",
]
@@ -3502,18 +6104,33 @@ checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a"
dependencies = [
"indexmap 2.11.4",
"serde",
- "serde_spanned",
- "toml_datetime",
+ "serde_spanned 0.6.9",
+ "toml_datetime 0.6.11",
"toml_write",
"winnow 0.7.13",
]
+[[package]]
+name = "toml_parser"
+version = "1.1.2+spec-1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526"
+dependencies = [
+ "winnow 1.0.3",
+]
+
[[package]]
name = "toml_write"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801"
+[[package]]
+name = "toml_writer"
+version = "1.1.1+spec-1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db"
+
[[package]]
name = "tonic"
version = "0.12.3"
@@ -3558,6 +6175,959 @@ dependencies = [
"syn 2.0.106",
]
+[[package]]
+name = "tor-async-utils"
+version = "0.40.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "895c61a46909134501c6815eceeb66c9c80fc494ee89429821b0f05ccf34b4f5"
+dependencies = [
+ "derive-deftly",
+ "educe",
+ "futures",
+ "oneshot-fused-workaround",
+ "pin-project",
+ "postage",
+ "thiserror 2.0.16",
+ "void",
+]
+
+[[package]]
+name = "tor-basic-utils"
+version = "0.40.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fac6e4d7e131b7d69bc85558383cd4ac61e46b4dd0d4ed51632f28fac98cac0c"
+dependencies = [
+ "derive_more",
+ "hex",
+ "itertools 0.14.0",
+ "libc",
+ "paste",
+ "rand 0.9.2",
+ "rand_chacha 0.9.0",
+ "serde",
+ "slab",
+ "smallvec",
+ "thiserror 2.0.16",
+ "weak-table",
+]
+
+[[package]]
+name = "tor-bytes"
+version = "0.40.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "64454947258e49f238a5f06a06250a0c54598a1c7409898b5c79505e6a99e7af"
+dependencies = [
+ "bytes",
+ "derive-deftly",
+ "digest",
+ "educe",
+ "getrandom 0.4.2",
+ "safelog",
+ "thiserror 2.0.16",
+ "tor-error",
+ "tor-llcrypto",
+ "zeroize",
+]
+
+[[package]]
+name = "tor-cell"
+version = "0.40.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4ab0c79bc92a957e85959cf397a2d8f9c8294c35fa4f65247a9393b20ac95551"
+dependencies = [
+ "amplify",
+ "bitflags 2.11.1",
+ "bytes",
+ "caret",
+ "derive-deftly",
+ "derive_more",
+ "educe",
+ "itertools 0.14.0",
+ "paste",
+ "rand 0.9.2",
+ "smallvec",
+ "thiserror 2.0.16",
+ "tor-basic-utils",
+ "tor-bytes",
+ "tor-cert",
+ "tor-error",
+ "tor-linkspec",
+ "tor-llcrypto",
+ "tor-memquota",
+ "tor-protover",
+ "tor-units",
+ "void",
+]
+
+[[package]]
+name = "tor-cert"
+version = "0.40.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "debc911738298ee801fce4577c36a50c55295b0bb9c5519461b83cc486a1f86e"
+dependencies = [
+ "caret",
+ "derive_builder_fork_arti",
+ "derive_more",
+ "digest",
+ "thiserror 2.0.16",
+ "tor-bytes",
+ "tor-checkable",
+ "tor-error",
+ "tor-llcrypto",
+]
+
+[[package]]
+name = "tor-chanmgr"
+version = "0.40.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7af5b7c2f1e16d1304b5185fcbc91ca5c8df991c21be00702f925f055573eea1"
+dependencies = [
+ "async-trait",
+ "caret",
+ "cfg-if",
+ "derive-deftly",
+ "derive_more",
+ "educe",
+ "futures",
+ "oneshot-fused-workaround",
+ "percent-encoding",
+ "postage",
+ "rand 0.9.2",
+ "safelog",
+ "serde",
+ "serde_with",
+ "thiserror 2.0.16",
+ "tor-async-utils",
+ "tor-basic-utils",
+ "tor-cell",
+ "tor-config",
+ "tor-error",
+ "tor-keymgr",
+ "tor-linkspec",
+ "tor-llcrypto",
+ "tor-memquota",
+ "tor-netdir",
+ "tor-proto",
+ "tor-rtcompat",
+ "tor-socksproto",
+ "tor-units",
+ "tracing",
+ "url",
+ "void",
+]
+
+[[package]]
+name = "tor-checkable"
+version = "0.40.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "15b13a5b50bb55037f2e81b25dde42f420d57c75154216b8ef989006cea3ebee"
+dependencies = [
+ "humantime",
+ "signature",
+ "thiserror 2.0.16",
+ "tor-llcrypto",
+]
+
+[[package]]
+name = "tor-circmgr"
+version = "0.40.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b878f3f7c6be0c7f130d90b347ada2e7c46519dfbdde8273eae2e5d1792caa87"
+dependencies = [
+ "amplify",
+ "async-trait",
+ "cfg-if",
+ "derive-deftly",
+ "derive_builder_fork_arti",
+ "derive_more",
+ "downcast-rs",
+ "dyn-clone",
+ "educe",
+ "futures",
+ "humantime-serde",
+ "itertools 0.14.0",
+ "once_cell",
+ "oneshot-fused-workaround",
+ "pin-project",
+ "rand 0.9.2",
+ "retry-error",
+ "safelog",
+ "serde",
+ "thiserror 2.0.16",
+ "tor-async-utils",
+ "tor-basic-utils",
+ "tor-cell",
+ "tor-chanmgr",
+ "tor-config",
+ "tor-dircommon",
+ "tor-error",
+ "tor-guardmgr",
+ "tor-linkspec",
+ "tor-memquota",
+ "tor-netdir",
+ "tor-netdoc",
+ "tor-persist",
+ "tor-proto",
+ "tor-protover",
+ "tor-relay-selection",
+ "tor-rtcompat",
+ "tor-units",
+ "tracing",
+ "void",
+ "weak-table",
+]
+
+[[package]]
+name = "tor-config"
+version = "0.40.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3cbc74a00ab15bb986e3747c6969e40a58a63065d6f99077e7ee2f4657bb8b03"
+dependencies = [
+ "amplify",
+ "cfg-if",
+ "derive-deftly",
+ "derive_builder_fork_arti",
+ "educe",
+ "either",
+ "figment",
+ "fs-mistrust",
+ "futures",
+ "humantime-serde",
+ "itertools 0.14.0",
+ "notify",
+ "paste",
+ "postage",
+ "regex",
+ "serde",
+ "serde-value",
+ "serde_ignored",
+ "strum",
+ "thiserror 2.0.16",
+ "toml 0.9.12+spec-1.1.0",
+ "tor-basic-utils",
+ "tor-error",
+ "tor-rtcompat",
+ "tracing",
+ "void",
+]
+
+[[package]]
+name = "tor-config-path"
+version = "0.40.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3005ab7b9a26a7271e5adf3dfb4ae18c09a943e32aeccc4f6d1c53a60de74b8d"
+dependencies = [
+ "directories",
+ "serde",
+ "shellexpand",
+ "thiserror 2.0.16",
+ "tor-error",
+ "tor-general-addr",
+]
+
+[[package]]
+name = "tor-consdiff"
+version = "0.40.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6bfa2b7b71c72830f61c48da4bb3e13191e0c0e1404b9c5168c795e4f5feb4a8"
+dependencies = [
+ "digest",
+ "hex",
+ "thiserror 2.0.16",
+ "tor-llcrypto",
+]
+
+[[package]]
+name = "tor-dirclient"
+version = "0.40.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ccd6fac844ac77c33ccdfcb56bf23ff40ebbb821ea708be79a481ec30e8c39c"
+dependencies = [
+ "async-compression",
+ "base64ct",
+ "derive_more",
+ "futures",
+ "hex",
+ "http 1.3.1",
+ "httparse",
+ "httpdate",
+ "itertools 0.14.0",
+ "memchr",
+ "thiserror 2.0.16",
+ "tor-circmgr",
+ "tor-error",
+ "tor-linkspec",
+ "tor-llcrypto",
+ "tor-netdoc",
+ "tor-proto",
+ "tor-rtcompat",
+ "tracing",
+]
+
+[[package]]
+name = "tor-dircommon"
+version = "0.40.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dd0cf39a3c30321d145a4d60753ae7ef5bb58a66a00ac9e2bfc30bd823faf2a4"
+dependencies = [
+ "base64ct",
+ "derive-deftly",
+ "getset",
+ "humantime",
+ "humantime-serde",
+ "serde",
+ "tor-basic-utils",
+ "tor-checkable",
+ "tor-config",
+ "tor-linkspec",
+ "tor-llcrypto",
+ "tor-netdoc",
+ "tracing",
+]
+
+[[package]]
+name = "tor-dirmgr"
+version = "0.40.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b52919aa9dbb82a354c5b904bef82e91beb702b9f8ad14e6eac4237d6128bf67"
+dependencies = [
+ "async-trait",
+ "base64ct",
+ "derive_builder_fork_arti",
+ "derive_more",
+ "digest",
+ "educe",
+ "event-listener 5.4.1",
+ "fs-mistrust",
+ "fslock",
+ "futures",
+ "hex",
+ "humantime",
+ "humantime-serde",
+ "itertools 0.14.0",
+ "memmap2",
+ "oneshot-fused-workaround",
+ "paste",
+ "postage",
+ "rand 0.9.2",
+ "rusqlite",
+ "safelog",
+ "scopeguard",
+ "serde",
+ "serde_json",
+ "signature",
+ "static_assertions",
+ "strum",
+ "thiserror 2.0.16",
+ "time",
+ "tor-async-utils",
+ "tor-basic-utils",
+ "tor-checkable",
+ "tor-circmgr",
+ "tor-config",
+ "tor-consdiff",
+ "tor-dirclient",
+ "tor-dircommon",
+ "tor-error",
+ "tor-guardmgr",
+ "tor-llcrypto",
+ "tor-netdir",
+ "tor-netdoc",
+ "tor-persist",
+ "tor-proto",
+ "tor-protover",
+ "tor-rtcompat",
+ "tracing",
+]
+
+[[package]]
+name = "tor-error"
+version = "0.40.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "595b005e6f571ac3890a34a00f361200aab781fd0218f2c528c86fc7af088df5"
+dependencies = [
+ "derive_more",
+ "futures",
+ "paste",
+ "retry-error",
+ "static_assertions",
+ "strum",
+ "thiserror 2.0.16",
+ "tracing",
+ "void",
+]
+
+[[package]]
+name = "tor-general-addr"
+version = "0.40.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "727b8c8bc01c1587486055edab5c2cd0d5c960f5bb3fac796fc9911872b8b397"
+dependencies = [
+ "derive_more",
+ "thiserror 2.0.16",
+ "void",
+]
+
+[[package]]
+name = "tor-guardmgr"
+version = "0.40.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d337f465a477c0fb3b2faafa4654d70ff9df3590e57d22707591dddb4e4450c1"
+dependencies = [
+ "amplify",
+ "base64ct",
+ "derive-deftly",
+ "derive_builder_fork_arti",
+ "derive_more",
+ "dyn-clone",
+ "educe",
+ "futures",
+ "humantime",
+ "humantime-serde",
+ "itertools 0.14.0",
+ "num_enum",
+ "oneshot-fused-workaround",
+ "pin-project",
+ "postage",
+ "rand 0.9.2",
+ "safelog",
+ "serde",
+ "strum",
+ "thiserror 2.0.16",
+ "tor-async-utils",
+ "tor-basic-utils",
+ "tor-config",
+ "tor-dircommon",
+ "tor-error",
+ "tor-linkspec",
+ "tor-llcrypto",
+ "tor-netdir",
+ "tor-netdoc",
+ "tor-persist",
+ "tor-proto",
+ "tor-relay-selection",
+ "tor-rtcompat",
+ "tor-units",
+ "tracing",
+]
+
+[[package]]
+name = "tor-hscrypto"
+version = "0.40.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a3693cd43f05cd01ac0aaa060dae5c5e53c4364f89e0d769e33cd629a2fd3118"
+dependencies = [
+ "data-encoding",
+ "derive-deftly",
+ "derive_more",
+ "digest",
+ "hex",
+ "humantime",
+ "itertools 0.14.0",
+ "paste",
+ "rand 0.9.2",
+ "safelog",
+ "serde",
+ "signature",
+ "subtle",
+ "thiserror 2.0.16",
+ "tor-basic-utils",
+ "tor-bytes",
+ "tor-error",
+ "tor-key-forge",
+ "tor-llcrypto",
+ "tor-units",
+ "void",
+]
+
+[[package]]
+name = "tor-key-forge"
+version = "0.40.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3ade9065ae49cfe2ab020ca9ca9f2b3c5c9b5fc0d8980fa681d8b3a0668e042f"
+dependencies = [
+ "derive-deftly",
+ "derive_more",
+ "downcast-rs",
+ "paste",
+ "rand 0.9.2",
+ "rsa",
+ "signature",
+ "ssh-key",
+ "thiserror 2.0.16",
+ "tor-bytes",
+ "tor-cert",
+ "tor-checkable",
+ "tor-error",
+ "tor-llcrypto",
+]
+
+[[package]]
+name = "tor-keymgr"
+version = "0.40.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "243c3163d376c4723cd67271fcd6e5d6b498a6865c6b98299640e1be01c38826"
+dependencies = [
+ "amplify",
+ "arrayvec",
+ "cfg-if",
+ "derive-deftly",
+ "derive_builder_fork_arti",
+ "derive_more",
+ "downcast-rs",
+ "dyn-clone",
+ "fs-mistrust",
+ "glob-match",
+ "humantime",
+ "inventory",
+ "itertools 0.14.0",
+ "rand 0.9.2",
+ "safelog",
+ "serde",
+ "signature",
+ "ssh-key",
+ "thiserror 2.0.16",
+ "tor-basic-utils",
+ "tor-bytes",
+ "tor-config",
+ "tor-config-path",
+ "tor-error",
+ "tor-hscrypto",
+ "tor-key-forge",
+ "tor-llcrypto",
+ "tor-persist",
+ "tracing",
+ "visibility",
+ "walkdir",
+ "zeroize",
+]
+
+[[package]]
+name = "tor-linkspec"
+version = "0.40.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "05f1ea8786900d6fbe4c9f775d341b1ba01bbd1f750d89bd63be78b6b01e1836"
+dependencies = [
+ "base64ct",
+ "by_address",
+ "caret",
+ "derive-deftly",
+ "derive_builder_fork_arti",
+ "derive_more",
+ "hex",
+ "itertools 0.14.0",
+ "safelog",
+ "serde",
+ "serde_with",
+ "strum",
+ "thiserror 2.0.16",
+ "tor-basic-utils",
+ "tor-bytes",
+ "tor-config",
+ "tor-llcrypto",
+ "tor-memquota",
+ "tor-protover",
+]
+
+[[package]]
+name = "tor-llcrypto"
+version = "0.40.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1c6989a1c6d06ffd6835e2917edaae4aeef544f8e5fdd68b54cc365f2af523de"
+dependencies = [
+ "aes",
+ "base64ct",
+ "ctr",
+ "curve25519-dalek",
+ "der-parser",
+ "derive-deftly",
+ "derive_more",
+ "digest",
+ "ed25519-dalek",
+ "educe",
+ "getrandom 0.4.2",
+ "hex",
+ "rand 0.9.2",
+ "rand_chacha 0.9.0",
+ "rand_core 0.6.4",
+ "rand_core 0.9.3",
+ "rand_jitter",
+ "rdrand",
+ "rsa",
+ "safelog",
+ "serde",
+ "sha1",
+ "sha2",
+ "sha3",
+ "signature",
+ "subtle",
+ "thiserror 2.0.16",
+ "tor-error",
+ "tor-memquota-cost",
+ "visibility",
+ "x25519-dalek",
+ "zeroize",
+]
+
+[[package]]
+name = "tor-log-ratelim"
+version = "0.40.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f1cd642180923d12e3fab5996b4aa2189718da7f465df6eb196ce2b9c70e293"
+dependencies = [
+ "futures",
+ "humantime",
+ "thiserror 2.0.16",
+ "tor-error",
+ "tor-rtcompat",
+ "tracing",
+ "weak-table",
+]
+
+[[package]]
+name = "tor-memquota"
+version = "0.40.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "599daea60fd3272eb72a795d1c593b45bbe15343cbc702340a81db124c06eed5"
+dependencies = [
+ "cfg-if",
+ "derive-deftly",
+ "derive_more",
+ "dyn-clone",
+ "educe",
+ "futures",
+ "itertools 0.14.0",
+ "paste",
+ "pin-project",
+ "serde",
+ "slotmap-careful",
+ "static_assertions",
+ "sysinfo",
+ "thiserror 2.0.16",
+ "tor-async-utils",
+ "tor-basic-utils",
+ "tor-config",
+ "tor-error",
+ "tor-log-ratelim",
+ "tor-memquota-cost",
+ "tor-rtcompat",
+ "tracing",
+ "void",
+]
+
+[[package]]
+name = "tor-memquota-cost"
+version = "0.40.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dd92b07c0fc24e6d8166a5ff45e5b8654e68d89743c46d01889a16ab74c0b578"
+dependencies = [
+ "derive-deftly",
+ "itertools 0.14.0",
+ "paste",
+ "void",
+]
+
+[[package]]
+name = "tor-netdir"
+version = "0.40.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "41be8f47f521fc95206d2ba5facac8fb1a5b5b82169bd41ebeecdf46d1e77246"
+dependencies = [
+ "async-trait",
+ "bitflags 2.11.1",
+ "derive_more",
+ "futures",
+ "humantime",
+ "itertools 0.14.0",
+ "num_enum",
+ "rand 0.9.2",
+ "serde",
+ "strum",
+ "thiserror 2.0.16",
+ "tor-basic-utils",
+ "tor-error",
+ "tor-linkspec",
+ "tor-llcrypto",
+ "tor-netdoc",
+ "tor-protover",
+ "tor-units",
+ "tracing",
+ "typed-index-collections",
+]
+
+[[package]]
+name = "tor-netdoc"
+version = "0.40.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ea8bce73d2c78bd78a2a927336ca639cf6bd5d8ad092ebcd0b3fdeaa47dcc77e"
+dependencies = [
+ "amplify",
+ "base64ct",
+ "cipher",
+ "derive-deftly",
+ "derive_builder_fork_arti",
+ "derive_more",
+ "digest",
+ "educe",
+ "enumset",
+ "hex",
+ "humantime",
+ "itertools 0.14.0",
+ "memchr",
+ "paste",
+ "phf",
+ "saturating-time",
+ "serde",
+ "serde_with",
+ "signature",
+ "smallvec",
+ "strum",
+ "subtle",
+ "thiserror 2.0.16",
+ "time",
+ "tinystr",
+ "tor-basic-utils",
+ "tor-bytes",
+ "tor-cell",
+ "tor-cert",
+ "tor-checkable",
+ "tor-error",
+ "tor-llcrypto",
+ "tor-protover",
+ "void",
+ "zeroize",
+]
+
+[[package]]
+name = "tor-persist"
+version = "0.40.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "507ab4b6a3d59ed0df5804eeed66dcacde75e3be13d3694216cdfdb666bce625"
+dependencies = [
+ "derive-deftly",
+ "derive_more",
+ "filetime",
+ "fs-mistrust",
+ "fslock",
+ "futures",
+ "itertools 0.14.0",
+ "oneshot-fused-workaround",
+ "paste",
+ "sanitize-filename",
+ "serde",
+ "serde_json",
+ "thiserror 2.0.16",
+ "time",
+ "tor-async-utils",
+ "tor-basic-utils",
+ "tor-error",
+ "tracing",
+ "void",
+]
+
+[[package]]
+name = "tor-proto"
+version = "0.40.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbfc552d535d36539d5782bb02028590bc472d219e49da51a96810725e80ff56"
+dependencies = [
+ "amplify",
+ "async-trait",
+ "asynchronous-codec",
+ "bitvec",
+ "bytes",
+ "caret",
+ "cfg-if",
+ "cipher",
+ "coarsetime",
+ "criterion-cycles-per-byte",
+ "derive-deftly",
+ "derive_builder_fork_arti",
+ "derive_more",
+ "digest",
+ "educe",
+ "enum_dispatch",
+ "futures",
+ "futures-util",
+ "hkdf",
+ "hmac",
+ "itertools 0.14.0",
+ "nonany",
+ "oneshot-fused-workaround",
+ "pin-project",
+ "postage",
+ "rand 0.9.2",
+ "rand_core 0.9.3",
+ "safelog",
+ "slotmap-careful",
+ "smallvec",
+ "static_assertions",
+ "subtle",
+ "sync_wrapper",
+ "thiserror 2.0.16",
+ "tokio",
+ "tokio-util",
+ "tor-async-utils",
+ "tor-basic-utils",
+ "tor-bytes",
+ "tor-cell",
+ "tor-cert",
+ "tor-checkable",
+ "tor-config",
+ "tor-error",
+ "tor-linkspec",
+ "tor-llcrypto",
+ "tor-log-ratelim",
+ "tor-memquota",
+ "tor-protover",
+ "tor-relay-crypto",
+ "tor-rtcompat",
+ "tor-rtmock",
+ "tor-units",
+ "tracing",
+ "typenum",
+ "visibility",
+ "void",
+ "zeroize",
+]
+
+[[package]]
+name = "tor-protover"
+version = "0.40.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aed88527d070c4b7ea4e55a36d2d56d0500e30ca66298b5264f047f7f2f89cfa"
+dependencies = [
+ "caret",
+ "paste",
+ "serde_with",
+ "thiserror 2.0.16",
+ "tor-basic-utils",
+ "tor-bytes",
+]
+
+[[package]]
+name = "tor-relay-crypto"
+version = "0.40.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f7e57e9f71b22ae1df63dbccc8e428cb07feec0abd654735109fa563c10bbb90"
+dependencies = [
+ "derive-deftly",
+ "derive_more",
+ "humantime",
+ "tor-cert",
+ "tor-checkable",
+ "tor-error",
+ "tor-key-forge",
+ "tor-keymgr",
+ "tor-llcrypto",
+ "tor-persist",
+]
+
+[[package]]
+name = "tor-relay-selection"
+version = "0.40.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a372072ac9dea7d17e49693cc3f3ae77b3abf8125630516c9f2d622239b1920a"
+dependencies = [
+ "rand 0.9.2",
+ "serde",
+ "tor-basic-utils",
+ "tor-linkspec",
+ "tor-netdir",
+ "tor-netdoc",
+]
+
+[[package]]
+name = "tor-rtcompat"
+version = "0.40.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "14428b930e59003e801c0c32697c0aeb9b0495ad33ecbe8c6753bdb596233270"
+dependencies = [
+ "async-native-tls",
+ "async-trait",
+ "async_executors",
+ "asynchronous-codec",
+ "cfg-if",
+ "coarsetime",
+ "derive_more",
+ "dyn-clone",
+ "educe",
+ "futures",
+ "hex",
+ "libc",
+ "native-tls",
+ "paste",
+ "pin-project",
+ "socket2 0.6.3",
+ "thiserror 2.0.16",
+ "tokio",
+ "tokio-util",
+ "tor-error",
+ "tor-general-addr",
+ "tracing",
+ "void",
+ "zeroize",
+]
+
+[[package]]
+name = "tor-rtmock"
+version = "0.40.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d2da91a432cdaee8a93e0bb21b02f3e9c7667832ccbb4b54e00d9c1214638e70"
+dependencies = [
+ "amplify",
+ "assert_matches",
+ "async-trait",
+ "derive-deftly",
+ "derive_more",
+ "educe",
+ "futures",
+ "humantime",
+ "itertools 0.14.0",
+ "oneshot-fused-workaround",
+ "pin-project",
+ "priority-queue",
+ "slotmap-careful",
+ "strum",
+ "thiserror 2.0.16",
+ "tor-error",
+ "tor-general-addr",
+ "tor-rtcompat",
+ "tracing",
+ "tracing-test",
+ "void",
+]
+
+[[package]]
+name = "tor-socksproto"
+version = "0.40.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "adbc9115a2f506d9bb86ae4446f0ca70eb523dc2f5e900a33582e7c39decc23a"
+dependencies = [
+ "amplify",
+ "caret",
+ "derive-deftly",
+ "educe",
+ "safelog",
+ "subtle",
+ "thiserror 2.0.16",
+ "tor-bytes",
+ "tor-error",
+]
+
+[[package]]
+name = "tor-units"
+version = "0.40.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da90e93b4b4aa4ec356ecbe9e19aced36fdd655e94ca459d1915120d873363f0"
+dependencies = [
+ "derive-deftly",
+ "derive_more",
+ "serde",
+ "thiserror 2.0.16",
+ "tor-memquota",
+]
+
[[package]]
name = "tower"
version = "0.4.13"
@@ -3592,9 +7162,9 @@ checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52"
[[package]]
name = "tracing"
-version = "0.1.40"
+version = "0.1.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef"
+checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100"
dependencies = [
"log",
"pin-project-lite",
@@ -3604,9 +7174,9 @@ dependencies = [
[[package]]
name = "tracing-attributes"
-version = "0.1.27"
+version = "0.1.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
+checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da"
dependencies = [
"proc-macro2",
"quote",
@@ -3615,9 +7185,9 @@ dependencies = [
[[package]]
name = "tracing-core"
-version = "0.1.32"
+version = "0.1.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54"
+checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a"
dependencies = [
"once_cell",
"valuable",
@@ -3689,6 +7259,27 @@ dependencies = [
"tracing-log 0.2.0",
]
+[[package]]
+name = "tracing-test"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "19a4c448db514d4f24c5ddb9f73f2ee71bfb24c526cf0c570ba142d1119e0051"
+dependencies = [
+ "tracing-core",
+ "tracing-subscriber",
+ "tracing-test-macro",
+]
+
+[[package]]
+name = "tracing-test-macro"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ad06847b7afb65c7866a36664b75c40b895e318cea4f71299f013fb22965329d"
+dependencies = [
+ "quote",
+ "syn 2.0.106",
+]
+
[[package]]
name = "try-lock"
version = "0.2.5"
@@ -3710,7 +7301,7 @@ dependencies = [
"log",
"nix 0.26.4",
"reqwest 0.11.23",
- "schemars",
+ "schemars 0.8.16",
"serde",
"socket2 0.5.10",
"ssri",
@@ -3718,10 +7309,20 @@ dependencies = [
"tokio",
"tracing",
"widestring",
- "windows",
+ "windows 0.48.0",
"zip",
]
+[[package]]
+name = "typed-index-collections"
+version = "3.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "898160f1dfd383b4e92e17f0512a7d62f3c51c44937b23b6ffc3a1614a8eaccd"
+dependencies = [
+ "bincode",
+ "serde",
+]
+
[[package]]
name = "typenum"
version = "1.17.0"
@@ -3729,10 +7330,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
[[package]]
-name = "unicode-bidi"
-version = "0.3.15"
+name = "uncased"
+version = "0.9.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75"
+checksum = "e1b88fcfe09e89d3866a5c11019378088af2d24c3fbd4f0543f96b479ec90697"
+dependencies = [
+ "version_check",
+]
[[package]]
name = "unicode-ident"
@@ -3741,13 +7345,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
-name = "unicode-normalization"
-version = "0.1.22"
+name = "unicode-segmentation"
+version = "1.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921"
-dependencies = [
- "tinyvec",
-]
+checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c"
[[package]]
name = "unicode-width"
@@ -3755,6 +7356,12 @@ version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85"
+[[package]]
+name = "unicode-xid"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
+
[[package]]
name = "universal-hash"
version = "0.5.1"
@@ -3765,6 +7372,12 @@ dependencies = [
"subtle",
]
+[[package]]
+name = "unsafe-libyaml"
+version = "0.2.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861"
+
[[package]]
name = "untrusted"
version = "0.9.0"
@@ -3772,16 +7385,29 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
[[package]]
-name = "url"
-version = "2.5.0"
+name = "unty"
+version = "0.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633"
+checksum = "6d49784317cd0d1ee7ec5c716dd598ec5b4483ea832a2dced265471cc0f690ae"
+
+[[package]]
+name = "url"
+version = "2.5.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed"
dependencies = [
"form_urlencoded",
"idna",
"percent-encoding",
+ "serde",
]
+[[package]]
+name = "utf8_iter"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
+
[[package]]
name = "utf8parse"
version = "0.2.1"
@@ -3821,6 +7447,33 @@ version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
+[[package]]
+name = "visibility"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d674d135b4a8c1d7e813e2f8d1c9a58308aee4a680323066025e53132218bd91"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.106",
+]
+
+[[package]]
+name = "void"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
+
+[[package]]
+name = "walkdir"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
+dependencies = [
+ "same-file",
+ "winapi-util",
+]
+
[[package]]
name = "want"
version = "0.3.1"
@@ -3851,14 +7504,32 @@ version = "1.0.1+wasi-0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7"
dependencies = [
- "wit-bindgen",
+ "wit-bindgen 0.46.0",
+]
+
+[[package]]
+name = "wasip3"
+version = "0.4.0+wasi-0.3.0-rc-2026-01-06"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5"
+dependencies = [
+ "wit-bindgen 0.51.0",
+]
+
+[[package]]
+name = "wasix"
+version = "0.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1757e0d1f8456693c7e5c6c629bdb54884e032aa0bb53c155f6a39f94440d332"
+dependencies = [
+ "wasi 0.11.0+wasi-snapshot-preview1",
]
[[package]]
name = "wasm-bindgen"
-version = "0.2.103"
+version = "0.2.122"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ab10a69fbd0a177f5f649ad4d8d3305499c42bab9aef2f7ff592d0ec8f833819"
+checksum = "3ed04576f974d2b2fba0f38c51dbc5518011e38c36bf1143164be765528fd409"
dependencies = [
"cfg-if",
"once_cell",
@@ -3867,20 +7538,6 @@ dependencies = [
"wasm-bindgen-shared",
]
-[[package]]
-name = "wasm-bindgen-backend"
-version = "0.2.103"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0bb702423545a6007bbc368fde243ba47ca275e549c8a28617f56f6ba53b1d1c"
-dependencies = [
- "bumpalo",
- "log",
- "proc-macro2",
- "quote",
- "syn 2.0.106",
- "wasm-bindgen-shared",
-]
-
[[package]]
name = "wasm-bindgen-futures"
version = "0.4.40"
@@ -3895,9 +7552,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro"
-version = "0.2.103"
+version = "0.2.122"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fc65f4f411d91494355917b605e1480033152658d71f722a90647f56a70c88a0"
+checksum = "916151b09da36bd82f6615cbf3a419e2f0ba23a03c6160e8e92eb6bd4aa1dec6"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@@ -3905,26 +7562,66 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
-version = "0.2.103"
+version = "0.2.122"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ffc003a991398a8ee604a401e194b6b3a39677b3173d6e74495eb51b82e99a32"
+checksum = "299047362ccbfce148b67ab7e73349f77748e00c8296f9542adfad2ad82c5c5e"
dependencies = [
+ "bumpalo",
"proc-macro2",
"quote",
"syn 2.0.106",
- "wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
-version = "0.2.103"
+version = "0.2.122"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "293c37f4efa430ca14db3721dfbe48d8c33308096bd44d80ebaa775ab71ba1cf"
+checksum = "9a929b2c61f11ba3e9bc35b50c1f25cb38e0e892c0c231ae2b8cf78d5dad4437"
dependencies = [
"unicode-ident",
]
+[[package]]
+name = "wasm-encoder"
+version = "0.244.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319"
+dependencies = [
+ "leb128fmt",
+ "wasmparser",
+]
+
+[[package]]
+name = "wasm-metadata"
+version = "0.244.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909"
+dependencies = [
+ "anyhow",
+ "indexmap 2.11.4",
+ "wasm-encoder",
+ "wasmparser",
+]
+
+[[package]]
+name = "wasmparser"
+version = "0.244.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe"
+dependencies = [
+ "bitflags 2.11.1",
+ "hashbrown 0.15.5",
+ "indexmap 2.11.4",
+ "semver",
+]
+
+[[package]]
+name = "weak-table"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "323f4da9523e9a669e1eaf9c6e763892769b1d38c623913647bfdc1532fe4549"
+
[[package]]
name = "web-sys"
version = "0.3.67"
@@ -3997,6 +7694,15 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+[[package]]
+name = "winapi-util"
+version = "0.1.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
+dependencies = [
+ "windows-sys 0.61.2",
+]
+
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
@@ -4012,6 +7718,145 @@ dependencies = [
"windows-targets 0.48.5",
]
+[[package]]
+name = "windows"
+version = "0.61.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893"
+dependencies = [
+ "windows-collections",
+ "windows-core 0.61.2",
+ "windows-future",
+ "windows-link 0.1.3",
+ "windows-numerics",
+]
+
+[[package]]
+name = "windows-collections"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8"
+dependencies = [
+ "windows-core 0.61.2",
+]
+
+[[package]]
+name = "windows-core"
+version = "0.61.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3"
+dependencies = [
+ "windows-implement",
+ "windows-interface",
+ "windows-link 0.1.3",
+ "windows-result 0.3.4",
+ "windows-strings 0.4.2",
+]
+
+[[package]]
+name = "windows-core"
+version = "0.62.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb"
+dependencies = [
+ "windows-implement",
+ "windows-interface",
+ "windows-link 0.2.1",
+ "windows-result 0.4.1",
+ "windows-strings 0.5.1",
+]
+
+[[package]]
+name = "windows-future"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e"
+dependencies = [
+ "windows-core 0.61.2",
+ "windows-link 0.1.3",
+ "windows-threading",
+]
+
+[[package]]
+name = "windows-implement"
+version = "0.60.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.106",
+]
+
+[[package]]
+name = "windows-interface"
+version = "0.59.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.106",
+]
+
+[[package]]
+name = "windows-link"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a"
+
+[[package]]
+name = "windows-link"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
+
+[[package]]
+name = "windows-numerics"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1"
+dependencies = [
+ "windows-core 0.61.2",
+ "windows-link 0.1.3",
+]
+
+[[package]]
+name = "windows-result"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6"
+dependencies = [
+ "windows-link 0.1.3",
+]
+
+[[package]]
+name = "windows-result"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5"
+dependencies = [
+ "windows-link 0.2.1",
+]
+
+[[package]]
+name = "windows-strings"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57"
+dependencies = [
+ "windows-link 0.1.3",
+]
+
+[[package]]
+name = "windows-strings"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091"
+dependencies = [
+ "windows-link 0.2.1",
+]
+
[[package]]
name = "windows-sys"
version = "0.48.0"
@@ -4030,6 +7875,24 @@ dependencies = [
"windows-targets 0.52.0",
]
+[[package]]
+name = "windows-sys"
+version = "0.60.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
+dependencies = [
+ "windows-targets 0.53.5",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.61.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
+dependencies = [
+ "windows-link 0.2.1",
+]
+
[[package]]
name = "windows-targets"
version = "0.48.5"
@@ -4060,6 +7923,32 @@ dependencies = [
"windows_x86_64_msvc 0.52.0",
]
+[[package]]
+name = "windows-targets"
+version = "0.53.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3"
+dependencies = [
+ "windows-link 0.2.1",
+ "windows_aarch64_gnullvm 0.53.1",
+ "windows_aarch64_msvc 0.53.1",
+ "windows_i686_gnu 0.53.1",
+ "windows_i686_gnullvm",
+ "windows_i686_msvc 0.53.1",
+ "windows_x86_64_gnu 0.53.1",
+ "windows_x86_64_gnullvm 0.53.1",
+ "windows_x86_64_msvc 0.53.1",
+]
+
+[[package]]
+name = "windows-threading"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6"
+dependencies = [
+ "windows-link 0.1.3",
+]
+
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.48.5"
@@ -4072,6 +7961,12 @@ version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea"
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53"
+
[[package]]
name = "windows_aarch64_msvc"
version = "0.48.5"
@@ -4084,6 +7979,12 @@ version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef"
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006"
+
[[package]]
name = "windows_i686_gnu"
version = "0.48.5"
@@ -4096,6 +7997,18 @@ version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313"
+[[package]]
+name = "windows_i686_gnu"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3"
+
+[[package]]
+name = "windows_i686_gnullvm"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c"
+
[[package]]
name = "windows_i686_msvc"
version = "0.48.5"
@@ -4108,6 +8021,12 @@ version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a"
+[[package]]
+name = "windows_i686_msvc"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2"
+
[[package]]
name = "windows_x86_64_gnu"
version = "0.48.5"
@@ -4120,6 +8039,12 @@ version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd"
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499"
+
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.48.5"
@@ -4132,6 +8057,12 @@ version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e"
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1"
+
[[package]]
name = "windows_x86_64_msvc"
version = "0.48.5"
@@ -4144,6 +8075,12 @@ version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650"
+
[[package]]
name = "winnow"
version = "0.5.34"
@@ -4162,6 +8099,12 @@ dependencies = [
"memchr",
]
+[[package]]
+name = "winnow"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1"
+
[[package]]
name = "winreg"
version = "0.50.0"
@@ -4188,6 +8131,109 @@ version = "0.46.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59"
+[[package]]
+name = "wit-bindgen"
+version = "0.51.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5"
+dependencies = [
+ "wit-bindgen-rust-macro",
+]
+
+[[package]]
+name = "wit-bindgen-core"
+version = "0.51.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc"
+dependencies = [
+ "anyhow",
+ "heck 0.5.0",
+ "wit-parser",
+]
+
+[[package]]
+name = "wit-bindgen-rust"
+version = "0.51.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21"
+dependencies = [
+ "anyhow",
+ "heck 0.5.0",
+ "indexmap 2.11.4",
+ "prettyplease",
+ "syn 2.0.106",
+ "wasm-metadata",
+ "wit-bindgen-core",
+ "wit-component",
+]
+
+[[package]]
+name = "wit-bindgen-rust-macro"
+version = "0.51.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a"
+dependencies = [
+ "anyhow",
+ "prettyplease",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.106",
+ "wit-bindgen-core",
+ "wit-bindgen-rust",
+]
+
+[[package]]
+name = "wit-component"
+version = "0.244.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2"
+dependencies = [
+ "anyhow",
+ "bitflags 2.11.1",
+ "indexmap 2.11.4",
+ "log",
+ "serde",
+ "serde_derive",
+ "serde_json",
+ "wasm-encoder",
+ "wasm-metadata",
+ "wasmparser",
+ "wit-parser",
+]
+
+[[package]]
+name = "wit-parser"
+version = "0.244.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736"
+dependencies = [
+ "anyhow",
+ "id-arena",
+ "indexmap 2.11.4",
+ "log",
+ "semver",
+ "serde",
+ "serde_derive",
+ "serde_json",
+ "unicode-xid",
+ "wasmparser",
+]
+
+[[package]]
+name = "writeable"
+version = "0.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4"
+
+[[package]]
+name = "wyz"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed"
+dependencies = [
+ "tap",
+]
+
[[package]]
name = "x25519-dalek"
version = "2.0.0"
@@ -4206,6 +8252,38 @@ version = "0.8.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53be06678ed9e83edb1745eb72efc0bbcd7b5c3c35711a860906aed827a13d61"
+[[package]]
+name = "xz2"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "388c44dc09d76f1536602ead6d325eb532f5c122f17782bd57fb47baeeb767e2"
+dependencies = [
+ "lzma-sys",
+]
+
+[[package]]
+name = "yoke"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "abe8c5fda708d9ca3df187cae8bfb9ceda00dd96231bed36e445a1a48e66f9ca"
+dependencies = [
+ "stable_deref_trait",
+ "yoke-derive",
+ "zerofrom",
+]
+
+[[package]]
+name = "yoke-derive"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.106",
+ "synstructure",
+]
+
[[package]]
name = "zerocopy"
version = "0.8.27"
@@ -4226,6 +8304,27 @@ dependencies = [
"syn 2.0.106",
]
+[[package]]
+name = "zerofrom"
+version = "0.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272"
+dependencies = [
+ "zerofrom-derive",
+]
+
+[[package]]
+name = "zerofrom-derive"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.106",
+ "synstructure",
+]
+
[[package]]
name = "zeroize"
version = "1.7.0"
@@ -4246,6 +8345,40 @@ dependencies = [
"syn 2.0.106",
]
+[[package]]
+name = "zerotrie"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf"
+dependencies = [
+ "displaydoc",
+ "yoke",
+ "zerofrom",
+]
+
+[[package]]
+name = "zerovec"
+version = "0.11.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239"
+dependencies = [
+ "serde",
+ "yoke",
+ "zerofrom",
+ "zerovec-derive",
+]
+
+[[package]]
+name = "zerovec-derive"
+version = "0.11.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.106",
+]
+
[[package]]
name = "zip"
version = "0.6.6"
@@ -4263,16 +8396,31 @@ dependencies = [
"pbkdf2",
"sha1",
"time",
- "zstd",
+ "zstd 0.11.2+zstd.1.5.2",
]
+[[package]]
+name = "zmij"
+version = "1.0.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
+
[[package]]
name = "zstd"
version = "0.11.2+zstd.1.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "20cc960326ece64f010d2d2107537f26dc589a6573a316bd5b1dba685fa5fde4"
dependencies = [
- "zstd-safe",
+ "zstd-safe 5.0.2+zstd.1.5.2",
+]
+
+[[package]]
+name = "zstd"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bffb3309596d527cfcba7dfc6ed6052f1d39dfbd7c867aa2e865e4a449c10110"
+dependencies = [
+ "zstd-safe 7.0.0",
]
[[package]]
@@ -4285,6 +8433,15 @@ dependencies = [
"zstd-sys",
]
+[[package]]
+name = "zstd-safe"
+version = "7.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "43747c7422e2924c11144d5229878b98180ef8b06cca4ab5af37afc8a8d8ea3e"
+dependencies = [
+ "zstd-sys",
+]
+
[[package]]
name = "zstd-sys"
version = "2.0.9+zstd.1.5.5"
diff --git a/burrow-gtk/src/components/home_screen.rs b/burrow-gtk/src/components/home_screen.rs
index 0bfdda2..655cad3 100644
--- a/burrow-gtk/src/components/home_screen.rs
+++ b/burrow-gtk/src/components/home_screen.rs
@@ -1,6 +1,6 @@
use super::*;
use crate::account_store::{self, AccountKind, AccountRecord};
-use std::time::Duration;
+use std::{cell::RefCell, rc::Rc, time::Duration};
pub struct HomeScreen {
daemon_banner: adw::Banner,
@@ -23,6 +23,7 @@ pub enum HomeScreenMsg {
OpenWireGuard,
OpenTor,
OpenTailnet,
+ OpenProxySubscription,
AddWireGuard {
title: String,
account: String,
@@ -52,6 +53,14 @@ pub enum HomeScreenMsg {
hostname: Option,
tailnet: Option,
},
+ PreviewProxySubscription {
+ url: String,
+ },
+ AddProxySubscription {
+ url: String,
+ name: String,
+ selected_ordinal: Option,
+ },
}
#[relm4::component(pub, async)]
@@ -234,7 +243,7 @@ impl AsyncComponent for HomeScreen {
configure_add_popover(&widgets.add_button, &sender);
let refresh_sender = sender.input_sender().clone();
- relm4::spawn(async move {
+ gtk::glib::MainContext::default().spawn_local(async move {
loop {
tokio::time::sleep(Duration::from_secs(5)).await;
refresh_sender.emit(HomeScreenMsg::Refresh);
@@ -272,6 +281,7 @@ impl AsyncComponent for HomeScreen {
HomeScreenMsg::OpenWireGuard => open_wireguard_window(root, &sender),
HomeScreenMsg::OpenTor => open_tor_window(root, &sender),
HomeScreenMsg::OpenTailnet => open_tailnet_window(root, &sender),
+ HomeScreenMsg::OpenProxySubscription => open_proxy_subscription_window(root, &sender),
HomeScreenMsg::AddWireGuard {
title,
account,
@@ -304,6 +314,13 @@ impl AsyncComponent for HomeScreen {
self.add_tailnet(authority, account, identity, hostname, tailnet)
.await;
}
+ HomeScreenMsg::PreviewProxySubscription { url } => {
+ self.preview_proxy_subscription(url).await;
+ }
+ HomeScreenMsg::AddProxySubscription { url, name, selected_ordinal } => {
+ self.add_proxy_subscription(url, name, selected_ordinal)
+ .await;
+ }
}
}
}
@@ -667,6 +684,85 @@ impl HomeScreen {
}
}
+ async fn preview_proxy_subscription(&mut self, url: String) {
+ let Ok(url) = daemon_api::require_value(&url, "Subscription URL") else {
+ self.network_status
+ .set_label("Enter a subscription URL before previewing.");
+ return;
+ };
+
+ self.network_status
+ .set_label("Previewing proxy subscription...");
+ match daemon_api::preview_proxy_subscription(url).await {
+ Ok(preview) => {
+ let first_nodes = preview
+ .nodes
+ .iter()
+ .take(3)
+ .map(|node| {
+ let warning = if node.warnings.is_empty() {
+ String::new()
+ } else {
+ format!(" - {}", node.warnings.join(", "))
+ };
+ format!(
+ "{}: {} {}:{}{}",
+ node.protocol, node.name, node.server, node.port, warning
+ )
+ })
+ .collect::>()
+ .join("\n");
+ let warnings = if preview.warnings.is_empty() {
+ String::new()
+ } else {
+ format!("\n{}", preview.warnings.join("\n"))
+ };
+ self.network_status.set_label(&format!(
+ "Found {} compatible nodes and {} rejected entries in {}. Suggested name: {}.{}{}",
+ preview.compatible_count,
+ preview.rejected_count,
+ preview.detected_format,
+ preview.suggested_name,
+ warnings,
+ if first_nodes.is_empty() {
+ String::new()
+ } else {
+ format!("\n{first_nodes}")
+ }
+ ));
+ }
+ Err(error) => self
+ .network_status
+ .set_label(&format!("Proxy subscription preview failed: {error}")),
+ }
+ }
+
+ async fn add_proxy_subscription(
+ &mut self,
+ url: String,
+ name: String,
+ selected_ordinal: Option,
+ ) {
+ let Ok(url) = daemon_api::require_value(&url, "Subscription URL") else {
+ self.network_status
+ .set_label("Enter a subscription URL before importing.");
+ return;
+ };
+
+ self.network_status
+ .set_label("Importing proxy subscription...");
+ match daemon_api::add_proxy_subscription(url, name, selected_ordinal).await {
+ Ok(id) => {
+ self.network_status
+ .set_label(&format!("Imported proxy subscription network #{id}."));
+ self.refresh().await;
+ }
+ Err(error) => self
+ .network_status
+ .set_label(&format!("Unable to import proxy subscription: {error}")),
+ }
+ }
+
fn apply_login_status(&mut self, status: &daemon_api::TailnetLoginStatus) {
self.tailnet_session_id = Some(status.session_id.clone());
self.tailnet_running = status.running;
@@ -735,6 +831,10 @@ fn configure_add_popover(button: >k::MenuButton, sender: &AsyncComponentSender
for (label, msg) in [
("Add WireGuard Network", HomeScreenMsg::OpenWireGuard),
+ (
+ "Import Proxy Subscription",
+ HomeScreenMsg::OpenProxySubscription,
+ ),
("Save Tor Account", HomeScreenMsg::OpenTor),
("Add Tailnet Account", HomeScreenMsg::OpenTailnet),
] {
@@ -755,6 +855,7 @@ fn msg_from_template(msg: &HomeScreenMsg) -> HomeScreenMsg {
HomeScreenMsg::OpenWireGuard => HomeScreenMsg::OpenWireGuard,
HomeScreenMsg::OpenTor => HomeScreenMsg::OpenTor,
HomeScreenMsg::OpenTailnet => HomeScreenMsg::OpenTailnet,
+ HomeScreenMsg::OpenProxySubscription => HomeScreenMsg::OpenProxySubscription,
_ => unreachable!(),
}
}
@@ -1091,6 +1192,169 @@ fn open_tailnet_window(root: >k::ScrolledWindow, sender: &AsyncComponentSender
window.present();
}
+fn open_proxy_subscription_window(
+ root: >k::ScrolledWindow,
+ sender: &AsyncComponentSender,
+) {
+ let window = sheet_window(root, "Proxy Subscription", 560, 520);
+ let content = sheet_content(
+ &window,
+ "Import Proxy Subscription",
+ "Import Trojan and Shadowsocks nodes through the Burrow daemon.",
+ );
+
+ let url = gtk::Entry::new();
+ url.set_placeholder_text(Some("Subscription URL"));
+ let name = gtk::Entry::new();
+ name.set_placeholder_text(Some("Name"));
+ let node_list = gtk::ListBox::new();
+ node_list.set_selection_mode(gtk::SelectionMode::Single);
+ node_list.set_sensitive(false);
+ node_list.add_css_class("boxed-list");
+ let node_scroll = gtk::ScrolledWindow::builder()
+ .min_content_height(220)
+ .vexpand(true)
+ .child(&node_list)
+ .build();
+ let preview_status = gtk::Label::new(Some("Preview the subscription to choose a node."));
+ preview_status.add_css_class("dim-label");
+ preview_status.set_xalign(0.0);
+ preview_status.set_wrap(true);
+ let selected_ordinal = Rc::new(RefCell::new(None::));
+ let node_ordinals = Rc::new(RefCell::new(Vec::::new()));
+
+ content.append(§ion_label("Subscription"));
+ content.append(&url);
+ content.append(&name);
+ content.append(§ion_label("Node"));
+ content.append(&node_scroll);
+ content.append(&preview_status);
+
+ let actions = gtk::Box::new(gtk::Orientation::Horizontal, 8);
+ let preview = gtk::Button::with_label("Preview");
+ let import = gtk::Button::with_label("Import");
+ import.add_css_class("suggested-action");
+ actions.append(&preview);
+ actions.append(&import);
+ content.append(&actions);
+
+ let url_for_preview = url.clone();
+ let name_for_preview = name.clone();
+ let list_for_preview = node_list.clone();
+ let status_for_preview = preview_status.clone();
+ let selected_for_preview = selected_ordinal.clone();
+ let ordinals_for_preview = node_ordinals.clone();
+ preview.connect_clicked(move |_| {
+ let url = url_for_preview.text().to_string();
+ let name = name_for_preview.clone();
+ let list = list_for_preview.clone();
+ let status = status_for_preview.clone();
+ let selected = selected_for_preview.clone();
+ let ordinals = ordinals_for_preview.clone();
+ status.set_label("Previewing proxy subscription...");
+ while let Some(child) = list.first_child() {
+ list.remove(&child);
+ }
+ list.set_sensitive(false);
+ *selected.borrow_mut() = None;
+ ordinals.borrow_mut().clear();
+ gtk::glib::MainContext::default().spawn_local(async move {
+ match daemon_api::preview_proxy_subscription(url).await {
+ Ok(preview) => {
+ if name.text().trim().is_empty() {
+ name.set_text(&preview.suggested_name);
+ }
+ for node in preview.nodes.iter().filter(|node| node.runtime_supported) {
+ ordinals.borrow_mut().push(node.ordinal);
+ let row = gtk::ListBoxRow::new();
+ row.set_selectable(true);
+ row.set_activatable(true);
+
+ let row_content = gtk::Box::new(gtk::Orientation::Vertical, 2);
+ row_content.set_margin_top(8);
+ row_content.set_margin_bottom(8);
+ row_content.set_margin_start(10);
+ row_content.set_margin_end(10);
+
+ let title = gtk::Label::new(Some(&format!(
+ "{} {}",
+ node.protocol.to_uppercase(),
+ node.name
+ )));
+ title.set_xalign(0.0);
+ title.set_ellipsize(gtk::pango::EllipsizeMode::End);
+ let endpoint = gtk::Label::new(Some(&format!("{}:{}", node.server, node.port)));
+ endpoint.add_css_class("dim-label");
+ endpoint.set_xalign(0.0);
+ endpoint.set_ellipsize(gtk::pango::EllipsizeMode::Middle);
+
+ row_content.append(&title);
+ row_content.append(&endpoint);
+ row.set_child(Some(&row_content));
+ list.append(&row);
+ }
+ if let Some(first) = preview.nodes.iter().find(|node| node.runtime_supported) {
+ if let Some(row) = list.row_at_index(0) {
+ list.select_row(Some(&row));
+ }
+ list.set_sensitive(true);
+ *selected.borrow_mut() = Some(first.ordinal);
+ }
+ let unsupported = preview
+ .nodes
+ .iter()
+ .filter(|node| !node.runtime_supported)
+ .count();
+ let supported = preview.nodes.len().saturating_sub(unsupported);
+ let warnings = if preview.warnings.is_empty() {
+ String::new()
+ } else {
+ format!(" {}", preview.warnings.join(" "))
+ };
+ status.set_label(&format!(
+ "Found {} runtime-supported nodes, {} unsupported nodes, and {} rejected entries in {}.{}",
+ supported,
+ unsupported,
+ preview.rejected_count,
+ preview.detected_format,
+ warnings
+ ));
+ }
+ Err(error) => {
+ status.set_label(&format!("Proxy subscription preview failed: {error}"));
+ }
+ }
+ });
+ });
+
+ let selected_for_list = selected_ordinal.clone();
+ let ordinals_for_list = node_ordinals.clone();
+ node_list.connect_row_selected(move |_, row| {
+ *selected_for_list.borrow_mut() = row.and_then(|row| {
+ let index = row.index();
+ if index < 0 {
+ return None;
+ }
+ ordinals_for_list.borrow().get(index as usize).copied()
+ });
+ });
+
+ let input = sender.input_sender().clone();
+ let window_for_click = window.clone();
+ let selected_for_import = selected_ordinal.clone();
+ import.connect_clicked(move |_| {
+ input.emit(HomeScreenMsg::AddProxySubscription {
+ url: url.text().to_string(),
+ name: name.text().to_string(),
+ selected_ordinal: *selected_for_import.borrow(),
+ });
+ window_for_click.close();
+ });
+
+ window.set_child(Some(&content));
+ window.present();
+}
+
fn sheet_window(root: >k::ScrolledWindow, title: &str, width: i32, height: i32) -> gtk::Window {
let window = gtk::Window::builder()
.title(title)
diff --git a/burrow-gtk/src/daemon_api.rs b/burrow-gtk/src/daemon_api.rs
index 4ff8bf5..3b1c5db 100644
--- a/burrow-gtk/src/daemon_api.rs
+++ b/burrow-gtk/src/daemon_api.rs
@@ -4,7 +4,9 @@ use burrow::{
grpc_defs::{
Empty, Network, NetworkType, State, TailnetDiscoverRequest, TailnetLoginCancelRequest,
TailnetLoginStartRequest, TailnetLoginStatusRequest, TailnetProbeRequest,
+ ProxySubscriptionApplyRequest, ProxySubscriptionImportRequest,
},
+ proxy_subscription::ProxySubscriptionPayload,
BurrowClient,
};
use std::{path::PathBuf, sync::OnceLock};
@@ -54,6 +56,27 @@ pub struct TailnetLoginStatus {
pub health: Vec,
}
+#[derive(Debug, Clone)]
+pub struct ProxySubscriptionPreview {
+ pub suggested_name: String,
+ pub detected_format: String,
+ pub compatible_count: i32,
+ pub rejected_count: i32,
+ pub nodes: Vec,
+ pub warnings: Vec,
+}
+
+#[derive(Debug, Clone)]
+pub struct ProxyNodePreview {
+ pub ordinal: i32,
+ pub name: String,
+ pub protocol: String,
+ pub server: String,
+ pub port: i32,
+ pub warnings: Vec,
+ pub runtime_supported: bool,
+}
+
pub fn default_tailnet_authority() -> &'static str {
MANAGED_TAILSCALE_AUTHORITY
}
@@ -216,6 +239,63 @@ pub async fn add_tailnet(
add_network(NetworkType::Tailnet, payload).await
}
+pub async fn preview_proxy_subscription(url: String) -> Result {
+ let mut client = BurrowClient::from_uds().await?;
+ let response = timeout(
+ Duration::from_secs(20),
+ client
+ .proxy_subscription_client
+ .preview_import(ProxySubscriptionImportRequest { url }),
+ )
+ .await
+ .context("timed out previewing proxy subscription")??
+ .into_inner();
+
+ Ok(ProxySubscriptionPreview {
+ suggested_name: response.suggested_name,
+ detected_format: response.detected_format,
+ compatible_count: response.compatible_count,
+ rejected_count: response.rejected_count,
+ nodes: response
+ .node
+ .into_iter()
+ .map(|node| ProxyNodePreview {
+ ordinal: node.ordinal,
+ name: node.name,
+ protocol: node.protocol,
+ server: node.server,
+ port: node.port,
+ warnings: node.warnings,
+ runtime_supported: node.runtime_supported,
+ })
+ .collect(),
+ warnings: response.warnings,
+ })
+}
+
+pub async fn add_proxy_subscription(
+ url: String,
+ name: String,
+ selected_ordinal: Option,
+) -> Result {
+ let id = next_network_id().await?;
+ let mut client = BurrowClient::from_uds().await?;
+ timeout(
+ Duration::from_secs(25),
+ client
+ .proxy_subscription_client
+ .apply_import(ProxySubscriptionApplyRequest {
+ id,
+ url,
+ name,
+ selected_ordinal: selected_ordinal.unwrap_or(-1),
+ }),
+ )
+ .await
+ .context("timed out importing proxy subscription")??;
+ Ok(id)
+}
+
pub async fn discover_tailnet(email: String) -> Result {
let mut client = BurrowClient::from_uds().await?;
let response = timeout(
@@ -328,6 +408,12 @@ fn summarize_network(network: &Network) -> NetworkSummary {
match network.r#type() {
NetworkType::WireGuard => summarize_wireguard(network),
NetworkType::Tailnet => summarize_tailnet(network),
+ NetworkType::Trojan => NetworkSummary {
+ id: network.id,
+ title: "Legacy Trojan Proxy".to_owned(),
+ detail: "Unsupported SOCKS-era profile. Import a proxy subscription instead.".to_owned(),
+ },
+ NetworkType::ProxySubscription => summarize_proxy_subscription(network),
}
}
@@ -372,6 +458,21 @@ fn summarize_tailnet(network: &Network) -> NetworkSummary {
}
}
+fn summarize_proxy_subscription(network: &Network) -> NetworkSummary {
+ match serde_json::from_slice::(&network.payload) {
+ Ok(payload) => NetworkSummary {
+ id: network.id,
+ title: payload.name.clone(),
+ detail: burrow::proxy_subscription::summarize_payload(&payload),
+ },
+ Err(error) => NetworkSummary {
+ id: network.id,
+ title: "Proxy Subscription".to_owned(),
+ detail: format!("Unable to read proxy subscription payload: {error}"),
+ },
+ }
+}
+
fn decode_tailnet_status(
response: burrow::grpc_defs::TailnetLoginStatusResponse,
) -> TailnetLoginStatus {
diff --git a/burrow/Cargo.toml b/burrow/Cargo.toml
index 22f3d25..27d072d 100644
--- a/burrow/Cargo.toml
+++ b/burrow/Cargo.toml
@@ -31,6 +31,7 @@ tracing-subscriber = { version = "0.3", features = ["std", "env-filter"] }
log = "0.4"
serde = { version = "1", features = ["derive"] }
serde_json = "1.0"
+serde_yaml = "0.9"
blake2 = "0.10"
chacha20poly1305 = "0.10"
rand = "0.8"
@@ -44,6 +45,7 @@ x25519-dalek = { version = "2.0", features = [
ring = "0.17"
parking_lot = "0.12"
hmac = "0.12"
+md-5 = "0.10"
base64 = "0.21"
fehler = "1.0"
ip_network_table = "0.2"
@@ -78,6 +80,15 @@ hyper-util = "0.1.6"
toml = "0.8.15"
rust-ini = "0.21.0"
subtle = "2.6"
+rustls = { version = "0.23", default-features = false, features = [
+ "aws_lc_rs",
+ "logging",
+ "std",
+ "tls12",
+] }
+sha2 = "0.10"
+tokio-rustls = "0.26"
+webpki-roots = "0.26"
[target.'cfg(target_os = "linux")'.dependencies]
caps = "0.5"
@@ -87,8 +98,11 @@ nix = { version = "0.27", features = ["fs", "socket", "uio"] }
tracing-journald = "0.3"
[target.'cfg(target_vendor = "apple")'.dependencies]
+libc = "0.2"
+native-tls = { version = "0.2", features = ["alpn"] }
nix = { version = "0.27" }
rusqlite = { version = "0.38.0", features = ["bundled", "blob"] }
+tokio-native-tls = "0.3"
[target.'cfg(target_os = "macos")'.dependencies]
tracing-oslog = { git = "https://github.com/Stormshield-robinc/tracing-oslog" }
diff --git a/burrow/src/auth/server/mod.rs b/burrow/src/auth/server/mod.rs
index fdffce3..d14e9a3 100644
--- a/burrow/src/auth/server/mod.rs
+++ b/burrow/src/auth/server/mod.rs
@@ -147,7 +147,10 @@ pub fn build_router(config: AuthServerConfig) -> Router {
.route("/v1/control/map", post(control_map))
.route("/v1/tailnet/discover", get(tailnet_discover))
.route("/v1/tailscale/login/start", post(tailscale_login_start))
- .route("/v1/tailscale/login/:session_id", get(tailscale_login_status))
+ .route(
+ "/v1/tailscale/login/:session_id",
+ get(tailscale_login_status),
+ )
.with_state(AppState {
config,
tailscale: tailscale::TailscaleBridgeManager::default(),
@@ -247,7 +250,12 @@ async fn tailscale_login_status(
.await
.map_err(internal_error)?
.map(Json)
- .ok_or_else(|| (StatusCode::NOT_FOUND, "unknown tailscale login session".to_owned()))
+ .ok_or_else(|| {
+ (
+ StatusCode::NOT_FOUND,
+ "unknown tailscale login session".to_owned(),
+ )
+ })
}
async fn healthz() -> impl IntoResponse {
@@ -419,10 +427,7 @@ mod tests {
async fn tailnet_discover_requires_email() -> Result<()> {
let app = build_router(AuthServerConfig::default());
let response = app
- .oneshot(
- Request::get("/v1/tailnet/discover?email=")
- .body(Body::empty())?,
- )
+ .oneshot(Request::get("/v1/tailnet/discover?email=").body(Body::empty())?)
.await?;
assert_eq!(response.status(), StatusCode::BAD_REQUEST);
Ok(())
diff --git a/burrow/src/auth/server/tailscale.rs b/burrow/src/auth/server/tailscale.rs
index d08c807..85bb1f2 100644
--- a/burrow/src/auth/server/tailscale.rs
+++ b/burrow/src/auth/server/tailscale.rs
@@ -291,7 +291,10 @@ impl TailscaleHelperProcess {
}
async fn shutdown_with_client(&self, client: &Client) -> Result<()> {
- let _ = client.post(format!("{}/shutdown", self.listen_url)).send().await;
+ let _ = client
+ .post(format!("{}/shutdown", self.listen_url))
+ .send()
+ .await;
for _ in 0..10 {
let mut child = self.child.lock().await;
@@ -402,7 +405,9 @@ fn helper_command(request: &TailscaleLoginStartRequest, state_dir: &Path) -> Res
}
pub(crate) fn packet_socket_path(request: &TailscaleLoginStartRequest) -> PathBuf {
- state_root().join(session_dir_name(request)).join("packet.sock")
+ state_root()
+ .join(session_dir_name(request))
+ .join("packet.sock")
}
pub(crate) fn state_root() -> PathBuf {
@@ -504,7 +509,10 @@ mod tests {
control_url: None,
packet_socket: None,
};
- assert_eq!(session_dir_name(&request), "default-apple-tailscale-managed");
+ assert_eq!(
+ session_dir_name(&request),
+ "default-apple-tailscale-managed"
+ );
assert_eq!(default_hostname(&request), "burrow-apple");
let custom_request = TailscaleLoginStartRequest {
diff --git a/burrow/src/control/discovery.rs b/burrow/src/control/discovery.rs
index d044a62..b86aedb 100644
--- a/burrow/src/control/discovery.rs
+++ b/burrow/src/control/discovery.rs
@@ -92,8 +92,13 @@ pub async fn probe_tailnet_authority(authority: &str) -> Result Result