diff --git a/.claude/settings.json b/.claude/settings.json index ce5d2734a..671a08447 100644 --- a/.claude/settings.json +++ b/.claude/settings.json @@ -1,5 +1,6 @@ { "attribution": { - "commit": "" + "commit": "", + "pr": "" } } diff --git a/CLAUDE.md b/CLAUDE.md index 22d94db31..2ed0a6368 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -29,6 +29,24 @@ make update-startbox REMOTE=start9@ # Fastest iteration (binary + UI) make test-core # Run Rust tests ``` +### Verifying code changes + +When making changes across multiple layers (Rust, SDK, web, container-runtime), verify in this order: + +1. **Rust**: `cargo check -p start-os` — verifies core compiles +2. **TS bindings**: `make ts-bindings` — regenerates TypeScript types from Rust `#[ts(export)]` structs + - Runs `./core/build/build-ts.sh` to export ts-rs types to `core/bindings/` + - Syncs `core/bindings/` → `sdk/base/lib/osBindings/` via rsync + - If you manually edit files in `sdk/base/lib/osBindings/`, you must still rebuild the SDK (step 3) +3. **SDK bundle**: `cd sdk && make baseDist dist` — compiles SDK source into packages + - `baseDist/` is consumed by `/web` (via `@start9labs/start-sdk-base`) + - `dist/` is consumed by `/container-runtime` (via `@start9labs/start-sdk`) + - Web and container-runtime reference the **built** SDK, not source files +4. **Web type check**: `cd web && npm run check` — type-checks all Angular projects +5. **Container runtime type check**: `cd container-runtime && npm run check` — type-checks the runtime + +**Important**: Editing `sdk/base/lib/osBindings/*.ts` alone is NOT sufficient — you must rebuild the SDK bundle (step 3) before web/container-runtime can see the changes. + ## Architecture ### Core (`/core`) diff --git a/agents/TODO.md b/agents/TODO.md index 70124aa74..cabae12fb 100644 --- a/agents/TODO.md +++ b/agents/TODO.md @@ -6,4 +6,223 @@ Pending tasks for AI agents. Remove items when completed. - [ ] Architecture - Web (`/web`) - @MattDHill +## Features +- [ ] Support preferred external ports besides 443 - @dr-bonez + + **Problem**: Currently, port 443 is the only preferred external port that is actually honored. When a + service requests `preferred_external_port: 8443` (or any non-443 value) for SSL, the system ignores + the preference and assigns a dynamic-range port (49152-65535). The `preferred_external_port` is only + used as a label for Tor mappings and as a trigger for the port-443 special case in `update()`. + + **Goal**: Honor `preferred_external_port` for both SSL and non-SSL binds when the requested port is + available, with proper conflict resolution and fallback to dynamic-range allocation. + + ### Design + + **Key distinction**: There are two separate concepts for SSL port usage: + + 1. **Port ownership** (`assigned_ssl_port`) — A port exclusively owned by a binding, allocated from + `AvailablePorts`. Used for server hostnames (`.local`, mDNS, etc.) and iptables forwards. + 2. **Domain SSL port** — The port used for domain-based vhost entries. A binding does NOT need to own + a port to have a domain vhost on it. The VHostController already supports multiple hostnames on the + same port via SNI. Any binding can create a domain vhost entry on any SSL port that the + VHostController has a listener for, regardless of who "owns" that port. + + For example: the OS owns port 443 as its `assigned_ssl_port`. A service with + `preferred_external_port: 443` won't get 443 as its `assigned_ssl_port` (it's taken), but it CAN + still have domain vhost entries on port 443 — SNI routes by hostname. + + #### 1. Preferred Port Allocation for Ownership ✅ DONE + + `AvailablePorts::try_alloc(port) -> Option` added to `forward.rs`. `BindInfo::new()` and + `BindInfo::update()` attempt the preferred port first, falling back to dynamic-range allocation. + + #### 2. Per-Address Enable/Disable ✅ DONE + + Gateway-level `private_disabled`/`public_enabled` on `NetInfo` replaced with per-address + `DerivedAddressInfo` on `BindInfo`. `hostname_info` removed from `Host` — computed addresses now + live in `BindInfo.addresses.possible`. + + **`DerivedAddressInfo` struct** (on `BindInfo`): + + ```rust + pub struct DerivedAddressInfo { + pub private_disabled: BTreeSet, + pub public_enabled: BTreeSet, + pub possible: BTreeSet, // COMPUTED by update() + } + ``` + + `DerivedAddressInfo::enabled()` returns `possible` filtered by the two sets. `HostnameInfo` derives + `Ord` for `BTreeSet` usage. `AddressFilter` (implementing `InterfaceFilter`) derives enabled + gateway set from `DerivedAddressInfo` for vhost/forward filtering. + + **RPC endpoint**: `set-gateway-enabled` replaced with `set-address-enabled` (on both + `server.host.binding` and `package.host.binding`). + + **How disabling works per address type** (enforcement deferred to Section 3): + + - **WAN/LAN IP:port**: Will be enforced via **source-IP gating** in the vhost layer (Section 3). + - **Hostname-based addresses** (`.local`, domains): Disabled by **not creating the vhost/SNI + entry** for that hostname. + + #### 3. Eliminate the Port 5443 Hack: Source-IP-Based WAN Blocking (`vhost.rs`, `net_controller.rs`) + + **Current problem**: The `if ssl.preferred_external_port == 443` branch (line 341 of + `net_controller.rs`) creates a bespoke dual-vhost setup: port 5443 for private-only access and port + 443 for public (or public+private). This exists because both public and private traffic arrive on the + same port 443 listener, and the current `InterfaceFilter`/`PublicFilter` model distinguishes + public/private by which *network interface* the connection arrived on — which doesn't work when both + traffic types share a listener. + + **Solution**: Determine public vs private based on **source IP** at the vhost level. Traffic arriving + from the gateway IP should be treated as public (the gateway may MASQUERADE/NAT internet traffic, so + anything from the gateway is potentially public). Traffic from LAN IPs is private. + + This applies to **all** vhost targets, not just port 443: + + - **Add a `public` field to `ProxyTarget`** (or an enum: `Public`, `Private`, `Both`) indicating + what traffic this target accepts, derived from the binding's user-controlled `public` field. + - **Modify `VHostTarget::filter()`** (`vhost.rs:342`): Instead of (or in addition to) checking the + network interface via `GatewayInfo`, check the source IP of the TCP connection against known gateway + IPs. If the source IP matches a gateway or IP outside the subnet, the connection is public; + otherwise it's private. Use this to gate against the target's `public` field. + - **Eliminate the 5443 port entirely**: A single vhost entry on port 443 (or any shared SSL port) can + serve both public and private traffic, with per-target source-IP gating determining which backend + handles which connections. + + #### 4. Port Forward Mapping in Patch-DB + + When a binding is marked `public = true`, StartOS must record the required port forwards in patch-db + so the frontend can display them to the user. The user then configures these on their router manually. + + For each public binding, store: + - The external port the router should forward (the actual vhost port used for domains, or the + `assigned_port` / `assigned_ssl_port` for non-domain access) + - The protocol (TCP/UDP) + - The StartOS LAN IP as the forward target + - Which service/binding this forward is for (for display purposes) + + This mapping should be in the public database model so the frontend can read and display it. + + #### 5. Simplify `update()` Domain Vhost Logic (`net_controller.rs`) + + With source-IP gating in the vhost controller: + + - **Remove the `== 443` special case** and the 5443 secondary vhost. + - For **server hostnames** (`.local`, mDNS, embassy, startos, localhost): use `assigned_ssl_port` + (the port the binding owns). + - For **domain-based vhost entries**: attempt to use `preferred_external_port` as the vhost port. + This succeeds if the port is either unused or already has an SSL listener (SNI handles sharing). + It fails only if the port is already in use by a non-SSL binding, or is a restricted port. On + failure, fall back to `assigned_ssl_port`. + - The binding's `public` field determines the `ProxyTarget`'s public/private gating. + - Hostname info must exactly match the actual vhost port used: for server hostnames, report + `ssl_port: assigned_ssl_port`. For domains, report `ssl_port: preferred_external_port` if it was + successfully used for the domain vhost, otherwise report `ssl_port: assigned_ssl_port`. + + #### 6. Frontend: Interfaces Page Overhaul (View/Manage Split) + + The current interfaces page is a single page showing gateways (with toggle), addresses, public + domains, and private domains. It gets split into two pages: **View** and **Manage**. + + **SDK**: `preferredExternalPort` is already exposed. No additional SDK changes needed. + + ##### View Page + + Displays all computed addresses for the interface (from `BindInfo.addresses`) as a flat list. For each + address, show: URL, type (IPv4, IPv6, .local, domain), access level (public/private), + gateway name, SSL indicator, enable/disable state, port forward info for public addresses, and a test button + for reachability (see Section 7). + + No gateway-level toggles. The old `gateways.component.ts` toggle UI is removed. + + **Note**: Exact UI element placement (where toggles, buttons, info badges go) is sensitive. + Prompt the user for specific placement decisions during implementation. + + ##### Manage Page + + Simple CRUD interface for configuring which addresses exist. Two sections: + + - **Public domains**: Add/remove. Uses existing RPC endpoints: + - `{server,package}.host.address.domain.public.add` + - `{server,package}.host.address.domain.public.remove` + - **Private domains**: Add/remove. Uses existing RPC endpoints: + - `{server,package}.host.address.domain.private.add` + - `{server,package}.host.address.domain.private.remove` + + ##### Key Frontend Files to Modify + + | File | Change | + |------|--------| + | `web/projects/ui/src/app/routes/portal/components/interfaces/` | Overhaul: split into view/manage | + | `web/projects/ui/src/app/routes/portal/components/interfaces/gateways.component.ts` | Remove (replaced by per-address toggles on View page) | + | `web/projects/ui/src/app/routes/portal/components/interfaces/interface.service.ts` | Update `MappedServiceInterface` to compute enabled addresses from `DerivedAddressInfo` | + | `web/projects/ui/src/app/routes/portal/components/interfaces/addresses/` | Refactor for View page with overflow menu (enable/disable) and test buttons | + | `web/projects/ui/src/app/routes/portal/routes/services/services.routes.ts` | Add routes for view/manage sub-pages | + | `web/projects/ui/src/app/routes/portal/routes/system/system.routes.ts` | Add routes for view/manage sub-pages | + + #### 7. Reachability Test Endpoint + + New RPC endpoint that tests whether an address is actually reachable, with diagnostic info on + failure. + + **RPC endpoint** (`binding.rs` or new file): + + - **`test-address`** — Test reachability of a specific address. + + ```ts + interface BindingTestAddressParams { + internalPort: number + address: HostnameInfo + } + ``` + + The backend simply performs the raw checks and returns the results. The **frontend** owns all + interpretation — it already knows the address type, expected IP, expected port, etc. from the + `HostnameInfo` data, so it can compare against the backend results and construct fix messaging. + + ```ts + interface TestAddressResult { + dns: string[] | null // resolved IPs, null if not a domain address or lookup failed + portOpen: boolean | null // TCP connect result, null if not applicable + } + ``` + + This yields two RPC methods: + - `server.host.binding.test-address` + - `package.host.binding.test-address` + + The frontend already has the full `HostnameInfo` context (expected IP, domain, port, gateway, + public/private). It compares the backend's raw results against the expected state and constructs + localized fix instructions. For example: + - `dns` returned but doesn't contain the expected WAN IP → "Update DNS A record for {domain} + to {wanIp}" + - `dns` is `null` for a domain address → "DNS lookup failed for {domain}" + - `portOpen` is `false` → "Configure port forward on your router: external {port} TCP → + {lanIp}:{port}" + + ### Key Files + + | File | Role | + |------|------| + | `core/src/net/forward.rs` | `AvailablePorts` — port pool allocation, `try_alloc()` for preferred ports | + | `core/src/net/host/binding.rs` | `Bindings` (Map wrapper for patchdb), `BindInfo`/`NetInfo`/`DerivedAddressInfo`/`AddressFilter` — per-address enable/disable, `set-address-enabled` RPC | + | `core/src/net/net_controller.rs:259` | `NetServiceData::update()` — computes `DerivedAddressInfo.possible`, vhost/forward/DNS reconciliation, 5443 hack removal | + | `core/src/net/vhost.rs` | `VHostController` / `ProxyTarget` — source-IP gating for public/private | + | `core/src/net/gateway.rs` | `InterfaceFilter` trait and filter types (`AddressFilter`, `PublicFilter`, etc.) | + | `core/src/net/service_interface.rs` | `HostnameInfo` — derives `Ord` for `BTreeSet` usage | + | `core/src/net/host/address.rs` | `HostAddress` (flattened struct), domain CRUD endpoints | + | `sdk/base/lib/interfaces/Host.ts` | SDK `MultiHost.bindPort()` — no changes needed | + | `core/src/db/model/public.rs` | Public DB model — port forward mapping | + +- [ ] Auto-configure port forwards via UPnP/NAT-PMP/PCP - @dr-bonez + + **Blocked by**: "Support preferred external ports besides 443" (must be implemented and tested + end-to-end first). + + **Goal**: When a binding is marked public, automatically configure port forwards on the user's router + using UPnP, NAT-PMP, or PCP, instead of requiring manual router configuration. Fall back to + displaying manual instructions (the port forward mapping from patch-db) when auto-configuration is + unavailable or fails. diff --git a/build/lib/scripts/forward-port b/build/lib/scripts/forward-port index 705c1e6a7..31f42a39e 100755 --- a/build/lib/scripts/forward-port +++ b/build/lib/scripts/forward-port @@ -5,7 +5,7 @@ if [ -z "$sip" ] || [ -z "$dip" ] || [ -z "$dprefix" ] || [ -z "$sport" ] || [ - exit 1 fi -NAME="F$(echo "$sip:$sport -> $dip/$dprefix:$dport" | sha256sum | head -c 15)" +NAME="F$(echo "$sip:$sport -> $dip/$dprefix:$dport ${src_subnet:-any} ${excluded_src:-none}" | sha256sum | head -c 15)" for kind in INPUT FORWARD ACCEPT; do if ! iptables -C $kind -j "${NAME}_${kind}" 2> /dev/null; then @@ -36,8 +36,22 @@ if [ "$UNDO" = 1 ]; then fi # DNAT: rewrite destination for incoming packets (external traffic) -iptables -t nat -A ${NAME}_PREROUTING -d "$sip" -p tcp --dport "$sport" -j DNAT --to-destination "$dip:$dport" -iptables -t nat -A ${NAME}_PREROUTING -d "$sip" -p udp --dport "$sport" -j DNAT --to-destination "$dip:$dport" +# When src_subnet is set, only forward traffic from that subnet (private forwards) +# excluded_src: comma-separated gateway/router IPs to reject (they may masquerade internet traffic) +if [ -n "$src_subnet" ]; then + if [ -n "$excluded_src" ]; then + IFS=',' read -ra EXCLUDED <<< "$excluded_src" + for excl in "${EXCLUDED[@]}"; do + iptables -t nat -A ${NAME}_PREROUTING -s "$excl" -d "$sip" -p tcp --dport "$sport" -j RETURN + iptables -t nat -A ${NAME}_PREROUTING -s "$excl" -d "$sip" -p udp --dport "$sport" -j RETURN + done + fi + iptables -t nat -A ${NAME}_PREROUTING -s "$src_subnet" -d "$sip" -p tcp --dport "$sport" -j DNAT --to-destination "$dip:$dport" + iptables -t nat -A ${NAME}_PREROUTING -s "$src_subnet" -d "$sip" -p udp --dport "$sport" -j DNAT --to-destination "$dip:$dport" +else + iptables -t nat -A ${NAME}_PREROUTING -d "$sip" -p tcp --dport "$sport" -j DNAT --to-destination "$dip:$dport" + iptables -t nat -A ${NAME}_PREROUTING -d "$sip" -p udp --dport "$sport" -j DNAT --to-destination "$dip:$dport" +fi # DNAT: rewrite destination for locally-originated packets (hairpin from host itself) iptables -t nat -A ${NAME}_OUTPUT -d "$sip" -p tcp --dport "$sport" -j DNAT --to-destination "$dip:$dport" @@ -52,4 +66,4 @@ iptables -t nat -A ${NAME}_POSTROUTING -d "$dip" -p udp --dport "$dport" -j MASQ iptables -A ${NAME}_FORWARD -d $dip -p tcp --dport $dport -m state --state NEW -j ACCEPT iptables -A ${NAME}_FORWARD -d $dip -p udp --dport $dport -m state --state NEW -j ACCEPT -exit $err \ No newline at end of file +exit $err diff --git a/container-runtime/RPCSpec.md b/container-runtime/RPCSpec.md index 7c43467ba..57ff31348 100644 --- a/container-runtime/RPCSpec.md +++ b/container-runtime/RPCSpec.md @@ -139,8 +139,8 @@ Evaluate a script in the runtime context. Used for debugging. The `execute` and `sandbox` methods route to procedures based on the `procedure` path: -| Procedure | Description | -|-----------|-------------| -| `/backup/create` | Create a backup | +| Procedure | Description | +| -------------------------- | ---------------------------- | +| `/backup/create` | Create a backup | | `/actions/{name}/getInput` | Get input spec for an action | -| `/actions/{name}/run` | Run an action with input | +| `/actions/{name}/run` | Run an action with input | diff --git a/container-runtime/src/Adapters/Systems/SystemForEmbassy/DockerProcedureContainer.ts b/container-runtime/src/Adapters/Systems/SystemForEmbassy/DockerProcedureContainer.ts index 029483212..1a8975317 100644 --- a/container-runtime/src/Adapters/Systems/SystemForEmbassy/DockerProcedureContainer.ts +++ b/container-runtime/src/Adapters/Systems/SystemForEmbassy/DockerProcedureContainer.ts @@ -82,18 +82,15 @@ export class DockerProcedureContainer extends Drop { }), ) } else if (volumeMount.type === "certificate") { + const hostInfo = await effects.getHostInfo({ + hostId: volumeMount["interface-id"], + }) const hostnames = [ `${packageId}.embassy`, ...new Set( - Object.values( - ( - await effects.getHostInfo({ - hostId: volumeMount["interface-id"], - }) - )?.hostnameInfo || {}, - ) - .flatMap((h) => h) - .flatMap((h) => (h.kind === "onion" ? [h.hostname.value] : [])), + Object.values(hostInfo?.bindings || {}) + .flatMap((b) => b.addresses.possible) + .map((h) => h.hostname.value), ).values(), ] const certChain = await effects.getSslCertificate({ diff --git a/container-runtime/src/Adapters/Systems/SystemForEmbassy/index.ts b/container-runtime/src/Adapters/Systems/SystemForEmbassy/index.ts index c2f84c19f..cb7585ac9 100644 --- a/container-runtime/src/Adapters/Systems/SystemForEmbassy/index.ts +++ b/container-runtime/src/Adapters/Systems/SystemForEmbassy/index.ts @@ -1244,12 +1244,8 @@ async function updateConfig( ? "" : catchFn( () => - (specValue.target === "lan-address" - ? filled.addressInfo!.filter({ kind: "mdns" }) || - filled.addressInfo!.onion - : filled.addressInfo!.onion || - filled.addressInfo!.filter({ kind: "mdns" }) - ).hostnames[0].hostname.value, + filled.addressInfo!.filter({ kind: "mdns" })!.hostnames[0] + .hostname.value, ) || "" mutConfigValue[key] = url } diff --git a/core/Cargo.lock b/core/Cargo.lock index 484974f9f..0e96d08e4 100644 --- a/core/Cargo.lock +++ b/core/Cargo.lock @@ -26,22 +26,10 @@ dependencies = [ "cfg-if", "cipher 0.3.0", "cpufeatures", - "ctr 0.8.0", + "ctr", "opaque-debug", ] -[[package]] -name = "aes" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" -dependencies = [ - "cfg-if", - "cipher 0.4.4", - "cpufeatures", - "zeroize", -] - [[package]] name = "ahash" version = "0.7.8" @@ -95,52 +83,6 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" -[[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.17", - "getrandom 0.3.4", - "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.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99bcb75a2982047f733547042fc3968c0f460dfcf7d90b90dea3b2744580e9ad" -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" @@ -150,12 +92,6 @@ dependencies = [ "libc", ] -[[package]] -name = "anes" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" - [[package]] name = "ansi-regex" version = "0.1.0" @@ -230,15 +166,6 @@ dependencies = [ "object 0.32.2", ] -[[package]] -name = "arbitrary" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1" -dependencies = [ - "derive_arbitrary", -] - [[package]] name = "arc-swap" version = "1.8.0" @@ -272,62 +199,6 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" -[[package]] -name = "arti-client" -version = "0.33.0" -source = "git+https://github.com/Start9Labs/arti.git?branch=patch%2Fdisable-exit#24730694701a83432d791d80802db8bda0699700" -dependencies = [ - "async-trait", - "cfg-if", - "derive-deftly", - "derive_builder_fork_arti", - "derive_more 2.1.1", - "educe", - "fs-mistrust", - "futures", - "hostname-validator", - "humantime", - "humantime-serde", - "libc", - "once_cell", - "postage", - "rand 0.9.2", - "safelog", - "serde", - "thiserror 2.0.17", - "time", - "tor-async-utils", - "tor-basic-utils", - "tor-chanmgr", - "tor-circmgr", - "tor-config", - "tor-config-path", - "tor-dirmgr", - "tor-error", - "tor-guardmgr", - "tor-hsclient", - "tor-hscrypto", - "tor-hsservice", - "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 = "ascii-canvas" version = "3.0.0" @@ -343,7 +214,7 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5493c3bedbacf7fd7382c6346bbd66687d12bbaad3a89a2d2c303ee6cf20b048" dependencies = [ - "asn1-rs-derive 0.5.1", + "asn1-rs-derive", "asn1-rs-impl", "displaydoc", "nom 7.1.3", @@ -353,21 +224,6 @@ dependencies = [ "time", ] -[[package]] -name = "asn1-rs" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56624a96882bb8c26d61312ae18cb45868e5a9992ea73c58e45c3101e56a1e60" -dependencies = [ - "asn1-rs-derive 0.6.0", - "asn1-rs-impl", - "displaydoc", - "nom 7.1.3", - "num-traits", - "rusticata-macros", - "thiserror 2.0.17", -] - [[package]] name = "asn1-rs-derive" version = "0.5.1" @@ -380,18 +236,6 @@ dependencies = [ "synstructure", ] -[[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.114", - "synstructure", -] - [[package]] name = "asn1-rs-impl" version = "0.2.0" @@ -403,12 +247,6 @@ dependencies = [ "syn 2.0.114", ] -[[package]] -name = "assert_matches" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9" - [[package]] name = "async-acme" version = "0.6.0" @@ -474,7 +312,6 @@ checksum = "d10e4f991a553474232bc0a31799f6d24b034a84c0971d80d2e2f78b2e576e40" dependencies = [ "compression-codecs", "compression-core", - "futures-io", "pin-project-lite", "tokio", ] @@ -493,21 +330,6 @@ dependencies = [ "slab", ] -[[package]] -name = "async-global-executor" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c" -dependencies = [ - "async-channel 2.5.0", - "async-executor", - "async-io", - "async-lock", - "blocking", - "futures-lite", - "once_cell", -] - [[package]] name = "async-io" version = "2.6.0" @@ -537,18 +359,6 @@ dependencies = [ "pin-project-lite", ] -[[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.69", - "url", -] - [[package]] name = "async-process" version = "2.5.0" @@ -596,33 +406,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "async-std" -version = "1.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c8e079a4ab67ae52b7403632e4618815d6db36d2a010cfe41b02c1b1578f93b" -dependencies = [ - "async-channel 1.9.0", - "async-global-executor", - "async-io", - "async-lock", - "async-process", - "crossbeam-utils", - "futures-channel", - "futures-core", - "futures-io", - "futures-lite", - "gloo-timers", - "kv-log-macro", - "log", - "memchr", - "once_cell", - "pin-project-lite", - "pin-utils", - "slab", - "wasm-bindgen-futures", -] - [[package]] name = "async-stream" version = "0.3.6" @@ -662,35 +445,6 @@ dependencies = [ "syn 2.0.114", ] -[[package]] -name = "async_executors" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a982d2f86de6137cc05c9db9a915a19886c97911f9790d04f174cede74be01a5" -dependencies = [ - "async-std", - "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 = "atoi" version = "2.0.0" @@ -700,21 +454,6 @@ dependencies = [ "num-traits", ] -[[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" @@ -816,7 +555,7 @@ dependencies = [ "miniz_oxide", "object 0.37.3", "rustc-demangle", - "windows-link 0.2.1", + "windows-link", ] [[package]] @@ -845,12 +584,6 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" -[[package]] -name = "base32" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23ce669cd6c8588f79e15cf450314f9638f967fc5770ff1c7c1deb0925ea7cfa" - [[package]] name = "base32" version = "0.5.1" @@ -863,12 +596,6 @@ version = "2.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1adf9755786e27479693dedd3271691a92b5e242ab139cacb9fb8e7fb5381111" -[[package]] -name = "base64" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" - [[package]] name = "base64" version = "0.21.7" @@ -999,15 +726,6 @@ dependencies = [ "wyz 0.5.1", ] -[[package]] -name = "blake2" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" -dependencies = [ - "digest 0.10.7", -] - [[package]] name = "blake2b_simd" version = "1.0.4" @@ -1035,17 +753,6 @@ dependencies = [ "rayon-core", ] -[[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.114", -] - [[package]] name = "block" version = "0.1.6" @@ -1058,7 +765,6 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" dependencies = [ - "block-padding", "generic-array", ] @@ -1071,12 +777,6 @@ dependencies = [ "generic-array", ] -[[package]] -name = "block-padding" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" - [[package]] name = "blocking" version = "1.6.2" @@ -1090,12 +790,6 @@ dependencies = [ "piper", ] -[[package]] -name = "bounded-vec-deque" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2225b558afc76c596898f5f1b3fc35cfce0eb1b13635cbd7d1b2a7177dc10ccd" - [[package]] name = "brotli" version = "8.0.2" @@ -1124,7 +818,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "63044e1ae8e69f3b5a92c736ca6269b8d12fa7efe39bf34ddb06d102cf0e2cab" dependencies = [ "memchr", - "regex-automata 0.4.13", "serde", ] @@ -1134,12 +827,6 @@ version = "3.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" -[[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.24.0" @@ -1164,17 +851,6 @@ version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" -[[package]] -name = "caret" -version = "0.5.3" -source = "git+https://github.com/Start9Labs/arti.git?branch=patch%2Fdisable-exit#24730694701a83432d791d80802db8bda0699700" - -[[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.53" @@ -1222,7 +898,7 @@ dependencies = [ "num-traits", "serde", "wasm-bindgen", - "windows-link 0.2.1", + "windows-link", ] [[package]] @@ -1279,7 +955,6 @@ checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" dependencies = [ "crypto-common", "inout", - "zeroize", ] [[package]] @@ -1301,7 +976,7 @@ dependencies = [ "anstream", "anstyle", "clap_lex", - "strsim 0.11.1", + "strsim", "terminal_size", ] @@ -1342,17 +1017,6 @@ 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 = "color-eyre" version = "0.6.5" @@ -1414,7 +1078,6 @@ dependencies = [ "brotli", "compression-core", "flate2", - "liblzma", "memchr", "zstd", "zstd-safe", @@ -1520,12 +1183,6 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d52eff69cd5e647efe296129160853a42795992097e8af39800e1060caeea9b" -[[package]] -name = "convert_case" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" - [[package]] name = "convert_case" version = "0.6.0" @@ -1555,15 +1212,6 @@ dependencies = [ "version_check", ] -[[package]] -name = "cookie-factory" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9885fa71e26b8ab7855e2ec7cae6e9b380edff76cd052e07c683a0319d51b3a2" -dependencies = [ - "futures", -] - [[package]] name = "cookie_store" version = "0.22.0" @@ -1667,52 +1315,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "criterion" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f" -dependencies = [ - "anes", - "cast", - "ciborium", - "clap", - "criterion-plot", - "is-terminal", - "itertools 0.10.5", - "num-traits", - "once_cell", - "oorandom", - "plotters", - "rayon", - "regex", - "serde", - "serde_derive", - "serde_json", - "tinytemplate", - "walkdir", -] - -[[package]] -name = "criterion-cycles-per-byte" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1029452fa751c93f8834962dd74807d69f0a6c7624d5b06625b393aeb6a14fc2" -dependencies = [ - "cfg-if", - "criterion", -] - -[[package]] -name = "criterion-plot" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" -dependencies = [ - "cast", - "itertools 0.10.5", -] - [[package]] name = "critical-section" version = "1.2.0" @@ -1770,7 +1372,7 @@ checksum = "d8b9f2e4c67f833b660cdb0a3523065869fb35570177239812ed4c905aeff87b" dependencies = [ "bitflags 2.10.0", "crossterm_winapi", - "derive_more 2.1.1", + "derive_more", "document-features", "futures-core", "mio", @@ -1818,23 +1420,13 @@ dependencies = [ "typenum", ] -[[package]] -name = "crypto-mac" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25fab6889090c8133f3deb8f73ba3c65a7f456f66436fc012a1b1e272b1e103e" -dependencies = [ - "generic-array", - "subtle", -] - [[package]] name = "csscolorparser" version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5fda6aace1fbef3aa217b27f4c8d7d071ef2a70a5ca51050b1f17d40299d3f16" dependencies = [ - "phf 0.11.3", + "phf", ] [[package]] @@ -1867,15 +1459,6 @@ dependencies = [ "cipher 0.3.0", ] -[[package]] -name = "ctr" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" -dependencies = [ - "cipher 0.4.4", -] - [[package]] name = "curve25519-dalek" version = "3.2.0" @@ -1916,76 +1499,6 @@ dependencies = [ "syn 2.0.114", ] -[[package]] -name = "darling" -version = "0.14.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -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_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", - "strsim 0.11.1", - "syn 2.0.114", -] - -[[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.114", -] - [[package]] name = "data-encoding" version = "2.10.0" @@ -2010,7 +1523,7 @@ version = "9.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5cd0a5c643689626bec213c4d8bd4d96acc8ffdb4ad4bb6bc16abf27d5f4b553" dependencies = [ - "asn1-rs 0.6.2", + "asn1-rs", "displaydoc", "nom 7.1.3", "num-bigint", @@ -2018,20 +1531,6 @@ dependencies = [ "rusticata-macros", ] -[[package]] -name = "der-parser" -version = "10.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07da5016415d5a3c4dd39b11ed26f915f52fc4e0dc197d87908bc916e51bc1a6" -dependencies = [ - "asn1-rs 0.7.1", - "cookie-factory", - "displaydoc", - "nom 7.1.3", - "num-traits", - "rusticata-macros", -] - [[package]] name = "der_derive" version = "0.7.3" @@ -2050,90 +1549,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" dependencies = [ "powerfmt", - "serde_core", -] - -[[package]] -name = "derive-deftly" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "957bb73a3a9c0bbcac67e129b81954661b3cfcb9e28873d8441f91b54852e77a" -dependencies = [ - "derive-deftly-macros", - "heck 0.5.0", -] - -[[package]] -name = "derive-deftly-macros" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ea41269bd490d251b9eca50ccb43117e641cc68b129849757c15ece88fe0574" -dependencies = [ - "heck 0.5.0", - "indexmap 2.13.0", - "itertools 0.14.0", - "proc-macro-crate", - "proc-macro2", - "quote", - "sha3 0.10.8", - "strum", - "syn 2.0.114", - "void", -] - -[[package]] -name = "derive_arbitrary" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.114", -] - -[[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 = "0.99.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6edb4b64a43d977b8e99788fe3a04d483834fba1215a7e02caa415b626497f7f" -dependencies = [ - "convert_case 0.4.0", - "proc-macro2", - "quote", - "rustc_version", - "syn 2.0.114", ] [[package]] @@ -2156,7 +1571,6 @@ dependencies = [ "quote", "rustc_version", "syn 2.0.114", - "unicode-xid", ] [[package]] @@ -2186,24 +1600,6 @@ dependencies = [ "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-next" version = "2.0.0" @@ -2214,18 +1610,6 @@ dependencies = [ "dirs-sys-next", ] -[[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 0.5.2", - "windows-sys 0.61.2", -] - [[package]] name = "dirs-sys-next" version = "0.1.2" @@ -2233,7 +1617,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" dependencies = [ "libc", - "redox_users 0.4.6", + "redox_users", "winapi", ] @@ -2296,12 +1680,6 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" -[[package]] -name = "downcast-rs" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "117240f60069e65410b3ae1bb213295bd828f707b5bec6596a1afc8793ce0cbc" - [[package]] name = "dunce" version = "1.0.5" @@ -2314,33 +1692,6 @@ version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" -[[package]] -name = "dynasm" -version = "3.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f7d4c414c94bc830797115b8e5f434d58e7e80cb42ba88508c14bc6ea270625" -dependencies = [ - "bitflags 2.10.0", - "byteorder", - "lazy_static", - "proc-macro-error2", - "proc-macro2", - "quote", - "syn 2.0.114", -] - -[[package]] -name = "dynasmrt" -version = "3.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "602f7458a3859195fb840e6e0cce5f4330dd9dfbfece0edaf31fe427af346f55" -dependencies = [ - "byteorder", - "dynasm", - "fnv", - "memmap2 0.9.9", -] - [[package]] name = "ecdsa" version = "0.16.9" @@ -2397,7 +1748,6 @@ checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9" dependencies = [ "curve25519-dalek 4.1.3", "ed25519 2.2.3", - "merlin", "rand_core 0.6.4", "serde", "sha2 0.10.9", @@ -2406,18 +1756,6 @@ dependencies = [ "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" version = "1.15.0" @@ -2517,19 +1855,6 @@ dependencies = [ "syn 2.0.114", ] -[[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.114", -] - [[package]] name = "enumflags2" version = "0.7.12" @@ -2557,18 +1882,6 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" -[[package]] -name = "equix" -version = "0.2.5" -source = "git+https://github.com/Start9Labs/arti.git?branch=patch%2Fdisable-exit#24730694701a83432d791d80802db8bda0699700" -dependencies = [ - "arrayvec 0.7.6", - "hashx", - "num-traits", - "thiserror 2.0.17", - "visibility", -] - [[package]] name = "errno" version = "0.3.14" @@ -2645,18 +1958,6 @@ dependencies = [ "once_cell", ] -[[package]] -name = "fallible-iterator" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" - -[[package]] -name = "fallible-streaming-iterator" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" - [[package]] name = "fastrand" version = "2.3.0" @@ -2688,19 +1989,6 @@ version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" -[[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 = "filedescriptor" version = "0.8.3" @@ -2730,12 +2018,6 @@ version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8591b0bcc8a98a64310a2fae1bb3e9b8564dd10e381e6e28010fde8e8e8568db" -[[package]] -name = "fixed-capacity-vec" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b31a14f5ee08ed1a40e1252b35af18bed062e3f39b69aab34decde36bc43e40" - [[package]] name = "fixedbitset" version = "0.4.2" @@ -2752,12 +2034,6 @@ 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 = "fnv" version = "1.0.7" @@ -2809,20 +2085,6 @@ dependencies = [ "itertools 0.8.2", ] -[[package]] -name = "fs-mistrust" -version = "0.10.0" -source = "git+https://github.com/Start9Labs/arti.git?branch=patch%2Fdisable-exit#24730694701a83432d791d80802db8bda0699700" -dependencies = [ - "derive_builder_fork_arti", - "dirs", - "libc", - "pwd-grp", - "serde", - "thiserror 2.0.17", - "walkdir", -] - [[package]] name = "fs2" version = "0.4.3" @@ -2839,36 +2101,6 @@ 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 = "fslock-arti-fork" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b21bd626aaab7b904b20bef6d9e06298914a0c8d9fb8b010483766b2e532791" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "fslock-guard" -version = "0.2.4" -source = "git+https://github.com/Start9Labs/arti.git?branch=patch%2Fdisable-exit#24730694701a83432d791d80802db8bda0699700" -dependencies = [ - "fslock-arti-fork", - "thiserror 2.0.17", - "winapi", -] - [[package]] name = "funty" version = "1.1.0" @@ -2975,17 +2207,6 @@ dependencies = [ "rustls-pki-types", ] -[[package]] -name = "futures-rustls" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f2f12607f92c69b12ed746fabf9ca4f5c482cba46679c1a75b874ed7c26adb" -dependencies = [ - "futures-io", - "rustls 0.23.36", - "rustls-pki-types", -] - [[package]] name = "futures-sink" version = "0.3.31" @@ -3043,7 +2264,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75cec8bb4d3d32542cfcb9517f78366b52c17931e30d7ee1682c13686c19cee7" dependencies = [ "futures", - "futures-rustls 0.25.1", + "futures-rustls", "hyper", "log", "serde", @@ -3115,12 +2336,6 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" -[[package]] -name = "glob-match" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9985c9503b412198aa4197559e9a318524ebc4519c229bfa05a535828c950b9d" - [[package]] name = "globset" version = "0.4.18" @@ -3145,18 +2360,6 @@ dependencies = [ "walkdir", ] -[[package]] -name = "gloo-timers" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbb143cf96099802033e0d4f4963b19fd2e0b728bcf076cd9cf7f6634f092994" -dependencies = [ - "futures-channel", - "futures-core", - "js-sys", - "wasm-bindgen", -] - [[package]] name = "gpt" version = "4.1.0" @@ -3180,18 +2383,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "growable-bloom-filter" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d174ccb4ba660d431329e7f0797870d0a4281e36353ec4b4a3c5eab6c2cfb6f1" -dependencies = [ - "serde", - "serde_bytes", - "serde_derive", - "xxhash-rust", -] - [[package]] name = "h2" version = "0.4.13" @@ -3292,20 +2483,6 @@ dependencies = [ "hashbrown 0.15.5", ] -[[package]] -name = "hashx" -version = "0.3.4" -source = "git+https://github.com/Start9Labs/arti.git?branch=patch%2Fdisable-exit#24730694701a83432d791d80802db8bda0699700" -dependencies = [ - "arrayvec 0.7.6", - "blake2", - "dynasmrt", - "fixed-capacity-vec", - "hex", - "rand_core 0.9.5", - "thiserror 2.0.17", -] - [[package]] name = "hdrhistogram" version = "7.5.4" @@ -3427,17 +2604,7 @@ version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" dependencies = [ - "hmac 0.12.1", -] - -[[package]] -name = "hmac" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b" -dependencies = [ - "crypto-mac", - "digest 0.9.0", + "hmac", ] [[package]] @@ -3466,15 +2633,9 @@ checksum = "617aaa3557aef3810a6369d0a99fac8a080891b68bd9f9812a1eeda0c0730cbd" dependencies = [ "cfg-if", "libc", - "windows-link 0.2.1", + "windows-link", ] -[[package]] -name = "hostname-validator" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f558a64ac9af88b5ba400d99b579451af0d39c6d360980045b91aac966d705e2" - [[package]] name = "http" version = "1.4.0" @@ -3526,16 +2687,6 @@ 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 = "1.8.1" @@ -3643,7 +2794,7 @@ dependencies = [ "js-sys", "log", "wasm-bindgen", - "windows-core 0.62.2", + "windows-core", ] [[package]] @@ -3758,12 +2909,6 @@ dependencies = [ "rustc-hash", ] -[[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" @@ -3894,7 +3039,6 @@ checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", "hashbrown 0.12.3", - "serde", ] [[package]] @@ -3973,15 +3117,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "inventory" -version = "0.3.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc61209c082fbeb19919bee74b176221b27223e27b65d781eb91af24eb1fb46e" -dependencies = [ - "rustversion", -] - [[package]] name = "ipconfig" version = "0.3.2" @@ -4224,45 +3359,6 @@ dependencies = [ "serde_json", ] -[[package]] -name = "k12" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4dc5fdb62af2f520116927304f15d25b3c2667b4817b90efdc045194c912c54" -dependencies = [ - "digest 0.10.7", - "sha3 0.10.8", -] - -[[package]] -name = "keccak" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" -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.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed9625ffda8729b85e45cf04090035ac368927b8cebc34898e7c120f52e4838b" -dependencies = [ - "bitflags 1.3.2", - "libc", -] - [[package]] name = "kv" version = "0.24.0" @@ -4278,15 +3374,6 @@ dependencies = [ "toml 0.5.11", ] -[[package]] -name = "kv-log-macro" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f" -dependencies = [ - "log", -] - [[package]] name = "lalrpop" version = "0.20.2" @@ -4404,27 +3491,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" dependencies = [ "cfg-if", - "windows-link 0.2.1", -] - -[[package]] -name = "liblzma" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73c36d08cad03a3fbe2c4e7bb3a9e84c57e4ee4135ed0b065cade3d98480c648" -dependencies = [ - "liblzma-sys", -] - -[[package]] -name = "liblzma-sys" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01b9596486f6d60c3bbe644c0e1be1aa6ccc472ad630fe8927b456973d7cb736" -dependencies = [ - "cc", - "libc", - "pkg-config", + "windows-link", ] [[package]] @@ -4454,17 +3521,6 @@ dependencies = [ "redox_syscall 0.7.0", ] -[[package]] -name = "libsqlite3-sys" -version = "0.35.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "133c182a6a2c87864fe97778797e46c7e999672690dc9fa3ee8e241aa4a9c13f" -dependencies = [ - "cc", - "pkg-config", - "vcpkg", -] - [[package]] name = "libyml" version = "0.0.5" @@ -4519,9 +3575,6 @@ name = "log" version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" -dependencies = [ - "value-bag", -] [[package]] name = "lru-slab" @@ -4618,18 +3671,6 @@ 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 = "7.6.0" @@ -4854,38 +3895,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "notify" -version = "8.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d3d07927151ff8575b7087f245456e549fea62edf0ec4e565a5ee50c8402bc3" -dependencies = [ - "bitflags 2.10.0", - "inotify", - "kqueue", - "libc", - "log", - "mio", - "notify-types", - "walkdir", - "windows-sys 0.60.2", -] - -[[package]] -name = "notify-types" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e0826a989adedc2a244799e823aece04662b66609d96af8dff7ac6df9a8925d" - -[[package]] -name = "ntapi" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c70f219e21142367c70c0b30c6a9e3a14d55b4d12a204d897fbec83a0363f081" -dependencies = [ - "winapi", -] - [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -5050,25 +4059,6 @@ 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.10.0", -] - -[[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" @@ -5102,7 +4092,7 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8d8034d9489cdaf79228eb9f6a3b8d7bb32ba00d6645ebd48eef4077ceb5bd9" dependencies = [ - "asn1-rs 0.6.2", + "asn1-rs", ] [[package]] @@ -5121,14 +4111,6 @@ version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" -[[package]] -name = "oneshot-fused-workaround" -version = "0.2.3" -source = "git+https://github.com/Start9Labs/arti.git?branch=patch%2Fdisable-exit#24730694701a83432d791d80802db8bda0699700" -dependencies = [ - "futures", -] - [[package]] name = "onig" version = "6.5.1" @@ -5151,12 +4133,6 @@ dependencies = [ "pkg-config", ] -[[package]] -name = "oorandom" -version = "11.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" - [[package]] name = "opaque-debug" version = "0.3.1" @@ -5236,21 +4212,6 @@ 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-stream" version = "0.2.0" @@ -5261,15 +4222,6 @@ dependencies = [ "pin-project-lite", ] -[[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" @@ -5371,15 +4323,9 @@ dependencies = [ "libc", "redox_syscall 0.5.18", "smallvec", - "windows-link 0.2.1", + "windows-link", ] -[[package]] -name = "paste" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" - [[package]] name = "patch-db" version = "0.1.0" @@ -5428,7 +4374,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" dependencies = [ "digest 0.10.7", - "hmac 0.12.1", + "hmac", ] [[package]] @@ -5515,19 +4461,8 @@ version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" dependencies = [ - "phf_macros 0.11.3", - "phf_shared 0.11.3", -] - -[[package]] -name = "phf" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1562dc717473dbaa4c1f85a36410e03c047b2e7df7f45ee938fbef64ae7fadf" -dependencies = [ - "phf_macros 0.13.1", - "phf_shared 0.13.1", - "serde", + "phf_macros", + "phf_shared", ] [[package]] @@ -5536,41 +4471,18 @@ version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" dependencies = [ - "phf_shared 0.11.3", + "phf_shared", "rand 0.8.5", ] -[[package]] -name = "phf_generator" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "135ace3a761e564ec88c03a77317a7c6b80bb7f7135ef2544dbe054243b89737" -dependencies = [ - "fastrand", - "phf_shared 0.13.1", -] - [[package]] name = "phf_macros" version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216" dependencies = [ - "phf_generator 0.11.3", - "phf_shared 0.11.3", - "proc-macro2", - "quote", - "syn 2.0.114", -] - -[[package]] -name = "phf_macros" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "812f032b54b1e759ccd5f8b6677695d5268c588701effba24601f6932f8269ef" -dependencies = [ - "phf_generator 0.13.1", - "phf_shared 0.13.1", + "phf_generator", + "phf_shared", "proc-macro2", "quote", "syn 2.0.114", @@ -5585,15 +4497,6 @@ dependencies = [ "siphasher", ] -[[package]] -name = "phf_shared" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e57fef6bc5981e38c2ce2d63bfa546861309f875b8a75f092d1d54ae2d64f266" -dependencies = [ - "siphasher", -] - [[package]] name = "pico-args" version = "0.5.0" @@ -5683,34 +4586,6 @@ dependencies = [ "time", ] -[[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 = "polling" version = "3.11.0" @@ -5739,7 +4614,7 @@ checksum = "b4a596a2b3d2752d94f51fac2d4a96737b8705dddd311a32b9af47211f08671e" dependencies = [ "anyhow", "bitflags 1.3.2", - "downcast-rs 1.2.1", + "downcast-rs", "filedescriptor", "lazy_static", "libc", @@ -5752,21 +4627,6 @@ dependencies = [ "winreg 0.10.1", ] -[[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 0.12.5", - "pin-project", - "static_assertions", - "thiserror 1.0.69", -] - [[package]] name = "potential_utf" version = "0.1.4" @@ -5840,17 +4700,6 @@ 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.13.0", - "serde", -] - [[package]] name = "proc-macro-crate" version = "3.4.0" @@ -5860,28 +4709,6 @@ dependencies = [ "toml_edit 0.23.10+spec-1.0.0", ] -[[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.114", -] - [[package]] name = "proc-macro2" version = "1.0.105" @@ -6012,18 +4839,6 @@ dependencies = [ "psl-types", ] -[[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.17", -] - [[package]] name = "pxfm" version = "0.1.27" @@ -6294,17 +5109,6 @@ dependencies = [ "rand_core 0.5.1", ] -[[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.5", - "winapi", -] - [[package]] name = "rand_xorshift" version = "0.4.0" @@ -6323,16 +5127,6 @@ dependencies = [ "rand_core 0.9.5", ] -[[package]] -name = "rayon" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" -dependencies = [ - "either", - "rayon-core", -] - [[package]] name = "rayon-core" version = "1.13.0" @@ -6355,15 +5149,6 @@ dependencies = [ "yasna", ] -[[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.2.16" @@ -6402,37 +5187,6 @@ dependencies = [ "thiserror 1.0.69", ] -[[package]] -name = "redox_users" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" -dependencies = [ - "getrandom 0.2.17", - "libredox", - "thiserror 2.0.17", -] - -[[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.114", -] - [[package]] name = "regex" version = "1.12.2" @@ -6544,18 +5298,13 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e061d1b48cb8d38042de4ae0a7a6401009d6143dc80d2e2d6f31f0bdd6470c7" -[[package]] -name = "retry-error" -version = "0.6.5" -source = "git+https://github.com/Start9Labs/arti.git?branch=patch%2Fdisable-exit#24730694701a83432d791d80802db8bda0699700" - [[package]] name = "rfc6979" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" dependencies = [ - "hmac 0.12.1", + "hmac", "subtle", ] @@ -6644,21 +5393,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "rusqlite" -version = "0.37.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "165ca6e57b20e1351573e3729b958bc62f0e48025386970b6e4d29e7a7e71f3f" -dependencies = [ - "bitflags 2.10.0", - "fallible-iterator", - "fallible-streaming-iterator", - "hashlink", - "libsqlite3-sys", - "smallvec", - "time", -] - [[package]] name = "rust-argon2" version = "3.0.0" @@ -6916,18 +5650,6 @@ version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a50f4cf475b65d88e057964e0e9bb1f0aa9bbb2036dc65c64596b42932536984" -[[package]] -name = "safelog" -version = "0.4.8" -source = "git+https://github.com/Start9Labs/arti.git?branch=patch%2Fdisable-exit#24730694701a83432d791d80802db8bda0699700" -dependencies = [ - "derive_more 2.1.1", - "educe", - "either", - "fluid-let", - "thiserror 2.0.17", -] - [[package]] name = "same-file" version = "1.0.6" @@ -6937,15 +5659,6 @@ 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 = "schannel" version = "0.1.28" @@ -6955,30 +5668,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[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.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54e910108742c57a770f492731f99be216a52fadd361b06c8fb59d74ccc267d2" -dependencies = [ - "dyn-clone", - "ref-cast", - "serde", - "serde_json", -] - [[package]] name = "scoped-tls" version = "1.0.1" @@ -7070,26 +5759,6 @@ dependencies = [ "serde", ] -[[package]] -name = "serde-value" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c" -dependencies = [ - "ordered-float", - "serde", -] - -[[package]] -name = "serde_bytes" -version = "0.11.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5d440709e79d88e51ac01c4b72fc6cb7314017bb7da9eeff678aa94c10e3ea8" -dependencies = [ - "serde", - "serde_core", -] - [[package]] name = "serde_cbor" version = "0.11.1" @@ -7128,16 +5797,6 @@ dependencies = [ "syn 2.0.114", ] -[[package]] -name = "serde_ignored" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "115dffd5f3853e06e746965a20dcbae6ee747ae30b543d91b0e089668bb07798" -dependencies = [ - "serde", - "serde_core", -] - [[package]] name = "serde_json" version = "1.0.149" @@ -7215,37 +5874,6 @@ dependencies = [ "serde", ] -[[package]] -name = "serde_with" -version = "3.16.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fa237f2807440d238e0364a218270b98f767a00d3dada77b1c53ae88940e2e7" -dependencies = [ - "base64 0.22.1", - "chrono", - "hex", - "indexmap 1.9.3", - "indexmap 2.13.0", - "schemars 0.9.0", - "schemars 1.2.0", - "serde_core", - "serde_json", - "serde_with_macros", - "time", -] - -[[package]] -name = "serde_with_macros" -version = "3.16.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52a8e3ca0ca629121f70ab50f95249e5a6f925cc0f6ffe8256c45b728875706c" -dependencies = [ - "darling 0.21.3", - "proc-macro2", - "quote", - "syn 2.0.114", -] - [[package]] name = "serde_yaml" version = "0.9.34+deprecated" @@ -7332,28 +5960,6 @@ dependencies = [ "digest 0.10.7", ] -[[package]] -name = "sha3" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f81199417d4e5de3f04b1e871023acea7389672c4135918f05aa9cbf2f2fa809" -dependencies = [ - "block-buffer 0.9.0", - "digest 0.9.0", - "keccak", - "opaque-debug", -] - -[[package]] -name = "sha3" -version = "0.10.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" -dependencies = [ - "digest 0.10.7", - "keccak", -] - [[package]] name = "sharded-slab" version = "0.1.7" @@ -7379,17 +5985,6 @@ version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc6fe69c597f9c37bfeeeeeb33da3530379845f10be461a66d16d03eca2ded77" -[[package]] -name = "shellexpand" -version = "3.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b1fdf65dd6331831494dd616b30351c38e96e45921a27745cf98490458b90bb" -dependencies = [ - "bstr", - "dirs", - "os_str_bytes", -] - [[package]] name = "shlex" version = "1.3.0" @@ -7483,28 +6078,6 @@ dependencies = [ "parking_lot 0.11.2", ] -[[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.2.5" -source = "git+https://github.com/Start9Labs/arti.git?branch=patch%2Fdisable-exit#24730694701a83432d791d80802db8bda0699700" -dependencies = [ - "paste", - "serde", - "slotmap", - "thiserror 2.0.17", - "void", -] - [[package]] name = "smallstr" version = "0.3.1" @@ -7708,7 +6281,7 @@ dependencies = [ "futures-util", "hex", "hkdf", - "hmac 0.12.1", + "hmac", "home", "itoa", "log", @@ -7749,7 +6322,7 @@ dependencies = [ "proc-macro2", "quote", "regex-syntax 0.6.29", - "strsim 0.11.1", + "strsim", "syn 2.0.114", "unicode-width 0.1.14", ] @@ -7817,17 +6390,16 @@ dependencies = [ [[package]] name = "start-os" -version = "0.4.0-alpha.19" +version = "0.4.0-alpha.20" dependencies = [ - "aes 0.7.5", - "arti-client", + "aes", "async-acme", "async-compression", "async-stream", "async-trait", "axum", "backtrace-on-stack-overflow", - "base32 0.5.1", + "base32", "base64 0.22.1", "base64ct", "basic-cookies", @@ -7842,7 +6414,6 @@ dependencies = [ "const_format", "cookie", "cookie_store", - "curve25519-dalek 4.1.3", "der", "digest 0.10.7", "divrem", @@ -7858,7 +6429,7 @@ dependencies = [ "hashing-serializer", "hex", "hickory-server", - "hmac 0.12.1", + "hmac", "http", "http-body-util", "hyper", @@ -7917,7 +6488,6 @@ dependencies = [ "rpc-toolkit", "rust-argon2", "rust-i18n", - "safelog", "semver", "serde", "serde_json", @@ -7941,14 +6511,6 @@ dependencies = [ "tokio-tungstenite 0.26.2", "tokio-util", "toml 0.9.11+spec-1.1.0", - "tor-cell", - "tor-hscrypto", - "tor-hsservice", - "tor-keymgr", - "tor-llcrypto", - "tor-proto", - "tor-rtcompat", - "torut", "tower-service", "tracing", "tracing-error", @@ -7977,7 +6539,7 @@ checksum = "bf776ba3fa74f83bf4b63c3dcbbf82173db2632ed8452cb2d891d33f459de70f" dependencies = [ "new_debug_unreachable", "parking_lot 0.12.5", - "phf_shared 0.11.3", + "phf_shared", "precomputed-hash", ] @@ -8010,12 +6572,6 @@ dependencies = [ "vte", ] -[[package]] -name = "strsim" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" - [[package]] name = "strsim" version = "0.11.1" @@ -8027,9 +6583,6 @@ 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" @@ -8133,20 +6686,6 @@ dependencies = [ "yaml-rust", ] -[[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", -] - [[package]] name = "system-configuration" version = "0.6.1" @@ -8351,20 +6890,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" 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.10.0" @@ -8500,7 +7028,6 @@ checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" dependencies = [ "bytes", "futures-core", - "futures-io", "futures-sink", "pin-project-lite", "tokio", @@ -8647,1001 +7174,6 @@ dependencies = [ "tonic", ] -[[package]] -name = "tor-async-utils" -version = "0.33.0" -source = "git+https://github.com/Start9Labs/arti.git?branch=patch%2Fdisable-exit#24730694701a83432d791d80802db8bda0699700" -dependencies = [ - "derive-deftly", - "educe", - "futures", - "oneshot-fused-workaround", - "pin-project", - "postage", - "thiserror 2.0.17", - "void", -] - -[[package]] -name = "tor-basic-utils" -version = "0.33.0" -source = "git+https://github.com/Start9Labs/arti.git?branch=patch%2Fdisable-exit#24730694701a83432d791d80802db8bda0699700" -dependencies = [ - "derive_more 2.1.1", - "hex", - "itertools 0.14.0", - "libc", - "paste", - "rand 0.9.2", - "rand_chacha 0.9.0", - "serde", - "slab", - "smallvec", - "thiserror 2.0.17", -] - -[[package]] -name = "tor-bytes" -version = "0.33.0" -source = "git+https://github.com/Start9Labs/arti.git?branch=patch%2Fdisable-exit#24730694701a83432d791d80802db8bda0699700" -dependencies = [ - "bytes", - "derive-deftly", - "digest 0.10.7", - "educe", - "getrandom 0.3.4", - "safelog", - "thiserror 2.0.17", - "tor-error", - "tor-llcrypto", - "zeroize", -] - -[[package]] -name = "tor-cell" -version = "0.33.0" -source = "git+https://github.com/Start9Labs/arti.git?branch=patch%2Fdisable-exit#24730694701a83432d791d80802db8bda0699700" -dependencies = [ - "amplify", - "bitflags 2.10.0", - "bytes", - "caret", - "derive-deftly", - "derive_more 2.1.1", - "educe", - "itertools 0.14.0", - "paste", - "rand 0.9.2", - "smallvec", - "thiserror 2.0.17", - "tor-basic-utils", - "tor-bytes", - "tor-cert", - "tor-error", - "tor-hscrypto", - "tor-linkspec", - "tor-llcrypto", - "tor-memquota", - "tor-protover", - "tor-units", - "void", -] - -[[package]] -name = "tor-cert" -version = "0.33.0" -source = "git+https://github.com/Start9Labs/arti.git?branch=patch%2Fdisable-exit#24730694701a83432d791d80802db8bda0699700" -dependencies = [ - "caret", - "derive_builder_fork_arti", - "derive_more 2.1.1", - "digest 0.10.7", - "thiserror 2.0.17", - "tor-bytes", - "tor-checkable", - "tor-llcrypto", -] - -[[package]] -name = "tor-chanmgr" -version = "0.33.0" -source = "git+https://github.com/Start9Labs/arti.git?branch=patch%2Fdisable-exit#24730694701a83432d791d80802db8bda0699700" -dependencies = [ - "async-trait", - "caret", - "derive_builder_fork_arti", - "derive_more 2.1.1", - "educe", - "futures", - "oneshot-fused-workaround", - "postage", - "rand 0.9.2", - "safelog", - "serde", - "thiserror 2.0.17", - "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", - "void", -] - -[[package]] -name = "tor-checkable" -version = "0.33.0" -source = "git+https://github.com/Start9Labs/arti.git?branch=patch%2Fdisable-exit#24730694701a83432d791d80802db8bda0699700" -dependencies = [ - "humantime", - "signature 2.2.0", - "thiserror 2.0.17", - "tor-llcrypto", -] - -[[package]] -name = "tor-circmgr" -version = "0.33.0" -source = "git+https://github.com/Start9Labs/arti.git?branch=patch%2Fdisable-exit#24730694701a83432d791d80802db8bda0699700" -dependencies = [ - "amplify", - "async-trait", - "bounded-vec-deque", - "cfg-if", - "derive-deftly", - "derive_builder_fork_arti", - "derive_more 2.1.1", - "downcast-rs 2.0.2", - "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", - "static_assertions", - "thiserror 2.0.17", - "tor-async-utils", - "tor-basic-utils", - "tor-cell", - "tor-chanmgr", - "tor-config", - "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.33.0" -source = "git+https://github.com/Start9Labs/arti.git?branch=patch%2Fdisable-exit#24730694701a83432d791d80802db8bda0699700" -dependencies = [ - "amplify", - "cfg-if", - "derive-deftly", - "derive_builder_fork_arti", - "educe", - "either", - "figment", - "fs-mistrust", - "futures", - "itertools 0.14.0", - "notify", - "paste", - "postage", - "regex", - "serde", - "serde-value", - "serde_ignored", - "strum", - "thiserror 2.0.17", - "toml 0.9.11+spec-1.1.0", - "tor-basic-utils", - "tor-error", - "tor-rtcompat", - "tracing", - "void", -] - -[[package]] -name = "tor-config-path" -version = "0.33.0" -source = "git+https://github.com/Start9Labs/arti.git?branch=patch%2Fdisable-exit#24730694701a83432d791d80802db8bda0699700" -dependencies = [ - "directories", - "serde", - "shellexpand", - "thiserror 2.0.17", - "tor-error", - "tor-general-addr", -] - -[[package]] -name = "tor-consdiff" -version = "0.33.0" -source = "git+https://github.com/Start9Labs/arti.git?branch=patch%2Fdisable-exit#24730694701a83432d791d80802db8bda0699700" -dependencies = [ - "digest 0.10.7", - "hex", - "thiserror 2.0.17", - "tor-llcrypto", -] - -[[package]] -name = "tor-dirclient" -version = "0.33.0" -source = "git+https://github.com/Start9Labs/arti.git?branch=patch%2Fdisable-exit#24730694701a83432d791d80802db8bda0699700" -dependencies = [ - "async-compression", - "base64ct", - "derive_more 2.1.1", - "futures", - "hex", - "http", - "httparse", - "httpdate", - "itertools 0.14.0", - "memchr", - "thiserror 2.0.17", - "tor-circmgr", - "tor-error", - "tor-hscrypto", - "tor-linkspec", - "tor-llcrypto", - "tor-netdoc", - "tor-proto", - "tor-rtcompat", - "tracing", -] - -[[package]] -name = "tor-dirmgr" -version = "0.33.0" -source = "git+https://github.com/Start9Labs/arti.git?branch=patch%2Fdisable-exit#24730694701a83432d791d80802db8bda0699700" -dependencies = [ - "async-trait", - "base64ct", - "derive_builder_fork_arti", - "derive_more 2.1.1", - "digest 0.10.7", - "educe", - "event-listener 5.4.1", - "fs-mistrust", - "fslock", - "futures", - "hex", - "humantime", - "humantime-serde", - "itertools 0.14.0", - "memmap2 0.9.9", - "oneshot-fused-workaround", - "paste", - "postage", - "rand 0.9.2", - "rusqlite", - "safelog", - "scopeguard", - "serde", - "serde_json", - "signature 2.2.0", - "static_assertions", - "strum", - "thiserror 2.0.17", - "time", - "tor-async-utils", - "tor-basic-utils", - "tor-checkable", - "tor-circmgr", - "tor-config", - "tor-consdiff", - "tor-dirclient", - "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.33.0" -source = "git+https://github.com/Start9Labs/arti.git?branch=patch%2Fdisable-exit#24730694701a83432d791d80802db8bda0699700" -dependencies = [ - "derive_more 2.1.1", - "futures", - "paste", - "retry-error", - "static_assertions", - "strum", - "thiserror 2.0.17", - "tracing", - "void", -] - -[[package]] -name = "tor-general-addr" -version = "0.33.0" -source = "git+https://github.com/Start9Labs/arti.git?branch=patch%2Fdisable-exit#24730694701a83432d791d80802db8bda0699700" -dependencies = [ - "arbitrary", - "derive_more 2.1.1", - "thiserror 2.0.17", - "void", -] - -[[package]] -name = "tor-guardmgr" -version = "0.33.0" -source = "git+https://github.com/Start9Labs/arti.git?branch=patch%2Fdisable-exit#24730694701a83432d791d80802db8bda0699700" -dependencies = [ - "amplify", - "base64ct", - "derive-deftly", - "derive_builder_fork_arti", - "derive_more 2.1.1", - "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.17", - "tor-async-utils", - "tor-basic-utils", - "tor-config", - "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-hsclient" -version = "0.33.0" -source = "git+https://github.com/Start9Labs/arti.git?branch=patch%2Fdisable-exit#24730694701a83432d791d80802db8bda0699700" -dependencies = [ - "async-trait", - "derive-deftly", - "derive_more 2.1.1", - "educe", - "either", - "futures", - "itertools 0.14.0", - "oneshot-fused-workaround", - "postage", - "rand 0.9.2", - "retry-error", - "safelog", - "slotmap-careful", - "strum", - "thiserror 2.0.17", - "tor-async-utils", - "tor-basic-utils", - "tor-bytes", - "tor-cell", - "tor-checkable", - "tor-circmgr", - "tor-config", - "tor-dirclient", - "tor-error", - "tor-hscrypto", - "tor-keymgr", - "tor-linkspec", - "tor-llcrypto", - "tor-memquota", - "tor-netdir", - "tor-netdoc", - "tor-persist", - "tor-proto", - "tor-protover", - "tor-rtcompat", - "tracing", -] - -[[package]] -name = "tor-hscrypto" -version = "0.33.0" -source = "git+https://github.com/Start9Labs/arti.git?branch=patch%2Fdisable-exit#24730694701a83432d791d80802db8bda0699700" -dependencies = [ - "cipher 0.4.4", - "data-encoding", - "derive-deftly", - "derive_more 2.1.1", - "digest 0.10.7", - "equix", - "hex", - "humantime", - "itertools 0.14.0", - "paste", - "rand 0.9.2", - "safelog", - "serde", - "signature 2.2.0", - "subtle", - "thiserror 2.0.17", - "tor-basic-utils", - "tor-bytes", - "tor-error", - "tor-key-forge", - "tor-llcrypto", - "tor-memquota", - "tor-units", - "void", - "zeroize", -] - -[[package]] -name = "tor-hsservice" -version = "0.33.0" -source = "git+https://github.com/Start9Labs/arti.git?branch=patch%2Fdisable-exit#24730694701a83432d791d80802db8bda0699700" -dependencies = [ - "amplify", - "async-trait", - "base64ct", - "cfg-if", - "derive-deftly", - "derive_builder_fork_arti", - "derive_more 2.1.1", - "digest 0.10.7", - "educe", - "fs-mistrust", - "futures", - "growable-bloom-filter", - "hex", - "humantime", - "itertools 0.14.0", - "k12", - "once_cell", - "oneshot-fused-workaround", - "postage", - "rand 0.9.2", - "rand_core 0.9.5", - "retry-error", - "safelog", - "serde", - "serde_with", - "strum", - "thiserror 2.0.17", - "tor-async-utils", - "tor-basic-utils", - "tor-bytes", - "tor-cell", - "tor-circmgr", - "tor-config", - "tor-config-path", - "tor-dirclient", - "tor-error", - "tor-hscrypto", - "tor-keymgr", - "tor-linkspec", - "tor-llcrypto", - "tor-log-ratelim", - "tor-netdir", - "tor-netdoc", - "tor-persist", - "tor-proto", - "tor-protover", - "tor-relay-selection", - "tor-rtcompat", - "tracing", - "void", -] - -[[package]] -name = "tor-key-forge" -version = "0.33.0" -source = "git+https://github.com/Start9Labs/arti.git?branch=patch%2Fdisable-exit#24730694701a83432d791d80802db8bda0699700" -dependencies = [ - "derive-deftly", - "derive_more 2.1.1", - "downcast-rs 2.0.2", - "paste", - "rand 0.9.2", - "signature 2.2.0", - "ssh-key", - "thiserror 2.0.17", - "tor-bytes", - "tor-cert", - "tor-checkable", - "tor-error", - "tor-llcrypto", -] - -[[package]] -name = "tor-keymgr" -version = "0.33.0" -source = "git+https://github.com/Start9Labs/arti.git?branch=patch%2Fdisable-exit#24730694701a83432d791d80802db8bda0699700" -dependencies = [ - "amplify", - "arrayvec 0.7.6", - "cfg-if", - "derive-deftly", - "derive_builder_fork_arti", - "derive_more 2.1.1", - "downcast-rs 2.0.2", - "dyn-clone", - "fs-mistrust", - "glob-match", - "humantime", - "inventory", - "itertools 0.14.0", - "rand 0.9.2", - "safelog", - "serde", - "signature 2.2.0", - "ssh-key", - "thiserror 2.0.17", - "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.33.0" -source = "git+https://github.com/Start9Labs/arti.git?branch=patch%2Fdisable-exit#24730694701a83432d791d80802db8bda0699700" -dependencies = [ - "base64ct", - "by_address", - "caret", - "derive-deftly", - "derive_builder_fork_arti", - "derive_more 2.1.1", - "hex", - "itertools 0.14.0", - "safelog", - "serde", - "serde_with", - "strum", - "thiserror 2.0.17", - "tor-basic-utils", - "tor-bytes", - "tor-config", - "tor-llcrypto", - "tor-memquota", - "tor-protover", -] - -[[package]] -name = "tor-llcrypto" -version = "0.33.0" -source = "git+https://github.com/Start9Labs/arti.git?branch=patch%2Fdisable-exit#24730694701a83432d791d80802db8bda0699700" -dependencies = [ - "aes 0.8.4", - "base64ct", - "ctr 0.9.2", - "curve25519-dalek 4.1.3", - "der-parser 10.0.0", - "derive-deftly", - "derive_more 2.1.1", - "digest 0.10.7", - "ed25519-dalek 2.2.0", - "educe", - "getrandom 0.3.4", - "hex", - "rand 0.9.2", - "rand_chacha 0.9.0", - "rand_core 0.6.4", - "rand_core 0.9.5", - "rand_jitter", - "rdrand", - "rsa", - "safelog", - "serde", - "sha1", - "sha2 0.10.9", - "sha3 0.10.8", - "signature 2.2.0", - "subtle", - "thiserror 2.0.17", - "tor-memquota", - "visibility", - "x25519-dalek", - "zeroize", -] - -[[package]] -name = "tor-log-ratelim" -version = "0.33.0" -source = "git+https://github.com/Start9Labs/arti.git?branch=patch%2Fdisable-exit#24730694701a83432d791d80802db8bda0699700" -dependencies = [ - "futures", - "humantime", - "thiserror 2.0.17", - "tor-error", - "tor-rtcompat", - "tracing", - "weak-table", -] - -[[package]] -name = "tor-memquota" -version = "0.33.0" -source = "git+https://github.com/Start9Labs/arti.git?branch=patch%2Fdisable-exit#24730694701a83432d791d80802db8bda0699700" -dependencies = [ - "cfg-if", - "derive-deftly", - "derive_more 2.1.1", - "dyn-clone", - "educe", - "futures", - "itertools 0.14.0", - "paste", - "pin-project", - "serde", - "slotmap-careful", - "static_assertions", - "sysinfo", - "thiserror 2.0.17", - "tor-async-utils", - "tor-basic-utils", - "tor-config", - "tor-error", - "tor-log-ratelim", - "tor-rtcompat", - "tracing", - "void", -] - -[[package]] -name = "tor-netdir" -version = "0.33.0" -source = "git+https://github.com/Start9Labs/arti.git?branch=patch%2Fdisable-exit#24730694701a83432d791d80802db8bda0699700" -dependencies = [ - "async-trait", - "bitflags 2.10.0", - "derive_more 2.1.1", - "digest 0.10.7", - "futures", - "hex", - "humantime", - "itertools 0.14.0", - "num_enum", - "rand 0.9.2", - "serde", - "static_assertions", - "strum", - "thiserror 2.0.17", - "time", - "tor-basic-utils", - "tor-error", - "tor-hscrypto", - "tor-linkspec", - "tor-llcrypto", - "tor-netdoc", - "tor-protover", - "tor-units", - "tracing", - "typed-index-collections", -] - -[[package]] -name = "tor-netdoc" -version = "0.33.0" -source = "git+https://github.com/Start9Labs/arti.git?branch=patch%2Fdisable-exit#24730694701a83432d791d80802db8bda0699700" -dependencies = [ - "amplify", - "base64ct", - "bitflags 2.10.0", - "cipher 0.4.4", - "derive-deftly", - "derive_builder_fork_arti", - "derive_more 2.1.1", - "digest 0.10.7", - "educe", - "hex", - "humantime", - "itertools 0.14.0", - "memchr", - "paste", - "phf 0.13.1", - "rand 0.9.2", - "serde", - "serde_with", - "signature 2.2.0", - "smallvec", - "strum", - "subtle", - "thiserror 2.0.17", - "time", - "tinystr", - "tor-basic-utils", - "tor-bytes", - "tor-cell", - "tor-cert", - "tor-checkable", - "tor-error", - "tor-hscrypto", - "tor-linkspec", - "tor-llcrypto", - "tor-protover", - "tor-units", - "void", - "weak-table", - "zeroize", -] - -[[package]] -name = "tor-persist" -version = "0.33.0" -source = "git+https://github.com/Start9Labs/arti.git?branch=patch%2Fdisable-exit#24730694701a83432d791d80802db8bda0699700" -dependencies = [ - "amplify", - "derive-deftly", - "derive_more 2.1.1", - "filetime", - "fs-mistrust", - "fslock", - "fslock-guard", - "futures", - "itertools 0.14.0", - "oneshot-fused-workaround", - "paste", - "sanitize-filename", - "serde", - "serde_json", - "thiserror 2.0.17", - "time", - "tor-async-utils", - "tor-basic-utils", - "tor-error", - "tracing", - "void", -] - -[[package]] -name = "tor-proto" -version = "0.33.0" -source = "git+https://github.com/Start9Labs/arti.git?branch=patch%2Fdisable-exit#24730694701a83432d791d80802db8bda0699700" -dependencies = [ - "amplify", - "asynchronous-codec", - "bitvec 1.0.1", - "bytes", - "caret", - "cfg-if", - "cipher 0.4.4", - "coarsetime", - "criterion-cycles-per-byte", - "derive-deftly", - "derive_builder_fork_arti", - "derive_more 2.1.1", - "digest 0.10.7", - "educe", - "futures", - "futures-util", - "hkdf", - "hmac 0.12.1", - "itertools 0.14.0", - "oneshot-fused-workaround", - "pin-project", - "postage", - "rand 0.9.2", - "rand_core 0.9.5", - "safelog", - "slotmap-careful", - "smallvec", - "static_assertions", - "subtle", - "sync_wrapper", - "thiserror 2.0.17", - "tokio", - "tokio-util", - "tor-async-utils", - "tor-basic-utils", - "tor-bytes", - "tor-cell", - "tor-cert", - "tor-checkable", - "tor-config", - "tor-error", - "tor-hscrypto", - "tor-linkspec", - "tor-llcrypto", - "tor-log-ratelim", - "tor-memquota", - "tor-protover", - "tor-rtcompat", - "tor-rtmock", - "tor-units", - "tracing", - "typenum", - "visibility", - "void", - "zeroize", -] - -[[package]] -name = "tor-protover" -version = "0.33.0" -source = "git+https://github.com/Start9Labs/arti.git?branch=patch%2Fdisable-exit#24730694701a83432d791d80802db8bda0699700" -dependencies = [ - "caret", - "paste", - "serde_with", - "thiserror 2.0.17", - "tor-bytes", -] - -[[package]] -name = "tor-relay-selection" -version = "0.33.0" -source = "git+https://github.com/Start9Labs/arti.git?branch=patch%2Fdisable-exit#24730694701a83432d791d80802db8bda0699700" -dependencies = [ - "rand 0.9.2", - "serde", - "tor-basic-utils", - "tor-linkspec", - "tor-netdir", - "tor-netdoc", -] - -[[package]] -name = "tor-rtcompat" -version = "0.33.0" -source = "git+https://github.com/Start9Labs/arti.git?branch=patch%2Fdisable-exit#24730694701a83432d791d80802db8bda0699700" -dependencies = [ - "arbitrary", - "async-io", - "async-native-tls", - "async-std", - "async-trait", - "async_executors", - "asynchronous-codec", - "coarsetime", - "derive_more 2.1.1", - "dyn-clone", - "educe", - "futures", - "futures-rustls 0.26.0", - "hex", - "libc", - "native-tls", - "paste", - "pin-project", - "rustls-pki-types", - "rustls-webpki 0.103.9", - "thiserror 2.0.17", - "tokio", - "tokio-util", - "tor-error", - "tor-general-addr", - "tracing", - "void", -] - -[[package]] -name = "tor-rtmock" -version = "0.33.0" -source = "git+https://github.com/Start9Labs/arti.git?branch=patch%2Fdisable-exit#24730694701a83432d791d80802db8bda0699700" -dependencies = [ - "amplify", - "assert_matches", - "async-trait", - "derive-deftly", - "derive_more 2.1.1", - "educe", - "futures", - "humantime", - "itertools 0.14.0", - "oneshot-fused-workaround", - "pin-project", - "priority-queue", - "slotmap-careful", - "strum", - "thiserror 2.0.17", - "tor-error", - "tor-general-addr", - "tor-rtcompat", - "tracing", - "tracing-test", - "void", -] - -[[package]] -name = "tor-socksproto" -version = "0.33.0" -source = "git+https://github.com/Start9Labs/arti.git?branch=patch%2Fdisable-exit#24730694701a83432d791d80802db8bda0699700" -dependencies = [ - "amplify", - "caret", - "derive-deftly", - "educe", - "safelog", - "subtle", - "thiserror 2.0.17", - "tor-bytes", - "tor-error", -] - -[[package]] -name = "tor-units" -version = "0.33.0" -source = "git+https://github.com/Start9Labs/arti.git?branch=patch%2Fdisable-exit#24730694701a83432d791d80802db8bda0699700" -dependencies = [ - "derive-deftly", - "derive_more 2.1.1", - "serde", - "thiserror 2.0.17", - "tor-memquota", -] - -[[package]] -name = "torut" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99febc413f26cf855b3a309c5872edff5c31e0ffe9c2fce5681868761df36f69" -dependencies = [ - "base32 0.4.0", - "base64 0.13.1", - "derive_more 0.99.20", - "ed25519-dalek 1.0.1", - "hex", - "hmac 0.11.0", - "rand 0.7.3", - "serde", - "serde_derive", - "sha2 0.9.9", - "sha3 0.9.1", - "tokio", -] - [[package]] name = "tower" version = "0.5.3" @@ -9786,27 +7318,6 @@ dependencies = [ "tracing-log", ] -[[package]] -name = "tracing-test" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "557b891436fe0d5e0e363427fc7f217abf9ccd510d5136549847bdcbcd011d68" -dependencies = [ - "tracing-core", - "tracing-subscriber", - "tracing-test-macro", -] - -[[package]] -name = "tracing-test-macro" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04659ddb06c87d233c566112c1c9c5b9e98256d9af50ec3bc9c8327f873a7568" -dependencies = [ - "quote", - "syn 2.0.114", -] - [[package]] name = "triomphe" version = "0.1.15" @@ -9902,16 +7413,6 @@ dependencies = [ "syn 2.0.114", ] -[[package]] -name = "typed-index-collections" -version = "3.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5318ee4ce62a4e948a33915574021a7a953d83e84fba6e25c72ffcfd7dad35ff" -dependencies = [ - "bincode 2.0.1", - "serde", -] - [[package]] name = "typenum" version = "1.19.0" @@ -9941,15 +7442,6 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" -[[package]] -name = "uncased" -version = "0.9.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1b88fcfe09e89d3866a5c11019378088af2d24c3fbd4f0543f96b479ec90697" -dependencies = [ - "version_check", -] - [[package]] name = "unicase" version = "2.9.0" @@ -10086,12 +7578,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" -[[package]] -name = "value-bag" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ba6f5989077681266825251a52748b8c1d8a4ad098cc37e440103d0ea717fc0" - [[package]] name = "vcpkg" version = "0.2.15" @@ -10110,17 +7596,6 @@ version = "0.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "051eb1abcf10076295e815102942cc58f9d5e3b4560e46e53c21e8ff6f3af7b1" -[[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.114", -] - [[package]] name = "visit-rs" version = "0.1.9" @@ -10144,12 +7619,6 @@ dependencies = [ "syn 2.0.114", ] -[[package]] -name = "void" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" - [[package]] name = "vte" version = "0.14.1" @@ -10214,15 +7683,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" -[[package]] -name = "wasix" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1757e0d1f8456693c7e5c6c629bdb54884e032aa0bb53c155f6a39f94440d332" -dependencies = [ - "wasi 0.11.1+wasi-snapshot-preview1", -] - [[package]] name = "wasm-bindgen" version = "0.2.108" @@ -10302,7 +7762,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f3b068c05a039c9f755f881dc50f01732214f5685e379829759088967c46715" dependencies = [ "bitflags 1.3.2", - "downcast-rs 1.2.1", + "downcast-rs", "libc", "nix 0.24.3", "scoped-tls", @@ -10368,12 +7828,6 @@ dependencies = [ "pkg-config", ] -[[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.85" @@ -10489,41 +7943,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[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" @@ -10532,20 +7951,9 @@ 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", + "windows-link", + "windows-result", + "windows-strings", ] [[package]] @@ -10570,46 +7978,21 @@ dependencies = [ "syn 2.0.114", ] -[[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-registry" version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720" dependencies = [ - "windows-link 0.2.1", - "windows-result 0.4.1", - "windows-strings 0.5.1", -] - -[[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", + "windows-link", + "windows-result", + "windows-strings", ] [[package]] @@ -10618,16 +8001,7 @@ 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", + "windows-link", ] [[package]] @@ -10636,7 +8010,7 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" dependencies = [ - "windows-link 0.2.1", + "windows-link", ] [[package]] @@ -10690,7 +8064,7 @@ version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" dependencies = [ - "windows-link 0.2.1", + "windows-link", ] [[package]] @@ -10745,7 +8119,7 @@ version = "0.53.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" dependencies = [ - "windows-link 0.2.1", + "windows-link", "windows_aarch64_gnullvm 0.53.1", "windows_aarch64_msvc 0.53.1", "windows_i686_gnu 0.53.1", @@ -10756,15 +8130,6 @@ dependencies = [ "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.42.2" @@ -11049,9 +8414,9 @@ version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcbc162f30700d6f3f82a24bf7cc62ffe7caea42c0b2cba8bf7f3ae50cf51f69" dependencies = [ - "asn1-rs 0.6.2", + "asn1-rs", "data-encoding", - "der-parser 9.0.0", + "der-parser", "lazy_static", "nom 7.1.3", "oid-registry", @@ -11309,7 +8674,6 @@ version = "0.11.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" dependencies = [ - "serde", "yoke", "zerofrom", "zerovec-derive", diff --git a/core/Cargo.toml b/core/Cargo.toml index 0256ac433..9937dfaa1 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -15,7 +15,7 @@ license = "MIT" name = "start-os" readme = "README.md" repository = "https://github.com/Start9Labs/start-os" -version = "0.4.0-alpha.19" # VERSION_BUMP +version = "0.4.0-alpha.20" # VERSION_BUMP [lib] name = "startos" @@ -42,17 +42,6 @@ name = "tunnelbox" path = "src/main/tunnelbox.rs" [features] -arti = [ - "arti-client", - "safelog", - "tor-cell", - "tor-hscrypto", - "tor-hsservice", - "tor-keymgr", - "tor-llcrypto", - "tor-proto", - "tor-rtcompat", -] beta = [] console = ["console-subscriber", "tokio/tracing"] default = [] @@ -62,16 +51,6 @@ unstable = ["backtrace-on-stack-overflow"] [dependencies] aes = { version = "0.7.5", features = ["ctr"] } -arti-client = { version = "0.33", features = [ - "compression", - "ephemeral-keystore", - "experimental-api", - "onion-service-client", - "onion-service-service", - "rustls", - "static", - "tokio", -], default-features = false, git = "https://github.com/Start9Labs/arti.git", branch = "patch/disable-exit", optional = true } async-acme = { version = "0.6.0", git = "https://github.com/dr-bonez/async-acme.git", features = [ "use_rustls", "use_tokio", @@ -100,7 +79,6 @@ console-subscriber = { version = "0.5.0", optional = true } const_format = "0.2.34" cookie = "0.18.0" cookie_store = "0.22.0" -curve25519-dalek = "4.1.3" der = { version = "0.7.9", features = ["derive", "pem"] } digest = "0.10.7" divrem = "1.0.0" @@ -216,7 +194,6 @@ rpassword = "7.2.0" rust-argon2 = "3.0.0" rust-i18n = "3.1.5" rpc-toolkit = { git = "https://github.com/Start9Labs/rpc-toolkit.git" } -safelog = { version = "0.4.8", git = "https://github.com/Start9Labs/arti.git", branch = "patch/disable-exit", optional = true } semver = { version = "1.0.20", features = ["serde"] } serde = { version = "1.0", features = ["derive", "rc"] } serde_cbor = { package = "ciborium", version = "0.2.1" } @@ -244,23 +221,6 @@ tokio-stream = { version = "0.1.14", features = ["io-util", "net", "sync"] } tokio-tar = { git = "https://github.com/dr-bonez/tokio-tar.git" } tokio-tungstenite = { version = "0.26.2", features = ["native-tls", "url"] } tokio-util = { version = "0.7.9", features = ["io"] } -tor-cell = { version = "0.33", git = "https://github.com/Start9Labs/arti.git", branch = "patch/disable-exit", optional = true } -tor-hscrypto = { version = "0.33", features = [ - "full", -], git = "https://github.com/Start9Labs/arti.git", branch = "patch/disable-exit", optional = true } -tor-hsservice = { version = "0.33", git = "https://github.com/Start9Labs/arti.git", branch = "patch/disable-exit", optional = true } -tor-keymgr = { version = "0.33", features = [ - "ephemeral-keystore", -], git = "https://github.com/Start9Labs/arti.git", branch = "patch/disable-exit", optional = true } -tor-llcrypto = { version = "0.33", features = [ - "full", -], git = "https://github.com/Start9Labs/arti.git", branch = "patch/disable-exit", optional = true } -tor-proto = { version = "0.33", git = "https://github.com/Start9Labs/arti.git", branch = "patch/disable-exit", optional = true } -tor-rtcompat = { version = "0.33", features = [ - "rustls", - "tokio", -], git = "https://github.com/Start9Labs/arti.git", branch = "patch/disable-exit", optional = true } -torut = "0.2.1" tower-service = "0.3.3" tracing = "0.1.39" tracing-error = "0.2.0" diff --git a/core/src/account.rs b/core/src/account.rs index d583c95f7..2216fc361 100644 --- a/core/src/account.rs +++ b/core/src/account.rs @@ -8,7 +8,6 @@ use openssl::x509::X509; use crate::db::model::DatabaseModel; use crate::hostname::{Hostname, generate_hostname, generate_id}; use crate::net::ssl::{gen_nistp256, make_root_cert}; -use crate::net::tor::TorSecretKey; use crate::prelude::*; use crate::util::serde::Pem; @@ -26,7 +25,6 @@ pub struct AccountInfo { pub server_id: String, pub hostname: Hostname, pub password: String, - pub tor_keys: Vec, pub root_ca_key: PKey, pub root_ca_cert: X509, pub ssh_key: ssh_key::PrivateKey, @@ -36,7 +34,6 @@ impl AccountInfo { pub fn new(password: &str, start_time: SystemTime) -> Result { let server_id = generate_id(); let hostname = generate_hostname(); - let tor_key = vec![TorSecretKey::generate()]; let root_ca_key = gen_nistp256()?; let root_ca_cert = make_root_cert(&root_ca_key, &hostname, start_time)?; let ssh_key = ssh_key::PrivateKey::from(ssh_key::private::Ed25519Keypair::random( @@ -48,7 +45,6 @@ impl AccountInfo { server_id, hostname, password: hash_password(password)?, - tor_keys: tor_key, root_ca_key, root_ca_cert, ssh_key, @@ -61,17 +57,6 @@ impl AccountInfo { let hostname = Hostname(db.as_public().as_server_info().as_hostname().de()?); let password = db.as_private().as_password().de()?; let key_store = db.as_private().as_key_store(); - let tor_addrs = db - .as_public() - .as_server_info() - .as_network() - .as_host() - .as_onions() - .de()?; - let tor_keys = tor_addrs - .into_iter() - .map(|tor_addr| key_store.as_onion().get_key(&tor_addr)) - .collect::>()?; let cert_store = key_store.as_local_certs(); let root_ca_key = cert_store.as_root_key().de()?.0; let root_ca_cert = cert_store.as_root_cert().de()?.0; @@ -82,7 +67,6 @@ impl AccountInfo { server_id, hostname, password, - tor_keys, root_ca_key, root_ca_cert, ssh_key, @@ -97,17 +81,6 @@ impl AccountInfo { server_info .as_pubkey_mut() .ser(&self.ssh_key.public_key().to_openssh()?)?; - server_info - .as_network_mut() - .as_host_mut() - .as_onions_mut() - .ser( - &self - .tor_keys - .iter() - .map(|tor_key| tor_key.onion_address()) - .collect(), - )?; server_info.as_password_hash_mut().ser(&self.password)?; db.as_private_mut().as_password_mut().ser(&self.password)?; db.as_private_mut() @@ -117,9 +90,6 @@ impl AccountInfo { .as_developer_key_mut() .ser(Pem::new_ref(&self.developer_key))?; let key_store = db.as_private_mut().as_key_store_mut(); - for tor_key in &self.tor_keys { - key_store.as_onion_mut().insert_key(tor_key)?; - } let cert_store = key_store.as_local_certs_mut(); if cert_store.as_root_cert().de()?.0 != self.root_ca_cert { cert_store @@ -148,11 +118,5 @@ impl AccountInfo { self.hostname.no_dot_host_name(), self.hostname.local_domain_name(), ] - .into_iter() - .chain( - self.tor_keys - .iter() - .map(|k| InternedString::from_display(&k.onion_address())), - ) } } diff --git a/core/src/backup/os.rs b/core/src/backup/os.rs index 380772b77..8837f72f0 100644 --- a/core/src/backup/os.rs +++ b/core/src/backup/os.rs @@ -7,9 +7,7 @@ use ssh_key::private::Ed25519Keypair; use crate::account::AccountInfo; use crate::hostname::{Hostname, generate_hostname, generate_id}; -use crate::net::tor::TorSecretKey; use crate::prelude::*; -use crate::util::crypto::ed25519_expand_key; use crate::util::serde::{Base32, Base64, Pem}; pub struct OsBackup { @@ -85,10 +83,6 @@ impl OsBackupV0 { &mut ssh_key::rand_core::OsRng::default(), ssh_key::Algorithm::Ed25519, )?, - tor_keys: TorSecretKey::from_bytes(self.tor_key.0) - .ok() - .into_iter() - .collect(), developer_key: ed25519_dalek::SigningKey::generate( &mut ssh_key::rand_core::OsRng::default(), ), @@ -119,10 +113,6 @@ impl OsBackupV1 { root_ca_key: self.root_ca_key.0, root_ca_cert: self.root_ca_cert.0, ssh_key: ssh_key::PrivateKey::from(Ed25519Keypair::from_seed(&self.net_key.0)), - tor_keys: TorSecretKey::from_bytes(ed25519_expand_key(&self.net_key.0)) - .ok() - .into_iter() - .collect(), developer_key: ed25519_dalek::SigningKey::from_bytes(&self.net_key), }, ui: self.ui, @@ -140,7 +130,6 @@ struct OsBackupV2 { root_ca_key: Pem>, // PEM Encoded OpenSSL Key root_ca_cert: Pem, // PEM Encoded OpenSSL X509 Certificate ssh_key: Pem, // PEM Encoded OpenSSH Key - tor_keys: Vec, // Base64 Encoded Ed25519 Expanded Secret Key compat_s9pk_key: Pem, // PEM Encoded ED25519 Key ui: Value, // JSON Value } @@ -154,7 +143,6 @@ impl OsBackupV2 { root_ca_key: self.root_ca_key.0, root_ca_cert: self.root_ca_cert.0, ssh_key: self.ssh_key.0, - tor_keys: self.tor_keys, developer_key: self.compat_s9pk_key.0, }, ui: self.ui, @@ -167,7 +155,6 @@ impl OsBackupV2 { root_ca_key: Pem(backup.account.root_ca_key.clone()), root_ca_cert: Pem(backup.account.root_ca_cert.clone()), ssh_key: Pem(backup.account.ssh_key.clone()), - tor_keys: backup.account.tor_keys.clone(), compat_s9pk_key: Pem(backup.account.developer_key.clone()), ui: backup.ui.clone(), } diff --git a/core/src/bins/start_init.rs b/core/src/bins/start_init.rs index 48e65f5af..5c53a6e0c 100644 --- a/core/src/bins/start_init.rs +++ b/core/src/bins/start_init.rs @@ -9,7 +9,7 @@ use crate::disk::fsck::RepairStrategy; use crate::disk::main::DEFAULT_PASSWORD; use crate::firmware::{check_for_firmware_update, update_firmware}; use crate::init::{InitPhases, STANDBY_MODE_PATH}; -use crate::net::gateway::UpgradableListener; +use crate::net::gateway::WildcardListener; use crate::net::web_server::WebServer; use crate::prelude::*; use crate::progress::FullProgressTracker; @@ -19,7 +19,7 @@ use crate::{DATA_DIR, PLATFORM}; #[instrument(skip_all)] async fn setup_or_init( - server: &mut WebServer, + server: &mut WebServer, config: &ServerConfig, ) -> Result, Error> { if let Some(firmware) = check_for_firmware_update() @@ -204,7 +204,7 @@ async fn setup_or_init( #[instrument(skip_all)] pub async fn main( - server: &mut WebServer, + server: &mut WebServer, config: &ServerConfig, ) -> Result, Error> { if &*PLATFORM == "raspberrypi" && tokio::fs::metadata(STANDBY_MODE_PATH).await.is_ok() { diff --git a/core/src/bins/startd.rs b/core/src/bins/startd.rs index f4a7784f4..b88f622e5 100644 --- a/core/src/bins/startd.rs +++ b/core/src/bins/startd.rs @@ -12,7 +12,7 @@ use tracing::instrument; use crate::context::config::ServerConfig; use crate::context::rpc::InitRpcContextPhases; use crate::context::{DiagnosticContext, InitContext, RpcContext}; -use crate::net::gateway::{BindTcp, SelfContainedNetworkInterfaceListener, UpgradableListener}; +use crate::net::gateway::WildcardListener; use crate::net::static_server::refresher; use crate::net::web_server::{Acceptor, WebServer}; use crate::prelude::*; @@ -23,7 +23,7 @@ use crate::util::logger::LOGGER; #[instrument(skip_all)] async fn inner_main( - server: &mut WebServer, + server: &mut WebServer, config: &ServerConfig, ) -> Result, Error> { let rpc_ctx = if !tokio::fs::metadata("/run/startos/initialized") @@ -148,7 +148,7 @@ pub fn main(args: impl IntoIterator) { .expect(&t!("bins.startd.failed-to-initialize-runtime")); let res = rt.block_on(async { let mut server = WebServer::new( - Acceptor::bind_upgradable(SelfContainedNetworkInterfaceListener::bind(BindTcp, 80)), + Acceptor::new(WildcardListener::new(80)?), refresher(), ); match inner_main(&mut server, &config).await { diff --git a/core/src/bins/tunnel.rs b/core/src/bins/tunnel.rs index 97fb818ea..57615c00c 100644 --- a/core/src/bins/tunnel.rs +++ b/core/src/bins/tunnel.rs @@ -13,7 +13,7 @@ use visit_rs::Visit; use crate::context::CliContext; use crate::context::config::ClientConfig; -use crate::net::gateway::{Bind, BindTcp}; +use tokio::net::TcpListener; use crate::net::tls::TlsListener; use crate::net::web_server::{Accept, Acceptor, MetadataVisitor, WebServer}; use crate::prelude::*; @@ -57,7 +57,12 @@ async fn inner_main(config: &TunnelConfig) -> Result<(), Error> { if !a.contains_key(&key) { match (|| { Ok::<_, Error>(TlsListener::new( - BindTcp.bind(addr)?, + TcpListener::from_std( + mio::net::TcpListener::bind(addr) + .with_kind(ErrorKind::Network)? + .into(), + ) + .with_kind(ErrorKind::Network)?, TunnelCertHandler { db: https_db.clone(), crypto_provider: Arc::new(tokio_rustls::rustls::crypto::ring::default_provider()), diff --git a/core/src/context/rpc.rs b/core/src/context/rpc.rs index a59d60236..6350672f1 100644 --- a/core/src/context/rpc.rs +++ b/core/src/context/rpc.rs @@ -34,7 +34,7 @@ use crate::disk::mount::guard::MountGuard; use crate::init::{InitResult, check_time_is_synchronized}; use crate::install::PKG_ARCHIVE_DIR; use crate::lxc::LxcManager; -use crate::net::gateway::UpgradableListener; +use crate::net::gateway::WildcardListener; use crate::net::net_controller::{NetController, NetService}; use crate::net::socks::DEFAULT_SOCKS_LISTEN; use crate::net::utils::{find_eth_iface, find_wifi_iface}; @@ -132,7 +132,7 @@ pub struct RpcContext(Arc); impl RpcContext { #[instrument(skip_all)] pub async fn init( - webserver: &WebServerAcceptorSetter, + webserver: &WebServerAcceptorSetter, config: &ServerConfig, disk_guid: InternedString, init_result: Option, @@ -167,7 +167,7 @@ impl RpcContext { } else { let net_ctrl = Arc::new(NetController::init(db.clone(), &account.hostname, socks_proxy).await?); - webserver.try_upgrade(|a| net_ctrl.net_iface.watcher.upgrade_listener(a))?; + webserver.send_modify(|wl| wl.set_ip_info(net_ctrl.net_iface.watcher.subscribe())); let os_net_service = net_ctrl.os_bindings().await?; (net_ctrl, os_net_service) }; diff --git a/core/src/context/setup.rs b/core/src/context/setup.rs index bbfee9862..39a6b5fd3 100644 --- a/core/src/context/setup.rs +++ b/core/src/context/setup.rs @@ -20,7 +20,7 @@ use crate::context::RpcContext; use crate::context::config::ServerConfig; use crate::disk::mount::guard::{MountGuard, TmpMountGuard}; use crate::hostname::Hostname; -use crate::net::gateway::UpgradableListener; +use crate::net::gateway::WildcardListener; use crate::net::web_server::{WebServer, WebServerAcceptorSetter}; use crate::prelude::*; use crate::progress::FullProgressTracker; @@ -51,7 +51,7 @@ pub struct SetupResult { } pub struct SetupContextSeed { - pub webserver: WebServerAcceptorSetter, + pub webserver: WebServerAcceptorSetter, pub config: SyncMutex, pub disable_encryption: bool, pub progress: FullProgressTracker, @@ -70,7 +70,7 @@ pub struct SetupContext(Arc); impl SetupContext { #[instrument(skip_all)] pub fn init( - webserver: &WebServer, + webserver: &WebServer, config: ServerConfig, ) -> Result { let (shutdown, _) = tokio::sync::broadcast::channel(1); diff --git a/core/src/db/model/public.rs b/core/src/db/model/public.rs index be4291215..0cdf868c9 100644 --- a/core/src/db/model/public.rs +++ b/core/src/db/model/public.rs @@ -20,7 +20,7 @@ use crate::db::model::Database; use crate::db::model::package::AllPackageData; use crate::net::acme::AcmeProvider; use crate::net::host::Host; -use crate::net::host::binding::{AddSslOptions, BindInfo, BindOptions, NetInfo}; +use crate::net::host::binding::{AddSslOptions, BindInfo, BindOptions, Bindings, DerivedAddressInfo, NetInfo}; use crate::net::utils::ipv6_is_local; use crate::net::vhost::AlpnInfo; use crate::prelude::*; @@ -63,36 +63,35 @@ impl Public { post_init_migration_todos: BTreeMap::new(), network: NetworkInfo { host: Host { - bindings: [( - 80, - BindInfo { - enabled: false, - options: BindOptions { - preferred_external_port: 80, - add_ssl: Some(AddSslOptions { - preferred_external_port: 443, - add_x_forwarded_headers: false, - alpn: Some(AlpnInfo::Specified(vec![ - MaybeUtf8String("h2".into()), - MaybeUtf8String("http/1.1".into()), - ])), - }), - secure: None, + bindings: Bindings( + [( + 80, + BindInfo { + enabled: false, + options: BindOptions { + preferred_external_port: 80, + add_ssl: Some(AddSslOptions { + preferred_external_port: 443, + add_x_forwarded_headers: false, + alpn: Some(AlpnInfo::Specified(vec![ + MaybeUtf8String("h2".into()), + MaybeUtf8String("http/1.1".into()), + ])), + }), + secure: None, + }, + net: NetInfo { + assigned_port: None, + assigned_ssl_port: Some(443), + }, + addresses: DerivedAddressInfo::default(), }, - net: NetInfo { - assigned_port: None, - assigned_ssl_port: Some(443), - private_disabled: OrdSet::new(), - public_enabled: OrdSet::new(), - }, - }, - )] - .into_iter() - .collect(), - onions: account.tor_keys.iter().map(|k| k.onion_address()).collect(), + )] + .into_iter() + .collect(), + ), public_domains: BTreeMap::new(), private_domains: BTreeSet::new(), - hostname_info: BTreeMap::new(), }, wifi: WifiInfo { enabled: true, diff --git a/core/src/error.rs b/core/src/error.rs index dba631303..0624be4dc 100644 --- a/core/src/error.rs +++ b/core/src/error.rs @@ -42,11 +42,11 @@ pub enum ErrorKind { ParseUrl = 19, DiskNotAvailable = 20, BlockDevice = 21, - InvalidOnionAddress = 22, + // InvalidOnionAddress = 22, Pack = 23, ValidateS9pk = 24, DiskCorrupted = 25, // Remove - Tor = 26, + // Tor = 26, ConfigGen = 27, ParseNumber = 28, Database = 29, @@ -126,11 +126,11 @@ impl ErrorKind { ParseUrl => t!("error.parse-url"), DiskNotAvailable => t!("error.disk-not-available"), BlockDevice => t!("error.block-device"), - InvalidOnionAddress => t!("error.invalid-onion-address"), + // InvalidOnionAddress => t!("error.invalid-onion-address"), Pack => t!("error.pack"), ValidateS9pk => t!("error.validate-s9pk"), DiskCorrupted => t!("error.disk-corrupted"), // Remove - Tor => t!("error.tor"), + // Tor => t!("error.tor"), ConfigGen => t!("error.config-gen"), ParseNumber => t!("error.parse-number"), Database => t!("error.database"), @@ -370,17 +370,6 @@ impl From for Error { Error::new(e, kind) } } -#[cfg(feature = "arti")] -impl From for Error { - fn from(e: arti_client::Error) -> Self { - Error::new(e, ErrorKind::Tor) - } -} -impl From for Error { - fn from(e: torut::control::ConnError) -> Self { - Error::new(e, ErrorKind::Tor) - } -} impl From for Error { fn from(e: zbus::Error) -> Self { Error::new(e, ErrorKind::DBus) diff --git a/core/src/init.rs b/core/src/init.rs index 39680015e..e9507ef49 100644 --- a/core/src/init.rs +++ b/core/src/init.rs @@ -20,7 +20,7 @@ use crate::db::model::public::ServerStatus; use crate::developer::OS_DEVELOPER_KEY_PATH; use crate::hostname::Hostname; use crate::middleware::auth::local::LocalAuthContext; -use crate::net::gateway::UpgradableListener; +use crate::net::gateway::WildcardListener; use crate::net::net_controller::{NetController, NetService}; use crate::net::socks::DEFAULT_SOCKS_LISTEN; use crate::net::utils::find_wifi_iface; @@ -144,7 +144,7 @@ pub async fn run_script>(path: P, mut progress: PhaseProgressTrac #[instrument(skip_all)] pub async fn init( - webserver: &WebServerAcceptorSetter, + webserver: &WebServerAcceptorSetter, cfg: &ServerConfig, InitPhases { preinit, @@ -218,7 +218,7 @@ pub async fn init( ) .await?, ); - webserver.try_upgrade(|a| net_ctrl.net_iface.watcher.upgrade_listener(a))?; + webserver.send_modify(|wl| wl.set_ip_info(net_ctrl.net_iface.watcher.subscribe())); let os_net_service = net_ctrl.os_bindings().await?; start_net.complete(); diff --git a/core/src/net/forward.rs b/core/src/net/forward.rs index b18ed7f1b..b9fe30a37 100644 --- a/core/src/net/forward.rs +++ b/core/src/net/forward.rs @@ -4,8 +4,8 @@ use std::sync::{Arc, Weak}; use std::time::Duration; use futures::channel::oneshot; -use id_pool::IdPool; use iddqd::{IdOrdItem, IdOrdMap}; +use rand::Rng; use imbl::OrdMap; use rpc_toolkit::{Context, HandlerArgs, HandlerExt, ParentHandler, from_fn_async}; use serde::{Deserialize, Serialize}; @@ -15,7 +15,6 @@ use tokio::sync::mpsc; use crate::GatewayId; use crate::context::{CliContext, RpcContext}; use crate::db::model::public::NetworkInterfaceInfo; -use crate::net::gateway::{DynInterfaceFilter, InterfaceFilter}; use crate::prelude::*; use crate::util::Invoke; use crate::util::future::NonDetachingJoinHandle; @@ -23,25 +22,76 @@ use crate::util::serde::{HandlerExtSerde, display_serializable}; use crate::util::sync::Watch; pub const START9_BRIDGE_IFACE: &str = "lxcbr0"; -pub const FIRST_DYNAMIC_PRIVATE_PORT: u16 = 49152; +const EPHEMERAL_PORT_START: u16 = 49152; +// vhost.rs:89 — not allowed: <=1024, >=32768, 5355, 5432, 9050, 6010, 9051, 5353 +const RESTRICTED_PORTS: &[u16] = &[5353, 5355, 5432, 6010, 9050, 9051]; + +fn is_restricted(port: u16) -> bool { + port <= 1024 || RESTRICTED_PORTS.contains(&port) +} + +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub struct ForwardRequirements { + pub public_gateways: BTreeSet, + pub private_ips: BTreeSet, + pub secure: bool, +} + +impl std::fmt::Display for ForwardRequirements { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "ForwardRequirements {{ public: {:?}, private: {:?}, secure: {} }}", + self.public_gateways, self.private_ips, self.secure + ) + } +} + +/// Source-IP filter for private forwards: restricts traffic to a subnet +/// while excluding gateway/router IPs that may masquerade internet traffic. +#[derive(Clone, Debug, PartialEq, Eq)] +pub(crate) struct SourceFilter { + /// Network CIDR to allow (e.g. "192.168.1.0/24") + subnet: String, + /// Comma-separated gateway IPs to exclude (they may masquerade internet traffic) + excluded: String, +} #[derive(Debug, Deserialize, Serialize)] -pub struct AvailablePorts(IdPool); +pub struct AvailablePorts(BTreeMap); impl AvailablePorts { pub fn new() -> Self { - Self(IdPool::new_ranged(FIRST_DYNAMIC_PRIVATE_PORT..u16::MAX)) + Self(BTreeMap::new()) } - pub fn alloc(&mut self) -> Result { - self.0.request_id().ok_or_else(|| { - Error::new( - eyre!("{}", t!("net.forward.no-dynamic-ports-available")), - ErrorKind::Network, - ) - }) + pub fn alloc(&mut self, ssl: bool) -> Result { + let mut rng = rand::rng(); + for _ in 0..1000 { + let port = rng.random_range(EPHEMERAL_PORT_START..u16::MAX); + if !self.0.contains_key(&port) { + self.0.insert(port, ssl); + return Ok(port); + } + } + Err(Error::new( + eyre!("{}", t!("net.forward.no-dynamic-ports-available")), + ErrorKind::Network, + )) + } + /// Try to allocate a specific port. Returns Some(port) if available, None if taken/restricted. + pub fn try_alloc(&mut self, port: u16, ssl: bool) -> Option { + if is_restricted(port) || self.0.contains_key(&port) { + return None; + } + self.0.insert(port, ssl); + Some(port) + } + /// Returns whether a given allocated port is SSL. + pub fn is_ssl(&self, port: u16) -> bool { + self.0.get(&port).copied().unwrap_or(false) } pub fn free(&mut self, ports: impl IntoIterator) { for port in ports { - self.0.return_id(port).unwrap_or_default(); + self.0.remove(&port); } } } @@ -61,10 +111,10 @@ pub fn forward_api() -> ParentHandler { } let mut table = Table::new(); - table.add_row(row![bc => "FROM", "TO", "FILTER"]); + table.add_row(row![bc => "FROM", "TO", "REQS"]); for (external, target) in res.0 { - table.add_row(row![external, target.target, target.filter]); + table.add_row(row![external, target.target, target.reqs]); } table.print_tty(false)?; @@ -79,6 +129,7 @@ struct ForwardMapping { source: SocketAddrV4, target: SocketAddrV4, target_prefix: u8, + src_filter: Option, rc: Weak<()>, } @@ -93,9 +144,10 @@ impl PortForwardState { source: SocketAddrV4, target: SocketAddrV4, target_prefix: u8, + src_filter: Option, ) -> Result, Error> { if let Some(existing) = self.mappings.get_mut(&source) { - if existing.target == target { + if existing.target == target && existing.src_filter == src_filter { if let Some(existing_rc) = existing.rc.upgrade() { return Ok(existing_rc); } else { @@ -104,21 +156,28 @@ impl PortForwardState { return Ok(rc); } } else { - // Different target, need to remove old and add new + // Different target or src_filter, need to remove old and add new if let Some(mapping) = self.mappings.remove(&source) { - unforward(mapping.source, mapping.target, mapping.target_prefix).await?; + unforward( + mapping.source, + mapping.target, + mapping.target_prefix, + mapping.src_filter.as_ref(), + ) + .await?; } } } let rc = Arc::new(()); - forward(source, target, target_prefix).await?; + forward(source, target, target_prefix, src_filter.as_ref()).await?; self.mappings.insert( source, ForwardMapping { source, target, target_prefix, + src_filter, rc: Arc::downgrade(&rc), }, ); @@ -136,7 +195,13 @@ impl PortForwardState { for source in to_remove { if let Some(mapping) = self.mappings.remove(&source) { - unforward(mapping.source, mapping.target, mapping.target_prefix).await?; + unforward( + mapping.source, + mapping.target, + mapping.target_prefix, + mapping.src_filter.as_ref(), + ) + .await?; } } Ok(()) @@ -157,9 +222,14 @@ impl Drop for PortForwardState { let mappings = std::mem::take(&mut self.mappings); tokio::spawn(async move { for (_, mapping) in mappings { - unforward(mapping.source, mapping.target, mapping.target_prefix) - .await - .log_err(); + unforward( + mapping.source, + mapping.target, + mapping.target_prefix, + mapping.src_filter.as_ref(), + ) + .await + .log_err(); } }); } @@ -171,6 +241,7 @@ enum PortForwardCommand { source: SocketAddrV4, target: SocketAddrV4, target_prefix: u8, + src_filter: Option, respond: oneshot::Sender, Error>>, }, Gc { @@ -257,9 +328,12 @@ impl PortForwardController { source, target, target_prefix, + src_filter, respond, } => { - let result = state.add_forward(source, target, target_prefix).await; + let result = state + .add_forward(source, target, target_prefix, src_filter) + .await; respond.send(result).ok(); } PortForwardCommand::Gc { respond } => { @@ -284,6 +358,7 @@ impl PortForwardController { source: SocketAddrV4, target: SocketAddrV4, target_prefix: u8, + src_filter: Option, ) -> Result, Error> { let (send, recv) = oneshot::channel(); self.req @@ -291,6 +366,7 @@ impl PortForwardController { source, target, target_prefix, + src_filter, respond: send, }) .map_err(err_has_exited)?; @@ -321,14 +397,14 @@ struct InterfaceForwardRequest { external: u16, target: SocketAddrV4, target_prefix: u8, - filter: DynInterfaceFilter, + reqs: ForwardRequirements, rc: Arc<()>, } #[derive(Clone)] struct InterfaceForwardEntry { external: u16, - filter: BTreeMap)>, + targets: BTreeMap)>, // Maps source SocketAddr -> strong reference for the forward created in PortForwardController forwards: BTreeMap>, } @@ -346,7 +422,7 @@ impl InterfaceForwardEntry { fn new(external: u16) -> Self { Self { external, - filter: BTreeMap::new(), + targets: BTreeMap::new(), forwards: BTreeMap::new(), } } @@ -358,28 +434,50 @@ impl InterfaceForwardEntry { ) -> Result<(), Error> { let mut keep = BTreeSet::::new(); - for (iface, info) in ip_info.iter() { - if let Some((target, target_prefix)) = self - .filter - .iter() - .filter(|(_, (_, _, rc))| rc.strong_count() > 0) - .find(|(filter, _)| filter.filter(iface, info)) - .map(|(_, (target, target_prefix, _))| (*target, *target_prefix)) - { - if let Some(ip_info) = &info.ip_info { - for addr in ip_info.subnets.iter().filter_map(|net| { - if let IpAddr::V4(ip) = net.addr() { - Some(SocketAddrV4::new(ip, self.external)) - } else { - None + for (gw_id, info) in ip_info.iter() { + if let Some(ip_info) = &info.ip_info { + for subnet in ip_info.subnets.iter() { + if let IpAddr::V4(ip) = subnet.addr() { + let addr = SocketAddrV4::new(ip, self.external); + if keep.contains(&addr) { + continue; } - }) { - keep.insert(addr); - if !self.forwards.contains_key(&addr) { - let rc = port_forward - .add_forward(addr, target, target_prefix) + + for (reqs, (target, target_prefix, rc)) in self.targets.iter() { + if rc.strong_count() == 0 { + continue; + } + if !reqs.secure && !info.secure() { + continue; + } + + let src_filter = + if reqs.public_gateways.contains(gw_id) { + None + } else if reqs.private_ips.contains(&IpAddr::V4(ip)) { + let excluded = ip_info + .lan_ip + .iter() + .filter_map(|ip| match ip { + IpAddr::V4(v4) => Some(v4.to_string()), + _ => None, + }) + .collect::>() + .join(","); + Some(SourceFilter { + subnet: subnet.trunc().to_string(), + excluded, + }) + } else { + continue; + }; + + keep.insert(addr); + let fwd_rc = port_forward + .add_forward(addr, *target, *target_prefix, src_filter) .await?; - self.forwards.insert(addr, rc); + self.forwards.insert(addr, fwd_rc); + break; } } } @@ -398,7 +496,7 @@ impl InterfaceForwardEntry { external, target, target_prefix, - filter, + reqs, mut rc, }: InterfaceForwardRequest, ip_info: &OrdMap, @@ -412,8 +510,8 @@ impl InterfaceForwardEntry { } let entry = self - .filter - .entry(filter) + .targets + .entry(reqs) .or_insert_with(|| (target, target_prefix, Arc::downgrade(&rc))); if entry.0 != target { entry.0 = target; @@ -436,7 +534,7 @@ impl InterfaceForwardEntry { ip_info: &OrdMap, port_forward: &PortForwardController, ) -> Result<(), Error> { - self.filter.retain(|_, (_, _, rc)| rc.strong_count() > 0); + self.targets.retain(|_, (_, _, rc)| rc.strong_count() > 0); self.update(ip_info, port_forward).await } @@ -495,7 +593,7 @@ pub struct ForwardTable(pub BTreeMap); pub struct ForwardTarget { pub target: SocketAddrV4, pub target_prefix: u8, - pub filter: String, + pub reqs: String, } impl From<&InterfaceForwardState> for ForwardTable { @@ -506,16 +604,16 @@ impl From<&InterfaceForwardState> for ForwardTable { .iter() .flat_map(|entry| { entry - .filter + .targets .iter() .filter(|(_, (_, _, rc))| rc.strong_count() > 0) - .map(|(filter, (target, target_prefix, _))| { + .map(|(reqs, (target, target_prefix, _))| { ( entry.external, ForwardTarget { target: *target, target_prefix: *target_prefix, - filter: format!("{:#?}", filter), + reqs: format!("{reqs}"), }, ) }) @@ -534,16 +632,6 @@ enum InterfaceForwardCommand { DumpTable(oneshot::Sender), } -#[test] -fn test() { - use crate::net::gateway::SecureFilter; - - assert_ne!( - false.into_dyn(), - SecureFilter { secure: false }.into_dyn().into_dyn() - ); -} - pub struct InterfacePortForwardController { req: mpsc::UnboundedSender, _thread: NonDetachingJoinHandle<()>, @@ -593,7 +681,7 @@ impl InterfacePortForwardController { pub async fn add( &self, external: u16, - filter: DynInterfaceFilter, + reqs: ForwardRequirements, target: SocketAddrV4, target_prefix: u8, ) -> Result, Error> { @@ -605,7 +693,7 @@ impl InterfacePortForwardController { external, target, target_prefix, - filter, + reqs, rc, }, send, @@ -637,15 +725,21 @@ async fn forward( source: SocketAddrV4, target: SocketAddrV4, target_prefix: u8, + src_filter: Option<&SourceFilter>, ) -> Result<(), Error> { - Command::new("/usr/lib/startos/scripts/forward-port") - .env("sip", source.ip().to_string()) + let mut cmd = Command::new("/usr/lib/startos/scripts/forward-port"); + cmd.env("sip", source.ip().to_string()) .env("dip", target.ip().to_string()) .env("dprefix", target_prefix.to_string()) .env("sport", source.port().to_string()) - .env("dport", target.port().to_string()) - .invoke(ErrorKind::Network) - .await?; + .env("dport", target.port().to_string()); + if let Some(filter) = src_filter { + cmd.env("src_subnet", &filter.subnet); + if !filter.excluded.is_empty() { + cmd.env("excluded_src", &filter.excluded); + } + } + cmd.invoke(ErrorKind::Network).await?; Ok(()) } @@ -653,15 +747,21 @@ async fn unforward( source: SocketAddrV4, target: SocketAddrV4, target_prefix: u8, + src_filter: Option<&SourceFilter>, ) -> Result<(), Error> { - Command::new("/usr/lib/startos/scripts/forward-port") - .env("UNDO", "1") + let mut cmd = Command::new("/usr/lib/startos/scripts/forward-port"); + cmd.env("UNDO", "1") .env("sip", source.ip().to_string()) .env("dip", target.ip().to_string()) .env("dprefix", target_prefix.to_string()) .env("sport", source.port().to_string()) - .env("dport", target.port().to_string()) - .invoke(ErrorKind::Network) - .await?; + .env("dport", target.port().to_string()); + if let Some(filter) = src_filter { + cmd.env("src_subnet", &filter.subnet); + if !filter.excluded.is_empty() { + cmd.env("excluded_src", &filter.excluded); + } + } + cmd.invoke(ErrorKind::Network).await?; Ok(()) } diff --git a/core/src/net/gateway.rs b/core/src/net/gateway.rs index 6f59273d2..d8d0a6c6e 100644 --- a/core/src/net/gateway.rs +++ b/core/src/net/gateway.rs @@ -1,14 +1,11 @@ -use std::any::Any; use std::collections::{BTreeMap, BTreeSet, HashMap}; -use std::fmt; use std::future::Future; -use std::net::{IpAddr, Ipv4Addr, SocketAddr, SocketAddrV6}; -use std::sync::{Arc, Weak}; -use std::task::{Poll, ready}; +use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}; +use std::sync::Arc; +use std::task::Poll; use std::time::Duration; use clap::Parser; -use futures::future::Either; use futures::{FutureExt, Stream, StreamExt, TryStreamExt}; use imbl::{OrdMap, OrdSet}; use imbl_value::InternedString; @@ -36,15 +33,14 @@ use crate::db::model::Database; use crate::db::model::public::{IpInfo, NetworkInterfaceInfo, NetworkInterfaceType}; use crate::net::forward::START9_BRIDGE_IFACE; use crate::net::gateway::device::DeviceProxy; -use crate::net::utils::ipv6_is_link_local; -use crate::net::web_server::{Accept, AcceptStream, Acceptor, MetadataVisitor}; +use crate::net::web_server::{Accept, AcceptStream, MetadataVisitor, TcpMetadata}; use crate::prelude::*; use crate::util::Invoke; use crate::util::collections::OrdMapIterMut; use crate::util::future::{NonDetachingJoinHandle, Until}; use crate::util::io::open_file; use crate::util::serde::{HandlerExtSerde, display_serializable}; -use crate::util::sync::{SyncMutex, Watch}; +use crate::util::sync::Watch; pub fn gateway_api() -> ParentHandler { ParentHandler::new() @@ -840,7 +836,6 @@ pub struct NetworkInterfaceWatcher { activated: Watch>, ip_info: Watch>, _watcher: NonDetachingJoinHandle<()>, - listeners: SyncMutex>>, } impl NetworkInterfaceWatcher { pub fn new( @@ -860,7 +855,6 @@ impl NetworkInterfaceWatcher { watcher(ip_info, activated).await }) .into(), - listeners: SyncMutex::new(BTreeMap::new()), } } @@ -887,51 +881,6 @@ impl NetworkInterfaceWatcher { pub fn ip_info(&self) -> OrdMap { self.ip_info.read() } - - pub fn bind(&self, bind: B, port: u16) -> Result, Error> { - let arc = Arc::new(()); - self.listeners.mutate(|l| { - if l.get(&port).filter(|w| w.strong_count() > 0).is_some() { - return Err(Error::new( - std::io::Error::from_raw_os_error(libc::EADDRINUSE), - ErrorKind::Network, - )); - } - l.insert(port, Arc::downgrade(&arc)); - Ok(()) - })?; - let ip_info = self.ip_info.clone_unseen(); - Ok(NetworkInterfaceListener { - _arc: arc, - ip_info, - listeners: ListenerMap::new(bind, port), - }) - } - - pub fn upgrade_listener( - &self, - SelfContainedNetworkInterfaceListener { - mut listener, - .. - }: SelfContainedNetworkInterfaceListener, - ) -> Result, Error> { - let port = listener.listeners.port; - let arc = &listener._arc; - self.listeners.mutate(|l| { - if l.get(&port).filter(|w| w.strong_count() > 0).is_some() { - return Err(Error::new( - std::io::Error::from_raw_os_error(libc::EADDRINUSE), - ErrorKind::Network, - )); - } - l.insert(port, Arc::downgrade(arc)); - Ok(()) - })?; - let ip_info = self.ip_info.clone_unseen(); - ip_info.mark_changed(); - listener.change_ip_info_source(ip_info); - Ok(listener) - } } pub struct NetworkInterfaceController { @@ -1239,235 +1188,6 @@ impl NetworkInterfaceController { } } -pub trait InterfaceFilter: Any + Clone + std::fmt::Debug + Eq + Ord + Send + Sync { - fn filter(&self, id: &GatewayId, info: &NetworkInterfaceInfo) -> bool; - fn eq(&self, other: &dyn Any) -> bool { - Some(self) == other.downcast_ref::() - } - fn cmp(&self, other: &dyn Any) -> std::cmp::Ordering { - match (self as &dyn Any).type_id().cmp(&other.type_id()) { - std::cmp::Ordering::Equal => { - std::cmp::Ord::cmp(self, other.downcast_ref::().unwrap()) - } - ord => ord, - } - } - fn as_any(&self) -> &dyn Any { - self - } - fn into_dyn(self) -> DynInterfaceFilter { - DynInterfaceFilter::new(self) - } -} - -impl InterfaceFilter for bool { - fn filter(&self, _: &GatewayId, _: &NetworkInterfaceInfo) -> bool { - *self - } -} - -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] -pub struct TypeFilter(pub NetworkInterfaceType); -impl InterfaceFilter for TypeFilter { - fn filter(&self, _: &GatewayId, info: &NetworkInterfaceInfo) -> bool { - info.ip_info.as_ref().and_then(|i| i.device_type) == Some(self.0) - } -} - -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] -pub struct IdFilter(pub GatewayId); -impl InterfaceFilter for IdFilter { - fn filter(&self, id: &GatewayId, _: &NetworkInterfaceInfo) -> bool { - id == &self.0 - } -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] -pub struct PublicFilter { - pub public: bool, -} -impl InterfaceFilter for PublicFilter { - fn filter(&self, _: &GatewayId, info: &NetworkInterfaceInfo) -> bool { - self.public == info.public() - } -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] -pub struct SecureFilter { - pub secure: bool, -} -impl InterfaceFilter for SecureFilter { - fn filter(&self, _: &GatewayId, info: &NetworkInterfaceInfo) -> bool { - self.secure || info.secure() - } -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] -pub struct AndFilter(pub A, pub B); -impl InterfaceFilter for AndFilter { - fn filter(&self, id: &GatewayId, info: &NetworkInterfaceInfo) -> bool { - self.0.filter(id, info) && self.1.filter(id, info) - } -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] -pub struct OrFilter(pub A, pub B); -impl InterfaceFilter for OrFilter { - fn filter(&self, id: &GatewayId, info: &NetworkInterfaceInfo) -> bool { - self.0.filter(id, info) || self.1.filter(id, info) - } -} - -#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] -pub struct AnyFilter(pub BTreeSet); -impl InterfaceFilter for AnyFilter { - fn filter(&self, id: &GatewayId, info: &NetworkInterfaceInfo) -> bool { - self.0.iter().any(|f| InterfaceFilter::filter(f, id, info)) - } -} - -#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] -pub struct AllFilter(pub BTreeSet); -impl InterfaceFilter for AllFilter { - fn filter(&self, id: &GatewayId, info: &NetworkInterfaceInfo) -> bool { - self.0.iter().all(|f| InterfaceFilter::filter(f, id, info)) - } -} - -pub trait DynInterfaceFilterT: std::fmt::Debug + Any + Send + Sync { - fn filter(&self, id: &GatewayId, info: &NetworkInterfaceInfo) -> bool; - fn eq(&self, other: &dyn Any) -> bool; - fn cmp(&self, other: &dyn Any) -> std::cmp::Ordering; - fn as_any(&self) -> &dyn Any; -} -impl DynInterfaceFilterT for T { - fn filter(&self, id: &GatewayId, info: &NetworkInterfaceInfo) -> bool { - InterfaceFilter::filter(self, id, info) - } - fn eq(&self, other: &dyn Any) -> bool { - InterfaceFilter::eq(self, other) - } - fn cmp(&self, other: &dyn Any) -> std::cmp::Ordering { - InterfaceFilter::cmp(self, other) - } - fn as_any(&self) -> &dyn Any { - InterfaceFilter::as_any(self) - } -} - -#[test] -fn test_interface_filter_eq() { - let dyn_t = true.into_dyn(); - assert!(DynInterfaceFilterT::eq( - &dyn_t, - DynInterfaceFilterT::as_any(&true), - )) -} - -#[derive(Clone, Debug)] -pub struct DynInterfaceFilter(Arc); -impl InterfaceFilter for DynInterfaceFilter { - fn filter(&self, id: &GatewayId, info: &NetworkInterfaceInfo) -> bool { - self.0.filter(id, info) - } - fn eq(&self, other: &dyn Any) -> bool { - self.0.eq(other) - } - fn cmp(&self, other: &dyn Any) -> std::cmp::Ordering { - self.0.cmp(other) - } - fn as_any(&self) -> &dyn Any { - self.0.as_any() - } - fn into_dyn(self) -> DynInterfaceFilter { - self - } -} -impl DynInterfaceFilter { - fn new(value: T) -> Self { - Self(Arc::new(value)) - } -} -impl PartialEq for DynInterfaceFilter { - fn eq(&self, other: &Self) -> bool { - DynInterfaceFilterT::eq(&*self.0, DynInterfaceFilterT::as_any(&*other.0)) - } -} -impl Eq for DynInterfaceFilter {} -impl PartialOrd for DynInterfaceFilter { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.0.cmp(other.0.as_any())) - } -} -impl Ord for DynInterfaceFilter { - fn cmp(&self, other: &Self) -> std::cmp::Ordering { - self.0.cmp(other.0.as_any()) - } -} - -struct ListenerMap { - prev_filter: DynInterfaceFilter, - bind: B, - port: u16, - listeners: BTreeMap, -} -impl ListenerMap { - fn new(bind: B, port: u16) -> Self { - Self { - prev_filter: false.into_dyn(), - bind, - port, - listeners: BTreeMap::new(), - } - } - - #[instrument(skip(self))] - fn update( - &mut self, - ip_info: &OrdMap, - filter: &impl InterfaceFilter, - ) -> Result<(), Error> { - let mut keep = BTreeSet::::new(); - for (_, info) in ip_info - .iter() - .filter(|(id, info)| filter.filter(*id, *info)) - { - if let Some(ip_info) = &info.ip_info { - for ipnet in &ip_info.subnets { - let addr = match ipnet.addr() { - IpAddr::V6(ip6) => SocketAddrV6::new( - ip6, - self.port, - 0, - if ipv6_is_link_local(ip6) { - ip_info.scope_id - } else { - 0 - }, - ) - .into(), - ip => SocketAddr::new(ip, self.port), - }; - keep.insert(addr); - if !self.listeners.contains_key(&addr) { - self.listeners.insert(addr, self.bind.bind(addr)?); - } - } - } - } - self.listeners.retain(|key, _| keep.contains(key)); - self.prev_filter = filter.clone().into_dyn(); - Ok(()) - } - fn poll_accept( - &mut self, - cx: &mut std::task::Context<'_>, - ) -> Poll::Metadata, AcceptStream), Error>> { - let (metadata, stream) = ready!(self.listeners.poll_accept(cx)?); - Poll::Ready(Ok((metadata.key, metadata.inner, stream))) - } -} - pub fn lookup_info_by_addr( ip_info: &OrdMap, addr: SocketAddr, @@ -1479,28 +1199,6 @@ pub fn lookup_info_by_addr( }) } -pub trait Bind { - type Accept: Accept; - fn bind(&mut self, addr: SocketAddr) -> Result; -} - -#[derive(Clone, Copy, Default)] -pub struct BindTcp; -impl Bind for BindTcp { - type Accept = TcpListener; - fn bind(&mut self, addr: SocketAddr) -> Result { - TcpListener::from_std( - mio::net::TcpListener::bind(addr) - .with_kind(ErrorKind::Network)? - .into(), - ) - .with_kind(ErrorKind::Network) - } -} - -pub trait FromGatewayInfo { - fn from_gateway_info(id: &GatewayId, info: &NetworkInterfaceInfo) -> Self; -} #[derive(Clone, Debug)] pub struct GatewayInfo { pub id: GatewayId, @@ -1511,213 +1209,88 @@ impl Visit for GatewayInfo { visitor.visit(self) } } -impl FromGatewayInfo for GatewayInfo { - fn from_gateway_info(id: &GatewayId, info: &NetworkInterfaceInfo) -> Self { - Self { - id: id.clone(), - info: info.clone(), - } - } -} -pub struct NetworkInterfaceListener { - pub ip_info: Watch>, - listeners: ListenerMap, - _arc: Arc<()>, -} -impl NetworkInterfaceListener { - pub(super) fn new( - mut ip_info: Watch>, - bind: B, - port: u16, - ) -> Self { - ip_info.mark_unseen(); - Self { - ip_info, - listeners: ListenerMap::new(bind, port), - _arc: Arc::new(()), - } - } - - pub fn port(&self) -> u16 { - self.listeners.port - } - - #[cfg_attr(feature = "unstable", inline(never))] - pub fn poll_accept( - &mut self, - cx: &mut std::task::Context<'_>, - filter: &impl InterfaceFilter, - ) -> Poll::Metadata, AcceptStream), Error>> { - while self.ip_info.poll_changed(cx).is_ready() - || !DynInterfaceFilterT::eq(&self.listeners.prev_filter, filter.as_any()) - { - self.ip_info - .peek_and_mark_seen(|ip_info| self.listeners.update(ip_info, filter))?; - } - let (addr, inner, stream) = ready!(self.listeners.poll_accept(cx)?); - Poll::Ready(Ok(( - self.ip_info - .peek(|ip_info| { - lookup_info_by_addr(ip_info, addr) - .map(|(id, info)| M::from_gateway_info(id, info)) - }) - .or_not_found(lazy_format!("gateway for {addr}"))?, - inner, - stream, - ))) - } - - pub fn change_ip_info_source( - &mut self, - mut ip_info: Watch>, - ) { - ip_info.mark_unseen(); - self.ip_info = ip_info; - } - - pub async fn accept( - &mut self, - filter: &impl InterfaceFilter, - ) -> Result<(M, ::Metadata, AcceptStream), Error> { - futures::future::poll_fn(|cx| self.poll_accept(cx, filter)).await - } - - pub fn check_filter(&self) -> impl FnOnce(SocketAddr, &DynInterfaceFilter) -> bool + 'static { - let ip_info = self.ip_info.clone(); - move |addr, filter| { - ip_info.peek(|i| { - lookup_info_by_addr(i, addr).map_or(false, |(id, info)| { - InterfaceFilter::filter(filter, id, info) - }) - }) - } - } -} - -#[derive(VisitFields)] -pub struct NetworkInterfaceListenerAcceptMetadata { - pub inner: ::Metadata, +/// Metadata for connections accepted by WildcardListener or VHostBindListener. +#[derive(Clone, Debug, VisitFields)] +pub struct NetworkInterfaceListenerAcceptMetadata { + pub inner: TcpMetadata, pub info: GatewayInfo, } -impl fmt::Debug for NetworkInterfaceListenerAcceptMetadata { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("NetworkInterfaceListenerAcceptMetadata") - .field("inner", &self.inner) - .field("info", &self.info) - .finish() - } -} -impl Clone for NetworkInterfaceListenerAcceptMetadata -where - ::Metadata: Clone, -{ - fn clone(&self) -> Self { - Self { - inner: self.inner.clone(), - info: self.info.clone(), - } - } -} -impl Visit for NetworkInterfaceListenerAcceptMetadata -where - B: Bind, - ::Metadata: Visit + Clone + Send + Sync + 'static, - V: MetadataVisitor, -{ +impl Visit for NetworkInterfaceListenerAcceptMetadata { fn visit(&self, visitor: &mut V) -> V::Result { self.visit_fields(visitor).collect() } } -impl Accept for NetworkInterfaceListener { - type Metadata = NetworkInterfaceListenerAcceptMetadata; +/// A simple TCP listener on 0.0.0.0:port that looks up GatewayInfo from the +/// connection's local address on each accepted connection. +pub struct WildcardListener { + listener: TcpListener, + ip_info: Watch>, + /// Handle to the self-contained watcher task started in `new()`. + /// Dropped (and thus aborted) when `set_ip_info` replaces the ip_info source. + _watcher: Option>, +} +impl WildcardListener { + pub fn new(port: u16) -> Result { + let listener = TcpListener::from_std( + mio::net::TcpListener::bind(SocketAddr::new(IpAddr::V6(Ipv6Addr::UNSPECIFIED), port)) + .with_kind(ErrorKind::Network)? + .into(), + ) + .with_kind(ErrorKind::Network)?; + let ip_info = Watch::new(OrdMap::new()); + let watcher_handle = + tokio::spawn(watcher(ip_info.clone(), Watch::new(BTreeMap::new()))).into(); + Ok(Self { + listener, + ip_info, + _watcher: Some(watcher_handle), + }) + } + + /// Replace the ip_info source with the one from the NetworkInterfaceController. + /// Aborts the self-contained watcher task. + pub fn set_ip_info(&mut self, ip_info: Watch>) { + self.ip_info = ip_info; + self._watcher = None; + } +} +impl Accept for WildcardListener { + type Metadata = NetworkInterfaceListenerAcceptMetadata; fn poll_accept( &mut self, cx: &mut std::task::Context<'_>, ) -> Poll> { - NetworkInterfaceListener::poll_accept(self, cx, &true).map(|res| { - res.map(|(info, inner, stream)| { - ( - NetworkInterfaceListenerAcceptMetadata { inner, info }, - stream, - ) - }) - }) - } -} - -pub struct SelfContainedNetworkInterfaceListener { - _watch_thread: NonDetachingJoinHandle<()>, - listener: NetworkInterfaceListener, -} -impl SelfContainedNetworkInterfaceListener { - pub fn bind(bind: B, port: u16) -> Self { - let ip_info = Watch::new(OrdMap::new()); - let _watch_thread = - tokio::spawn(watcher(ip_info.clone(), Watch::new(BTreeMap::new()))).into(); - Self { - _watch_thread, - listener: NetworkInterfaceListener::new(ip_info, bind, port), + if let Poll::Ready((stream, peer_addr)) = TcpListener::poll_accept(&self.listener, cx)? { + if let Err(e) = socket2::SockRef::from(&stream).set_keepalive(true) { + tracing::error!("Failed to set tcp keepalive: {e}"); + tracing::debug!("{e:?}"); + } + let local_addr = stream.local_addr()?; + let info = self + .ip_info + .peek(|ip_info| { + lookup_info_by_addr(ip_info, local_addr).map(|(id, info)| GatewayInfo { + id: id.clone(), + info: info.clone(), + }) + }) + .unwrap_or_else(|| GatewayInfo { + id: InternedString::from_static("").into(), + info: NetworkInterfaceInfo::default(), + }); + return Poll::Ready(Ok(( + NetworkInterfaceListenerAcceptMetadata { + inner: TcpMetadata { + local_addr, + peer_addr, + }, + info, + }, + Box::pin(stream), + ))); } + Poll::Pending } } -impl Accept for SelfContainedNetworkInterfaceListener { - type Metadata = as Accept>::Metadata; - fn poll_accept( - &mut self, - cx: &mut std::task::Context<'_>, - ) -> std::task::Poll> { - Accept::poll_accept(&mut self.listener, cx) - } -} - -pub type UpgradableListener = - Option, NetworkInterfaceListener>>; - -impl Acceptor> -where - B: Bind + Send + Sync + 'static, - B::Accept: Send + Sync, -{ - pub fn bind_upgradable(listener: SelfContainedNetworkInterfaceListener) -> Self { - Self::new(Some(Either::Left(listener))) - } -} - -#[test] -fn test_filter() { - use crate::net::host::binding::NetInfo; - let wg1 = "wg1".parse::().unwrap(); - assert!(!InterfaceFilter::filter( - &AndFilter( - NetInfo { - private_disabled: [wg1.clone()].into_iter().collect(), - public_enabled: Default::default(), - assigned_port: None, - assigned_ssl_port: None, - }, - AndFilter(IdFilter(wg1.clone()), PublicFilter { public: false }), - ) - .into_dyn(), - &wg1, - &NetworkInterfaceInfo { - name: None, - public: None, - secure: None, - ip_info: Some(Arc::new(IpInfo { - name: "".into(), - scope_id: 3, - device_type: Some(NetworkInterfaceType::Wireguard), - subnets: ["10.59.0.2/24".parse::().unwrap()] - .into_iter() - .collect(), - lan_ip: Default::default(), - wan_ip: None, - ntp_servers: Default::default(), - dns_servers: Default::default(), - })), - gateway_type: None, - }, - )); -} diff --git a/core/src/net/host/address.rs b/core/src/net/host/address.rs index 9c60ababe..33c32073b 100644 --- a/core/src/net/host/address.rs +++ b/core/src/net/host/address.rs @@ -12,23 +12,15 @@ use crate::context::{CliContext, RpcContext}; use crate::db::model::DatabaseModel; use crate::net::acme::AcmeProvider; use crate::net::host::{HostApiKind, all_hosts}; -use crate::net::tor::OnionAddress; use crate::prelude::*; use crate::util::serde::{HandlerExtSerde, display_serializable}; #[derive(Clone, Debug, Deserialize, Serialize)] -#[serde(rename_all = "kebab-case")] -#[serde(rename_all_fields = "camelCase")] -#[serde(tag = "kind")] -pub enum HostAddress { - Onion { - address: OnionAddress, - }, - Domain { - address: InternedString, - public: Option, - private: bool, - }, +#[serde(rename_all = "camelCase")] +pub struct HostAddress { + pub address: InternedString, + pub public: Option, + pub private: bool, } #[derive(Debug, Clone, Deserialize, Serialize, TS)] @@ -38,18 +30,7 @@ pub struct PublicDomainConfig { } fn handle_duplicates(db: &mut DatabaseModel) -> Result<(), Error> { - let mut onions = BTreeSet::::new(); let mut domains = BTreeSet::::new(); - let check_onion = |onions: &mut BTreeSet, onion: OnionAddress| { - if onions.contains(&onion) { - return Err(Error::new( - eyre!("onion address {onion} is already in use"), - ErrorKind::InvalidRequest, - )); - } - onions.insert(onion); - Ok(()) - }; let check_domain = |domains: &mut BTreeSet, domain: InternedString| { if domains.contains(&domain) { return Err(Error::new( @@ -68,9 +49,6 @@ fn handle_duplicates(db: &mut DatabaseModel) -> Result<(), Error> { not_in_use.push(host); continue; } - for onion in host.as_onions().de()? { - check_onion(&mut onions, onion)?; - } let public = host.as_public_domains().keys()?; for domain in &public { check_domain(&mut domains, domain.clone())?; @@ -82,16 +60,11 @@ fn handle_duplicates(db: &mut DatabaseModel) -> Result<(), Error> { } } for host in not_in_use { - host.as_onions_mut() - .mutate(|o| Ok(o.retain(|o| !onions.contains(o))))?; host.as_public_domains_mut() .mutate(|d| Ok(d.retain(|d, _| !domains.contains(d))))?; host.as_private_domains_mut() .mutate(|d| Ok(d.retain(|d| !domains.contains(d))))?; - for onion in host.as_onions().de()? { - check_onion(&mut onions, onion)?; - } let public = host.as_public_domains().keys()?; for domain in &public { check_domain(&mut domains, domain.clone())?; @@ -159,29 +132,6 @@ pub fn address_api() ) .with_inherited(Kind::inheritance), ) - .subcommand( - "onion", - ParentHandler::::new() - .subcommand( - "add", - from_fn_async(add_onion::) - .with_metadata("sync_db", Value::Bool(true)) - .with_inherited(|_, a| a) - .no_display() - .with_about("about.add-address-to-host") - .with_call_remote::(), - ) - .subcommand( - "remove", - from_fn_async(remove_onion::) - .with_metadata("sync_db", Value::Bool(true)) - .with_inherited(|_, a| a) - .no_display() - .with_about("about.remove-address-from-host") - .with_call_remote::(), - ) - .with_inherited(Kind::inheritance), - ) .subcommand( "list", from_fn_async(list_addresses::) @@ -197,32 +147,18 @@ pub fn address_api() let mut table = Table::new(); table.add_row(row![bc => "ADDRESS", "PUBLIC", "ACME PROVIDER"]); - for address in &res { - match address { - HostAddress::Onion { address } => { - table.add_row(row![address, true, "N/A"]); - } - HostAddress::Domain { - address, - public: Some(PublicDomainConfig { gateway, acme }), - private, - } => { - table.add_row(row![ - address, - &format!( - "{} ({gateway})", - if *private { "YES" } else { "ONLY" } - ), - acme.as_ref().map(|a| a.0.as_str()).unwrap_or("NONE") - ]); - } - HostAddress::Domain { - address, - public: None, - .. - } => { - table.add_row(row![address, &format!("NO"), "N/A"]); - } + for entry in &res { + if let Some(PublicDomainConfig { gateway, acme }) = &entry.public { + table.add_row(row![ + entry.address, + &format!( + "{} ({gateway})", + if entry.private { "YES" } else { "ONLY" } + ), + acme.as_ref().map(|a| a.0.as_str()).unwrap_or("NONE") + ]); + } else { + table.add_row(row![entry.address, &format!("NO"), "N/A"]); } } @@ -351,55 +287,6 @@ pub async fn remove_private_domain( Ok(()) } -#[derive(Deserialize, Serialize, Parser)] -pub struct OnionParams { - #[arg(help = "help.arg.onion-address")] - pub onion: String, -} - -pub async fn add_onion( - ctx: RpcContext, - OnionParams { onion }: OnionParams, - inheritance: Kind::Inheritance, -) -> Result<(), Error> { - let onion = onion.parse::()?; - ctx.db - .mutate(|db| { - db.as_private().as_key_store().as_onion().get_key(&onion)?; - - Kind::host_for(&inheritance, db)? - .as_onions_mut() - .mutate(|a| Ok(a.insert(onion)))?; - handle_duplicates(db) - }) - .await - .result?; - - Kind::sync_host(&ctx, inheritance).await?; - - Ok(()) -} - -pub async fn remove_onion( - ctx: RpcContext, - OnionParams { onion }: OnionParams, - inheritance: Kind::Inheritance, -) -> Result<(), Error> { - let onion = onion.parse::()?; - ctx.db - .mutate(|db| { - Kind::host_for(&inheritance, db)? - .as_onions_mut() - .mutate(|a| Ok(a.remove(&onion))) - }) - .await - .result?; - - Kind::sync_host(&ctx, inheritance).await?; - - Ok(()) -} - pub async fn list_addresses( ctx: RpcContext, _: Empty, diff --git a/core/src/net/host/binding.rs b/core/src/net/host/binding.rs index 8862e2bda..8db806399 100644 --- a/core/src/net/host/binding.rs +++ b/core/src/net/host/binding.rs @@ -3,21 +3,20 @@ use std::str::FromStr; use clap::Parser; use clap::builder::ValueParserFactory; -use imbl::OrdSet; use rpc_toolkit::{Context, Empty, HandlerArgs, HandlerExt, ParentHandler, from_fn_async}; use serde::{Deserialize, Serialize}; use ts_rs::TS; use crate::context::{CliContext, RpcContext}; -use crate::db::model::public::NetworkInterfaceInfo; +use crate::db::prelude::Map; use crate::net::forward::AvailablePorts; -use crate::net::gateway::InterfaceFilter; use crate::net::host::HostApiKind; +use crate::net::service_interface::HostnameInfo; use crate::net::vhost::AlpnInfo; use crate::prelude::*; use crate::util::FromStrParser; use crate::util::serde::{HandlerExtSerde, display_serializable}; -use crate::{GatewayId, HostId}; +use crate::HostId; #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, TS)] #[ts(export)] @@ -45,25 +44,82 @@ impl FromStr for BindId { } } -#[derive(Debug, Deserialize, Serialize, TS)] +#[derive(Debug, Default, Clone, Deserialize, Serialize, TS, HasModel)] #[serde(rename_all = "camelCase")] #[ts(export)] +#[model = "Model"] +pub struct DerivedAddressInfo { + /// User-controlled: private addresses the user has disabled + pub private_disabled: BTreeSet, + /// User-controlled: public addresses the user has enabled + pub public_enabled: BTreeSet, + /// COMPUTED: NetServiceData::update — all possible addresses for this binding + pub possible: BTreeSet, +} + +impl DerivedAddressInfo { + /// Returns addresses that are currently enabled. + /// Private addresses are enabled by default (disabled if in private_disabled). + /// Public addresses are disabled by default (enabled if in public_enabled). + pub fn enabled(&self) -> BTreeSet<&HostnameInfo> { + self.possible + .iter() + .filter(|h| { + if h.public { + self.public_enabled.contains(h) + } else { + !self.private_disabled.contains(h) + } + }) + .collect() + } + +} + +#[derive(Debug, Default, Deserialize, Serialize, HasModel, TS)] +#[model = "Model"] +#[ts(export)] +pub struct Bindings(pub BTreeMap); + +impl Map for Bindings { + type Key = u16; + type Value = BindInfo; + fn key_str(key: &Self::Key) -> Result, Error> { + Self::key_string(key) + } + fn key_string(key: &Self::Key) -> Result { + Ok(InternedString::from_display(key)) + } +} + +impl std::ops::Deref for Bindings { + type Target = BTreeMap; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl std::ops::DerefMut for Bindings { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +#[derive(Debug, Deserialize, Serialize, HasModel, TS)] +#[serde(rename_all = "camelCase")] +#[model = "Model"] +#[ts(export)] pub struct BindInfo { pub enabled: bool, pub options: BindOptions, pub net: NetInfo, + pub addresses: DerivedAddressInfo, } #[derive(Clone, Debug, Deserialize, Serialize, TS, PartialEq, Eq, PartialOrd, Ord)] #[serde(rename_all = "camelCase")] #[ts(export)] pub struct NetInfo { - #[ts(as = "BTreeSet::")] - #[serde(default)] - pub private_disabled: OrdSet, - #[ts(as = "BTreeSet::")] - #[serde(default)] - pub public_enabled: OrdSet, pub assigned_port: Option, pub assigned_ssl_port: Option, } @@ -71,25 +127,28 @@ impl BindInfo { pub fn new(available_ports: &mut AvailablePorts, options: BindOptions) -> Result { let mut assigned_port = None; let mut assigned_ssl_port = None; - if options.add_ssl.is_some() { - assigned_ssl_port = Some(available_ports.alloc()?); + if let Some(ssl) = &options.add_ssl { + assigned_ssl_port = available_ports + .try_alloc(ssl.preferred_external_port, true) + .or_else(|| Some(available_ports.alloc(true).ok()?)); } if options .secure .map_or(true, |s| !(s.ssl && options.add_ssl.is_some())) { - assigned_port = Some(available_ports.alloc()?); + assigned_port = available_ports + .try_alloc(options.preferred_external_port, false) + .or_else(|| Some(available_ports.alloc(false).ok()?)); } Ok(Self { enabled: true, options, net: NetInfo { - private_disabled: OrdSet::new(), - public_enabled: OrdSet::new(), assigned_port, assigned_ssl_port, }, + addresses: DerivedAddressInfo::default(), }) } pub fn update( @@ -97,7 +156,11 @@ impl BindInfo { available_ports: &mut AvailablePorts, options: BindOptions, ) -> Result { - let Self { net: mut lan, .. } = self; + let Self { + net: mut lan, + addresses, + .. + } = self; if options .secure .map_or(true, |s| !(s.ssl && options.add_ssl.is_some())) @@ -105,19 +168,26 @@ impl BindInfo { { lan.assigned_port = if let Some(port) = lan.assigned_port.take() { Some(port) + } else if let Some(port) = + available_ports.try_alloc(options.preferred_external_port, false) + { + Some(port) } else { - Some(available_ports.alloc()?) + Some(available_ports.alloc(false)?) }; } else { if let Some(port) = lan.assigned_port.take() { available_ports.free([port]); } } - if options.add_ssl.is_some() { + if let Some(ssl) = &options.add_ssl { lan.assigned_ssl_port = if let Some(port) = lan.assigned_ssl_port.take() { Some(port) + } else if let Some(port) = available_ports.try_alloc(ssl.preferred_external_port, true) + { + Some(port) } else { - Some(available_ports.alloc()?) + Some(available_ports.alloc(true)?) }; } else { if let Some(port) = lan.assigned_ssl_port.take() { @@ -128,22 +198,17 @@ impl BindInfo { enabled: true, options, net: lan, + addresses: DerivedAddressInfo { + private_disabled: addresses.private_disabled, + public_enabled: addresses.public_enabled, + possible: BTreeSet::new(), + }, }) } pub fn disable(&mut self) { self.enabled = false; } } -impl InterfaceFilter for NetInfo { - fn filter(&self, id: &GatewayId, info: &NetworkInterfaceInfo) -> bool { - info.ip_info.is_some() - && if info.public() { - self.public_enabled.contains(id) - } else { - !self.private_disabled.contains(id) - } - } -} #[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize, TS)] #[ts(export)] @@ -188,7 +253,7 @@ pub fn binding() let mut table = Table::new(); table.add_row(row![bc => "INTERNAL PORT", "ENABLED", "EXTERNAL PORT", "EXTERNAL SSL PORT"]); - for (internal, info) in res { + for (internal, info) in res.iter() { table.add_row(row![ internal, info.enabled, @@ -213,12 +278,12 @@ pub fn binding() .with_call_remote::(), ) .subcommand( - "set-gateway-enabled", - from_fn_async(set_gateway_enabled::) + "set-address-enabled", + from_fn_async(set_address_enabled::) .with_metadata("sync_db", Value::Bool(true)) .with_inherited(Kind::inheritance) .no_display() - .with_about("about.set-gateway-enabled-for-binding") + .with_about("about.set-address-enabled-for-binding") .with_call_remote::(), ) } @@ -227,7 +292,7 @@ pub async fn list_bindings( ctx: RpcContext, _: Empty, inheritance: Kind::Inheritance, -) -> Result, Error> { +) -> Result { Kind::host_for(&inheritance, &mut ctx.db.peek().await)? .as_bindings() .de() @@ -236,50 +301,44 @@ pub async fn list_bindings( #[derive(Deserialize, Serialize, Parser, TS)] #[serde(rename_all = "camelCase")] #[ts(export)] -pub struct BindingGatewaySetEnabledParams { +pub struct BindingSetAddressEnabledParams { #[arg(help = "help.arg.internal-port")] internal_port: u16, - #[arg(help = "help.arg.gateway-id")] - gateway: GatewayId, + #[arg(long, help = "help.arg.address")] + address: String, #[arg(long, help = "help.arg.binding-enabled")] enabled: Option, } -pub async fn set_gateway_enabled( +pub async fn set_address_enabled( ctx: RpcContext, - BindingGatewaySetEnabledParams { + BindingSetAddressEnabledParams { internal_port, - gateway, + address, enabled, - }: BindingGatewaySetEnabledParams, + }: BindingSetAddressEnabledParams, inheritance: Kind::Inheritance, ) -> Result<(), Error> { let enabled = enabled.unwrap_or(true); - let gateway_public = ctx - .net_controller - .net_iface - .watcher - .ip_info() - .get(&gateway) - .or_not_found(&gateway)? - .public(); + let address: HostnameInfo = + serde_json::from_str(&address).with_kind(ErrorKind::Deserialization)?; ctx.db .mutate(|db| { Kind::host_for(&inheritance, db)? .as_bindings_mut() .mutate(|b| { - let net = &mut b.get_mut(&internal_port).or_not_found(internal_port)?.net; - if gateway_public { + let bind = b.get_mut(&internal_port).or_not_found(internal_port)?; + if address.public { if enabled { - net.public_enabled.insert(gateway); + bind.addresses.public_enabled.insert(address.clone()); } else { - net.public_enabled.remove(&gateway); + bind.addresses.public_enabled.remove(&address); } } else { if enabled { - net.private_disabled.remove(&gateway); + bind.addresses.private_disabled.remove(&address); } else { - net.private_disabled.insert(gateway); + bind.addresses.private_disabled.insert(address.clone()); } } Ok(()) diff --git a/core/src/net/host/mod.rs b/core/src/net/host/mod.rs index 620991ca7..ef06313dd 100644 --- a/core/src/net/host/mod.rs +++ b/core/src/net/host/mod.rs @@ -13,9 +13,7 @@ use crate::context::RpcContext; use crate::db::model::DatabaseModel; use crate::net::forward::AvailablePorts; use crate::net::host::address::{HostAddress, PublicDomainConfig, address_api}; -use crate::net::host::binding::{BindInfo, BindOptions, binding}; -use crate::net::service_interface::HostnameInfo; -use crate::net::tor::OnionAddress; +use crate::net::host::binding::{BindInfo, BindOptions, Bindings, binding}; use crate::prelude::*; use crate::{HostId, PackageId}; @@ -27,13 +25,9 @@ pub mod binding; #[model = "Model"] #[ts(export)] pub struct Host { - pub bindings: BTreeMap, - #[ts(type = "string[]")] - pub onions: BTreeSet, + pub bindings: Bindings, pub public_domains: BTreeMap, pub private_domains: BTreeSet, - /// COMPUTED: NetService::update - pub hostname_info: BTreeMap>, // internal port -> Hostnames } impl AsRef for Host { @@ -46,24 +40,18 @@ impl Host { Self::default() } pub fn addresses<'a>(&'a self) -> impl Iterator + 'a { - self.onions + self.public_domains .iter() - .cloned() - .map(|address| HostAddress::Onion { address }) - .chain( - self.public_domains - .iter() - .map(|(address, config)| HostAddress::Domain { - address: address.clone(), - public: Some(config.clone()), - private: self.private_domains.contains(address), - }), - ) + .map(|(address, config)| HostAddress { + address: address.clone(), + public: Some(config.clone()), + private: self.private_domains.contains(address), + }) .chain( self.private_domains .iter() .filter(|a| !self.public_domains.contains_key(*a)) - .map(|address| HostAddress::Domain { + .map(|address| HostAddress { address: address.clone(), public: None, private: true, @@ -112,22 +100,7 @@ pub fn host_for<'a>( .as_hosts_mut(), ) } - let tor_key = if host_info(db, package_id)?.as_idx(host_id).is_none() { - Some( - db.as_private_mut() - .as_key_store_mut() - .as_onion_mut() - .new_key()?, - ) - } else { - None - }; - host_info(db, package_id)?.upsert(host_id, || { - let mut h = Host::new(); - h.onions - .insert(tor_key.or_not_found("generated tor key")?.onion_address()); - Ok(h) - }) + host_info(db, package_id)?.upsert(host_id, || Ok(Host::new())) } pub fn all_hosts(db: &mut DatabaseModel) -> impl Iterator, Error>> { diff --git a/core/src/net/keys.rs b/core/src/net/keys.rs index 41e96eedd..077f56875 100644 --- a/core/src/net/keys.rs +++ b/core/src/net/keys.rs @@ -3,28 +3,21 @@ use serde::{Deserialize, Serialize}; use crate::account::AccountInfo; use crate::net::acme::AcmeCertStore; use crate::net::ssl::CertStore; -use crate::net::tor::OnionStore; use crate::prelude::*; #[derive(Debug, Deserialize, Serialize, HasModel)] #[model = "Model"] #[serde(rename_all = "camelCase")] pub struct KeyStore { - pub onion: OnionStore, pub local_certs: CertStore, #[serde(default)] pub acme: AcmeCertStore, } impl KeyStore { pub fn new(account: &AccountInfo) -> Result { - let mut res = Self { - onion: OnionStore::new(), + Ok(Self { local_certs: CertStore::new(account)?, acme: AcmeCertStore::new(), - }; - for tor_key in account.tor_keys.iter().cloned() { - res.onion.insert(tor_key); - } - Ok(res) + }) } } diff --git a/core/src/net/mod.rs b/core/src/net/mod.rs index f30c1383b..f199b3194 100644 --- a/core/src/net/mod.rs +++ b/core/src/net/mod.rs @@ -14,7 +14,6 @@ pub mod socks; pub mod ssl; pub mod static_server; pub mod tls; -pub mod tor; pub mod tunnel; pub mod utils; pub mod vhost; @@ -23,7 +22,6 @@ pub mod wifi; pub fn net_api() -> ParentHandler { ParentHandler::new() - .subcommand("tor", tor::tor_api::().with_about("about.tor-commands")) .subcommand( "acme", acme::acme_api::().with_about("about.setup-acme-certificate"), diff --git a/core/src/net/net_controller.rs b/core/src/net/net_controller.rs index dc46fde33..37c63f333 100644 --- a/core/src/net/net_controller.rs +++ b/core/src/net/net_controller.rs @@ -3,7 +3,7 @@ use std::net::{IpAddr, Ipv4Addr, SocketAddr, SocketAddrV4}; use std::sync::{Arc, Weak}; use color_eyre::eyre::eyre; -use imbl::{OrdMap, vector}; +use imbl::vector; use imbl_value::InternedString; use ipnet::IpNet; use tokio::sync::Mutex; @@ -16,17 +16,15 @@ use crate::db::model::public::NetworkInterfaceType; use crate::error::ErrorCollection; use crate::hostname::Hostname; use crate::net::dns::DnsController; -use crate::net::forward::{InterfacePortForwardController, START9_BRIDGE_IFACE, add_iptables_rule}; -use crate::net::gateway::{ - AndFilter, DynInterfaceFilter, IdFilter, InterfaceFilter, NetworkInterfaceController, OrFilter, - PublicFilter, SecureFilter, TypeFilter, +use crate::net::forward::{ + ForwardRequirements, InterfacePortForwardController, START9_BRIDGE_IFACE, add_iptables_rule, }; +use crate::net::gateway::NetworkInterfaceController; use crate::net::host::address::HostAddress; use crate::net::host::binding::{AddSslOptions, BindId, BindOptions}; use crate::net::host::{Host, Hosts, host_for}; -use crate::net::service_interface::{GatewayInfo, HostnameInfo, IpHostname, OnionHostname}; +use crate::net::service_interface::{GatewayInfo, HostnameInfo, IpHostname}; use crate::net::socks::SocksController; -use crate::net::tor::{OnionAddress, TorController, TorSecretKey}; use crate::net::utils::ipv6_is_local; use crate::net::vhost::{AlpnInfo, DynVHostTarget, ProxyTarget, VHostController}; use crate::prelude::*; @@ -36,7 +34,6 @@ use crate::{GatewayId, HOST_IP, HostId, OptionExt, PackageId}; pub struct NetController { pub(crate) db: TypedPatchDb, - pub(super) tor: TorController, pub(super) vhost: VHostController, pub(super) tls_client_config: Arc, pub(crate) net_iface: Arc, @@ -54,8 +51,7 @@ impl NetController { socks_listen: SocketAddr, ) -> Result { let net_iface = Arc::new(NetworkInterfaceController::new(db.clone())); - let tor = TorController::new()?; - let socks = SocksController::new(socks_listen, tor.clone())?; + let socks = SocksController::new(socks_listen)?; let crypto_provider = Arc::new(tokio_rustls::rustls::crypto::ring::default_provider()); let tls_client_config = Arc::new(crate::net::tls::client_config( crypto_provider.clone(), @@ -87,7 +83,6 @@ impl NetController { .await?; Ok(Self { db: db.clone(), - tor, vhost: VHostController::new(db.clone(), net_iface.clone(), crypto_provider), tls_client_config, dns: DnsController::init(db, &net_iface.watcher).await?, @@ -165,10 +160,9 @@ impl NetController { #[derive(Default, Debug)] struct HostBinds { - forwards: BTreeMap)>, + forwards: BTreeMap)>, vhosts: BTreeMap<(Option, u16), (ProxyTarget, Arc<()>)>, private_dns: BTreeMap>, - tor: BTreeMap, Vec>)>, } pub struct NetServiceData { @@ -207,7 +201,7 @@ impl NetServiceData { .as_entries_mut()? { host.as_bindings_mut().mutate(|b| { - for (internal_port, info) in b { + for (internal_port, info) in b.iter_mut() { if !except.contains(&BindId { id: host_id.clone(), internal_port: *internal_port, @@ -238,7 +232,7 @@ impl NetServiceData { .as_network_mut() .as_host_mut(); host.as_bindings_mut().mutate(|b| { - for (internal_port, info) in b { + for (internal_port, info) in b.iter_mut() { if !except.contains(&BindId { id: HostId::default(), internal_port: *internal_port, @@ -256,425 +250,295 @@ impl NetServiceData { } } - async fn update(&mut self, ctrl: &NetController, id: HostId, host: Host) -> Result<(), Error> { - let mut forwards: BTreeMap = BTreeMap::new(); + async fn update( + &mut self, + ctrl: &NetController, + id: HostId, + mut host: Host, + ) -> Result<(), Error> { + let mut forwards: BTreeMap = BTreeMap::new(); let mut vhosts: BTreeMap<(Option, u16), ProxyTarget> = BTreeMap::new(); let mut private_dns: BTreeSet = BTreeSet::new(); - let mut tor: BTreeMap)> = - BTreeMap::new(); - let mut hostname_info: BTreeMap> = BTreeMap::new(); let binds = self.binds.entry(id.clone()).or_default(); let peek = ctrl.db.peek().await; - - // LAN let server_info = peek.as_public().as_server_info(); let net_ifaces = ctrl.net_iface.watcher.ip_info(); let hostname = server_info.as_hostname().de()?; - for (port, bind) in &host.bindings { + let host_addresses: Vec<_> = host.addresses().collect(); + + // Collect private DNS entries (domains without public config) + for HostAddress { + address, public, .. + } in &host_addresses + { + if public.is_none() { + private_dns.insert(address.clone()); + } + } + + // ── Phase 1: Compute possible addresses ── + for (_port, bind) in host.bindings.iter_mut() { if !bind.enabled { continue; } - if bind.net.assigned_port.is_some() || bind.net.assigned_ssl_port.is_some() { - let mut hostnames = BTreeSet::new(); - if let Some(ssl) = &bind.options.add_ssl { - let external = bind - .net - .assigned_ssl_port - .or_not_found("assigned ssl port")?; - let addr = (self.ip, *port).into(); - let connect_ssl = if let Some(alpn) = ssl.alpn.clone() { - Err(alpn) - } else { - if bind.options.secure.as_ref().map_or(false, |s| s.ssl) { - Ok(()) - } else { - Err(AlpnInfo::Reflect) - } - }; - for hostname in ctrl.server_hostnames.iter().cloned() { - vhosts.insert( - (hostname, external), - ProxyTarget { - filter: bind.net.clone().into_dyn(), - acme: None, - addr, - add_x_forwarded_headers: ssl.add_x_forwarded_headers, - connect_ssl: connect_ssl - .clone() - .map(|_| ctrl.tls_client_config.clone()), - }, - ); - } - for address in host.addresses() { - match address { - HostAddress::Onion { address } => { - let hostname = InternedString::from_display(&address); - if hostnames.insert(hostname.clone()) { - vhosts.insert( - (Some(hostname), external), - ProxyTarget { - filter: OrFilter( - TypeFilter(NetworkInterfaceType::Loopback), - IdFilter(GatewayId::from(InternedString::from( - START9_BRIDGE_IFACE, - ))), - ) - .into_dyn(), - acme: None, - addr, - add_x_forwarded_headers: ssl.add_x_forwarded_headers, - connect_ssl: connect_ssl - .clone() - .map(|_| ctrl.tls_client_config.clone()), - }, - ); // TODO: wrap onion ssl stream directly in tor ctrl - } - } - HostAddress::Domain { - address, - public, - private, - } => { - if hostnames.insert(address.clone()) { - let address = Some(address.clone()); - if ssl.preferred_external_port == 443 { - if let Some(public) = &public { - vhosts.insert( - (address.clone(), 5443), - ProxyTarget { - filter: AndFilter( - bind.net.clone(), - AndFilter( - IdFilter(public.gateway.clone()), - PublicFilter { public: false }, - ), - ) - .into_dyn(), - acme: public.acme.clone(), - addr, - add_x_forwarded_headers: ssl - .add_x_forwarded_headers, - connect_ssl: connect_ssl - .clone() - .map(|_| ctrl.tls_client_config.clone()), - }, - ); - vhosts.insert( - (address.clone(), 443), - ProxyTarget { - filter: AndFilter( - bind.net.clone(), - if private { - OrFilter( - IdFilter(public.gateway.clone()), - PublicFilter { public: false }, - ) - .into_dyn() - } else { - AndFilter( - IdFilter(public.gateway.clone()), - PublicFilter { public: true }, - ) - .into_dyn() - }, - ) - .into_dyn(), - acme: public.acme.clone(), - addr, - add_x_forwarded_headers: ssl - .add_x_forwarded_headers, - connect_ssl: connect_ssl - .clone() - .map(|_| ctrl.tls_client_config.clone()), - }, - ); - } else { - vhosts.insert( - (address.clone(), 443), - ProxyTarget { - filter: AndFilter( - bind.net.clone(), - PublicFilter { public: false }, - ) - .into_dyn(), - acme: None, - addr, - add_x_forwarded_headers: ssl - .add_x_forwarded_headers, - connect_ssl: connect_ssl - .clone() - .map(|_| ctrl.tls_client_config.clone()), - }, - ); - } - } else { - if let Some(public) = public { - vhosts.insert( - (address.clone(), external), - ProxyTarget { - filter: AndFilter( - bind.net.clone(), - if private { - OrFilter( - IdFilter(public.gateway.clone()), - PublicFilter { public: false }, - ) - .into_dyn() - } else { - IdFilter(public.gateway.clone()) - .into_dyn() - }, - ) - .into_dyn(), - acme: public.acme.clone(), - addr, - add_x_forwarded_headers: ssl - .add_x_forwarded_headers, - connect_ssl: connect_ssl - .clone() - .map(|_| ctrl.tls_client_config.clone()), - }, - ); - } else { - vhosts.insert( - (address.clone(), external), - ProxyTarget { - filter: AndFilter( - bind.net.clone(), - PublicFilter { public: false }, - ) - .into_dyn(), - acme: None, - addr, - add_x_forwarded_headers: ssl - .add_x_forwarded_headers, - connect_ssl: connect_ssl - .clone() - .map(|_| ctrl.tls_client_config.clone()), - }, - ); - } - } - } - } - } - } - } - if bind - .options - .secure - .map_or(true, |s| !(s.ssl && bind.options.add_ssl.is_some())) - { - let external = bind.net.assigned_port.or_not_found("assigned lan port")?; - forwards.insert( - external, - ( - SocketAddrV4::new(self.ip, *port), - AndFilter( - SecureFilter { - secure: bind.options.secure.is_some(), - }, - bind.net.clone(), - ) - .into_dyn(), - ), - ); - } - let mut bind_hostname_info: Vec = - hostname_info.remove(port).unwrap_or_default(); - for (gateway_id, info) in net_ifaces - .iter() - .filter(|(_, info)| { - info.ip_info.as_ref().map_or(false, |i| { - !matches!(i.device_type, Some(NetworkInterfaceType::Bridge)) - }) + if bind.net.assigned_port.is_none() && bind.net.assigned_ssl_port.is_none() { + continue; + } + + bind.addresses.possible.clear(); + for (gateway_id, info) in net_ifaces + .iter() + .filter(|(_, info)| { + info.ip_info.as_ref().map_or(false, |i| { + !matches!(i.device_type, Some(NetworkInterfaceType::Bridge)) + }) + }) + .filter(|(_, info)| info.ip_info.is_some()) + { + let gateway = GatewayInfo { + id: gateway_id.clone(), + name: info + .name + .clone() + .or_else(|| info.ip_info.as_ref().map(|i| i.name.clone())) + .unwrap_or_else(|| gateway_id.clone().into()), + public: info.public(), + }; + let port = bind.net.assigned_port.filter(|_| { + bind.options.secure.map_or(false, |s| { + !(s.ssl && bind.options.add_ssl.is_some()) || info.secure() + }) + }); + // .local addresses (private only, non-public, non-wireguard gateways) + if !info.public() + && info.ip_info.as_ref().map_or(false, |i| { + i.device_type != Some(NetworkInterfaceType::Wireguard) }) - .filter(|(id, info)| bind.net.filter(id, info)) { - let gateway = GatewayInfo { - id: gateway_id.clone(), - name: info - .name - .clone() - .or_else(|| info.ip_info.as_ref().map(|i| i.name.clone())) - .unwrap_or_else(|| gateway_id.clone().into()), - public: info.public(), - }; - let port = bind.net.assigned_port.filter(|_| { - bind.options.secure.map_or(false, |s| { - !(s.ssl && bind.options.add_ssl.is_some()) || info.secure() - }) + bind.addresses.possible.insert(HostnameInfo { + gateway: gateway.clone(), + public: false, + hostname: IpHostname::Local { + value: InternedString::from_display(&{ + let hostname = &hostname; + lazy_format!("{hostname}.local") + }), + port, + ssl_port: bind.net.assigned_ssl_port, + }, }); - if !info.public() - && info.ip_info.as_ref().map_or(false, |i| { - i.device_type != Some(NetworkInterfaceType::Wireguard) - }) - { - bind_hostname_info.push(HostnameInfo::Ip { + } + // Domain addresses + for HostAddress { + address, + public, + private, + } in host_addresses.iter().cloned() + { + let private = private && !info.public(); + let public = + public.as_ref().map_or(false, |p| &p.gateway == gateway_id); + if public || private { + let (domain_port, domain_ssl_port) = if bind + .options + .add_ssl + .as_ref() + .map_or(false, |ssl| ssl.preferred_external_port == 443) + { + (None, Some(443)) + } else { + (port, bind.net.assigned_ssl_port) + }; + bind.addresses.possible.insert(HostnameInfo { gateway: gateway.clone(), - public: false, - hostname: IpHostname::Local { - value: InternedString::from_display(&{ - let hostname = &hostname; - lazy_format!("{hostname}.local") - }), + public, + hostname: IpHostname::Domain { + value: address.clone(), + port: domain_port, + ssl_port: domain_ssl_port, + }, + }); + } + } + // IP addresses + if let Some(ip_info) = &info.ip_info { + let public = info.public(); + if let Some(wan_ip) = ip_info.wan_ip { + bind.addresses.possible.insert(HostnameInfo { + gateway: gateway.clone(), + public: true, + hostname: IpHostname::Ipv4 { + value: wan_ip, port, ssl_port: bind.net.assigned_ssl_port, }, }); } - for address in host.addresses() { - if let HostAddress::Domain { - address, - public, - private, - } = address - { - if public.is_none() { - private_dns.insert(address.clone()); - } - let private = private && !info.public(); - let public = - public.as_ref().map_or(false, |p| &p.gateway == gateway_id); - if public || private { - if bind - .options - .add_ssl - .as_ref() - .map_or(false, |ssl| ssl.preferred_external_port == 443) - { - bind_hostname_info.push(HostnameInfo::Ip { + for ipnet in &ip_info.subnets { + match ipnet { + IpNet::V4(net) => { + if !public { + bind.addresses.possible.insert(HostnameInfo { gateway: gateway.clone(), public, - hostname: IpHostname::Domain { - value: address.clone(), - port: None, - ssl_port: Some(443), - }, - }); - } else { - bind_hostname_info.push(HostnameInfo::Ip { - gateway: gateway.clone(), - public, - hostname: IpHostname::Domain { - value: address.clone(), + hostname: IpHostname::Ipv4 { + value: net.addr(), port, ssl_port: bind.net.assigned_ssl_port, }, }); } } + IpNet::V6(net) => { + bind.addresses.possible.insert(HostnameInfo { + gateway: gateway.clone(), + public: public && !ipv6_is_local(net.addr()), + hostname: IpHostname::Ipv6 { + value: net.addr(), + scope_id: ip_info.scope_id, + port, + ssl_port: bind.net.assigned_ssl_port, + }, + }); + } } } - if let Some(ip_info) = &info.ip_info { - let public = info.public(); - if let Some(wan_ip) = ip_info.wan_ip { - bind_hostname_info.push(HostnameInfo::Ip { - gateway: gateway.clone(), - public: true, - hostname: IpHostname::Ipv4 { - value: wan_ip, - port, - ssl_port: bind.net.assigned_ssl_port, + } + } + } + + // ── Phase 2: Build controller entries from enabled addresses ── + for (port, bind) in host.bindings.iter() { + if !bind.enabled { + continue; + } + if bind.net.assigned_port.is_none() && bind.net.assigned_ssl_port.is_none() { + continue; + } + + let enabled_addresses = bind.addresses.enabled(); + let addr: SocketAddr = (self.ip, *port).into(); + + // SSL vhosts + if let Some(ssl) = &bind.options.add_ssl { + let connect_ssl = if let Some(alpn) = ssl.alpn.clone() { + Err(alpn) + } else if bind.options.secure.as_ref().map_or(false, |s| s.ssl) { + Ok(()) + } else { + Err(AlpnInfo::Reflect) + }; + + if let Some(assigned_ssl_port) = bind.net.assigned_ssl_port { + // Collect private IPs from enabled private addresses' gateways + let server_private_ips: BTreeSet = enabled_addresses + .iter() + .filter(|a| !a.public) + .filter_map(|a| { + net_ifaces + .get(&a.gateway.id) + .and_then(|info| info.ip_info.as_ref()) + }) + .flat_map(|ip_info| ip_info.subnets.iter().map(|s| s.addr())) + .collect(); + + // Server hostname vhosts (on assigned_ssl_port) — private only + if !server_private_ips.is_empty() { + for hostname in ctrl.server_hostnames.iter().cloned() { + vhosts.insert( + (hostname, assigned_ssl_port), + ProxyTarget { + public: BTreeSet::new(), + private: server_private_ips.clone(), + acme: None, + addr, + add_x_forwarded_headers: ssl.add_x_forwarded_headers, + connect_ssl: connect_ssl + .clone() + .map(|_| ctrl.tls_client_config.clone()), }, - }); + ); } - for ipnet in &ip_info.subnets { - match ipnet { - IpNet::V4(net) => { - if !public { - bind_hostname_info.push(HostnameInfo::Ip { - gateway: gateway.clone(), - public, - hostname: IpHostname::Ipv4 { - value: net.addr(), - port, - ssl_port: bind.net.assigned_ssl_port, - }, - }); + } + } + + // Domain vhosts: group by (domain, ssl_port), merge public/private sets + for addr_info in &enabled_addresses { + if let IpHostname::Domain { + value: domain, + ssl_port: Some(domain_ssl_port), + .. + } = &addr_info.hostname + { + let key = (Some(domain.clone()), *domain_ssl_port); + let target = vhosts.entry(key).or_insert_with(|| ProxyTarget { + public: BTreeSet::new(), + private: BTreeSet::new(), + acme: host_addresses + .iter() + .find(|a| &a.address == domain) + .and_then(|a| a.public.as_ref()) + .and_then(|p| p.acme.clone()), + addr, + add_x_forwarded_headers: ssl.add_x_forwarded_headers, + connect_ssl: connect_ssl + .clone() + .map(|_| ctrl.tls_client_config.clone()), + }); + if addr_info.public { + target.public.insert(addr_info.gateway.id.clone()); + } else { + // Add interface IPs for this gateway to private set + if let Some(info) = net_ifaces.get(&addr_info.gateway.id) { + if let Some(ip_info) = &info.ip_info { + for subnet in &ip_info.subnets { + target.private.insert(subnet.addr()); } } - IpNet::V6(net) => { - bind_hostname_info.push(HostnameInfo::Ip { - gateway: gateway.clone(), - public: public && !ipv6_is_local(net.addr()), - hostname: IpHostname::Ipv6 { - value: net.addr(), - scope_id: ip_info.scope_id, - port, - ssl_port: bind.net.assigned_ssl_port, - }, - }); - } } } } } - hostname_info.insert(*port, bind_hostname_info); } - } - struct TorHostnamePorts { - non_ssl: Option, - ssl: Option, - } - let mut tor_hostname_ports = BTreeMap::::new(); - let mut tor_binds = OrdMap::::new(); - for (internal, info) in &host.bindings { - if !info.enabled { - continue; - } - tor_binds.insert( - info.options.preferred_external_port, - SocketAddr::from((self.ip, *internal)), - ); - if let (Some(ssl), Some(ssl_internal)) = - (&info.options.add_ssl, info.net.assigned_ssl_port) + // Non-SSL forwards + if bind + .options + .secure + .map_or(true, |s| !(s.ssl && bind.options.add_ssl.is_some())) { - tor_binds.insert( - ssl.preferred_external_port, - SocketAddr::from(([127, 0, 0, 1], ssl_internal)), - ); - tor_hostname_ports.insert( - *internal, - TorHostnamePorts { - non_ssl: Some(info.options.preferred_external_port) - .filter(|p| *p != ssl.preferred_external_port), - ssl: Some(ssl.preferred_external_port), - }, - ); - } else { - tor_hostname_ports.insert( - *internal, - TorHostnamePorts { - non_ssl: Some(info.options.preferred_external_port), - ssl: None, - }, + let external = bind.net.assigned_port.or_not_found("assigned lan port")?; + let fwd_public: BTreeSet = enabled_addresses + .iter() + .filter(|a| a.public) + .map(|a| a.gateway.id.clone()) + .collect(); + let fwd_private: BTreeSet = enabled_addresses + .iter() + .filter(|a| !a.public) + .filter_map(|a| { + net_ifaces + .get(&a.gateway.id) + .and_then(|i| i.ip_info.as_ref()) + }) + .flat_map(|ip| ip.subnets.iter().map(|s| s.addr())) + .collect(); + forwards.insert( + external, + ( + SocketAddrV4::new(self.ip, *port), + ForwardRequirements { + public_gateways: fwd_public, + private_ips: fwd_private, + secure: bind.options.secure.is_some(), + }, + ), ); } } - for tor_addr in host.onions.iter() { - let key = peek - .as_private() - .as_key_store() - .as_onion() - .get_key(tor_addr)?; - tor.insert(key.onion_address(), (key, tor_binds.clone())); - for (internal, ports) in &tor_hostname_ports { - let mut bind_hostname_info = hostname_info.remove(internal).unwrap_or_default(); - bind_hostname_info.push(HostnameInfo::Onion { - hostname: OnionHostname { - value: InternedString::from_display(tor_addr), - port: ports.non_ssl, - ssl_port: ports.ssl, - }, - }); - hostname_info.insert(*internal, bind_hostname_info); - } - } - + // ── Phase 3: Reconcile ── let all = binds .forwards .keys() @@ -683,8 +547,8 @@ impl NetServiceData { .collect::>(); for external in all { let mut prev = binds.forwards.remove(&external); - if let Some((internal, filter)) = forwards.remove(&external) { - prev = prev.filter(|(i, f, _)| i == &internal && *f == filter); + if let Some((internal, reqs)) = forwards.remove(&external) { + prev = prev.filter(|(i, r, _)| i == &internal && *r == reqs); binds.forwards.insert( external, if let Some(prev) = prev { @@ -692,11 +556,11 @@ impl NetServiceData { } else { ( internal, - filter.clone(), + reqs.clone(), ctrl.forward .add( external, - filter, + reqs, internal, net_ifaces .iter() @@ -763,40 +627,18 @@ impl NetServiceData { } ctrl.dns.gc_private_domains(&rm)?; - let all = binds - .tor - .keys() - .chain(tor.keys()) - .cloned() - .collect::>(); - for onion in all { - let mut prev = binds.tor.remove(&onion); - if let Some((key, tor_binds)) = tor.remove(&onion).filter(|(_, b)| !b.is_empty()) { - prev = prev.filter(|(b, _)| b == &tor_binds); - binds.tor.insert( - onion, - if let Some(prev) = prev { - prev - } else { - let service = ctrl.tor.service(key)?; - let rcs = service.proxy_all(tor_binds.iter().map(|(k, v)| (*k, *v))); - (tor_binds, rcs) - }, - ); - } else { - if let Some((_, rc)) = prev { - drop(rc); - ctrl.tor.gc(Some(onion)).await?; - } - } - } - let res = ctrl .db .mutate(|db| { - host_for(db, self.id.as_ref(), &id)? - .as_hostname_info_mut() - .ser(&hostname_info) + let bindings = host_for(db, self.id.as_ref(), &id)?.as_bindings_mut(); + for (port, bind) in host.bindings.0 { + if let Some(b) = bindings.as_idx_mut(&port) { + b.as_addresses_mut() + .as_possible_mut() + .ser(&bind.addresses.possible)?; + } + } + Ok(()) }) .await; res.result?; diff --git a/core/src/net/service_interface.rs b/core/src/net/service_interface.rs index 499e1a321..1eae3ebca 100644 --- a/core/src/net/service_interface.rs +++ b/core/src/net/service_interface.rs @@ -6,31 +6,21 @@ use ts_rs::TS; use crate::{GatewayId, HostId, ServiceInterfaceId}; -#[derive(Clone, Debug, Deserialize, Serialize, TS)] +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize, TS)] #[ts(export)] #[serde(rename_all = "camelCase")] -#[serde(rename_all_fields = "camelCase")] -#[serde(tag = "kind")] -pub enum HostnameInfo { - Ip { - gateway: GatewayInfo, - public: bool, - hostname: IpHostname, - }, - Onion { - hostname: OnionHostname, - }, +pub struct HostnameInfo { + pub gateway: GatewayInfo, + pub public: bool, + pub hostname: IpHostname, } impl HostnameInfo { pub fn to_san_hostname(&self) -> InternedString { - match self { - Self::Ip { hostname, .. } => hostname.to_san_hostname(), - Self::Onion { hostname } => hostname.to_san_hostname(), - } + self.hostname.to_san_hostname() } } -#[derive(Clone, Debug, Deserialize, Serialize, TS)] +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize, TS)] #[ts(export)] #[serde(rename_all = "camelCase")] pub struct GatewayInfo { @@ -39,22 +29,7 @@ pub struct GatewayInfo { pub public: bool, } -#[derive(Clone, Debug, Deserialize, Serialize, TS)] -#[ts(export)] -#[serde(rename_all = "camelCase")] -pub struct OnionHostname { - #[ts(type = "string")] - pub value: InternedString, - pub port: Option, - pub ssl_port: Option, -} -impl OnionHostname { - pub fn to_san_hostname(&self) -> InternedString { - self.value.clone() - } -} - -#[derive(Clone, Debug, Deserialize, Serialize, TS)] +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize, TS)] #[ts(export)] #[serde(rename_all = "camelCase")] #[serde(rename_all_fields = "camelCase")] diff --git a/core/src/net/socks.rs b/core/src/net/socks.rs index 5d1be66f0..7f54a8010 100644 --- a/core/src/net/socks.rs +++ b/core/src/net/socks.rs @@ -8,7 +8,6 @@ use socks5_impl::server::{AuthAdaptor, ClientConnection, Server}; use tokio::net::{TcpListener, TcpStream}; use crate::HOST_IP; -use crate::net::tor::TorController; use crate::prelude::*; use crate::util::actor::background::BackgroundJobQueue; use crate::util::future::NonDetachingJoinHandle; @@ -22,7 +21,7 @@ pub struct SocksController { _thread: NonDetachingJoinHandle<()>, } impl SocksController { - pub fn new(listen: SocketAddr, tor: TorController) -> Result { + pub fn new(listen: SocketAddr) -> Result { Ok(Self { _thread: tokio::spawn(async move { let auth: AuthAdaptor<()> = Arc::new(NoAuth); @@ -45,7 +44,6 @@ impl SocksController { loop { match server.accept().await { Ok((stream, _)) => { - let tor = tor.clone(); bg.add_job(async move { if let Err(e) = async { match stream @@ -57,40 +55,6 @@ impl SocksController { .await .with_kind(ErrorKind::Network)? { - ClientConnection::Connect( - reply, - Address::DomainAddress(domain, port), - ) if domain.ends_with(".onion") => { - if let Ok(mut target) = tor - .connect_onion(&domain.parse()?, port) - .await - { - let mut sock = reply - .reply( - Reply::Succeeded, - Address::unspecified(), - ) - .await - .with_kind(ErrorKind::Network)?; - tokio::io::copy_bidirectional( - &mut sock, - &mut target, - ) - .await - .with_kind(ErrorKind::Network)?; - } else { - let mut sock = reply - .reply( - Reply::HostUnreachable, - Address::unspecified(), - ) - .await - .with_kind(ErrorKind::Network)?; - sock.shutdown() - .await - .with_kind(ErrorKind::Network)?; - } - } ClientConnection::Connect(reply, addr) => { if let Ok(mut target) = match addr { Address::DomainAddress(domain, port) => { diff --git a/core/src/net/tor/arti.rs b/core/src/net/tor/arti.rs deleted file mode 100644 index c44d8d528..000000000 --- a/core/src/net/tor/arti.rs +++ /dev/null @@ -1,964 +0,0 @@ -use std::borrow::Cow; -use std::collections::{BTreeMap, BTreeSet}; -use std::net::SocketAddr; -use std::str::FromStr; -use std::sync::{Arc, Weak}; -use std::time::{Duration, Instant}; - -use arti_client::config::onion_service::OnionServiceConfigBuilder; -use arti_client::{TorClient, TorClientConfig}; -use base64::Engine; -use clap::Parser; -use color_eyre::eyre::eyre; -use futures::{FutureExt, StreamExt}; -use imbl_value::InternedString; -use itertools::Itertools; -use rpc_toolkit::{Context, Empty, HandlerExt, ParentHandler, from_fn_async}; -use serde::{Deserialize, Serialize}; -use tokio::io::{AsyncReadExt, AsyncWriteExt}; -use tokio::net::TcpStream; -use tokio::sync::Notify; -use tor_cell::relaycell::msg::Connected; -use tor_hscrypto::pk::{HsId, HsIdKeypair}; -use tor_hsservice::status::State as ArtiOnionServiceState; -use tor_hsservice::{HsNickname, RunningOnionService}; -use tor_keymgr::config::ArtiKeystoreKind; -use tor_proto::client::stream::IncomingStreamRequest; -use tor_rtcompat::tokio::TokioRustlsRuntime; -use ts_rs::TS; - -use crate::context::{CliContext, RpcContext}; -use crate::prelude::*; -use crate::util::actor::background::BackgroundJobQueue; -use crate::util::future::{NonDetachingJoinHandle, Until}; -use crate::util::io::ReadWriter; -use crate::util::serde::{ - BASE64, Base64, HandlerExtSerde, WithIoFormat, deserialize_from_str, display_serializable, - serialize_display, -}; -use crate::util::sync::{SyncMutex, SyncRwLock, Watch}; - -const BOOTSTRAP_PROGRESS_TIMEOUT: Duration = Duration::from_secs(300); -const HS_BOOTSTRAP_TIMEOUT: Duration = Duration::from_secs(300); -const RETRY_COOLDOWN: Duration = Duration::from_secs(15); -const HEALTH_CHECK_FAILURE_ALLOWANCE: usize = 5; -const HEALTH_CHECK_COOLDOWN: Duration = Duration::from_secs(120); - -#[derive(Debug, Clone, Copy)] -pub struct OnionAddress(pub HsId); -impl std::fmt::Display for OnionAddress { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - safelog::DisplayRedacted::fmt_unredacted(&self.0, f) - } -} -impl FromStr for OnionAddress { - type Err = Error; - fn from_str(s: &str) -> Result { - Ok(Self( - if s.ends_with(".onion") { - Cow::Borrowed(s) - } else { - Cow::Owned(format!("{s}.onion")) - } - .parse::() - .with_kind(ErrorKind::Tor)?, - )) - } -} -impl Serialize for OnionAddress { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - serialize_display(self, serializer) - } -} -impl<'de> Deserialize<'de> for OnionAddress { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - deserialize_from_str(deserializer) - } -} -impl PartialEq for OnionAddress { - fn eq(&self, other: &Self) -> bool { - self.0.as_ref() == other.0.as_ref() - } -} -impl Eq for OnionAddress {} -impl PartialOrd for OnionAddress { - fn partial_cmp(&self, other: &Self) -> Option { - self.0.as_ref().partial_cmp(other.0.as_ref()) - } -} -impl Ord for OnionAddress { - fn cmp(&self, other: &Self) -> std::cmp::Ordering { - self.0.as_ref().cmp(other.0.as_ref()) - } -} - -pub struct TorSecretKey(pub HsIdKeypair); -impl TorSecretKey { - pub fn onion_address(&self) -> OnionAddress { - OnionAddress(HsId::from(self.0.as_ref().public().to_bytes())) - } - pub fn from_bytes(bytes: [u8; 64]) -> Result { - Ok(Self( - tor_llcrypto::pk::ed25519::ExpandedKeypair::from_secret_key_bytes(bytes) - .ok_or_else(|| { - Error::new( - eyre!("{}", t!("net.tor.invalid-ed25519-key")), - ErrorKind::Tor, - ) - })? - .into(), - )) - } - pub fn generate() -> Self { - Self( - tor_llcrypto::pk::ed25519::ExpandedKeypair::from( - &tor_llcrypto::pk::ed25519::Keypair::generate(&mut rand::rng()), - ) - .into(), - ) - } -} -impl Clone for TorSecretKey { - fn clone(&self) -> Self { - Self(HsIdKeypair::from( - tor_llcrypto::pk::ed25519::ExpandedKeypair::from_secret_key_bytes( - self.0.as_ref().to_secret_key_bytes(), - ) - .unwrap(), - )) - } -} -impl std::fmt::Display for TorSecretKey { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!( - f, - "{}", - BASE64.encode(self.0.as_ref().to_secret_key_bytes()) - ) - } -} -impl FromStr for TorSecretKey { - type Err = Error; - fn from_str(s: &str) -> Result { - Self::from_bytes(Base64::<[u8; 64]>::from_str(s)?.0) - } -} -impl Serialize for TorSecretKey { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - serialize_display(self, serializer) - } -} -impl<'de> Deserialize<'de> for TorSecretKey { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - deserialize_from_str(deserializer) - } -} - -#[derive(Default, Deserialize, Serialize)] -pub struct OnionStore(BTreeMap); -impl Map for OnionStore { - type Key = OnionAddress; - type Value = TorSecretKey; - fn key_str(key: &Self::Key) -> Result, Error> { - Self::key_string(key) - } - fn key_string(key: &Self::Key) -> Result { - Ok(InternedString::from_display(key)) - } -} -impl OnionStore { - pub fn new() -> Self { - Self::default() - } - pub fn insert(&mut self, key: TorSecretKey) { - self.0.insert(key.onion_address(), key); - } -} -impl Model { - pub fn new_key(&mut self) -> Result { - let key = TorSecretKey::generate(); - self.insert(&key.onion_address(), &key)?; - Ok(key) - } - pub fn insert_key(&mut self, key: &TorSecretKey) -> Result<(), Error> { - self.insert(&key.onion_address(), &key) - } - pub fn get_key(&self, address: &OnionAddress) -> Result { - self.as_idx(address) - .or_not_found(lazy_format!("private key for {address}"))? - .de() - } -} -impl std::fmt::Debug for OnionStore { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - struct OnionStoreMap<'a>(&'a BTreeMap); - impl<'a> std::fmt::Debug for OnionStoreMap<'a> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - #[derive(Debug)] - struct KeyFor(#[allow(unused)] OnionAddress); - let mut map = f.debug_map(); - for (k, v) in self.0 { - map.key(k); - map.value(&KeyFor(v.onion_address())); - } - map.finish() - } - } - f.debug_tuple("OnionStore") - .field(&OnionStoreMap(&self.0)) - .finish() - } -} - -pub fn tor_api() -> ParentHandler { - ParentHandler::new() - .subcommand( - "list-services", - from_fn_async(list_services) - .with_display_serializable() - .with_custom_display_fn(|handle, result| display_services(handle.params, result)) - .with_about("about.display-tor-v3-onion-addresses") - .with_call_remote::(), - ) - .subcommand( - "reset", - from_fn_async(reset) - .no_display() - .with_about("about.reset-tor-daemon") - .with_call_remote::(), - ) - .subcommand( - "key", - key::().with_about("about.manage-onion-service-key-store"), - ) -} - -pub fn key() -> ParentHandler { - ParentHandler::new() - .subcommand( - "generate", - from_fn_async(generate_key) - .with_about("about.generate-onion-service-key-add-to-store") - .with_call_remote::(), - ) - .subcommand( - "add", - from_fn_async(add_key) - .with_about("about.add-onion-service-key-to-store") - .with_call_remote::(), - ) - .subcommand( - "list", - from_fn_async(list_keys) - .with_custom_display_fn(|_, res| { - for addr in res { - println!("{addr}"); - } - Ok(()) - }) - .with_about("about.list-onion-services-with-keys-in-store") - .with_call_remote::(), - ) -} - -pub async fn generate_key(ctx: RpcContext) -> Result { - ctx.db - .mutate(|db| { - Ok(db - .as_private_mut() - .as_key_store_mut() - .as_onion_mut() - .new_key()? - .onion_address()) - }) - .await - .result -} - -#[derive(Deserialize, Serialize, Parser)] -pub struct AddKeyParams { - #[arg(help = "help.arg.onion-secret-key")] - pub key: Base64<[u8; 64]>, -} - -pub async fn add_key( - ctx: RpcContext, - AddKeyParams { key }: AddKeyParams, -) -> Result { - let key = TorSecretKey::from_bytes(key.0)?; - ctx.db - .mutate(|db| { - db.as_private_mut() - .as_key_store_mut() - .as_onion_mut() - .insert_key(&key) - }) - .await - .result?; - Ok(key.onion_address()) -} - -pub async fn list_keys(ctx: RpcContext) -> Result, Error> { - ctx.db - .peek() - .await - .into_private() - .into_key_store() - .into_onion() - .keys() -} - -#[derive(Deserialize, Serialize, Parser, TS)] -#[serde(rename_all = "camelCase")] -#[command(rename_all = "kebab-case")] -pub struct ResetParams { - #[arg( - name = "wipe-state", - short = 'w', - long = "wipe-state", - help = "help.arg.wipe-tor-state" - )] - wipe_state: bool, -} - -pub async fn reset(ctx: RpcContext, ResetParams { wipe_state }: ResetParams) -> Result<(), Error> { - ctx.net_controller.tor.reset(wipe_state).await -} - -pub fn display_services( - params: WithIoFormat, - services: BTreeMap, -) -> Result<(), Error> { - use prettytable::*; - - if let Some(format) = params.format { - return display_serializable(format, services); - } - - let mut table = Table::new(); - table.add_row(row![bc => "ADDRESS", "STATE", "BINDINGS"]); - for (service, info) in services { - let row = row![ - &service.to_string(), - &format!("{:?}", info.state), - &info - .bindings - .into_iter() - .map(|(port, addr)| lazy_format!("{port} -> {addr}")) - .join("; ") - ]; - table.add_row(row); - } - table.print_tty(false)?; - Ok(()) -} - -#[derive(Debug, Serialize, Deserialize)] -#[serde(rename_all = "kebab-case")] -pub enum OnionServiceState { - Shutdown, - Bootstrapping, - DegradedReachable, - DegradedUnreachable, - Running, - Recovering, - Broken, -} -impl From for OnionServiceState { - fn from(value: ArtiOnionServiceState) -> Self { - match value { - ArtiOnionServiceState::Shutdown => Self::Shutdown, - ArtiOnionServiceState::Bootstrapping => Self::Bootstrapping, - ArtiOnionServiceState::DegradedReachable => Self::DegradedReachable, - ArtiOnionServiceState::DegradedUnreachable => Self::DegradedUnreachable, - ArtiOnionServiceState::Running => Self::Running, - ArtiOnionServiceState::Recovering => Self::Recovering, - ArtiOnionServiceState::Broken => Self::Broken, - _ => unreachable!(), - } - } -} - -#[derive(Debug, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct OnionServiceInfo { - pub state: OnionServiceState, - pub bindings: BTreeMap, -} - -pub async fn list_services( - ctx: RpcContext, - _: Empty, -) -> Result, Error> { - ctx.net_controller.tor.list_services().await -} - -#[derive(Clone)] -pub struct TorController(Arc); -struct TorControllerInner { - client: Watch<(usize, TorClient)>, - _bootstrapper: NonDetachingJoinHandle<()>, - services: SyncMutex>, - reset: Arc, -} -impl TorController { - pub fn new() -> Result { - let mut config = TorClientConfig::builder(); - config - .storage() - .keystore() - .primary() - .kind(ArtiKeystoreKind::Ephemeral.into()); - let client = Watch::new(( - 0, - TorClient::with_runtime(TokioRustlsRuntime::current()?) - .config(config.build().with_kind(ErrorKind::Tor)?) - .local_resource_timeout(Duration::from_secs(0)) - .create_unbootstrapped()?, - )); - let reset = Arc::new(Notify::new()); - let bootstrapper_reset = reset.clone(); - let bootstrapper_client = client.clone(); - let bootstrapper = tokio::spawn(async move { - loop { - let (epoch, client): (usize, _) = bootstrapper_client.read(); - if let Err(e) = Until::new() - .with_async_fn(|| bootstrapper_reset.notified().map(Ok)) - .run(async { - let mut events = client.bootstrap_events(); - let bootstrap_fut = - client.bootstrap().map(|res| res.with_kind(ErrorKind::Tor)); - let failure_fut = async { - let mut prev_frac = 0_f32; - let mut prev_inst = Instant::now(); - while let Some(event) = - tokio::time::timeout(BOOTSTRAP_PROGRESS_TIMEOUT, events.next()) - .await - .with_kind(ErrorKind::Tor)? - { - if event.ready_for_traffic() { - return Ok::<_, Error>(()); - } - let frac = event.as_frac(); - if frac == prev_frac { - if prev_inst.elapsed() > BOOTSTRAP_PROGRESS_TIMEOUT { - return Err(Error::new( - eyre!( - "{}", - t!( - "net.tor.bootstrap-no-progress", - duration = crate::util::serde::Duration::from( - BOOTSTRAP_PROGRESS_TIMEOUT - ) - .to_string() - ) - ), - ErrorKind::Tor, - )); - } - } else { - prev_frac = frac; - prev_inst = Instant::now(); - } - } - futures::future::pending().await - }; - if let Err::<(), Error>(e) = tokio::select! { - res = bootstrap_fut => res, - res = failure_fut => res, - } { - tracing::error!( - "{}", - t!("net.tor.bootstrap-error", error = e.to_string()) - ); - tracing::debug!("{e:?}"); - } else { - bootstrapper_client.send_modify(|_| ()); - - for _ in 0..HEALTH_CHECK_FAILURE_ALLOWANCE { - if let Err::<(), Error>(e) = async { - loop { - let (bg, mut runner) = BackgroundJobQueue::new(); - runner - .run_while(async { - const PING_BUF_LEN: usize = 8; - let key = TorSecretKey::generate(); - let onion = key.onion_address(); - let (hs, stream) = client - .launch_onion_service_with_hsid( - OnionServiceConfigBuilder::default() - .nickname( - onion - .to_string() - .trim_end_matches(".onion") - .parse::() - .with_kind(ErrorKind::Tor)?, - ) - .build() - .with_kind(ErrorKind::Tor)?, - key.clone().0, - ) - .with_kind(ErrorKind::Tor)?; - bg.add_job(async move { - if let Err(e) = async { - let mut stream = - tor_hsservice::handle_rend_requests( - stream, - ); - while let Some(req) = stream.next().await { - let mut stream = req - .accept(Connected::new_empty()) - .await - .with_kind(ErrorKind::Tor)?; - let mut buf = [0; PING_BUF_LEN]; - stream.read_exact(&mut buf).await?; - stream.write_all(&buf).await?; - stream.flush().await?; - stream.shutdown().await?; - } - Ok::<_, Error>(()) - } - .await - { - tracing::error!( - "{}", - t!( - "net.tor.health-error", - error = e.to_string() - ) - ); - tracing::debug!("{e:?}"); - } - }); - - tokio::time::timeout(HS_BOOTSTRAP_TIMEOUT, async { - let mut status = hs.status_events(); - while let Some(status) = status.next().await { - if status.state().is_fully_reachable() { - return Ok(()); - } - } - Err(Error::new( - eyre!( - "{}", - t!("net.tor.status-stream-ended") - ), - ErrorKind::Tor, - )) - }) - .await - .with_kind(ErrorKind::Tor)??; - - let mut stream = client - .connect((onion.to_string(), 8080)) - .await?; - let mut ping_buf = [0; PING_BUF_LEN]; - rand::fill(&mut ping_buf); - stream.write_all(&ping_buf).await?; - stream.flush().await?; - let mut ping_res = [0; PING_BUF_LEN]; - stream.read_exact(&mut ping_res).await?; - ensure_code!( - ping_buf == ping_res, - ErrorKind::Tor, - "ping buffer mismatch" - ); - stream.shutdown().await?; - - Ok::<_, Error>(()) - }) - .await?; - tokio::time::sleep(HEALTH_CHECK_COOLDOWN).await; - } - } - .await - { - tracing::error!( - "{}", - t!("net.tor.client-health-error", error = e.to_string()) - ); - tracing::debug!("{e:?}"); - } - } - tracing::error!( - "{}", - t!( - "net.tor.health-check-failed-recycling", - count = HEALTH_CHECK_FAILURE_ALLOWANCE - ) - ); - } - - Ok(()) - }) - .await - { - tracing::error!( - "{}", - t!("net.tor.bootstrapper-error", error = e.to_string()) - ); - tracing::debug!("{e:?}"); - } - if let Err::<(), Error>(e) = async { - tokio::time::sleep(RETRY_COOLDOWN).await; - bootstrapper_client.send(( - epoch.wrapping_add(1), - TorClient::with_runtime(TokioRustlsRuntime::current()?) - .config(config.build().with_kind(ErrorKind::Tor)?) - .local_resource_timeout(Duration::from_secs(0)) - .create_unbootstrapped_async() - .await?, - )); - tracing::debug!("TorClient recycled"); - Ok(()) - } - .await - { - tracing::error!( - "{}", - t!("net.tor.client-creation-error", error = e.to_string()) - ); - tracing::debug!("{e:?}"); - } - } - }) - .into(); - Ok(Self(Arc::new(TorControllerInner { - client, - _bootstrapper: bootstrapper, - services: SyncMutex::new(BTreeMap::new()), - reset, - }))) - } - - pub fn service(&self, key: TorSecretKey) -> Result { - self.0.services.mutate(|s| { - use std::collections::btree_map::Entry; - let addr = key.onion_address(); - match s.entry(addr) { - Entry::Occupied(e) => Ok(e.get().clone()), - Entry::Vacant(e) => Ok(e - .insert(OnionService::launch(self.0.client.clone(), key)?) - .clone()), - } - }) - } - - pub async fn gc(&self, addr: Option) -> Result<(), Error> { - if let Some(addr) = addr { - if let Some(s) = self.0.services.mutate(|s| { - let rm = if let Some(s) = s.get(&addr) { - !s.gc() - } else { - false - }; - if rm { s.remove(&addr) } else { None } - }) { - s.shutdown().await - } else { - Ok(()) - } - } else { - for s in self.0.services.mutate(|s| { - let mut rm = Vec::new(); - s.retain(|_, s| { - if s.gc() { - true - } else { - rm.push(s.clone()); - false - } - }); - rm - }) { - s.shutdown().await?; - } - Ok(()) - } - } - - pub async fn reset(&self, wipe_state: bool) -> Result<(), Error> { - self.0.reset.notify_waiters(); - Ok(()) - } - - pub async fn list_services(&self) -> Result, Error> { - Ok(self - .0 - .services - .peek(|s| s.iter().map(|(a, s)| (a.clone(), s.info())).collect())) - } - - pub async fn connect_onion( - &self, - addr: &OnionAddress, - port: u16, - ) -> Result, Error> { - if let Some(target) = self.0.services.peek(|s| { - s.get(addr).and_then(|s| { - s.0.bindings.peek(|b| { - b.get(&port).and_then(|b| { - b.iter() - .find(|(_, rc)| rc.strong_count() > 0) - .map(|(a, _)| *a) - }) - }) - }) - }) { - let tcp_stream = TcpStream::connect(target) - .await - .with_kind(ErrorKind::Network)?; - if let Err(e) = socket2::SockRef::from(&tcp_stream).set_keepalive(true) { - tracing::error!( - "{}", - t!("net.tor.failed-to-set-tcp-keepalive", error = e.to_string()) - ); - tracing::debug!("{e:?}"); - } - Ok(Box::new(tcp_stream)) - } else { - let mut client = self.0.client.clone(); - client - .wait_for(|(_, c)| c.bootstrap_status().ready_for_traffic()) - .await; - let stream = client - .read() - .1 - .connect((addr.to_string(), port)) - .await - .with_kind(ErrorKind::Tor)?; - Ok(Box::new(stream)) - } - } -} - -#[derive(Clone)] -pub struct OnionService(Arc); -struct OnionServiceData { - service: Arc>>>, - bindings: Arc>>>>, - _thread: NonDetachingJoinHandle<()>, -} -impl OnionService { - fn launch( - mut client: Watch<(usize, TorClient)>, - key: TorSecretKey, - ) -> Result { - let service = Arc::new(SyncMutex::new(None)); - let bindings = Arc::new(SyncRwLock::new(BTreeMap::< - u16, - BTreeMap>, - >::new())); - Ok(Self(Arc::new(OnionServiceData { - service: service.clone(), - bindings: bindings.clone(), - _thread: tokio::spawn(async move { - let (bg, mut runner) = BackgroundJobQueue::new(); - runner - .run_while(async { - loop { - if let Err(e) = async { - client.wait_for(|(_,c)| c.bootstrap_status().ready_for_traffic()).await; - let epoch = client.peek(|(e, c)| { - ensure_code!(c.bootstrap_status().ready_for_traffic(), ErrorKind::Tor, "TorClient recycled"); - Ok::<_, Error>(*e) - })?; - let addr = key.onion_address(); - let (new_service, stream) = client.peek(|(_, c)| { - c.launch_onion_service_with_hsid( - OnionServiceConfigBuilder::default() - .nickname( - addr - .to_string() - .trim_end_matches(".onion") - .parse::() - .with_kind(ErrorKind::Tor)?, - ) - .build() - .with_kind(ErrorKind::Tor)?, - key.clone().0, - ) - .with_kind(ErrorKind::Tor) - })?; - let mut status_stream = new_service.status_events(); - let mut status = new_service.status(); - if status.state().is_fully_reachable() { - tracing::debug!("{addr} is fully reachable"); - } else { - tracing::debug!("{addr} is not fully reachable"); - } - bg.add_job(async move { - while let Some(new_status) = status_stream.next().await { - if status.state().is_fully_reachable() && !new_status.state().is_fully_reachable() { - tracing::debug!("{addr} is no longer fully reachable"); - } else if !status.state().is_fully_reachable() && new_status.state().is_fully_reachable() { - tracing::debug!("{addr} is now fully reachable"); - } - status = new_status; - // TODO: health daemon? - } - }); - service.replace(Some(new_service)); - let mut stream = tor_hsservice::handle_rend_requests(stream); - while let Some(req) = tokio::select! { - req = stream.next() => req, - _ = client.wait_for(|(e, _)| *e != epoch) => None - } { - bg.add_job({ - let bg = bg.clone(); - let bindings = bindings.clone(); - async move { - if let Err(e) = async { - let IncomingStreamRequest::Begin(begin) = - req.request() - else { - return req - .reject(tor_cell::relaycell::msg::End::new_with_reason( - tor_cell::relaycell::msg::EndReason::DONE, - )) - .await - .with_kind(ErrorKind::Tor); - }; - let Some(target) = bindings.peek(|b| { - b.get(&begin.port()).and_then(|a| { - a.iter() - .find(|(_, rc)| rc.strong_count() > 0) - .map(|(addr, _)| *addr) - }) - }) else { - return req - .reject(tor_cell::relaycell::msg::End::new_with_reason( - tor_cell::relaycell::msg::EndReason::DONE, - )) - .await - .with_kind(ErrorKind::Tor); - }; - bg.add_job(async move { - if let Err(e) = async { - let mut outgoing = - TcpStream::connect(target) - .await - .with_kind(ErrorKind::Network)?; - if let Err(e) = socket2::SockRef::from(&outgoing).set_keepalive(true) { - tracing::error!("{}", t!("net.tor.failed-to-set-tcp-keepalive", error = e.to_string())); - tracing::debug!("{e:?}"); - } - let mut incoming = req - .accept(Connected::new_empty()) - .await - .with_kind(ErrorKind::Tor)?; - if let Err(e) = - tokio::io::copy_bidirectional( - &mut outgoing, - &mut incoming, - ) - .await - { - tracing::trace!("Tor Stream Error: {e}"); - tracing::trace!("{e:?}"); - } - - Ok::<_, Error>(()) - } - .await - { - tracing::trace!("Tor Stream Error: {e}"); - tracing::trace!("{e:?}"); - } - }); - Ok::<_, Error>(()) - } - .await - { - tracing::trace!("Tor Request Error: {e}"); - tracing::trace!("{e:?}"); - } - } - }); - } - Ok::<_, Error>(()) - } - .await - { - tracing::error!("{}", t!("net.tor.client-error", error = e.to_string())); - tracing::debug!("{e:?}"); - } - } - }) - .await - }) - .into(), - }))) - } - - pub async fn proxy_all>>( - &self, - bindings: impl IntoIterator, - ) -> Result { - Ok(self.0.bindings.mutate(|b| { - bindings - .into_iter() - .map(|(port, target)| { - let entry = b.entry(port).or_default().entry(target).or_default(); - if let Some(rc) = entry.upgrade() { - rc - } else { - let rc = Arc::new(()); - *entry = Arc::downgrade(&rc); - rc - } - }) - .collect() - })) - } - - pub fn gc(&self) -> bool { - self.0.bindings.mutate(|b| { - b.retain(|_, targets| { - targets.retain(|_, rc| rc.strong_count() > 0); - !targets.is_empty() - }); - !b.is_empty() - }) - } - - pub async fn shutdown(self) -> Result<(), Error> { - self.0.service.replace(None); - self.0._thread.abort(); - Ok(()) - } - - pub fn state(&self) -> OnionServiceState { - self.0 - .service - .peek(|s| s.as_ref().map(|s| s.status().state().into())) - .unwrap_or(OnionServiceState::Bootstrapping) - } - - pub fn info(&self) -> OnionServiceInfo { - OnionServiceInfo { - state: self.state(), - bindings: self.0.bindings.peek(|b| { - b.iter() - .filter_map(|(port, b)| { - b.iter() - .find(|(_, rc)| rc.strong_count() > 0) - .map(|(addr, _)| (*port, *addr)) - }) - .collect() - }), - } - } -} diff --git a/core/src/net/tor/ctor.rs b/core/src/net/tor/ctor.rs deleted file mode 100644 index 1fc3485fe..000000000 --- a/core/src/net/tor/ctor.rs +++ /dev/null @@ -1,1092 +0,0 @@ -use std::collections::{BTreeMap, BTreeSet}; -use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4}; -use std::str::FromStr; -use std::sync::atomic::AtomicBool; -use std::sync::{Arc, Weak}; -use std::time::Duration; - -use base64::Engine; -use clap::Parser; -use color_eyre::eyre::eyre; -use futures::future::BoxFuture; -use futures::{FutureExt, TryFutureExt, TryStreamExt}; -use imbl::OrdMap; -use imbl_value::InternedString; -use lazy_static::lazy_static; -use regex::Regex; -use rpc_toolkit::{Context, Empty, HandlerExt, ParentHandler, from_fn_async}; -use serde::{Deserialize, Serialize}; -use tokio::net::TcpStream; -use tokio::process::Command; -use tokio::sync::{mpsc, oneshot}; -use tokio::time::Instant; -use torut::control::{AsyncEvent, AuthenticatedConn, ConnError}; -use torut::onion::{OnionAddressV3, TorSecretKeyV3}; -use tracing::instrument; -use ts_rs::TS; - -use crate::context::{CliContext, RpcContext}; -use crate::logs::{LogSource, LogsParams, journalctl}; -use crate::prelude::*; -use crate::util::Invoke; -use crate::util::collections::ordmap_retain; -use crate::util::future::NonDetachingJoinHandle; -use crate::util::io::{ReadWriter, write_file_atomic}; -use crate::util::serde::{ - BASE64, Base64, HandlerExtSerde, WithIoFormat, deserialize_from_str, display_serializable, - serialize_display, -}; -use crate::util::sync::Watch; - -pub const SYSTEMD_UNIT: &str = "tor@default"; -const STARTING_HEALTH_TIMEOUT: u64 = 120; // 2min - -const TOR_CONTROL: SocketAddr = - SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127, 0, 1, 1), 9051)); - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct OnionAddress(OnionAddressV3); -impl std::fmt::Display for OnionAddress { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.0) - } -} -impl FromStr for OnionAddress { - type Err = Error; - fn from_str(s: &str) -> Result { - Ok(Self( - s.strip_suffix(".onion") - .unwrap_or(s) - .rsplit(".") - .next() - .unwrap_or(s) - .parse::() - .with_kind(ErrorKind::Tor)?, - )) - } -} -impl Serialize for OnionAddress { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - serialize_display(self, serializer) - } -} -impl<'de> Deserialize<'de> for OnionAddress { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - deserialize_from_str(deserializer) - } -} -impl Ord for OnionAddress { - fn cmp(&self, other: &Self) -> std::cmp::Ordering { - self.0.get_raw_bytes().cmp(&other.0.get_raw_bytes()) - } -} -impl PartialOrd for OnionAddress { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct TorSecretKey(pub TorSecretKeyV3); -impl TorSecretKey { - pub fn onion_address(&self) -> OnionAddress { - OnionAddress(self.0.public().get_onion_address()) - } - pub fn from_bytes(bytes: [u8; 64]) -> Result { - Ok(Self(TorSecretKeyV3::from(bytes))) - } - pub fn generate() -> Self { - Self(TorSecretKeyV3::generate()) - } - pub fn is_valid(&self) -> bool { - let bytes = self.0.as_bytes()[..32].try_into().unwrap(); - curve25519_dalek::scalar::clamp_integer(bytes) == bytes - } -} -impl std::fmt::Display for TorSecretKey { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", BASE64.encode(self.0.as_bytes())) - } -} -impl FromStr for TorSecretKey { - type Err = Error; - fn from_str(s: &str) -> Result { - Self::from_bytes(Base64::<[u8; 64]>::from_str(s)?.0) - } -} -impl Serialize for TorSecretKey { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - serialize_display(self, serializer) - } -} -impl<'de> Deserialize<'de> for TorSecretKey { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - deserialize_from_str(deserializer) - } -} - -#[test] -fn test_generated_is_valid() { - for _ in 0..100 { - assert!(TorSecretKey::generate().is_valid()); - } -} - -#[test] -fn test_tor_key() { - // let key = crate::util::crypto::ed25519_expand_key( - // &hex::decode("c4b1a617bfdbcfb3f31e98c95542ce61718100e81cc6766eeebaa0dab42f0a93") - // .unwrap() - // .try_into() - // .unwrap(), - // ); - let key = - "4FpKpT4GZeEkUvH32AWMsndW+EG3XH46EmSFTh286G4AfG2U/Cc7y7L6k1dW5bl996QGDwe8gnaglq2hR2aD2w" - .parse::() - .unwrap(); - assert_eq!( - InternedString::from_display(&key.onion_address()), - InternedString::from("ja24lucrzgcusm72r2kmiujaa2g6b5o2w4wrwt5crfrhaz2qek5ozhqd.onion") - ); - eprintln!("{:?}", key.0.as_bytes()); - dbg!(key.to_string()); - dbg!(key.0.as_bytes()[0] & 0b111); - dbg!(key.onion_address()); - assert!(key.is_valid()); -} - -#[derive(Default, Deserialize, Serialize)] -pub struct OnionStore(BTreeMap); -impl Map for OnionStore { - type Key = OnionAddress; - type Value = TorSecretKey; - fn key_str(key: &Self::Key) -> Result, Error> { - Self::key_string(key) - } - fn key_string(key: &Self::Key) -> Result { - Ok(InternedString::from_display(key)) - } -} -impl OnionStore { - pub fn new() -> Self { - Self::default() - } - pub fn insert(&mut self, key: TorSecretKey) { - self.0.insert(key.onion_address(), key); - } -} -impl Model { - pub fn new_key(&mut self) -> Result { - let key = TorSecretKey::generate(); - self.insert_key(&key)?; - Ok(key) - } - pub fn insert_key(&mut self, key: &TorSecretKey) -> Result<(), Error> { - self.insert(&key.onion_address(), &key).map(|_| ()) - } - pub fn get_key(&self, address: &OnionAddress) -> Result { - self.as_idx(address) - .or_not_found(lazy_format!("private key for {address}"))? - .de() - } -} -impl std::fmt::Debug for OnionStore { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - struct OnionStoreMap<'a>(&'a BTreeMap); - impl<'a> std::fmt::Debug for OnionStoreMap<'a> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - #[derive(Debug)] - struct KeyFor(#[allow(unused)] OnionAddress); - let mut map = f.debug_map(); - for (k, v) in self.0 { - map.key(k); - map.value(&KeyFor(v.onion_address())); - } - map.finish() - } - } - f.debug_tuple("OnionStore") - .field(&OnionStoreMap(&self.0)) - .finish() - } -} - -enum ErrorLogSeverity { - Fatal { wipe_state: bool }, - Unknown { wipe_state: bool }, -} - -lazy_static! { - static ref LOG_REGEXES: Vec<(Regex, ErrorLogSeverity)> = vec![( - Regex::new("This could indicate a route manipulation attack, network overload, bad local network connectivity, or a bug\\.").unwrap(), - ErrorLogSeverity::Unknown { wipe_state: true } - ),( - Regex::new("died due to an invalid selected path").unwrap(), - ErrorLogSeverity::Fatal { wipe_state: false } - ),( - Regex::new("Tor has not observed any network activity for the past").unwrap(), - ErrorLogSeverity::Unknown { wipe_state: false } - )]; - static ref PROGRESS_REGEX: Regex = Regex::new("PROGRESS=([0-9]+)").unwrap(); -} - -pub fn tor_api() -> ParentHandler { - ParentHandler::new() - .subcommand( - "list-services", - from_fn_async(list_services) - .with_display_serializable() - .with_custom_display_fn(|handle, result| display_services(handle.params, result)) - .with_about("about.display-tor-v3-onion-addresses") - .with_call_remote::(), - ) - .subcommand("logs", logs().with_about("about.display-tor-logs")) - .subcommand( - "logs", - from_fn_async(crate::logs::cli_logs::) - .no_display() - .with_about("about.display-tor-logs"), - ) - .subcommand( - "reset", - from_fn_async(reset) - .no_display() - .with_about("about.reset-tor-daemon") - .with_call_remote::(), - ) - .subcommand( - "key", - key::().with_about("about.manage-onion-service-key-store"), - ) -} - -pub fn key() -> ParentHandler { - ParentHandler::new() - .subcommand( - "generate", - from_fn_async(generate_key) - .with_about("about.generate-onion-service-key-add-to-store") - .with_call_remote::(), - ) - .subcommand( - "add", - from_fn_async(add_key) - .with_about("about.add-onion-service-key-to-store") - .with_call_remote::(), - ) - .subcommand( - "list", - from_fn_async(list_keys) - .with_custom_display_fn(|_, res| { - for addr in res { - println!("{addr}"); - } - Ok(()) - }) - .with_about("about.list-onion-services-with-keys-in-store") - .with_call_remote::(), - ) -} - -pub async fn generate_key(ctx: RpcContext) -> Result { - ctx.db - .mutate(|db| { - Ok(db - .as_private_mut() - .as_key_store_mut() - .as_onion_mut() - .new_key()? - .onion_address()) - }) - .await - .result -} - -#[derive(Deserialize, Serialize, Parser)] -pub struct AddKeyParams { - #[arg(help = "help.arg.onion-secret-key")] - pub key: Base64<[u8; 64]>, -} - -pub async fn add_key( - ctx: RpcContext, - AddKeyParams { key }: AddKeyParams, -) -> Result { - let key = TorSecretKey::from_bytes(key.0)?; - ctx.db - .mutate(|db| { - db.as_private_mut() - .as_key_store_mut() - .as_onion_mut() - .insert_key(&key) - }) - .await - .result?; - Ok(key.onion_address()) -} - -pub async fn list_keys(ctx: RpcContext) -> Result, Error> { - ctx.db - .peek() - .await - .into_private() - .into_key_store() - .into_onion() - .keys() -} - -#[derive(Deserialize, Serialize, Parser, TS)] -#[serde(rename_all = "camelCase")] -#[command(rename_all = "kebab-case")] -pub struct ResetParams { - #[arg( - name = "wipe-state", - short = 'w', - long = "wipe-state", - help = "help.arg.wipe-tor-state" - )] - wipe_state: bool, - #[arg(help = "help.arg.reset-reason")] - reason: String, -} - -pub async fn reset( - ctx: RpcContext, - ResetParams { reason, wipe_state }: ResetParams, -) -> Result<(), Error> { - ctx.net_controller - .tor - .reset(wipe_state, Error::new(eyre!("{reason}"), ErrorKind::Tor)) - .await -} - -pub fn display_services( - params: WithIoFormat, - services: Vec, -) -> Result<(), Error> { - use prettytable::*; - - if let Some(format) = params.format { - return display_serializable(format, services); - } - - let mut table = Table::new(); - for service in services { - let row = row![&service.to_string()]; - table.add_row(row); - } - table.print_tty(false)?; - Ok(()) -} - -pub async fn list_services(ctx: RpcContext, _: Empty) -> Result, Error> { - ctx.net_controller.tor.list_services().await -} - -pub fn logs() -> ParentHandler { - crate::logs::logs::(|_: &RpcContext, _| async { - Ok(LogSource::Unit(SYSTEMD_UNIT)) - }) -} - -fn event_handler(_event: AsyncEvent<'static>) -> BoxFuture<'static, Result<(), ConnError>> { - async move { Ok(()) }.boxed() -} - -#[derive(Clone)] -pub struct TorController(Arc); -impl TorController { - const TOR_SOCKS: &[SocketAddr] = &[ - SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 9050)), - SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(10, 0, 3, 1), 9050)), - ]; - - pub fn new() -> Result { - Ok(TorController(Arc::new(TorControl::new( - TOR_CONTROL, - Self::TOR_SOCKS, - )))) - } - - pub fn service(&self, key: TorSecretKey) -> Result { - Ok(TorService { - services: self.0.services.clone(), - key, - }) - } - - pub async fn gc(&self, addr: Option) -> Result<(), Error> { - self.0.services.send_if_modified(|services| { - let mut changed = false; - let mut gc = |bindings: &mut OrdMap>>| { - ordmap_retain(bindings, |_, targets| { - let start_len = targets.len(); - targets.retain(|_, rc| rc.strong_count() > 0); - changed |= start_len != targets.len(); - !targets.is_empty() - }); - if bindings.is_empty() { - changed = true; - false - } else { - true - } - }; - if let Some(addr) = addr { - if !if let Some((_, bindings, needs_sync)) = services.get_mut(&addr) { - let keep = gc(bindings); - if !keep { - *needs_sync = Some(SyncState::Remove); - } - keep - } else { - true - } { - services.remove(&addr); - } - } else { - services.retain(|_, (_, bindings, _)| gc(bindings)); - } - changed - }); - Ok(()) - } - - pub async fn reset(&self, wipe_state: bool, context: Error) -> Result<(), Error> { - self.0 - .send - .send(TorCommand::Reset { - wipe_state, - context, - }) - .ok() - .ok_or_else(|| Error::new(eyre!("TorControl died"), ErrorKind::Tor)) - } - - pub async fn list_services(&self) -> Result, Error> { - let (reply, res) = oneshot::channel(); - self.0 - .send - .send(TorCommand::GetInfo { - query: "onions/current".into(), - reply, - }) - .ok() - .ok_or_else(|| Error::new(eyre!("TorControl died"), ErrorKind::Tor))?; - res.await - .ok() - .ok_or_else(|| Error::new(eyre!("TorControl died"), ErrorKind::Tor))?? - .lines() - .map(|l| l.trim()) - .filter(|l| !l.is_empty()) - .map(|l| l.parse::().with_kind(ErrorKind::Tor)) - .collect() - } - - pub async fn connect_onion( - &self, - addr: &OnionAddress, - port: u16, - ) -> Result, Error> { - if let Some(target) = self.0.services.peek(|s| { - s.get(addr).and_then(|(_, bindings, _)| { - bindings.get(&port).and_then(|b| { - b.iter() - .find(|(_, rc)| rc.strong_count() > 0) - .map(|(a, _)| *a) - }) - }) - }) { - tracing::debug!("Resolving {addr} internally to {target}"); - let tcp_stream = TcpStream::connect(target) - .await - .with_kind(ErrorKind::Network)?; - if let Err(e) = socket2::SockRef::from(&tcp_stream).set_keepalive(true) { - tracing::error!("Failed to set tcp keepalive: {e}"); - tracing::debug!("{e:?}"); - } - Ok(Box::new(tcp_stream)) - } else { - let mut stream = TcpStream::connect(Self::TOR_SOCKS[0]) - .await - .with_kind(ErrorKind::Tor)?; - if let Err(e) = socket2::SockRef::from(&stream).set_keepalive(true) { - tracing::error!("Failed to set tcp keepalive: {e}"); - tracing::debug!("{e:?}"); - } - socks5_impl::client::connect(&mut stream, (addr.to_string(), port), None) - .await - .with_kind(ErrorKind::Tor)?; - Ok(Box::new(stream)) - } - } -} - -#[derive(Clone, Copy, PartialEq, Eq)] -enum SyncState { - Add, - Update, - Remove, -} - -pub struct TorService { - services: Watch< - BTreeMap< - OnionAddress, - ( - TorSecretKey, - OrdMap>>, - Option, - ), - >, - >, - key: TorSecretKey, -} - -impl TorService { - pub fn proxy_all>>( - &self, - bindings: impl IntoIterator, - ) -> Rcs { - self.services.send_modify(|services| { - let (_, entry, needs_sync) = services - .entry(self.key.onion_address()) - .or_insert_with(|| (self.key.clone(), OrdMap::new(), Some(SyncState::Add))); - let rcs = bindings - .into_iter() - .map(|(external, target)| { - let binding = entry.entry(external).or_default(); - let target = binding.entry(target).or_default(); - let rc = if let Some(rc) = Weak::upgrade(&*target) { - rc - } else { - if needs_sync.is_none() { - *needs_sync = Some(SyncState::Update); - } - Arc::new(()) - }; - *target = Arc::downgrade(&rc); - rc - }) - .collect(); - - rcs - }) - } -} - -type AuthenticatedConnection = AuthenticatedConn< - TcpStream, - Box) -> BoxFuture<'static, Result<(), ConnError>> + Send + Sync>, ->; - -enum TorCommand { - GetInfo { - query: String, - reply: oneshot::Sender>, - }, - Reset { - wipe_state: bool, - context: Error, - }, -} - -#[instrument(skip_all)] -async fn torctl( - tor_control: SocketAddr, - tor_socks: &[SocketAddr], - recv: &mut mpsc::UnboundedReceiver, - services: &mut Watch< - BTreeMap< - OnionAddress, - ( - TorSecretKey, - OrdMap>>, - Option, - ), - >, - >, - wipe_state: &AtomicBool, - health_timeout: &mut Duration, -) -> Result<(), Error> { - let bootstrap = async { - if Command::new("systemctl") - .arg("is-active") - .arg("--quiet") - .arg("tor") - .invoke(ErrorKind::Tor) - .await - .is_ok() - { - Command::new("systemctl") - .arg("stop") - .arg("tor") - .invoke(ErrorKind::Tor) - .await?; - for _ in 0..30 { - if TcpStream::connect(tor_control).await.is_err() { - break; - } - tokio::time::sleep(Duration::from_secs(1)).await; - } - if TcpStream::connect(tor_control).await.is_ok() { - return Err(Error::new( - eyre!("Tor is failing to shut down"), - ErrorKind::Tor, - )); - } - } - if wipe_state.load(std::sync::atomic::Ordering::SeqCst) { - tokio::fs::remove_dir_all("/var/lib/tor").await?; - wipe_state.store(false, std::sync::atomic::Ordering::SeqCst); - } - - write_file_atomic("/etc/tor/torrc", { - use std::fmt::Write; - let mut conf = String::new(); - - for tor_socks in tor_socks { - writeln!(&mut conf, "SocksPort {tor_socks}").unwrap(); - } - writeln!( - &mut conf, - "ControlPort {tor_control}\nCookieAuthentication 1" - ) - .unwrap(); - conf - }) - .await?; - tokio::fs::create_dir_all("/var/lib/tor").await?; - Command::new("chown") - .arg("-R") - .arg("debian-tor") - .arg("/var/lib/tor") - .invoke(ErrorKind::Filesystem) - .await?; - Command::new("systemctl") - .arg("start") - .arg("tor") - .invoke(ErrorKind::Tor) - .await?; - - let mut tcp_stream = None; - for _ in 0..60 { - if let Ok(conn) = TcpStream::connect(tor_control).await { - tcp_stream = Some(conn); - break; - } - tokio::time::sleep(Duration::from_secs(1)).await; - } - let tcp_stream = tcp_stream.ok_or_else(|| { - Error::new(eyre!("Timed out waiting for tor to start"), ErrorKind::Tor) - })?; - tracing::info!("Tor is started"); - - if let Err(e) = socket2::SockRef::from(&tcp_stream).set_keepalive(true) { - tracing::error!("Failed to set tcp keepalive: {e}"); - tracing::debug!("{e:?}"); - } - - let mut conn = torut::control::UnauthenticatedConn::new(tcp_stream); - let auth = conn - .load_protocol_info() - .await? - .make_auth_data()? - .ok_or_else(|| eyre!("Cookie Auth Not Available")) - .with_kind(crate::ErrorKind::Tor)?; - conn.authenticate(&auth).await?; - let mut connection: AuthenticatedConnection = conn.into_authenticated().await; - connection.set_async_event_handler(Some(Box::new(|event| event_handler(event)))); - - let mut bootstrapped = false; - let mut last_increment = (String::new(), Instant::now()); - for _ in 0..300 { - match connection.get_info("status/bootstrap-phase").await { - Ok(a) => { - if a.contains("TAG=done") { - bootstrapped = true; - break; - } - if let Some(p) = PROGRESS_REGEX.captures(&a) { - if let Some(p) = p.get(1) { - if p.as_str() != &*last_increment.0 { - last_increment = (p.as_str().into(), Instant::now()); - } - } - } - } - Err(e) => { - let e = Error::from(e); - tracing::error!("{}", e); - tracing::debug!("{:?}", e); - } - } - if last_increment.1.elapsed() > Duration::from_secs(30) { - return Err(Error::new( - eyre!("Tor stuck bootstrapping at {}%", last_increment.0), - ErrorKind::Tor, - )); - } - tokio::time::sleep(Duration::from_secs(1)).await; - } - if !bootstrapped { - return Err(Error::new( - eyre!("Timed out waiting for tor to bootstrap"), - ErrorKind::Tor, - )); - } - Ok(connection) - }; - let pre_handler = async { - while let Some(command) = recv.recv().await { - match command { - TorCommand::GetInfo { reply, .. } => { - reply - .send(Err(Error::new( - eyre!("Tor has not finished bootstrapping..."), - ErrorKind::Tor, - ))) - .unwrap_or_default(); - } - TorCommand::Reset { - wipe_state: new_wipe_state, - context, - } => { - wipe_state.fetch_or(new_wipe_state, std::sync::atomic::Ordering::SeqCst); - return Err(context); - } - } - } - Ok(()) - }; - - let mut connection = tokio::select! { - res = bootstrap => res?, - res = pre_handler => return res, - }; - - let hck_key = TorSecretKeyV3::generate(); - connection - .add_onion_v3( - &hck_key, - false, - false, - false, - None, - &mut [(80, SocketAddr::from(([127, 0, 0, 1], 80)))].iter(), - ) - .await?; - - services.send_modify(|s| { - for (_, _, s) in s.values_mut() { - *s = Some(SyncState::Add); - } - }); - - let handler = async { - loop { - let recv = recv.recv(); - tokio::pin!(recv); - let mut changed = services.changed().boxed(); - - match futures::future::select(recv, &mut changed).await { - futures::future::Either::Left((Some(command), _)) => match command { - TorCommand::GetInfo { query, reply } => { - reply - .send(connection.get_info(&query).await.with_kind(ErrorKind::Tor)) - .unwrap_or_default(); - } - TorCommand::Reset { - wipe_state: new_wipe_state, - context, - } => { - wipe_state.fetch_or(new_wipe_state, std::sync::atomic::Ordering::SeqCst); - return Err(context); - } - }, - futures::future::Either::Left((None, _)) => break, - futures::future::Either::Right(_) => { - drop(changed); - let to_add = services.peek_and_mark_seen(|services| { - services - .iter() - .filter(|(_, (_, _, s))| s.is_some()) - .map(|(k, v)| (k.clone(), (*v).clone())) - .collect::>() - }); - - for (addr, (key, bindings, state)) in &to_add { - if matches!(state, Some(SyncState::Update) | Some(SyncState::Remove)) { - connection - .del_onion(&addr.0.get_address_without_dot_onion()) - .await - .with_kind(ErrorKind::Tor)?; - } - let bindings = bindings - .iter() - .filter_map(|(external, targets)| { - targets - .iter() - .find(|(_, rc)| rc.strong_count() > 0) - .map(|(target, _)| (*external, *target)) - }) - .collect::>(); - if !bindings.is_empty() { - connection - .add_onion_v3( - &key.0, - false, - false, - false, - None, - &mut bindings.iter(), - ) - .await?; - } - } - services.send_if_modified(|services| { - for (addr, (_, bindings_a, _)) in to_add { - if let Some((_, bindings_b, needs_sync)) = services.get_mut(&addr) { - if OrdMap::ptr_eq(&bindings_a, bindings_b) - || bindings_a.len() == bindings_b.len() - && bindings_a.iter().zip(bindings_b.iter()).all( - |((a_port, a), (b_port, b))| { - a_port == b_port - && a.len() == b.len() - && a.keys().zip(b.keys()).all(|(a, b)| a == b) - }, - ) - { - *needs_sync = None; - } else { - *needs_sync = Some(SyncState::Update); - } - } - } - false - }); - } - } - } - - Ok(()) - }; - let log_parser = async { - loop { - let mut logs = journalctl( - LogSource::Unit(SYSTEMD_UNIT), - Some(0), - None, - Some("0"), - false, - true, - ) - .await?; - while let Some(log) = logs.try_next().await? { - for (regex, severity) in &*LOG_REGEXES { - if regex.is_match(&log.message) { - let (check, wipe_state) = match severity { - ErrorLogSeverity::Fatal { wipe_state } => (false, *wipe_state), - ErrorLogSeverity::Unknown { wipe_state } => (true, *wipe_state), - }; - let addr = hck_key.public().get_onion_address().to_string(); - if !check - || TcpStream::connect(tor_socks) - .map_err(|e| Error::new(e, ErrorKind::Tor)) - .and_then(|mut tor_socks| async move { - tokio::time::timeout( - Duration::from_secs(30), - socks5_impl::client::connect( - &mut tor_socks, - (addr, 80), - None, - ) - .map_err(|e| Error::new(e, ErrorKind::Tor)), - ) - .map_err(|e| Error::new(e, ErrorKind::Tor)) - .await? - }) - .await - .with_ctx(|_| (ErrorKind::Tor, "Tor is confirmed to be down")) - .log_err() - .is_some() - { - if wipe_state { - Command::new("systemctl") - .arg("stop") - .arg("tor") - .invoke(ErrorKind::Tor) - .await?; - tokio::fs::remove_dir_all("/var/lib/tor").await?; - } - return Err(Error::new(eyre!("{}", log.message), ErrorKind::Tor)); - } - } - } - } - } - }; - let health_checker = async { - let mut last_success = Instant::now(); - loop { - tokio::time::sleep(Duration::from_secs(30)).await; - let addr = hck_key.public().get_onion_address().to_string(); - if TcpStream::connect(tor_socks) - .map_err(|e| Error::new(e, ErrorKind::Tor)) - .and_then(|mut tor_socks| async move { - tokio::time::timeout( - Duration::from_secs(30), - socks5_impl::client::connect(&mut tor_socks, (addr, 80), None) - .map_err(|e| Error::new(e, ErrorKind::Tor)), - ) - .map_err(|e| Error::new(e, ErrorKind::Tor)) - .await - }) - .await - .is_err() - { - if last_success.elapsed() > *health_timeout { - let err = Error::new( - eyre!( - "Tor health check failed for longer than current timeout ({health_timeout:?})" - ), - crate::ErrorKind::Tor, - ); - *health_timeout *= 2; - wipe_state.store(true, std::sync::atomic::Ordering::SeqCst); - return Err(err); - } - } else { - last_success = Instant::now(); - } - } - }; - - tokio::select! { - res = handler => res?, - res = log_parser => res?, - res = health_checker => res?, - } - - Ok(()) -} - -struct TorControl { - _thread: NonDetachingJoinHandle<()>, - send: mpsc::UnboundedSender, - services: Watch< - BTreeMap< - OnionAddress, - ( - TorSecretKey, - OrdMap>>, - Option, - ), - >, - >, -} -impl TorControl { - pub fn new( - tor_control: SocketAddr, - tor_socks: impl AsRef<[SocketAddr]> + Send + 'static, - ) -> Self { - let (send, mut recv) = mpsc::unbounded_channel(); - let services = Watch::new(BTreeMap::new()); - let mut thread_services = services.clone(); - Self { - _thread: tokio::spawn(async move { - let wipe_state = AtomicBool::new(false); - let mut health_timeout = Duration::from_secs(STARTING_HEALTH_TIMEOUT); - loop { - if let Err(e) = torctl( - tor_control, - tor_socks.as_ref(), - &mut recv, - &mut thread_services, - &wipe_state, - &mut health_timeout, - ) - .await - { - tracing::error!("TorControl : {e}"); - tracing::debug!("{e:?}"); - } - tracing::info!("Restarting Tor"); - tokio::time::sleep(Duration::from_secs(1)).await; - } - }) - .into(), - send, - services, - } - } -} - -#[tokio::test] -#[ignore] -async fn test_connection() { - let mut conn = torut::control::UnauthenticatedConn::new( - TcpStream::connect(SocketAddr::from(([127, 0, 0, 1], 9051))) - .await - .unwrap(), - ); - let auth = conn - .load_protocol_info() - .await - .unwrap() - .make_auth_data() - .unwrap() - .ok_or_else(|| eyre!("Cookie Auth Not Available")) - .with_kind(crate::ErrorKind::Tor) - .unwrap(); - conn.authenticate(&auth).await.unwrap(); - let mut connection: AuthenticatedConn< - TcpStream, - fn(AsyncEvent<'static>) -> BoxFuture<'static, Result<(), ConnError>>, - > = conn.into_authenticated().await; - let tor_key = torut::onion::TorSecretKeyV3::generate(); - connection.get_conf("SocksPort").await.unwrap(); - connection - .add_onion_v3( - &tor_key, - false, - false, - false, - None, - &mut [(443_u16, SocketAddr::from(([127, 0, 0, 1], 8443)))].iter(), - ) - .await - .unwrap(); - connection - .del_onion( - &tor_key - .public() - .get_onion_address() - .get_address_without_dot_onion(), - ) - .await - .unwrap(); - connection - .add_onion_v3( - &tor_key, - false, - false, - false, - None, - &mut [(8443_u16, SocketAddr::from(([127, 0, 0, 1], 8443)))].iter(), - ) - .await - .unwrap(); -} diff --git a/core/src/net/tor/mod.rs b/core/src/net/tor/mod.rs deleted file mode 100644 index d4d5c8007..000000000 --- a/core/src/net/tor/mod.rs +++ /dev/null @@ -1,10 +0,0 @@ -#[cfg(feature = "arti")] -mod arti; - -#[cfg(not(feature = "arti"))] -mod ctor; - -#[cfg(feature = "arti")] -pub use arti::{OnionAddress, OnionStore, TorController, TorSecretKey, tor_api}; -#[cfg(not(feature = "arti"))] -pub use ctor::{OnionAddress, OnionStore, TorController, TorSecretKey, tor_api}; diff --git a/core/src/net/tunnel.rs b/core/src/net/tunnel.rs index 821db72eb..deb7ec6a3 100644 --- a/core/src/net/tunnel.rs +++ b/core/src/net/tunnel.rs @@ -195,8 +195,12 @@ pub async fn remove_tunnel( let host = host?; host.as_bindings_mut().mutate(|b| { Ok(b.values_mut().for_each(|v| { - v.net.private_disabled.remove(&id); - v.net.public_enabled.remove(&id); + v.addresses + .private_disabled + .retain(|h| h.gateway.id != id); + v.addresses + .public_enabled + .retain(|h| h.gateway.id != id); })) })?; } diff --git a/core/src/net/vhost.rs b/core/src/net/vhost.rs index 4996ca937..9023576c3 100644 --- a/core/src/net/vhost.rs +++ b/core/src/net/vhost.rs @@ -1,19 +1,19 @@ use std::any::Any; use std::collections::{BTreeMap, BTreeSet}; use std::fmt; -use std::net::{IpAddr, SocketAddr}; +use std::net::{IpAddr, SocketAddr, SocketAddrV6}; use std::sync::{Arc, Weak}; use std::task::{Poll, ready}; -use std::time::Duration; use async_acme::acme::ACME_TLS_ALPN_NAME; use color_eyre::eyre::eyre; use futures::FutureExt; use futures::future::BoxFuture; +use imbl::OrdMap; use imbl_value::{InOMap, InternedString}; use rpc_toolkit::{Context, HandlerArgs, HandlerExt, ParentHandler, from_fn}; use serde::{Deserialize, Serialize}; -use tokio::net::TcpStream; +use tokio::net::{TcpListener, TcpStream}; use tokio_rustls::TlsConnector; use tokio_rustls::rustls::crypto::CryptoProvider; use tokio_rustls::rustls::pki_types::ServerName; @@ -23,28 +23,28 @@ use tracing::instrument; use ts_rs::TS; use visit_rs::Visit; -use crate::ResultExt; use crate::context::{CliContext, RpcContext}; use crate::db::model::Database; -use crate::db::model::public::AcmeSettings; +use crate::db::model::public::{AcmeSettings, NetworkInterfaceInfo}; use crate::db::{DbAccessByKey, DbAccessMut}; use crate::net::acme::{ AcmeCertStore, AcmeProvider, AcmeTlsAlpnCache, AcmeTlsHandler, GetAcmeProvider, }; use crate::net::gateway::{ - AnyFilter, BindTcp, DynInterfaceFilter, GatewayInfo, InterfaceFilter, - NetworkInterfaceController, NetworkInterfaceListener, + GatewayInfo, NetworkInterfaceController, NetworkInterfaceListenerAcceptMetadata, }; use crate::net::ssl::{CertStore, RootCaTlsHandler}; use crate::net::tls::{ ChainedHandler, TlsHandlerWrapper, TlsListener, TlsMetadata, WrapTlsHandler, }; +use crate::net::utils::ipv6_is_link_local; use crate::net::web_server::{Accept, AcceptStream, ExtractVisitor, TcpMetadata, extract}; use crate::prelude::*; use crate::util::collections::EqSet; use crate::util::future::{NonDetachingJoinHandle, WeakFuture}; use crate::util::serde::{HandlerExtSerde, MaybeUtf8String, display_serializable}; use crate::util::sync::{SyncMutex, Watch}; +use crate::{GatewayId, ResultExt}; pub fn vhost_api() -> ParentHandler { ParentHandler::new().subcommand( @@ -93,7 +93,7 @@ pub struct VHostController { interfaces: Arc, crypto_provider: Arc, acme_cache: AcmeTlsAlpnCache, - servers: SyncMutex>>, + servers: SyncMutex>>, } impl VHostController { pub fn new( @@ -114,14 +114,22 @@ impl VHostController { &self, hostname: Option, external: u16, - target: DynVHostTarget, + target: DynVHostTarget, ) -> Result, Error> { self.servers.mutate(|writable| { let server = if let Some(server) = writable.remove(&external) { server } else { + let bind_reqs = Watch::new(VHostBindRequirements::default()); + let listener = VHostBindListener { + ip_info: self.interfaces.watcher.subscribe(), + port: external, + bind_reqs: bind_reqs.clone_unseen(), + listeners: BTreeMap::new(), + }; VHostServer::new( - self.interfaces.watcher.bind(BindTcp, external)?, + listener, + bind_reqs, self.db.clone(), self.crypto_provider.clone(), self.acme_cache.clone(), @@ -173,6 +181,143 @@ impl VHostController { } } +/// Union of all ProxyTargets' bind requirements for a VHostServer. +#[derive(Debug, Clone, Default, PartialEq, Eq)] +pub struct VHostBindRequirements { + pub public_gateways: BTreeSet, + pub private_ips: BTreeSet, +} + +fn compute_bind_reqs(mapping: &Mapping) -> VHostBindRequirements { + let mut reqs = VHostBindRequirements::default(); + for (_, targets) in mapping { + for (target, rc) in targets { + if rc.strong_count() > 0 { + let (pub_gw, priv_ip) = target.0.bind_requirements(); + reqs.public_gateways.extend(pub_gw); + reqs.private_ips.extend(priv_ip); + } + } + } + reqs +} + +/// Listener that manages its own TCP listeners with IP-level precision. +/// Binds ALL IPs of public gateways and ONLY matching private IPs. +pub struct VHostBindListener { + ip_info: Watch>, + port: u16, + bind_reqs: Watch, + listeners: BTreeMap, +} + +fn update_vhost_listeners( + listeners: &mut BTreeMap, + port: u16, + ip_info: &OrdMap, + reqs: &VHostBindRequirements, +) -> Result<(), Error> { + let mut keep = BTreeSet::::new(); + for (gw_id, info) in ip_info { + if let Some(ip_info) = &info.ip_info { + for ipnet in &ip_info.subnets { + let ip = ipnet.addr(); + let should_bind = + reqs.public_gateways.contains(gw_id) || reqs.private_ips.contains(&ip); + if should_bind { + let addr = match ip { + IpAddr::V6(ip6) => SocketAddrV6::new( + ip6, + port, + 0, + if ipv6_is_link_local(ip6) { + ip_info.scope_id + } else { + 0 + }, + ) + .into(), + ip => SocketAddr::new(ip, port), + }; + keep.insert(addr); + if let Some((_, existing_info)) = listeners.get_mut(&addr) { + *existing_info = GatewayInfo { + id: gw_id.clone(), + info: info.clone(), + }; + } else { + let tcp = TcpListener::from_std( + mio::net::TcpListener::bind(addr) + .with_kind(ErrorKind::Network)? + .into(), + ) + .with_kind(ErrorKind::Network)?; + listeners.insert( + addr, + ( + tcp, + GatewayInfo { + id: gw_id.clone(), + info: info.clone(), + }, + ), + ); + } + } + } + } + } + listeners.retain(|key, _| keep.contains(key)); + Ok(()) +} + +impl Accept for VHostBindListener { + type Metadata = NetworkInterfaceListenerAcceptMetadata; + fn poll_accept( + &mut self, + cx: &mut std::task::Context<'_>, + ) -> Poll> { + // Update listeners when ip_info or bind_reqs change + while self.ip_info.poll_changed(cx).is_ready() + || self.bind_reqs.poll_changed(cx).is_ready() + { + let reqs = self.bind_reqs.read_and_mark_seen(); + let listeners = &mut self.listeners; + let port = self.port; + self.ip_info.peek_and_mark_seen(|ip_info| { + update_vhost_listeners(listeners, port, ip_info, &reqs) + })?; + } + + // Poll each listener for incoming connections + for (&addr, (listener, gw_info)) in &self.listeners { + match listener.poll_accept(cx) { + Poll::Ready(Ok((stream, peer_addr))) => { + if let Err(e) = socket2::SockRef::from(&stream).set_keepalive(true) { + tracing::error!("Failed to set tcp keepalive: {e}"); + tracing::debug!("{e:?}"); + } + return Poll::Ready(Ok(( + NetworkInterfaceListenerAcceptMetadata { + inner: TcpMetadata { + local_addr: addr, + peer_addr, + }, + info: gw_info.clone(), + }, + Box::pin(stream), + ))); + } + Poll::Ready(Err(e)) => { + tracing::trace!("VHostBindListener accept error on {addr}: {e}"); + } + Poll::Pending => {} + } + } + Poll::Pending + } +} + pub trait VHostTarget: std::fmt::Debug + Eq { type PreprocessRes: Send + 'static; #[allow(unused_variables)] @@ -182,6 +327,10 @@ pub trait VHostTarget: std::fmt::Debug + Eq { fn acme(&self) -> Option<&AcmeProvider> { None } + /// Returns (public_gateways, private_ips) this target needs the listener to bind on. + fn bind_requirements(&self) -> (BTreeSet, BTreeSet) { + (BTreeSet::new(), BTreeSet::new()) + } fn preprocess<'a>( &'a self, prev: ServerConfig, @@ -200,6 +349,7 @@ pub trait VHostTarget: std::fmt::Debug + Eq { pub trait DynVHostTargetT: std::fmt::Debug + Any { fn filter(&self, metadata: &::Metadata) -> bool; fn acme(&self) -> Option<&AcmeProvider>; + fn bind_requirements(&self) -> (BTreeSet, BTreeSet); fn preprocess<'a>( &'a self, prev: ServerConfig, @@ -224,6 +374,9 @@ impl + 'static> DynVHostTargetT for T { fn acme(&self) -> Option<&AcmeProvider> { VHostTarget::acme(self) } + fn bind_requirements(&self) -> (BTreeSet, BTreeSet) { + VHostTarget::bind_requirements(self) + } fn preprocess<'a>( &'a self, prev: ServerConfig, @@ -301,7 +454,8 @@ impl Preprocessed { #[derive(Clone)] pub struct ProxyTarget { - pub filter: DynInterfaceFilter, + pub public: BTreeSet, + pub private: BTreeSet, pub acme: Option, pub addr: SocketAddr, pub add_x_forwarded_headers: bool, @@ -309,7 +463,8 @@ pub struct ProxyTarget { } impl PartialEq for ProxyTarget { fn eq(&self, other: &Self) -> bool { - self.filter == other.filter + self.public == other.public + && self.private == other.private && self.acme == other.acme && self.addr == other.addr && self.connect_ssl.as_ref().map(Arc::as_ptr) @@ -320,7 +475,8 @@ impl Eq for ProxyTarget {} impl fmt::Debug for ProxyTarget { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("ProxyTarget") - .field("filter", &self.filter) + .field("public", &self.public) + .field("private", &self.private) .field("acme", &self.acme) .field("addr", &self.addr) .field("add_x_forwarded_headers", &self.add_x_forwarded_headers) @@ -340,16 +496,37 @@ where { type PreprocessRes = AcceptStream; fn filter(&self, metadata: &::Metadata) -> bool { - let info = extract::(metadata); - if info.is_none() { - tracing::warn!("No GatewayInfo on metadata"); + let gw = extract::(metadata); + let tcp = extract::(metadata); + let (Some(gw), Some(tcp)) = (gw, tcp) else { + return false; + }; + let Some(ip_info) = &gw.info.ip_info else { + return false; + }; + + let src = tcp.peer_addr.ip(); + // Public if: source is a gateway/router IP (NAT'd internet), + // or source is outside all known subnets (direct internet) + let is_public = ip_info.lan_ip.contains(&src) + || !ip_info.subnets.iter().any(|s| s.contains(&src)); + + if is_public { + self.public.contains(&gw.id) + } else { + // Private: accept if connection arrived on an interface with a matching IP + ip_info + .subnets + .iter() + .any(|s| self.private.contains(&s.addr())) } - info.as_ref() - .map_or(true, |i| self.filter.filter(&i.id, &i.info)) } fn acme(&self) -> Option<&AcmeProvider> { self.acme.as_ref() } + fn bind_requirements(&self) -> (BTreeSet, BTreeSet) { + (self.public.clone(), self.private.clone()) + } async fn preprocess<'a>( &'a self, mut prev: ServerConfig, @@ -634,28 +811,15 @@ where struct VHostServer { mapping: Watch>, + bind_reqs: Watch, _thread: NonDetachingJoinHandle<()>, } -impl<'a> From<&'a BTreeMap, BTreeMap>>> for AnyFilter { - fn from(value: &'a BTreeMap, BTreeMap>>) -> Self { - Self( - value - .iter() - .flat_map(|(_, v)| { - v.iter() - .filter(|(_, r)| r.strong_count() > 0) - .map(|(t, _)| t.filter.clone()) - }) - .collect(), - ) - } -} - impl VHostServer { #[instrument(skip_all)] fn new( listener: A, + bind_reqs: Watch, db: TypedPatchDb, crypto_provider: Arc, acme_cache: AcmeTlsAlpnCache, @@ -679,6 +843,7 @@ impl VHostServer { let mapping = Watch::new(BTreeMap::new()); Self { mapping: mapping.clone(), + bind_reqs, _thread: tokio::spawn(async move { let mut listener = VHostListener(TlsListener::new( listener, @@ -729,6 +894,9 @@ impl VHostServer { targets.insert(target, Arc::downgrade(&rc)); writable.insert(hostname, targets); res = Ok(rc); + if changed { + self.update_bind_reqs(writable); + } changed }); if self.mapping.watcher_count() > 1 { @@ -752,9 +920,23 @@ impl VHostServer { if !targets.is_empty() { writable.insert(hostname, targets); } + if pre != post { + self.update_bind_reqs(writable); + } pre == post }); } + fn update_bind_reqs(&self, mapping: &Mapping) { + let new_reqs = compute_bind_reqs(mapping); + self.bind_reqs.send_if_modified(|reqs| { + if *reqs != new_reqs { + *reqs = new_reqs; + true + } else { + false + } + }); + } fn is_empty(&self) -> bool { self.mapping.peek(|m| m.is_empty()) } diff --git a/core/src/net/web_server.rs b/core/src/net/web_server.rs index 2ac5b035f..8ffe9deaa 100644 --- a/core/src/net/web_server.rs +++ b/core/src/net/web_server.rs @@ -366,28 +366,6 @@ where pub struct WebServerAcceptorSetter { acceptor: Watch, } -impl WebServerAcceptorSetter>> -where - A: Accept, - B: Accept, -{ - pub fn try_upgrade Result>(&self, f: F) -> Result<(), Error> { - let mut res = Ok(()); - self.acceptor.send_modify(|a| { - *a = match a.take() { - Some(Either::Left(a)) => match f(a) { - Ok(b) => Some(Either::Right(b)), - Err(e) => { - res = Err(e); - None - } - }, - x => x, - } - }); - res - } -} impl Deref for WebServerAcceptorSetter { type Target = Watch; fn deref(&self) -> &Self::Target { diff --git a/core/src/service/effects/net/ssl.rs b/core/src/service/effects/net/ssl.rs index fcd1dac9d..7de40aed6 100644 --- a/core/src/service/effects/net/ssl.rs +++ b/core/src/service/effects/net/ssl.rs @@ -55,20 +55,18 @@ pub async fn get_ssl_certificate( .map(|(_, m)| m.as_hosts().as_entries()) .flatten_ok() .map_ok(|(_, m)| { - Ok(m.as_onions() - .de()? - .iter() - .map(InternedString::from_display) - .chain(m.as_public_domains().keys()?) + Ok(m.as_public_domains() + .keys()? + .into_iter() .chain(m.as_private_domains().de()?) .chain( - m.as_hostname_info() + m.as_bindings() .de()? .values() - .flatten() + .flat_map(|b| b.addresses.possible.iter().cloned()) .map(|h| h.to_san_hostname()), ) - .collect::>()) + .collect::>()) }) .map(|a| a.and_then(|a| a)) .flatten_ok() @@ -181,20 +179,18 @@ pub async fn get_ssl_key( .map(|m| m.as_hosts().as_entries()) .flatten_ok() .map_ok(|(_, m)| { - Ok(m.as_onions() - .de()? - .iter() - .map(InternedString::from_display) - .chain(m.as_public_domains().keys()?) + Ok(m.as_public_domains() + .keys()? + .into_iter() .chain(m.as_private_domains().de()?) .chain( - m.as_hostname_info() + m.as_bindings() .de()? .values() - .flatten() + .flat_map(|b| b.addresses.possible.iter().cloned()) .map(|h| h.to_san_hostname()), ) - .collect::>()) + .collect::>()) }) .map(|a| a.and_then(|a| a)) .flatten_ok() diff --git a/core/src/tunnel/api.rs b/core/src/tunnel/api.rs index b8f5fd693..47f9a33e3 100644 --- a/core/src/tunnel/api.rs +++ b/core/src/tunnel/api.rs @@ -459,7 +459,7 @@ pub async fn add_forward( }) .map(|s| s.prefix_len()) .unwrap_or(32); - let rc = ctx.forward.add_forward(source, target, prefix).await?; + let rc = ctx.forward.add_forward(source, target, prefix, None).await?; ctx.active_forwards.mutate(|m| { m.insert(source, rc); }); diff --git a/core/src/tunnel/context.rs b/core/src/tunnel/context.rs index 5afac62ab..ac56eaa36 100644 --- a/core/src/tunnel/context.rs +++ b/core/src/tunnel/context.rs @@ -199,7 +199,7 @@ impl TunnelContext { }) .map(|s| s.prefix_len()) .unwrap_or(32); - active_forwards.insert(from, forward.add_forward(from, to, prefix).await?); + active_forwards.insert(from, forward.add_forward(from, to, prefix, None).await?); } Ok(Self(Arc::new(TunnelContextSeed { diff --git a/core/src/version/mod.rs b/core/src/version/mod.rs index 5c2a2a2f3..a470212f5 100644 --- a/core/src/version/mod.rs +++ b/core/src/version/mod.rs @@ -59,8 +59,9 @@ mod v0_4_0_alpha_16; mod v0_4_0_alpha_17; mod v0_4_0_alpha_18; mod v0_4_0_alpha_19; +mod v0_4_0_alpha_20; -pub type Current = v0_4_0_alpha_19::Version; // VERSION_BUMP +pub type Current = v0_4_0_alpha_20::Version; // VERSION_BUMP impl Current { #[instrument(skip(self, db))] @@ -181,7 +182,8 @@ enum Version { V0_4_0_alpha_16(Wrapper), V0_4_0_alpha_17(Wrapper), V0_4_0_alpha_18(Wrapper), - V0_4_0_alpha_19(Wrapper), // VERSION_BUMP + V0_4_0_alpha_19(Wrapper), + V0_4_0_alpha_20(Wrapper), // VERSION_BUMP Other(exver::Version), } @@ -243,7 +245,8 @@ impl Version { Self::V0_4_0_alpha_16(v) => DynVersion(Box::new(v.0)), Self::V0_4_0_alpha_17(v) => DynVersion(Box::new(v.0)), Self::V0_4_0_alpha_18(v) => DynVersion(Box::new(v.0)), - Self::V0_4_0_alpha_19(v) => DynVersion(Box::new(v.0)), // VERSION_BUMP + Self::V0_4_0_alpha_19(v) => DynVersion(Box::new(v.0)), + Self::V0_4_0_alpha_20(v) => DynVersion(Box::new(v.0)), // VERSION_BUMP Self::Other(v) => { return Err(Error::new( eyre!("unknown version {v}"), @@ -297,7 +300,8 @@ impl Version { Version::V0_4_0_alpha_16(Wrapper(x)) => x.semver(), Version::V0_4_0_alpha_17(Wrapper(x)) => x.semver(), Version::V0_4_0_alpha_18(Wrapper(x)) => x.semver(), - Version::V0_4_0_alpha_19(Wrapper(x)) => x.semver(), // VERSION_BUMP + Version::V0_4_0_alpha_19(Wrapper(x)) => x.semver(), + Version::V0_4_0_alpha_20(Wrapper(x)) => x.semver(), // VERSION_BUMP Version::Other(x) => x.clone(), } } diff --git a/core/src/version/v0_3_6_alpha_0.rs b/core/src/version/v0_3_6_alpha_0.rs index c048c97b2..f7a8dc0bf 100644 --- a/core/src/version/v0_3_6_alpha_0.rs +++ b/core/src/version/v0_3_6_alpha_0.rs @@ -1,4 +1,4 @@ -use std::collections::{BTreeMap, BTreeSet}; +use std::collections::BTreeMap; use std::ffi::OsStr; use std::path::Path; @@ -23,17 +23,14 @@ use crate::disk::mount::filesystem::cifs::Cifs; use crate::disk::mount::util::unmount; use crate::hostname::Hostname; use crate::net::forward::AvailablePorts; -use crate::net::host::Host; use crate::net::keys::KeyStore; -use crate::net::tor::{OnionAddress, TorSecretKey}; use crate::notifications::Notifications; use crate::prelude::*; use crate::s9pk::merkle_archive::source::multi_cursor_file::MultiCursorFile; use crate::ssh::{SshKeys, SshPubKey}; use crate::util::Invoke; -use crate::util::crypto::ed25519_expand_key; use crate::util::serde::Pem; -use crate::{DATA_DIR, HostId, Id, PACKAGE_DATA, PackageId, ReplayId}; +use crate::{DATA_DIR, PACKAGE_DATA, PackageId, ReplayId}; lazy_static::lazy_static! { static ref V0_3_6_alpha_0: exver::Version = exver::Version::new( @@ -146,12 +143,7 @@ pub struct Version; impl VersionT for Version { type Previous = v0_3_5_2::Version; - type PreUpRes = ( - AccountInfo, - SshKeys, - CifsTargets, - BTreeMap>, - ); + type PreUpRes = (AccountInfo, SshKeys, CifsTargets); fn semver(self) -> exver::Version { V0_3_6_alpha_0.clone() } @@ -166,20 +158,18 @@ impl VersionT for Version { let cifs = previous_cifs(&pg).await?; - let tor_keys = previous_tor_keys(&pg).await?; - Command::new("systemctl") .arg("stop") .arg("postgresql@*.service") .invoke(crate::ErrorKind::Database) .await?; - Ok((account, ssh_keys, cifs, tor_keys)) + Ok((account, ssh_keys, cifs)) } fn up( self, db: &mut Value, - (account, ssh_keys, cifs, tor_keys): Self::PreUpRes, + (account, ssh_keys, cifs): Self::PreUpRes, ) -> Result { let prev_package_data = db["package-data"].clone(); @@ -242,11 +232,7 @@ impl VersionT for Version { "ui": db["ui"], }); - let mut keystore = KeyStore::new(&account)?; - for key in tor_keys.values().flat_map(|v| v.values()) { - assert!(key.is_valid()); - keystore.onion.insert(key.clone()); - } + let keystore = KeyStore::new(&account)?; let private = { let mut value = json!({}); @@ -350,20 +336,6 @@ impl VersionT for Version { false }; - let onions = input[&*id]["installed"]["interface-addresses"] - .as_object() - .into_iter() - .flatten() - .filter_map(|(id, addrs)| { - addrs["tor-address"].as_str().map(|addr| { - Ok(( - HostId::from(Id::try_from(id.clone())?), - addr.parse::()?, - )) - }) - }) - .collect::, Error>>()?; - if let Err(e) = async { let package_s9pk = tokio::fs::File::open(path).await?; let file = MultiCursorFile::open(&package_s9pk).await?; @@ -381,11 +353,8 @@ impl VersionT for Version { .await? .await?; - let to_sync = ctx - .db + ctx.db .mutate(|db| { - let mut to_sync = BTreeSet::new(); - let package = db .as_public_mut() .as_package_data_mut() @@ -396,29 +365,11 @@ impl VersionT for Version { .as_tasks_mut() .remove(&ReplayId::from("needs-config"))?; } - for (id, onion) in onions { - package - .as_hosts_mut() - .upsert(&id, || Ok(Host::new()))? - .as_onions_mut() - .mutate(|o| { - o.clear(); - o.insert(onion); - Ok(()) - })?; - to_sync.insert(id); - } - Ok(to_sync) + Ok(()) }) .await .result?; - if let Some(service) = &*ctx.services.get(&id).await { - for host_id in to_sync { - service.sync_host(host_id.clone()).await?; - } - } - Ok::<_, Error>(()) } .await @@ -481,33 +432,6 @@ async fn previous_account_info(pg: &sqlx::Pool) -> Result>, _>("tor_key") - .with_ctx(|_| (ErrorKind::Database, "tor_key"))? - { - <[u8; 64]>::try_from(bytes).map_err(|e| { - Error::new( - eyre!("expected vec of len 64, got len {}", e.len()), - ErrorKind::ParseDbField, - ) - })? - } else { - ed25519_expand_key( - &<[u8; 32]>::try_from( - account_query - .try_get::, _>("network_key") - .with_kind(ErrorKind::Database)?, - ) - .map_err(|e| { - Error::new( - eyre!("expected vec of len 32, got len {}", e.len()), - ErrorKind::ParseDbField, - ) - })?, - ) - }, - )?], server_id: account_query .try_get("server_id") .with_ctx(|_| (ErrorKind::Database, "server_id"))?, @@ -579,68 +503,3 @@ async fn previous_ssh_keys(pg: &sqlx::Pool) -> Result, -) -> Result>, Error> { - let mut res = BTreeMap::>::new(); - let net_key_query = sqlx::query(r#"SELECT * FROM network_keys"#) - .fetch_all(pg) - .await - .with_kind(ErrorKind::Database)?; - - for row in net_key_query { - let package_id: PackageId = row - .try_get::("package") - .with_ctx(|_| (ErrorKind::Database, "network_keys::package"))? - .parse()?; - let interface_id: HostId = row - .try_get::("interface") - .with_ctx(|_| (ErrorKind::Database, "network_keys::interface"))? - .parse()?; - let key = TorSecretKey::from_bytes(ed25519_expand_key( - &<[u8; 32]>::try_from( - row.try_get::, _>("key") - .with_ctx(|_| (ErrorKind::Database, "network_keys::key"))?, - ) - .map_err(|e| { - Error::new( - eyre!("expected vec of len 32, got len {}", e.len()), - ErrorKind::ParseDbField, - ) - })?, - ))?; - res.entry(package_id).or_default().insert(interface_id, key); - } - - let tor_key_query = sqlx::query(r#"SELECT * FROM tor"#) - .fetch_all(pg) - .await - .with_kind(ErrorKind::Database)?; - - for row in tor_key_query { - let package_id: PackageId = row - .try_get::("package") - .with_ctx(|_| (ErrorKind::Database, "tor::package"))? - .parse()?; - let interface_id: HostId = row - .try_get::("interface") - .with_ctx(|_| (ErrorKind::Database, "tor::interface"))? - .parse()?; - let key = TorSecretKey::from_bytes( - <[u8; 64]>::try_from( - row.try_get::, _>("key") - .with_ctx(|_| (ErrorKind::Database, "tor::key"))?, - ) - .map_err(|e| { - Error::new( - eyre!("expected vec of len 64, got len {}", e.len()), - ErrorKind::ParseDbField, - ) - })?, - )?; - res.entry(package_id).or_default().insert(interface_id, key); - } - - Ok(res) -} diff --git a/core/src/version/v0_3_6_alpha_10.rs b/core/src/version/v0_3_6_alpha_10.rs index 08543d9e1..8cbf902a9 100644 --- a/core/src/version/v0_3_6_alpha_10.rs +++ b/core/src/version/v0_3_6_alpha_10.rs @@ -8,7 +8,6 @@ use super::v0_3_5::V0_3_0_COMPAT; use super::{VersionT, v0_3_6_alpha_9}; use crate::GatewayId; use crate::net::host::address::PublicDomainConfig; -use crate::net::tor::OnionAddress; use crate::prelude::*; lazy_static::lazy_static! { @@ -22,7 +21,7 @@ lazy_static::lazy_static! { #[serde(rename_all = "camelCase")] #[serde(tag = "kind")] enum HostAddress { - Onion { address: OnionAddress }, + Onion { address: String }, Domain { address: InternedString }, } diff --git a/core/src/version/v0_4_0_alpha_12.rs b/core/src/version/v0_4_0_alpha_12.rs index fa7e5a189..d998945a2 100644 --- a/core/src/version/v0_4_0_alpha_12.rs +++ b/core/src/version/v0_4_0_alpha_12.rs @@ -1,11 +1,7 @@ -use std::collections::BTreeSet; - use exver::{PreReleaseSegment, VersionRange}; -use imbl_value::InternedString; use super::v0_3_5::V0_3_0_COMPAT; use super::{VersionT, v0_4_0_alpha_11}; -use crate::net::tor::TorSecretKey; use crate::prelude::*; lazy_static::lazy_static! { @@ -33,48 +29,6 @@ impl VersionT for Version { } #[instrument(skip_all)] fn up(self, db: &mut Value, _: Self::PreUpRes) -> Result { - let mut err = None; - let onion_store = db["private"]["keyStore"]["onion"] - .as_object_mut() - .or_not_found("private.keyStore.onion")?; - onion_store.retain(|o, v| match from_value::(v.clone()) { - Ok(k) => k.is_valid() && &InternedString::from_display(&k.onion_address()) == o, - Err(e) => { - err = Some(e); - true - } - }); - if let Some(e) = err { - return Err(e); - } - let allowed_addresses = onion_store.keys().cloned().collect::>(); - let fix_host = |host: &mut Value| { - Ok::<_, Error>( - host["onions"] - .as_array_mut() - .or_not_found("host.onions")? - .retain(|addr| { - addr.as_str() - .map(|s| allowed_addresses.contains(s)) - .unwrap_or(false) - }), - ) - }; - for (_, pde) in db["public"]["packageData"] - .as_object_mut() - .or_not_found("public.packageData")? - .iter_mut() - { - for (_, host) in pde["hosts"] - .as_object_mut() - .or_not_found("public.packageData[].hosts")? - .iter_mut() - { - fix_host(host)?; - } - } - fix_host(&mut db["public"]["serverInfo"]["network"]["host"])?; - if db["private"]["keyStore"]["localCerts"].is_null() { db["private"]["keyStore"]["localCerts"] = db["private"]["keyStore"]["local_certs"].clone(); diff --git a/core/src/version/v0_4_0_alpha_20.rs b/core/src/version/v0_4_0_alpha_20.rs new file mode 100644 index 000000000..625d72a51 --- /dev/null +++ b/core/src/version/v0_4_0_alpha_20.rs @@ -0,0 +1,192 @@ +use exver::{PreReleaseSegment, VersionRange}; + +use super::v0_3_5::V0_3_0_COMPAT; +use super::{VersionT, v0_4_0_alpha_19}; +use crate::prelude::*; + +lazy_static::lazy_static! { + static ref V0_4_0_alpha_20: exver::Version = exver::Version::new( + [0, 4, 0], + [PreReleaseSegment::String("alpha".into()), 20.into()] + ); +} + +#[derive(Clone, Copy, Debug, Default)] +pub struct Version; + +impl VersionT for Version { + type Previous = v0_4_0_alpha_19::Version; + type PreUpRes = (); + + async fn pre_up(self) -> Result { + Ok(()) + } + fn semver(self) -> exver::Version { + V0_4_0_alpha_20.clone() + } + fn compat(self) -> &'static VersionRange { + &V0_3_0_COMPAT + } + #[instrument(skip_all)] + fn up(self, db: &mut Value, _: Self::PreUpRes) -> Result { + // Remove onions and tor-related fields from server host + if let Some(host) = db + .get_mut("public") + .and_then(|p| p.get_mut("serverInfo")) + .and_then(|s| s.get_mut("network")) + .and_then(|n| n.get_mut("host")) + .and_then(|h| h.as_object_mut()) + { + host.remove("onions"); + } + + // Remove onions from all package hosts + if let Some(packages) = db + .get_mut("public") + .and_then(|p| p.get_mut("packageData")) + .and_then(|p| p.as_object_mut()) + { + for (_, package) in packages.iter_mut() { + if let Some(hosts) = package.get_mut("hosts").and_then(|h| h.as_object_mut()) { + for (_, host) in hosts.iter_mut() { + if let Some(host_obj) = host.as_object_mut() { + host_obj.remove("onions"); + } + } + } + } + } + + // Remove onion store from private keyStore + if let Some(key_store) = db + .get_mut("private") + .and_then(|p| p.get_mut("keyStore")) + .and_then(|k| k.as_object_mut()) + { + key_store.remove("onion"); + } + + // Migrate server host: remove hostnameInfo, add addresses to bindings, clean net + migrate_host( + db.get_mut("public") + .and_then(|p| p.get_mut("serverInfo")) + .and_then(|s| s.get_mut("network")) + .and_then(|n| n.get_mut("host")), + ); + + // Migrate all package hosts + if let Some(packages) = db + .get_mut("public") + .and_then(|p| p.get_mut("packageData")) + .and_then(|p| p.as_object_mut()) + { + for (_, package) in packages.iter_mut() { + if let Some(hosts) = package.get_mut("hosts").and_then(|h| h.as_object_mut()) { + for (_, host) in hosts.iter_mut() { + migrate_host(Some(host)); + } + } + } + } + + // Migrate availablePorts from IdPool format to BTreeMap + // Rebuild from actual assigned ports in all bindings + migrate_available_ports(db); + + Ok(Value::Null) + } + fn down(self, _db: &mut Value) -> Result<(), Error> { + Ok(()) + } +} + +fn collect_ports_from_host(host: Option<&Value>, ports: &mut Value) { + let Some(bindings) = host + .and_then(|h| h.get("bindings")) + .and_then(|b| b.as_object()) + else { + return; + }; + for (_, binding) in bindings.iter() { + if let Some(net) = binding.get("net") { + if let Some(port) = net.get("assignedPort").and_then(|p| p.as_u64()) { + if let Some(obj) = ports.as_object_mut() { + obj.insert(port.to_string().into(), Value::from(false)); + } + } + if let Some(port) = net.get("assignedSslPort").and_then(|p| p.as_u64()) { + if let Some(obj) = ports.as_object_mut() { + obj.insert(port.to_string().into(), Value::from(true)); + } + } + } + } +} + +fn migrate_available_ports(db: &mut Value) { + let mut new_ports: Value = serde_json::json!({}).into(); + + // Collect from server host + let server_host = db + .get("public") + .and_then(|p| p.get("serverInfo")) + .and_then(|s| s.get("network")) + .and_then(|n| n.get("host")) + .cloned(); + collect_ports_from_host(server_host.as_ref(), &mut new_ports); + + // Collect from all package hosts + if let Some(packages) = db + .get("public") + .and_then(|p| p.get("packageData")) + .and_then(|p| p.as_object()) + { + for (_, package) in packages.iter() { + if let Some(hosts) = package.get("hosts").and_then(|h| h.as_object()) { + for (_, host) in hosts.iter() { + collect_ports_from_host(Some(host), &mut new_ports); + } + } + } + } + + // Replace private.availablePorts + if let Some(private) = db.get_mut("private").and_then(|p| p.as_object_mut()) { + private.insert("availablePorts".into(), new_ports); + } +} + +fn migrate_host(host: Option<&mut Value>) { + let Some(host) = host.and_then(|h| h.as_object_mut()) else { + return; + }; + + // Remove hostnameInfo from host + host.remove("hostnameInfo"); + + // For each binding: add "addresses" field, remove gateway-level fields from "net" + if let Some(bindings) = host.get_mut("bindings").and_then(|b| b.as_object_mut()) { + for (_, binding) in bindings.iter_mut() { + if let Some(binding_obj) = binding.as_object_mut() { + // Add addresses if not present + if !binding_obj.contains_key("addresses") { + binding_obj.insert( + "addresses".into(), + serde_json::json!({ + "privateDisabled": [], + "publicEnabled": [], + "possible": [] + }) + .into(), + ); + } + + // Remove gateway-level privateDisabled/publicEnabled from net + if let Some(net) = binding_obj.get_mut("net").and_then(|n| n.as_object_mut()) { + net.remove("privateDisabled"); + net.remove("publicEnabled"); + } + } + } + } +} diff --git a/sdk/base/lib/exver/exver.ts b/sdk/base/lib/exver/exver.ts index 352178cca..94202fde1 100644 --- a/sdk/base/lib/exver/exver.ts +++ b/sdk/base/lib/exver/exver.ts @@ -1,3195 +1,2902 @@ /* eslint-disable */ -const peggyParser: { parse: any; SyntaxError: any; DefaultTracer?: any } = // Generated by Peggy 3.0.2. - // - // https://peggyjs.org/ - // @ts-ignore - (function () { - // @ts-ignore - 'use strict' - // @ts-ignore - function peg$subclass(child, parent) { - // @ts-ignore - function C() { - this.constructor = child - } - // @ts-ignore - C.prototype = parent.prototype - // @ts-ignore - child.prototype = new C() - } - // @ts-ignore - function peg$SyntaxError(message, expected, found, location) { - // @ts-ignore - var self = Error.call(this, message) - // istanbul ignore next Check is a necessary evil to support older environments - // @ts-ignore - if (Object.setPrototypeOf) { - // @ts-ignore - Object.setPrototypeOf(self, peg$SyntaxError.prototype) - } - // @ts-ignore - self.expected = expected - // @ts-ignore - self.found = found - // @ts-ignore - self.location = location - // @ts-ignore - self.name = 'SyntaxError' - // @ts-ignore - return self - } - - // @ts-ignore - peg$subclass(peg$SyntaxError, Error) - - // @ts-ignore - function peg$padEnd(str, targetLength, padString) { - // @ts-ignore - padString = padString || ' ' - // @ts-ignore - if (str.length > targetLength) { - return str - } - // @ts-ignore - targetLength -= str.length - // @ts-ignore - padString += padString.repeat(targetLength) - // @ts-ignore - return str + padString.slice(0, targetLength) - } - - // @ts-ignore - peg$SyntaxError.prototype.format = function (sources) { - // @ts-ignore - var str = 'Error: ' + this.message - // @ts-ignore - if (this.location) { - // @ts-ignore - var src = null - // @ts-ignore - var k - // @ts-ignore - for (k = 0; k < sources.length; k++) { - // @ts-ignore - if (sources[k].source === this.location.source) { - // @ts-ignore - src = sources[k].text.split(/\r\n|\n|\r/g) - // @ts-ignore - break - } - } - // @ts-ignore - var s = this.location.start - // @ts-ignore - var offset_s = - this.location.source && - typeof this.location.source.offset === 'function' - ? // @ts-ignore - this.location.source.offset(s) - : // @ts-ignore - s - // @ts-ignore - var loc = - this.location.source + ':' + offset_s.line + ':' + offset_s.column - // @ts-ignore - if (src) { - // @ts-ignore - var e = this.location.end - // @ts-ignore - var filler = peg$padEnd('', offset_s.line.toString().length, ' ') - // @ts-ignore - var line = src[s.line - 1] - // @ts-ignore - var last = s.line === e.line ? e.column : line.length + 1 - // @ts-ignore - var hatLen = last - s.column || 1 - // @ts-ignore - str += - '\n --> ' + - loc + - '\n' + - // @ts-ignore - filler + - ' |\n' + - // @ts-ignore - offset_s.line + - ' | ' + - line + - '\n' + - // @ts-ignore - filler + - ' | ' + - peg$padEnd('', s.column - 1, ' ') + - // @ts-ignore - peg$padEnd('', hatLen, '^') - // @ts-ignore - } else { - // @ts-ignore - str += '\n at ' + loc - } - } - // @ts-ignore - return str - } - - // @ts-ignore - peg$SyntaxError.buildMessage = function (expected, found) { - // @ts-ignore - var DESCRIBE_EXPECTATION_FNS = { - // @ts-ignore - literal: function (expectation) { - // @ts-ignore - return '"' + literalEscape(expectation.text) + '"' - }, - - // @ts-ignore - class: function (expectation) { - // @ts-ignore - var escapedParts = expectation.parts.map(function (part) { - // @ts-ignore - return Array.isArray(part) - ? // @ts-ignore - classEscape(part[0]) + '-' + classEscape(part[1]) - : // @ts-ignore - classEscape(part) - }) - - // @ts-ignore - return ( - '[' + - (expectation.inverted ? '^' : '') + - escapedParts.join('') + - ']' - ) - }, - - // @ts-ignore - any: function () { - // @ts-ignore - return 'any character' - }, - - // @ts-ignore - end: function () { - // @ts-ignore - return 'end of input' - }, - - // @ts-ignore - other: function (expectation) { - // @ts-ignore - return expectation.description - }, - } - - // @ts-ignore - function hex(ch) { - // @ts-ignore - return ch.charCodeAt(0).toString(16).toUpperCase() - } - - // @ts-ignore - function literalEscape(s) { - // @ts-ignore - return ( - s - // @ts-ignore - .replace(/\\/g, '\\\\') - // @ts-ignore - .replace(/"/g, '\\"') - // @ts-ignore - .replace(/\0/g, '\\0') - // @ts-ignore - .replace(/\t/g, '\\t') - // @ts-ignore - .replace(/\n/g, '\\n') - // @ts-ignore - .replace(/\r/g, '\\r') - // @ts-ignore - .replace(/[\x00-\x0F]/g, function (ch) { - return '\\x0' + hex(ch) - }) - // @ts-ignore - .replace(/[\x10-\x1F\x7F-\x9F]/g, function (ch) { - return '\\x' + hex(ch) - }) - ) - } - - // @ts-ignore - function classEscape(s) { - // @ts-ignore - return ( - s - // @ts-ignore - .replace(/\\/g, '\\\\') - // @ts-ignore - .replace(/\]/g, '\\]') - // @ts-ignore - .replace(/\^/g, '\\^') - // @ts-ignore - .replace(/-/g, '\\-') - // @ts-ignore - .replace(/\0/g, '\\0') - // @ts-ignore - .replace(/\t/g, '\\t') - // @ts-ignore - .replace(/\n/g, '\\n') - // @ts-ignore - .replace(/\r/g, '\\r') - // @ts-ignore - .replace(/[\x00-\x0F]/g, function (ch) { - return '\\x0' + hex(ch) - }) - // @ts-ignore - .replace(/[\x10-\x1F\x7F-\x9F]/g, function (ch) { - return '\\x' + hex(ch) - }) - ) - } - - // @ts-ignore - function describeExpectation(expectation) { - // @ts-ignore - return DESCRIBE_EXPECTATION_FNS[expectation.type](expectation) - } - - // @ts-ignore - function describeExpected(expected) { - // @ts-ignore - var descriptions = expected.map(describeExpectation) - // @ts-ignore - var i, j - - // @ts-ignore - descriptions.sort() - - // @ts-ignore - if (descriptions.length > 0) { - // @ts-ignore - for (i = 1, j = 1; i < descriptions.length; i++) { - // @ts-ignore - if (descriptions[i - 1] !== descriptions[i]) { - // @ts-ignore - descriptions[j] = descriptions[i] - // @ts-ignore - j++ - } - } - // @ts-ignore - descriptions.length = j - } - - // @ts-ignore - switch (descriptions.length) { - // @ts-ignore - case 1: - // @ts-ignore - return descriptions[0] - - // @ts-ignore - case 2: - // @ts-ignore - return descriptions[0] + ' or ' + descriptions[1] - - // @ts-ignore - default: - // @ts-ignore - return ( - descriptions.slice(0, -1).join(', ') + - // @ts-ignore - ', or ' + - // @ts-ignore - descriptions[descriptions.length - 1] - ) - } - } - - // @ts-ignore - function describeFound(found) { - // @ts-ignore - return found ? '"' + literalEscape(found) + '"' : 'end of input' - } - - // @ts-ignore - return ( - 'Expected ' + - describeExpected(expected) + - ' but ' + - describeFound(found) + - ' found.' - ) - } - - // @ts-ignore - function peg$parse(input, options) { - // @ts-ignore - options = options !== undefined ? options : {} - - // @ts-ignore - var peg$FAILED = {} - // @ts-ignore - var peg$source = options.grammarSource - - // @ts-ignore - var peg$startRuleFunctions = { - VersionRange: peg$parseVersionRange, - Or: peg$parseOr, - And: peg$parseAnd, - VersionRangeAtom: peg$parseVersionRangeAtom, - Parens: peg$parseParens, - Anchor: peg$parseAnchor, - VersionSpec: peg$parseVersionSpec, - FlavorAtom: peg$parseFlavorAtom, - Not: peg$parseNot, - Any: peg$parseAny, - None: peg$parseNone, - CmpOp: peg$parseCmpOp, - ExtendedVersion: peg$parseExtendedVersion, - EmverVersionRange: peg$parseEmverVersionRange, - EmverVersionRangeAtom: peg$parseEmverVersionRangeAtom, - EmverParens: peg$parseEmverParens, - EmverAnchor: peg$parseEmverAnchor, - EmverNot: peg$parseEmverNot, - Emver: peg$parseEmver, - Flavor: peg$parseFlavor, - FlavorString: peg$parseFlavorString, - String: peg$parseString, - Version: peg$parseVersion, - PreRelease: peg$parsePreRelease, - PreReleaseSegment: peg$parsePreReleaseSegment, - VersionNumber: peg$parseVersionNumber, - Digit: peg$parseDigit, - _: peg$parse_, - } - // @ts-ignore - var peg$startRuleFunction = peg$parseVersionRange - - // @ts-ignore - var peg$c0 = '||' - var peg$c1 = '&&' - var peg$c2 = '(' - var peg$c3 = ')' - var peg$c4 = ':' - var peg$c5 = '#' - var peg$c6 = '!' - var peg$c7 = '*' - var peg$c8 = '>=' - var peg$c9 = '<=' - var peg$c10 = '>' - var peg$c11 = '<' - var peg$c12 = '=' - var peg$c13 = '!=' - var peg$c14 = '^' - var peg$c15 = '~' - var peg$c16 = '.' - var peg$c17 = '-' - - var peg$r0 = /^[a-z]/ - var peg$r1 = /^[a-zA-Z]/ - var peg$r2 = /^[0-9]/ - var peg$r3 = /^[ \t\n\r]/ - - var peg$e0 = peg$literalExpectation('||', false) - var peg$e1 = peg$literalExpectation('&&', false) - var peg$e2 = peg$literalExpectation('(', false) - var peg$e3 = peg$literalExpectation(')', false) - var peg$e4 = peg$literalExpectation(':', false) - var peg$e5 = peg$literalExpectation('#', false) - var peg$e6 = peg$literalExpectation('!', false) - var peg$e7 = peg$literalExpectation('*', false) - var peg$e8 = peg$literalExpectation('>=', false) - var peg$e9 = peg$literalExpectation('<=', false) - var peg$e10 = peg$literalExpectation('>', false) - var peg$e11 = peg$literalExpectation('<', false) - var peg$e12 = peg$literalExpectation('=', false) - var peg$e13 = peg$literalExpectation('!=', false) - var peg$e14 = peg$literalExpectation('^', false) - var peg$e15 = peg$literalExpectation('~', false) - var peg$e16 = peg$literalExpectation('.', false) - var peg$e17 = peg$classExpectation([['a', 'z']], false, false) - var peg$e18 = peg$classExpectation( - [ - ['a', 'z'], - ['A', 'Z'], - ], - false, - false, - ) - var peg$e19 = peg$literalExpectation('-', false) - var peg$e20 = peg$classExpectation([['0', '9']], false, false) - var peg$e21 = peg$otherExpectation('whitespace') - var peg$e22 = peg$classExpectation([' ', '\t', '\n', '\r'], false, false) - // @ts-ignore - - var peg$f0 = function (expr) { - // @ts-ignore - return { type: 'Parens', expr } - } // @ts-ignore - - var peg$f1 = function (operator, version) { - // @ts-ignore - return { type: 'Anchor', operator, version } - } // @ts-ignore - - var peg$f2 = function (flavor, upstream, downstream) { - // @ts-ignore - return { - flavor: flavor || null, - upstream, - downstream: downstream - ? downstream[1] - : { number: [0], prerelease: [] }, - } - } // @ts-ignore - - var peg$f3 = function (flavor) { - // @ts-ignore - return { type: 'Flavor', flavor: flavor } - } // @ts-ignore - - var peg$f4 = function (value) { - // @ts-ignore - return { type: 'Not', value: value } - } // @ts-ignore - - var peg$f5 = function () { - // @ts-ignore - return { type: 'Any' } - } // @ts-ignore - - var peg$f6 = function () { - // @ts-ignore - return { type: 'None' } - } // @ts-ignore - - var peg$f7 = function () { - // @ts-ignore - return '>=' - } // @ts-ignore - - var peg$f8 = function () { - // @ts-ignore - return '<=' - } // @ts-ignore - - var peg$f9 = function () { - // @ts-ignore - return '>' - } // @ts-ignore - - var peg$f10 = function () { - // @ts-ignore - return '<' - } // @ts-ignore - - var peg$f11 = function () { - // @ts-ignore - return '=' - } // @ts-ignore - - var peg$f12 = function () { - // @ts-ignore - return '!=' - } // @ts-ignore - - var peg$f13 = function () { - // @ts-ignore - return '^' - } // @ts-ignore - - var peg$f14 = function () { - // @ts-ignore - return '~' - } // @ts-ignore - - var peg$f15 = function (flavor, upstream, downstream) { - // @ts-ignore - return { flavor: flavor || null, upstream, downstream } - } // @ts-ignore - - var peg$f16 = function (expr) { - // @ts-ignore - return { type: 'Parens', expr } - } // @ts-ignore - - var peg$f17 = function (operator, version) { - // @ts-ignore - return { type: 'Anchor', operator, version } - } // @ts-ignore - - var peg$f18 = function (value) { - // @ts-ignore - return { type: 'Not', value: value } - } // @ts-ignore - - var peg$f19 = function (major, minor, patch, revision) { - // @ts-ignore - return revision - } // @ts-ignore - - var peg$f20 = function (major, minor, patch, revision) { - // @ts-ignore - return { - // @ts-ignore - flavor: null, - // @ts-ignore - upstream: { - // @ts-ignore - number: [major, minor, patch], - // @ts-ignore - prerelease: [], - }, - // @ts-ignore - downstream: { - // @ts-ignore - number: [revision || 0], - // @ts-ignore - prerelease: [], - }, - } - } // @ts-ignore - - var peg$f21 = function (flavor) { - // @ts-ignore - return flavor - } // @ts-ignore - - var peg$f22 = function () { - // @ts-ignore - return text() - } // @ts-ignore - - var peg$f23 = function () { - // @ts-ignore - return text() - } // @ts-ignore - - var peg$f24 = function (number, prerelease) { - // @ts-ignore - return { - // @ts-ignore - number, - // @ts-ignore - prerelease: prerelease || [], - } - } // @ts-ignore - - var peg$f25 = function (first, rest) { - // @ts-ignore - return [first].concat(rest.map((r) => r[1])) - } // @ts-ignore - - var peg$f26 = function (segment) { - // @ts-ignore - return segment - } // @ts-ignore - - var peg$f27 = function (first, rest) { - // @ts-ignore - return [first].concat(rest.map((r) => r[1])) - } // @ts-ignore - - var peg$f28 = function () { - // @ts-ignore - return parseInt(text(), 10) - } - // @ts-ignore - var peg$currPos = 0 - // @ts-ignore - var peg$savedPos = 0 - // @ts-ignore - var peg$posDetailsCache = [{ line: 1, column: 1 }] - // @ts-ignore - var peg$maxFailPos = 0 - // @ts-ignore - var peg$maxFailExpected = [] - // @ts-ignore - var peg$silentFails = 0 - - // @ts-ignore - var peg$result - - // @ts-ignore - if ('startRule' in options) { - // @ts-ignore - if (!(options.startRule in peg$startRuleFunctions)) { - // @ts-ignore - throw new Error( - 'Can\'t start parsing from rule "' + options.startRule + '".', - ) - } - - // @ts-ignore - peg$startRuleFunction = peg$startRuleFunctions[options.startRule] - } - - // @ts-ignore - function text() { - // @ts-ignore - return input.substring(peg$savedPos, peg$currPos) - } - - // @ts-ignore - function offset() { - // @ts-ignore - return peg$savedPos - } - - // @ts-ignore - function range() { - // @ts-ignore - return { - // @ts-ignore - source: peg$source, - // @ts-ignore - start: peg$savedPos, - // @ts-ignore - end: peg$currPos, - } - } - - // @ts-ignore - function location() { - // @ts-ignore - return peg$computeLocation(peg$savedPos, peg$currPos) - } - - // @ts-ignore - function expected(description, location) { - // @ts-ignore - location = - location !== undefined - ? // @ts-ignore - location - : // @ts-ignore - peg$computeLocation(peg$savedPos, peg$currPos) - - // @ts-ignore - throw peg$buildStructuredError( - // @ts-ignore - [peg$otherExpectation(description)], - // @ts-ignore - input.substring(peg$savedPos, peg$currPos), - // @ts-ignore - location, - ) - } - - // @ts-ignore - function error(message, location) { - // @ts-ignore - location = - location !== undefined - ? // @ts-ignore - location - : // @ts-ignore - peg$computeLocation(peg$savedPos, peg$currPos) - - // @ts-ignore - throw peg$buildSimpleError(message, location) - } - - // @ts-ignore - function peg$literalExpectation(text, ignoreCase) { - // @ts-ignore - return { type: 'literal', text: text, ignoreCase: ignoreCase } - } - - // @ts-ignore - function peg$classExpectation(parts, inverted, ignoreCase) { - // @ts-ignore - return { - type: 'class', - parts: parts, - inverted: inverted, - ignoreCase: ignoreCase, - } - } - - // @ts-ignore - function peg$anyExpectation() { - // @ts-ignore - return { type: 'any' } - } - - // @ts-ignore - function peg$endExpectation() { - // @ts-ignore - return { type: 'end' } - } - - // @ts-ignore - function peg$otherExpectation(description) { - // @ts-ignore - return { type: 'other', description: description } - } - - // @ts-ignore - function peg$computePosDetails(pos) { - // @ts-ignore - var details = peg$posDetailsCache[pos] - // @ts-ignore - var p - - // @ts-ignore - if (details) { - // @ts-ignore - return details - // @ts-ignore - } else { - // @ts-ignore - p = pos - 1 - // @ts-ignore - while (!peg$posDetailsCache[p]) { - // @ts-ignore - p-- - } - - // @ts-ignore - details = peg$posDetailsCache[p] - // @ts-ignore - details = { - // @ts-ignore - line: details.line, - // @ts-ignore - column: details.column, - } - - // @ts-ignore - while (p < pos) { - // @ts-ignore - if (input.charCodeAt(p) === 10) { - // @ts-ignore - details.line++ - // @ts-ignore - details.column = 1 - // @ts-ignore - } else { - // @ts-ignore - details.column++ - } - - // @ts-ignore - p++ - } - - // @ts-ignore - peg$posDetailsCache[pos] = details - - // @ts-ignore - return details - } - } - - // @ts-ignore - function peg$computeLocation(startPos, endPos, offset) { - // @ts-ignore - var startPosDetails = peg$computePosDetails(startPos) - // @ts-ignore - var endPosDetails = peg$computePosDetails(endPos) - - // @ts-ignore - var res = { - // @ts-ignore - source: peg$source, - // @ts-ignore - start: { - // @ts-ignore - offset: startPos, - // @ts-ignore - line: startPosDetails.line, - // @ts-ignore - column: startPosDetails.column, - }, - // @ts-ignore - end: { - // @ts-ignore - offset: endPos, - // @ts-ignore - line: endPosDetails.line, - // @ts-ignore - column: endPosDetails.column, - }, - } - // @ts-ignore - if (offset && peg$source && typeof peg$source.offset === 'function') { - // @ts-ignore - res.start = peg$source.offset(res.start) - // @ts-ignore - res.end = peg$source.offset(res.end) - } - // @ts-ignore - return res - } - - // @ts-ignore - function peg$fail(expected) { - // @ts-ignore - if (peg$currPos < peg$maxFailPos) { - return - } - - // @ts-ignore - if (peg$currPos > peg$maxFailPos) { - // @ts-ignore - peg$maxFailPos = peg$currPos - // @ts-ignore - peg$maxFailExpected = [] - } - - // @ts-ignore - peg$maxFailExpected.push(expected) - } - - // @ts-ignore - function peg$buildSimpleError(message, location) { - // @ts-ignore - return new peg$SyntaxError(message, null, null, location) - } - - // @ts-ignore - function peg$buildStructuredError(expected, found, location) { - // @ts-ignore - return new peg$SyntaxError( - // @ts-ignore - peg$SyntaxError.buildMessage(expected, found), - // @ts-ignore - expected, - // @ts-ignore - found, - // @ts-ignore - location, - ) - } - - // @ts-ignore - function // @ts-ignore - peg$parseVersionRange() { - // @ts-ignore - var s0, s1, s2, s3, s4, s5, s6, s7 - - // @ts-ignore - s0 = peg$currPos - // @ts-ignore - s1 = peg$parseVersionRangeAtom() - // @ts-ignore - if (s1 !== peg$FAILED) { - // @ts-ignore - s2 = [] - // @ts-ignore - s3 = peg$currPos - // @ts-ignore - s4 = peg$parse_() - // @ts-ignore - s5 = peg$currPos - // @ts-ignore - s6 = peg$parseOr() - // @ts-ignore - if (s6 === peg$FAILED) { - // @ts-ignore - s6 = peg$parseAnd() - } - // @ts-ignore - if (s6 !== peg$FAILED) { - // @ts-ignore - s7 = peg$parse_() - // @ts-ignore - s6 = [s6, s7] - // @ts-ignore - s5 = s6 - // @ts-ignore - } else { - // @ts-ignore - peg$currPos = s5 - // @ts-ignore - s5 = peg$FAILED - } - // @ts-ignore - if (s5 === peg$FAILED) { - // @ts-ignore - s5 = null - } - // @ts-ignore - s6 = peg$parseVersionRangeAtom() - // @ts-ignore - if (s6 !== peg$FAILED) { - // @ts-ignore - s4 = [s4, s5, s6] - // @ts-ignore - s3 = s4 - // @ts-ignore - } else { - // @ts-ignore - peg$currPos = s3 - // @ts-ignore - s3 = peg$FAILED - } - // @ts-ignore - while (s3 !== peg$FAILED) { - // @ts-ignore - s2.push(s3) - // @ts-ignore - s3 = peg$currPos - // @ts-ignore - s4 = peg$parse_() - // @ts-ignore - s5 = peg$currPos - // @ts-ignore - s6 = peg$parseOr() - // @ts-ignore - if (s6 === peg$FAILED) { - // @ts-ignore - s6 = peg$parseAnd() - } - // @ts-ignore - if (s6 !== peg$FAILED) { - // @ts-ignore - s7 = peg$parse_() - // @ts-ignore - s6 = [s6, s7] - // @ts-ignore - s5 = s6 - // @ts-ignore - } else { - // @ts-ignore - peg$currPos = s5 - // @ts-ignore - s5 = peg$FAILED - } - // @ts-ignore - if (s5 === peg$FAILED) { - // @ts-ignore - s5 = null - } - // @ts-ignore - s6 = peg$parseVersionRangeAtom() - // @ts-ignore - if (s6 !== peg$FAILED) { - // @ts-ignore - s4 = [s4, s5, s6] - // @ts-ignore - s3 = s4 - // @ts-ignore - } else { - // @ts-ignore - peg$currPos = s3 - // @ts-ignore - s3 = peg$FAILED - } - } - // @ts-ignore - s1 = [s1, s2] - // @ts-ignore - s0 = s1 - // @ts-ignore - } else { - // @ts-ignore - peg$currPos = s0 - // @ts-ignore - s0 = peg$FAILED - } - - // @ts-ignore - return s0 - } - - // @ts-ignore - function // @ts-ignore - peg$parseOr() { - // @ts-ignore - var s0 - - // @ts-ignore - if (input.substr(peg$currPos, 2) === peg$c0) { - // @ts-ignore - s0 = peg$c0 - // @ts-ignore - peg$currPos += 2 - // @ts-ignore - } else { - // @ts-ignore - s0 = peg$FAILED - // @ts-ignore - if (peg$silentFails === 0) { - peg$fail(peg$e0) - } - } - - // @ts-ignore - return s0 - } - - // @ts-ignore - function // @ts-ignore - peg$parseAnd() { - // @ts-ignore - var s0 - - // @ts-ignore - if (input.substr(peg$currPos, 2) === peg$c1) { - // @ts-ignore - s0 = peg$c1 - // @ts-ignore - peg$currPos += 2 - // @ts-ignore - } else { - // @ts-ignore - s0 = peg$FAILED - // @ts-ignore - if (peg$silentFails === 0) { - peg$fail(peg$e1) - } - } - - // @ts-ignore - return s0 - } - - // @ts-ignore - function // @ts-ignore - peg$parseVersionRangeAtom() { - // @ts-ignore - var s0 - - // @ts-ignore - s0 = peg$parseParens() - // @ts-ignore - if (s0 === peg$FAILED) { - // @ts-ignore - s0 = peg$parseAnchor() - // @ts-ignore - if (s0 === peg$FAILED) { - // @ts-ignore - s0 = peg$parseNot() - // @ts-ignore - if (s0 === peg$FAILED) { - // @ts-ignore - s0 = peg$parseAny() - // @ts-ignore - if (s0 === peg$FAILED) { - // @ts-ignore - s0 = peg$parseNone() - // @ts-ignore - if (s0 === peg$FAILED) { - // @ts-ignore - s0 = peg$parseFlavorAtom() - } - } - } - } - } - - // @ts-ignore - return s0 - } - - // @ts-ignore - function // @ts-ignore - peg$parseParens() { - // @ts-ignore - var s0, s1, s2, s3, s4, s5 - - // @ts-ignore - s0 = peg$currPos - // @ts-ignore - if (input.charCodeAt(peg$currPos) === 40) { - // @ts-ignore - s1 = peg$c2 - // @ts-ignore - peg$currPos++ - // @ts-ignore - } else { - // @ts-ignore - s1 = peg$FAILED - // @ts-ignore - if (peg$silentFails === 0) { - peg$fail(peg$e2) - } - } - // @ts-ignore - if (s1 !== peg$FAILED) { - // @ts-ignore - s2 = peg$parse_() - // @ts-ignore - s3 = peg$parseVersionRange() - // @ts-ignore - if (s3 !== peg$FAILED) { - // @ts-ignore - s4 = peg$parse_() - // @ts-ignore - if (input.charCodeAt(peg$currPos) === 41) { - // @ts-ignore - s5 = peg$c3 - // @ts-ignore - peg$currPos++ - // @ts-ignore - } else { - // @ts-ignore - s5 = peg$FAILED - // @ts-ignore - if (peg$silentFails === 0) { - peg$fail(peg$e3) - } - } - // @ts-ignore - if (s5 !== peg$FAILED) { - // @ts-ignore - peg$savedPos = s0 - // @ts-ignore - s0 = peg$f0(s3) - // @ts-ignore - } else { - // @ts-ignore - peg$currPos = s0 - // @ts-ignore - s0 = peg$FAILED - } - // @ts-ignore - } else { - // @ts-ignore - peg$currPos = s0 - // @ts-ignore - s0 = peg$FAILED - } - // @ts-ignore - } else { - // @ts-ignore - peg$currPos = s0 - // @ts-ignore - s0 = peg$FAILED - } - - // @ts-ignore - return s0 - } - - // @ts-ignore - function // @ts-ignore - peg$parseAnchor() { - // @ts-ignore - var s0, s1, s2, s3 - - // @ts-ignore - s0 = peg$currPos - // @ts-ignore - s1 = peg$parseCmpOp() - // @ts-ignore - if (s1 === peg$FAILED) { - // @ts-ignore - s1 = null - } - // @ts-ignore - s2 = peg$parse_() - // @ts-ignore - s3 = peg$parseVersionSpec() - // @ts-ignore - if (s3 !== peg$FAILED) { - // @ts-ignore - peg$savedPos = s0 - // @ts-ignore - s0 = peg$f1(s1, s3) - // @ts-ignore - } else { - // @ts-ignore - peg$currPos = s0 - // @ts-ignore - s0 = peg$FAILED - } - - // @ts-ignore - return s0 - } - - // @ts-ignore - function // @ts-ignore - peg$parseVersionSpec() { - // @ts-ignore - var s0, s1, s2, s3, s4, s5 - - // @ts-ignore - s0 = peg$currPos - // @ts-ignore - s1 = peg$parseFlavor() - // @ts-ignore - if (s1 === peg$FAILED) { - // @ts-ignore - s1 = null - } - // @ts-ignore - s2 = peg$parseVersion() - // @ts-ignore - if (s2 !== peg$FAILED) { - // @ts-ignore - s3 = peg$currPos - // @ts-ignore - if (input.charCodeAt(peg$currPos) === 58) { - // @ts-ignore - s4 = peg$c4 - // @ts-ignore - peg$currPos++ - // @ts-ignore - } else { - // @ts-ignore - s4 = peg$FAILED - // @ts-ignore - if (peg$silentFails === 0) { - peg$fail(peg$e4) - } - } - // @ts-ignore - if (s4 !== peg$FAILED) { - // @ts-ignore - s5 = peg$parseVersion() - // @ts-ignore - if (s5 !== peg$FAILED) { - // @ts-ignore - s4 = [s4, s5] - // @ts-ignore - s3 = s4 - // @ts-ignore - } else { - // @ts-ignore - peg$currPos = s3 - // @ts-ignore - s3 = peg$FAILED - } - // @ts-ignore - } else { - // @ts-ignore - peg$currPos = s3 - // @ts-ignore - s3 = peg$FAILED - } - // @ts-ignore - if (s3 === peg$FAILED) { - // @ts-ignore - s3 = null - } - // @ts-ignore - peg$savedPos = s0 - // @ts-ignore - s0 = peg$f2(s1, s2, s3) - // @ts-ignore - } else { - // @ts-ignore - peg$currPos = s0 - // @ts-ignore - s0 = peg$FAILED - } - - // @ts-ignore - return s0 - } - - // @ts-ignore - function // @ts-ignore - peg$parseFlavorAtom() { - // @ts-ignore - var s0, s1, s2 - - // @ts-ignore - s0 = peg$currPos - // @ts-ignore - if (input.charCodeAt(peg$currPos) === 35) { - // @ts-ignore - s1 = peg$c5 - // @ts-ignore - peg$currPos++ - // @ts-ignore - } else { - // @ts-ignore - s1 = peg$FAILED - // @ts-ignore - if (peg$silentFails === 0) { - peg$fail(peg$e5) - } - } - // @ts-ignore - if (s1 !== peg$FAILED) { - // @ts-ignore - s2 = peg$parseFlavorString() - // @ts-ignore - peg$savedPos = s0 - // @ts-ignore - s0 = peg$f3(s2) - // @ts-ignore - } else { - // @ts-ignore - peg$currPos = s0 - // @ts-ignore - s0 = peg$FAILED - } - - // @ts-ignore - return s0 - } - - // @ts-ignore - function // @ts-ignore - peg$parseNot() { - // @ts-ignore - var s0, s1, s2, s3 - - // @ts-ignore - s0 = peg$currPos - // @ts-ignore - if (input.charCodeAt(peg$currPos) === 33) { - // @ts-ignore - s1 = peg$c6 - // @ts-ignore - peg$currPos++ - // @ts-ignore - } else { - // @ts-ignore - s1 = peg$FAILED - // @ts-ignore - if (peg$silentFails === 0) { - peg$fail(peg$e6) - } - } - // @ts-ignore - if (s1 !== peg$FAILED) { - // @ts-ignore - s2 = peg$parse_() - // @ts-ignore - s3 = peg$parseVersionRangeAtom() - // @ts-ignore - if (s3 !== peg$FAILED) { - // @ts-ignore - peg$savedPos = s0 - // @ts-ignore - s0 = peg$f4(s3) - // @ts-ignore - } else { - // @ts-ignore - peg$currPos = s0 - // @ts-ignore - s0 = peg$FAILED - } - // @ts-ignore - } else { - // @ts-ignore - peg$currPos = s0 - // @ts-ignore - s0 = peg$FAILED - } - - // @ts-ignore - return s0 - } - - // @ts-ignore - function // @ts-ignore - peg$parseAny() { - // @ts-ignore - var s0, s1 - - // @ts-ignore - s0 = peg$currPos - // @ts-ignore - if (input.charCodeAt(peg$currPos) === 42) { - // @ts-ignore - s1 = peg$c7 - // @ts-ignore - peg$currPos++ - // @ts-ignore - } else { - // @ts-ignore - s1 = peg$FAILED - // @ts-ignore - if (peg$silentFails === 0) { - peg$fail(peg$e7) - } - } - // @ts-ignore - if (s1 !== peg$FAILED) { - // @ts-ignore - peg$savedPos = s0 - // @ts-ignore - s1 = peg$f5() - } - // @ts-ignore - s0 = s1 - - // @ts-ignore - return s0 - } - - // @ts-ignore - function // @ts-ignore - peg$parseNone() { - // @ts-ignore - var s0, s1 - - // @ts-ignore - s0 = peg$currPos - // @ts-ignore - if (input.charCodeAt(peg$currPos) === 33) { - // @ts-ignore - s1 = peg$c6 - // @ts-ignore - peg$currPos++ - // @ts-ignore - } else { - // @ts-ignore - s1 = peg$FAILED - // @ts-ignore - if (peg$silentFails === 0) { - peg$fail(peg$e6) - } - } - // @ts-ignore - if (s1 !== peg$FAILED) { - // @ts-ignore - peg$savedPos = s0 - // @ts-ignore - s1 = peg$f6() - } - // @ts-ignore - s0 = s1 - - // @ts-ignore - return s0 - } - - // @ts-ignore - function // @ts-ignore - peg$parseCmpOp() { - // @ts-ignore - var s0, s1 - - // @ts-ignore - s0 = peg$currPos - // @ts-ignore - if (input.substr(peg$currPos, 2) === peg$c8) { - // @ts-ignore - s1 = peg$c8 - // @ts-ignore - peg$currPos += 2 - // @ts-ignore - } else { - // @ts-ignore - s1 = peg$FAILED - // @ts-ignore - if (peg$silentFails === 0) { - peg$fail(peg$e8) - } - } - // @ts-ignore - if (s1 !== peg$FAILED) { - // @ts-ignore - peg$savedPos = s0 - // @ts-ignore - s1 = peg$f7() - } - // @ts-ignore - s0 = s1 - // @ts-ignore - if (s0 === peg$FAILED) { - // @ts-ignore - s0 = peg$currPos - // @ts-ignore - if (input.substr(peg$currPos, 2) === peg$c9) { - // @ts-ignore - s1 = peg$c9 - // @ts-ignore - peg$currPos += 2 - // @ts-ignore - } else { - // @ts-ignore - s1 = peg$FAILED - // @ts-ignore - if (peg$silentFails === 0) { - peg$fail(peg$e9) - } - } - // @ts-ignore - if (s1 !== peg$FAILED) { - // @ts-ignore - peg$savedPos = s0 - // @ts-ignore - s1 = peg$f8() - } - // @ts-ignore - s0 = s1 - // @ts-ignore - if (s0 === peg$FAILED) { - // @ts-ignore - s0 = peg$currPos - // @ts-ignore - if (input.charCodeAt(peg$currPos) === 62) { - // @ts-ignore - s1 = peg$c10 - // @ts-ignore - peg$currPos++ - // @ts-ignore - } else { - // @ts-ignore - s1 = peg$FAILED - // @ts-ignore - if (peg$silentFails === 0) { - peg$fail(peg$e10) - } - } - // @ts-ignore - if (s1 !== peg$FAILED) { - // @ts-ignore - peg$savedPos = s0 - // @ts-ignore - s1 = peg$f9() - } - // @ts-ignore - s0 = s1 - // @ts-ignore - if (s0 === peg$FAILED) { - // @ts-ignore - s0 = peg$currPos - // @ts-ignore - if (input.charCodeAt(peg$currPos) === 60) { - // @ts-ignore - s1 = peg$c11 - // @ts-ignore - peg$currPos++ - // @ts-ignore - } else { - // @ts-ignore - s1 = peg$FAILED - // @ts-ignore - if (peg$silentFails === 0) { - peg$fail(peg$e11) - } - } - // @ts-ignore - if (s1 !== peg$FAILED) { - // @ts-ignore - peg$savedPos = s0 - // @ts-ignore - s1 = peg$f10() - } - // @ts-ignore - s0 = s1 - // @ts-ignore - if (s0 === peg$FAILED) { - // @ts-ignore - s0 = peg$currPos - // @ts-ignore - if (input.charCodeAt(peg$currPos) === 61) { - // @ts-ignore - s1 = peg$c12 - // @ts-ignore - peg$currPos++ - // @ts-ignore - } else { - // @ts-ignore - s1 = peg$FAILED - // @ts-ignore - if (peg$silentFails === 0) { - peg$fail(peg$e12) - } - } - // @ts-ignore - if (s1 !== peg$FAILED) { - // @ts-ignore - peg$savedPos = s0 - // @ts-ignore - s1 = peg$f11() - } - // @ts-ignore - s0 = s1 - // @ts-ignore - if (s0 === peg$FAILED) { - // @ts-ignore - s0 = peg$currPos - // @ts-ignore - if (input.substr(peg$currPos, 2) === peg$c13) { - // @ts-ignore - s1 = peg$c13 - // @ts-ignore - peg$currPos += 2 - // @ts-ignore - } else { - // @ts-ignore - s1 = peg$FAILED - // @ts-ignore - if (peg$silentFails === 0) { - peg$fail(peg$e13) - } - } - // @ts-ignore - if (s1 !== peg$FAILED) { - // @ts-ignore - peg$savedPos = s0 - // @ts-ignore - s1 = peg$f12() - } - // @ts-ignore - s0 = s1 - // @ts-ignore - if (s0 === peg$FAILED) { - // @ts-ignore - s0 = peg$currPos - // @ts-ignore - if (input.charCodeAt(peg$currPos) === 94) { - // @ts-ignore - s1 = peg$c14 - // @ts-ignore - peg$currPos++ - // @ts-ignore - } else { - // @ts-ignore - s1 = peg$FAILED - // @ts-ignore - if (peg$silentFails === 0) { - peg$fail(peg$e14) - } - } - // @ts-ignore - if (s1 !== peg$FAILED) { - // @ts-ignore - peg$savedPos = s0 - // @ts-ignore - s1 = peg$f13() - } - // @ts-ignore - s0 = s1 - // @ts-ignore - if (s0 === peg$FAILED) { - // @ts-ignore - s0 = peg$currPos - // @ts-ignore - if (input.charCodeAt(peg$currPos) === 126) { - // @ts-ignore - s1 = peg$c15 - // @ts-ignore - peg$currPos++ - // @ts-ignore - } else { - // @ts-ignore - s1 = peg$FAILED - // @ts-ignore - if (peg$silentFails === 0) { - peg$fail(peg$e15) - } - } - // @ts-ignore - if (s1 !== peg$FAILED) { - // @ts-ignore - peg$savedPos = s0 - // @ts-ignore - s1 = peg$f14() - } - // @ts-ignore - s0 = s1 - } - } - } - } - } - } - } - - // @ts-ignore - return s0 - } - - // @ts-ignore - function // @ts-ignore - peg$parseExtendedVersion() { - // @ts-ignore - var s0, s1, s2, s3, s4 - - // @ts-ignore - s0 = peg$currPos - // @ts-ignore - s1 = peg$parseFlavor() - // @ts-ignore - if (s1 === peg$FAILED) { - // @ts-ignore - s1 = null - } - // @ts-ignore - s2 = peg$parseVersion() - // @ts-ignore - if (s2 !== peg$FAILED) { - // @ts-ignore - if (input.charCodeAt(peg$currPos) === 58) { - // @ts-ignore - s3 = peg$c4 - // @ts-ignore - peg$currPos++ - // @ts-ignore - } else { - // @ts-ignore - s3 = peg$FAILED - // @ts-ignore - if (peg$silentFails === 0) { - peg$fail(peg$e4) - } - } - // @ts-ignore - if (s3 !== peg$FAILED) { - // @ts-ignore - s4 = peg$parseVersion() - // @ts-ignore - if (s4 !== peg$FAILED) { - // @ts-ignore - peg$savedPos = s0 - // @ts-ignore - s0 = peg$f15(s1, s2, s4) - // @ts-ignore - } else { - // @ts-ignore - peg$currPos = s0 - // @ts-ignore - s0 = peg$FAILED - } - // @ts-ignore - } else { - // @ts-ignore - peg$currPos = s0 - // @ts-ignore - s0 = peg$FAILED - } - // @ts-ignore - } else { - // @ts-ignore - peg$currPos = s0 - // @ts-ignore - s0 = peg$FAILED - } - - // @ts-ignore - return s0 - } - - // @ts-ignore - function // @ts-ignore - peg$parseEmverVersionRange() { - // @ts-ignore - var s0, s1, s2, s3, s4, s5, s6, s7 - - // @ts-ignore - s0 = peg$currPos - // @ts-ignore - s1 = peg$parseEmverVersionRangeAtom() - // @ts-ignore - if (s1 !== peg$FAILED) { - // @ts-ignore - s2 = [] - // @ts-ignore - s3 = peg$currPos - // @ts-ignore - s4 = peg$parse_() - // @ts-ignore - s5 = peg$currPos - // @ts-ignore - s6 = peg$parseOr() - // @ts-ignore - if (s6 === peg$FAILED) { - // @ts-ignore - s6 = peg$parseAnd() - } - // @ts-ignore - if (s6 !== peg$FAILED) { - // @ts-ignore - s7 = peg$parse_() - // @ts-ignore - s6 = [s6, s7] - // @ts-ignore - s5 = s6 - // @ts-ignore - } else { - // @ts-ignore - peg$currPos = s5 - // @ts-ignore - s5 = peg$FAILED - } - // @ts-ignore - if (s5 === peg$FAILED) { - // @ts-ignore - s5 = null - } - // @ts-ignore - s6 = peg$parseEmverVersionRangeAtom() - // @ts-ignore - if (s6 !== peg$FAILED) { - // @ts-ignore - s4 = [s4, s5, s6] - // @ts-ignore - s3 = s4 - // @ts-ignore - } else { - // @ts-ignore - peg$currPos = s3 - // @ts-ignore - s3 = peg$FAILED - } - // @ts-ignore - while (s3 !== peg$FAILED) { - // @ts-ignore - s2.push(s3) - // @ts-ignore - s3 = peg$currPos - // @ts-ignore - s4 = peg$parse_() - // @ts-ignore - s5 = peg$currPos - // @ts-ignore - s6 = peg$parseOr() - // @ts-ignore - if (s6 === peg$FAILED) { - // @ts-ignore - s6 = peg$parseAnd() - } - // @ts-ignore - if (s6 !== peg$FAILED) { - // @ts-ignore - s7 = peg$parse_() - // @ts-ignore - s6 = [s6, s7] - // @ts-ignore - s5 = s6 - // @ts-ignore - } else { - // @ts-ignore - peg$currPos = s5 - // @ts-ignore - s5 = peg$FAILED - } - // @ts-ignore - if (s5 === peg$FAILED) { - // @ts-ignore - s5 = null - } - // @ts-ignore - s6 = peg$parseEmverVersionRangeAtom() - // @ts-ignore - if (s6 !== peg$FAILED) { - // @ts-ignore - s4 = [s4, s5, s6] - // @ts-ignore - s3 = s4 - // @ts-ignore - } else { - // @ts-ignore - peg$currPos = s3 - // @ts-ignore - s3 = peg$FAILED - } - } - // @ts-ignore - s1 = [s1, s2] - // @ts-ignore - s0 = s1 - // @ts-ignore - } else { - // @ts-ignore - peg$currPos = s0 - // @ts-ignore - s0 = peg$FAILED - } - - // @ts-ignore - return s0 - } - - // @ts-ignore - function // @ts-ignore - peg$parseEmverVersionRangeAtom() { - // @ts-ignore - var s0 - - // @ts-ignore - s0 = peg$parseEmverParens() - // @ts-ignore - if (s0 === peg$FAILED) { - // @ts-ignore - s0 = peg$parseEmverAnchor() - // @ts-ignore - if (s0 === peg$FAILED) { - // @ts-ignore - s0 = peg$parseEmverNot() - // @ts-ignore - if (s0 === peg$FAILED) { - // @ts-ignore - s0 = peg$parseAny() - // @ts-ignore - if (s0 === peg$FAILED) { - // @ts-ignore - s0 = peg$parseNone() - } - } - } - } - - // @ts-ignore - return s0 - } - - // @ts-ignore - function // @ts-ignore - peg$parseEmverParens() { - // @ts-ignore - var s0, s1, s2, s3, s4, s5 - - // @ts-ignore - s0 = peg$currPos - // @ts-ignore - if (input.charCodeAt(peg$currPos) === 40) { - // @ts-ignore - s1 = peg$c2 - // @ts-ignore - peg$currPos++ - // @ts-ignore - } else { - // @ts-ignore - s1 = peg$FAILED - // @ts-ignore - if (peg$silentFails === 0) { - peg$fail(peg$e2) - } - } - // @ts-ignore - if (s1 !== peg$FAILED) { - // @ts-ignore - s2 = peg$parse_() - // @ts-ignore - s3 = peg$parseEmverVersionRange() - // @ts-ignore - if (s3 !== peg$FAILED) { - // @ts-ignore - s4 = peg$parse_() - // @ts-ignore - if (input.charCodeAt(peg$currPos) === 41) { - // @ts-ignore - s5 = peg$c3 - // @ts-ignore - peg$currPos++ - // @ts-ignore - } else { - // @ts-ignore - s5 = peg$FAILED - // @ts-ignore - if (peg$silentFails === 0) { - peg$fail(peg$e3) - } - } - // @ts-ignore - if (s5 !== peg$FAILED) { - // @ts-ignore - peg$savedPos = s0 - // @ts-ignore - s0 = peg$f16(s3) - // @ts-ignore - } else { - // @ts-ignore - peg$currPos = s0 - // @ts-ignore - s0 = peg$FAILED - } - // @ts-ignore - } else { - // @ts-ignore - peg$currPos = s0 - // @ts-ignore - s0 = peg$FAILED - } - // @ts-ignore - } else { - // @ts-ignore - peg$currPos = s0 - // @ts-ignore - s0 = peg$FAILED - } - - // @ts-ignore - return s0 - } - - // @ts-ignore - function // @ts-ignore - peg$parseEmverAnchor() { - // @ts-ignore - var s0, s1, s2, s3 - - // @ts-ignore - s0 = peg$currPos - // @ts-ignore - s1 = peg$parseCmpOp() - // @ts-ignore - if (s1 === peg$FAILED) { - // @ts-ignore - s1 = null - } - // @ts-ignore - s2 = peg$parse_() - // @ts-ignore - s3 = peg$parseEmver() - // @ts-ignore - if (s3 !== peg$FAILED) { - // @ts-ignore - peg$savedPos = s0 - // @ts-ignore - s0 = peg$f17(s1, s3) - // @ts-ignore - } else { - // @ts-ignore - peg$currPos = s0 - // @ts-ignore - s0 = peg$FAILED - } - - // @ts-ignore - return s0 - } - - // @ts-ignore - function // @ts-ignore - peg$parseEmverNot() { - // @ts-ignore - var s0, s1, s2, s3 - - // @ts-ignore - s0 = peg$currPos - // @ts-ignore - if (input.charCodeAt(peg$currPos) === 33) { - // @ts-ignore - s1 = peg$c6 - // @ts-ignore - peg$currPos++ - // @ts-ignore - } else { - // @ts-ignore - s1 = peg$FAILED - // @ts-ignore - if (peg$silentFails === 0) { - peg$fail(peg$e6) - } - } - // @ts-ignore - if (s1 !== peg$FAILED) { - // @ts-ignore - s2 = peg$parse_() - // @ts-ignore - s3 = peg$parseEmverVersionRangeAtom() - // @ts-ignore - if (s3 !== peg$FAILED) { - // @ts-ignore - peg$savedPos = s0 - // @ts-ignore - s0 = peg$f18(s3) - // @ts-ignore - } else { - // @ts-ignore - peg$currPos = s0 - // @ts-ignore - s0 = peg$FAILED - } - // @ts-ignore - } else { - // @ts-ignore - peg$currPos = s0 - // @ts-ignore - s0 = peg$FAILED - } - - // @ts-ignore - return s0 - } - - // @ts-ignore - function // @ts-ignore - peg$parseEmver() { - // @ts-ignore - var s0, s1, s2, s3, s4, s5, s6, s7, s8 - - // @ts-ignore - s0 = peg$currPos - // @ts-ignore - s1 = peg$parseDigit() - // @ts-ignore - if (s1 !== peg$FAILED) { - // @ts-ignore - if (input.charCodeAt(peg$currPos) === 46) { - // @ts-ignore - s2 = peg$c16 - // @ts-ignore - peg$currPos++ - // @ts-ignore - } else { - // @ts-ignore - s2 = peg$FAILED - // @ts-ignore - if (peg$silentFails === 0) { - peg$fail(peg$e16) - } - } - // @ts-ignore - if (s2 !== peg$FAILED) { - // @ts-ignore - s3 = peg$parseDigit() - // @ts-ignore - if (s3 !== peg$FAILED) { - // @ts-ignore - if (input.charCodeAt(peg$currPos) === 46) { - // @ts-ignore - s4 = peg$c16 - // @ts-ignore - peg$currPos++ - // @ts-ignore - } else { - // @ts-ignore - s4 = peg$FAILED - // @ts-ignore - if (peg$silentFails === 0) { - peg$fail(peg$e16) - } - } - // @ts-ignore - if (s4 !== peg$FAILED) { - // @ts-ignore - s5 = peg$parseDigit() - // @ts-ignore - if (s5 !== peg$FAILED) { - // @ts-ignore - s6 = peg$currPos - // @ts-ignore - if (input.charCodeAt(peg$currPos) === 46) { - // @ts-ignore - s7 = peg$c16 - // @ts-ignore - peg$currPos++ - // @ts-ignore - } else { - // @ts-ignore - s7 = peg$FAILED - // @ts-ignore - if (peg$silentFails === 0) { - peg$fail(peg$e16) - } - } - // @ts-ignore - if (s7 !== peg$FAILED) { - // @ts-ignore - s8 = peg$parseDigit() - // @ts-ignore - if (s8 !== peg$FAILED) { - // @ts-ignore - peg$savedPos = s6 - // @ts-ignore - s6 = peg$f19(s1, s3, s5, s8) - // @ts-ignore - } else { - // @ts-ignore - peg$currPos = s6 - // @ts-ignore - s6 = peg$FAILED - } - // @ts-ignore - } else { - // @ts-ignore - peg$currPos = s6 - // @ts-ignore - s6 = peg$FAILED - } - // @ts-ignore - if (s6 === peg$FAILED) { - // @ts-ignore - s6 = null - } - // @ts-ignore - peg$savedPos = s0 - // @ts-ignore - s0 = peg$f20(s1, s3, s5, s6) - // @ts-ignore - } else { - // @ts-ignore - peg$currPos = s0 - // @ts-ignore - s0 = peg$FAILED - } - // @ts-ignore - } else { - // @ts-ignore - peg$currPos = s0 - // @ts-ignore - s0 = peg$FAILED - } - // @ts-ignore - } else { - // @ts-ignore - peg$currPos = s0 - // @ts-ignore - s0 = peg$FAILED - } - // @ts-ignore - } else { - // @ts-ignore - peg$currPos = s0 - // @ts-ignore - s0 = peg$FAILED - } - // @ts-ignore - } else { - // @ts-ignore - peg$currPos = s0 - // @ts-ignore - s0 = peg$FAILED - } - - // @ts-ignore - return s0 - } - - // @ts-ignore - function // @ts-ignore - peg$parseFlavor() { - // @ts-ignore - var s0, s1, s2, s3 - - // @ts-ignore - s0 = peg$currPos - // @ts-ignore - if (input.charCodeAt(peg$currPos) === 35) { - // @ts-ignore - s1 = peg$c5 - // @ts-ignore - peg$currPos++ - // @ts-ignore - } else { - // @ts-ignore - s1 = peg$FAILED - // @ts-ignore - if (peg$silentFails === 0) { - peg$fail(peg$e5) - } - } - // @ts-ignore - if (s1 !== peg$FAILED) { - // @ts-ignore - s2 = peg$parseFlavorString() - // @ts-ignore - if (input.charCodeAt(peg$currPos) === 58) { - // @ts-ignore - s3 = peg$c4 - // @ts-ignore - peg$currPos++ - // @ts-ignore - } else { - // @ts-ignore - s3 = peg$FAILED - // @ts-ignore - if (peg$silentFails === 0) { - peg$fail(peg$e4) - } - } - // @ts-ignore - if (s3 !== peg$FAILED) { - // @ts-ignore - peg$savedPos = s0 - // @ts-ignore - s0 = peg$f21(s2) - // @ts-ignore - } else { - // @ts-ignore - peg$currPos = s0 - // @ts-ignore - s0 = peg$FAILED - } - // @ts-ignore - } else { - // @ts-ignore - peg$currPos = s0 - // @ts-ignore - s0 = peg$FAILED - } - - // @ts-ignore - return s0 - } - - // @ts-ignore - function // @ts-ignore - peg$parseFlavorString() { - // @ts-ignore - var s0, s1, s2 - - // @ts-ignore - s0 = peg$currPos - // @ts-ignore - s1 = [] - // @ts-ignore - if (peg$r0.test(input.charAt(peg$currPos))) { - // @ts-ignore - s2 = input.charAt(peg$currPos) - // @ts-ignore - peg$currPos++ - // @ts-ignore - } else { - // @ts-ignore - s2 = peg$FAILED - // @ts-ignore - if (peg$silentFails === 0) { - peg$fail(peg$e17) - } - } - // @ts-ignore - while (s2 !== peg$FAILED) { - // @ts-ignore - s1.push(s2) - // @ts-ignore - if (peg$r0.test(input.charAt(peg$currPos))) { - // @ts-ignore - s2 = input.charAt(peg$currPos) - // @ts-ignore - peg$currPos++ - // @ts-ignore - } else { - // @ts-ignore - s2 = peg$FAILED - // @ts-ignore - if (peg$silentFails === 0) { - peg$fail(peg$e17) - } - } - } - // @ts-ignore - peg$savedPos = s0 - // @ts-ignore - s1 = peg$f22() - // @ts-ignore - s0 = s1 - - // @ts-ignore - return s0 - } - - // @ts-ignore - function // @ts-ignore - peg$parseString() { - // @ts-ignore - var s0, s1, s2 - - // @ts-ignore - s0 = peg$currPos - // @ts-ignore - s1 = [] - // @ts-ignore - if (peg$r1.test(input.charAt(peg$currPos))) { - // @ts-ignore - s2 = input.charAt(peg$currPos) - // @ts-ignore - peg$currPos++ - // @ts-ignore - } else { - // @ts-ignore - s2 = peg$FAILED - // @ts-ignore - if (peg$silentFails === 0) { - peg$fail(peg$e18) - } - } - // @ts-ignore - if (s2 !== peg$FAILED) { - // @ts-ignore - while (s2 !== peg$FAILED) { - // @ts-ignore - s1.push(s2) - // @ts-ignore - if (peg$r1.test(input.charAt(peg$currPos))) { - // @ts-ignore - s2 = input.charAt(peg$currPos) - // @ts-ignore - peg$currPos++ - // @ts-ignore - } else { - // @ts-ignore - s2 = peg$FAILED - // @ts-ignore - if (peg$silentFails === 0) { - peg$fail(peg$e18) - } - } - } - // @ts-ignore - } else { - // @ts-ignore - s1 = peg$FAILED - } - // @ts-ignore - if (s1 !== peg$FAILED) { - // @ts-ignore - peg$savedPos = s0 - // @ts-ignore - s1 = peg$f23() - } - // @ts-ignore - s0 = s1 - - // @ts-ignore - return s0 - } - - // @ts-ignore - function // @ts-ignore - peg$parseVersion() { - // @ts-ignore - var s0, s1, s2 - - // @ts-ignore - s0 = peg$currPos - // @ts-ignore - s1 = peg$parseVersionNumber() - // @ts-ignore - if (s1 !== peg$FAILED) { - // @ts-ignore - s2 = peg$parsePreRelease() - // @ts-ignore - if (s2 === peg$FAILED) { - // @ts-ignore - s2 = null - } - // @ts-ignore - peg$savedPos = s0 - // @ts-ignore - s0 = peg$f24(s1, s2) - // @ts-ignore - } else { - // @ts-ignore - peg$currPos = s0 - // @ts-ignore - s0 = peg$FAILED - } - - // @ts-ignore - return s0 - } - - // @ts-ignore - function // @ts-ignore - peg$parsePreRelease() { - // @ts-ignore - var s0, s1, s2, s3, s4, s5, s6 - - // @ts-ignore - s0 = peg$currPos - // @ts-ignore - if (input.charCodeAt(peg$currPos) === 45) { - // @ts-ignore - s1 = peg$c17 - // @ts-ignore - peg$currPos++ - // @ts-ignore - } else { - // @ts-ignore - s1 = peg$FAILED - // @ts-ignore - if (peg$silentFails === 0) { - peg$fail(peg$e19) - } - } - // @ts-ignore - if (s1 !== peg$FAILED) { - // @ts-ignore - s2 = peg$parsePreReleaseSegment() - // @ts-ignore - if (s2 !== peg$FAILED) { - // @ts-ignore - s3 = [] - // @ts-ignore - s4 = peg$currPos - // @ts-ignore - if (input.charCodeAt(peg$currPos) === 46) { - // @ts-ignore - s5 = peg$c16 - // @ts-ignore - peg$currPos++ - // @ts-ignore - } else { - // @ts-ignore - s5 = peg$FAILED - // @ts-ignore - if (peg$silentFails === 0) { - peg$fail(peg$e16) - } - } - // @ts-ignore - if (s5 !== peg$FAILED) { - // @ts-ignore - s6 = peg$parsePreReleaseSegment() - // @ts-ignore - if (s6 !== peg$FAILED) { - // @ts-ignore - s5 = [s5, s6] - // @ts-ignore - s4 = s5 - // @ts-ignore - } else { - // @ts-ignore - peg$currPos = s4 - // @ts-ignore - s4 = peg$FAILED - } - // @ts-ignore - } else { - // @ts-ignore - peg$currPos = s4 - // @ts-ignore - s4 = peg$FAILED - } - // @ts-ignore - while (s4 !== peg$FAILED) { - // @ts-ignore - s3.push(s4) - // @ts-ignore - s4 = peg$currPos - // @ts-ignore - if (input.charCodeAt(peg$currPos) === 46) { - // @ts-ignore - s5 = peg$c16 - // @ts-ignore - peg$currPos++ - // @ts-ignore - } else { - // @ts-ignore - s5 = peg$FAILED - // @ts-ignore - if (peg$silentFails === 0) { - peg$fail(peg$e16) - } - } - // @ts-ignore - if (s5 !== peg$FAILED) { - // @ts-ignore - s6 = peg$parsePreReleaseSegment() - // @ts-ignore - if (s6 !== peg$FAILED) { - // @ts-ignore - s5 = [s5, s6] - // @ts-ignore - s4 = s5 - // @ts-ignore - } else { - // @ts-ignore - peg$currPos = s4 - // @ts-ignore - s4 = peg$FAILED - } - // @ts-ignore - } else { - // @ts-ignore - peg$currPos = s4 - // @ts-ignore - s4 = peg$FAILED - } - } - // @ts-ignore - peg$savedPos = s0 - // @ts-ignore - s0 = peg$f25(s2, s3) - // @ts-ignore - } else { - // @ts-ignore - peg$currPos = s0 - // @ts-ignore - s0 = peg$FAILED - } - // @ts-ignore - } else { - // @ts-ignore - peg$currPos = s0 - // @ts-ignore - s0 = peg$FAILED - } - - // @ts-ignore - return s0 - } - - // @ts-ignore - function // @ts-ignore - peg$parsePreReleaseSegment() { - // @ts-ignore - var s0, s1, s2 - - // @ts-ignore - s0 = peg$currPos - // @ts-ignore - if (input.charCodeAt(peg$currPos) === 46) { - // @ts-ignore - s1 = peg$c16 - // @ts-ignore - peg$currPos++ - // @ts-ignore - } else { - // @ts-ignore - s1 = peg$FAILED - // @ts-ignore - if (peg$silentFails === 0) { - peg$fail(peg$e16) - } - } - // @ts-ignore - if (s1 === peg$FAILED) { - // @ts-ignore - s1 = null - } - // @ts-ignore - s2 = peg$parseDigit() - // @ts-ignore - if (s2 === peg$FAILED) { - // @ts-ignore - s2 = peg$parseString() - } - // @ts-ignore - if (s2 !== peg$FAILED) { - // @ts-ignore - peg$savedPos = s0 - // @ts-ignore - s0 = peg$f26(s2) - // @ts-ignore - } else { - // @ts-ignore - peg$currPos = s0 - // @ts-ignore - s0 = peg$FAILED - } - - // @ts-ignore - return s0 - } - - // @ts-ignore - function // @ts-ignore - peg$parseVersionNumber() { - // @ts-ignore - var s0, s1, s2, s3, s4, s5 - - // @ts-ignore - s0 = peg$currPos - // @ts-ignore - s1 = peg$parseDigit() - // @ts-ignore - if (s1 !== peg$FAILED) { - // @ts-ignore - s2 = [] - // @ts-ignore - s3 = peg$currPos - // @ts-ignore - if (input.charCodeAt(peg$currPos) === 46) { - // @ts-ignore - s4 = peg$c16 - // @ts-ignore - peg$currPos++ - // @ts-ignore - } else { - // @ts-ignore - s4 = peg$FAILED - // @ts-ignore - if (peg$silentFails === 0) { - peg$fail(peg$e16) - } - } - // @ts-ignore - if (s4 !== peg$FAILED) { - // @ts-ignore - s5 = peg$parseDigit() - // @ts-ignore - if (s5 !== peg$FAILED) { - // @ts-ignore - s4 = [s4, s5] - // @ts-ignore - s3 = s4 - // @ts-ignore - } else { - // @ts-ignore - peg$currPos = s3 - // @ts-ignore - s3 = peg$FAILED - } - // @ts-ignore - } else { - // @ts-ignore - peg$currPos = s3 - // @ts-ignore - s3 = peg$FAILED - } - // @ts-ignore - while (s3 !== peg$FAILED) { - // @ts-ignore - s2.push(s3) - // @ts-ignore - s3 = peg$currPos - // @ts-ignore - if (input.charCodeAt(peg$currPos) === 46) { - // @ts-ignore - s4 = peg$c16 - // @ts-ignore - peg$currPos++ - // @ts-ignore - } else { - // @ts-ignore - s4 = peg$FAILED - // @ts-ignore - if (peg$silentFails === 0) { - peg$fail(peg$e16) - } - } - // @ts-ignore - if (s4 !== peg$FAILED) { - // @ts-ignore - s5 = peg$parseDigit() - // @ts-ignore - if (s5 !== peg$FAILED) { - // @ts-ignore - s4 = [s4, s5] - // @ts-ignore - s3 = s4 - // @ts-ignore - } else { - // @ts-ignore - peg$currPos = s3 - // @ts-ignore - s3 = peg$FAILED - } - // @ts-ignore - } else { - // @ts-ignore - peg$currPos = s3 - // @ts-ignore - s3 = peg$FAILED - } - } - // @ts-ignore - peg$savedPos = s0 - // @ts-ignore - s0 = peg$f27(s1, s2) - // @ts-ignore - } else { - // @ts-ignore - peg$currPos = s0 - // @ts-ignore - s0 = peg$FAILED - } - - // @ts-ignore - return s0 - } - - // @ts-ignore - function // @ts-ignore - peg$parseDigit() { - // @ts-ignore - var s0, s1, s2 - - // @ts-ignore - s0 = peg$currPos - // @ts-ignore - s1 = [] - // @ts-ignore - if (peg$r2.test(input.charAt(peg$currPos))) { - // @ts-ignore - s2 = input.charAt(peg$currPos) - // @ts-ignore - peg$currPos++ - // @ts-ignore - } else { - // @ts-ignore - s2 = peg$FAILED - // @ts-ignore - if (peg$silentFails === 0) { - peg$fail(peg$e20) - } - } - // @ts-ignore - if (s2 !== peg$FAILED) { - // @ts-ignore - while (s2 !== peg$FAILED) { - // @ts-ignore - s1.push(s2) - // @ts-ignore - if (peg$r2.test(input.charAt(peg$currPos))) { - // @ts-ignore - s2 = input.charAt(peg$currPos) - // @ts-ignore - peg$currPos++ - // @ts-ignore - } else { - // @ts-ignore - s2 = peg$FAILED - // @ts-ignore - if (peg$silentFails === 0) { - peg$fail(peg$e20) - } - } - } - // @ts-ignore - } else { - // @ts-ignore - s1 = peg$FAILED - } - // @ts-ignore - if (s1 !== peg$FAILED) { - // @ts-ignore - peg$savedPos = s0 - // @ts-ignore - s1 = peg$f28() - } - // @ts-ignore - s0 = s1 - - // @ts-ignore - return s0 - } - - // @ts-ignore - function // @ts-ignore - peg$parse_() { - // @ts-ignore - var s0, s1 - - // @ts-ignore - peg$silentFails++ - // @ts-ignore - s0 = [] - // @ts-ignore - if (peg$r3.test(input.charAt(peg$currPos))) { - // @ts-ignore - s1 = input.charAt(peg$currPos) - // @ts-ignore - peg$currPos++ - // @ts-ignore - } else { - // @ts-ignore - s1 = peg$FAILED - // @ts-ignore - if (peg$silentFails === 0) { - peg$fail(peg$e22) - } - } - // @ts-ignore - while (s1 !== peg$FAILED) { - // @ts-ignore - s0.push(s1) - // @ts-ignore - if (peg$r3.test(input.charAt(peg$currPos))) { - // @ts-ignore - s1 = input.charAt(peg$currPos) - // @ts-ignore - peg$currPos++ - // @ts-ignore - } else { - // @ts-ignore - s1 = peg$FAILED - // @ts-ignore - if (peg$silentFails === 0) { - peg$fail(peg$e22) - } - } - } - // @ts-ignore - peg$silentFails-- - // @ts-ignore - s1 = peg$FAILED - // @ts-ignore - if (peg$silentFails === 0) { - peg$fail(peg$e21) - } - - // @ts-ignore - return s0 - } - - // @ts-ignore - peg$result = peg$startRuleFunction() - - // @ts-ignore - if (peg$result !== peg$FAILED && peg$currPos === input.length) { - // @ts-ignore - return peg$result - // @ts-ignore - } else { - // @ts-ignore - if (peg$result !== peg$FAILED && peg$currPos < input.length) { - // @ts-ignore - peg$fail(peg$endExpectation()) - } - - // @ts-ignore - throw peg$buildStructuredError( - // @ts-ignore - peg$maxFailExpected, - // @ts-ignore - peg$maxFailPos < input.length ? input.charAt(peg$maxFailPos) : null, - // @ts-ignore - peg$maxFailPos < input.length - ? // @ts-ignore - peg$computeLocation(peg$maxFailPos, peg$maxFailPos + 1) - : // @ts-ignore - peg$computeLocation(peg$maxFailPos, peg$maxFailPos), - ) +const peggyParser: {parse: any, SyntaxError: any, DefaultTracer?: any} = // Generated by Peggy 3.0.2. +// +// https://peggyjs.org/ +// @ts-ignore +(function() { +// @ts-ignore + "use strict"; + +// @ts-ignore +function peg$subclass(child, parent) { +// @ts-ignore + function C() { this.constructor = child; } +// @ts-ignore + C.prototype = parent.prototype; +// @ts-ignore + child.prototype = new C(); +} + +// @ts-ignore +function peg$SyntaxError(message, expected, found, location) { +// @ts-ignore + var self = Error.call(this, message); + // istanbul ignore next Check is a necessary evil to support older environments +// @ts-ignore + if (Object.setPrototypeOf) { +// @ts-ignore + Object.setPrototypeOf(self, peg$SyntaxError.prototype); + } +// @ts-ignore + self.expected = expected; +// @ts-ignore + self.found = found; +// @ts-ignore + self.location = location; +// @ts-ignore + self.name = "SyntaxError"; +// @ts-ignore + return self; +} + +// @ts-ignore +peg$subclass(peg$SyntaxError, Error); + +// @ts-ignore +function peg$padEnd(str, targetLength, padString) { +// @ts-ignore + padString = padString || " "; +// @ts-ignore + if (str.length > targetLength) { return str; } +// @ts-ignore + targetLength -= str.length; +// @ts-ignore + padString += padString.repeat(targetLength); +// @ts-ignore + return str + padString.slice(0, targetLength); +} + +// @ts-ignore +peg$SyntaxError.prototype.format = function(sources) { +// @ts-ignore + var str = "Error: " + this.message; +// @ts-ignore + if (this.location) { +// @ts-ignore + var src = null; +// @ts-ignore + var k; +// @ts-ignore + for (k = 0; k < sources.length; k++) { +// @ts-ignore + if (sources[k].source === this.location.source) { +// @ts-ignore + src = sources[k].text.split(/\r\n|\n|\r/g); +// @ts-ignore + break; } } +// @ts-ignore + var s = this.location.start; +// @ts-ignore + var offset_s = (this.location.source && (typeof this.location.source.offset === "function")) +// @ts-ignore + ? this.location.source.offset(s) +// @ts-ignore + : s; +// @ts-ignore + var loc = this.location.source + ":" + offset_s.line + ":" + offset_s.column; +// @ts-ignore + if (src) { +// @ts-ignore + var e = this.location.end; +// @ts-ignore + var filler = peg$padEnd("", offset_s.line.toString().length, ' '); +// @ts-ignore + var line = src[s.line - 1]; +// @ts-ignore + var last = s.line === e.line ? e.column : line.length + 1; +// @ts-ignore + var hatLen = (last - s.column) || 1; +// @ts-ignore + str += "\n --> " + loc + "\n" +// @ts-ignore + + filler + " |\n" +// @ts-ignore + + offset_s.line + " | " + line + "\n" +// @ts-ignore + + filler + " | " + peg$padEnd("", s.column - 1, ' ') +// @ts-ignore + + peg$padEnd("", hatLen, "^"); +// @ts-ignore + } else { +// @ts-ignore + str += "\n at " + loc; + } + } +// @ts-ignore + return str; +}; - // @ts-ignore +// @ts-ignore +peg$SyntaxError.buildMessage = function(expected, found) { +// @ts-ignore + var DESCRIBE_EXPECTATION_FNS = { +// @ts-ignore + literal: function(expectation) { +// @ts-ignore + return "\"" + literalEscape(expectation.text) + "\""; + }, + +// @ts-ignore + class: function(expectation) { +// @ts-ignore + var escapedParts = expectation.parts.map(function(part) { +// @ts-ignore + return Array.isArray(part) +// @ts-ignore + ? classEscape(part[0]) + "-" + classEscape(part[1]) +// @ts-ignore + : classEscape(part); + }); + +// @ts-ignore + return "[" + (expectation.inverted ? "^" : "") + escapedParts.join("") + "]"; + }, + +// @ts-ignore + any: function() { +// @ts-ignore + return "any character"; + }, + +// @ts-ignore + end: function() { +// @ts-ignore + return "end of input"; + }, + +// @ts-ignore + other: function(expectation) { +// @ts-ignore + return expectation.description; + } + }; + +// @ts-ignore + function hex(ch) { +// @ts-ignore + return ch.charCodeAt(0).toString(16).toUpperCase(); + } + +// @ts-ignore + function literalEscape(s) { +// @ts-ignore + return s +// @ts-ignore + .replace(/\\/g, "\\\\") +// @ts-ignore + .replace(/"/g, "\\\"") +// @ts-ignore + .replace(/\0/g, "\\0") +// @ts-ignore + .replace(/\t/g, "\\t") +// @ts-ignore + .replace(/\n/g, "\\n") +// @ts-ignore + .replace(/\r/g, "\\r") +// @ts-ignore + .replace(/[\x00-\x0F]/g, function(ch) { return "\\x0" + hex(ch); }) +// @ts-ignore + .replace(/[\x10-\x1F\x7F-\x9F]/g, function(ch) { return "\\x" + hex(ch); }); + } + +// @ts-ignore + function classEscape(s) { +// @ts-ignore + return s +// @ts-ignore + .replace(/\\/g, "\\\\") +// @ts-ignore + .replace(/\]/g, "\\]") +// @ts-ignore + .replace(/\^/g, "\\^") +// @ts-ignore + .replace(/-/g, "\\-") +// @ts-ignore + .replace(/\0/g, "\\0") +// @ts-ignore + .replace(/\t/g, "\\t") +// @ts-ignore + .replace(/\n/g, "\\n") +// @ts-ignore + .replace(/\r/g, "\\r") +// @ts-ignore + .replace(/[\x00-\x0F]/g, function(ch) { return "\\x0" + hex(ch); }) +// @ts-ignore + .replace(/[\x10-\x1F\x7F-\x9F]/g, function(ch) { return "\\x" + hex(ch); }); + } + +// @ts-ignore + function describeExpectation(expectation) { +// @ts-ignore + return DESCRIBE_EXPECTATION_FNS[expectation.type](expectation); + } + +// @ts-ignore + function describeExpected(expected) { +// @ts-ignore + var descriptions = expected.map(describeExpectation); +// @ts-ignore + var i, j; + +// @ts-ignore + descriptions.sort(); + +// @ts-ignore + if (descriptions.length > 0) { +// @ts-ignore + for (i = 1, j = 1; i < descriptions.length; i++) { +// @ts-ignore + if (descriptions[i - 1] !== descriptions[i]) { +// @ts-ignore + descriptions[j] = descriptions[i]; +// @ts-ignore + j++; + } + } +// @ts-ignore + descriptions.length = j; + } + +// @ts-ignore + switch (descriptions.length) { +// @ts-ignore + case 1: +// @ts-ignore + return descriptions[0]; + +// @ts-ignore + case 2: +// @ts-ignore + return descriptions[0] + " or " + descriptions[1]; + +// @ts-ignore + default: +// @ts-ignore + return descriptions.slice(0, -1).join(", ") +// @ts-ignore + + ", or " +// @ts-ignore + + descriptions[descriptions.length - 1]; + } + } + +// @ts-ignore + function describeFound(found) { +// @ts-ignore + return found ? "\"" + literalEscape(found) + "\"" : "end of input"; + } + +// @ts-ignore + return "Expected " + describeExpected(expected) + " but " + describeFound(found) + " found."; +}; + +// @ts-ignore +function peg$parse(input, options) { +// @ts-ignore + options = options !== undefined ? options : {}; + +// @ts-ignore + var peg$FAILED = {}; +// @ts-ignore + var peg$source = options.grammarSource; + +// @ts-ignore + var peg$startRuleFunctions = { VersionRange: peg$parseVersionRange, Or: peg$parseOr, And: peg$parseAnd, VersionRangeAtom: peg$parseVersionRangeAtom, Parens: peg$parseParens, Anchor: peg$parseAnchor, VersionSpec: peg$parseVersionSpec, FlavorAtom: peg$parseFlavorAtom, Not: peg$parseNot, Any: peg$parseAny, None: peg$parseNone, CmpOp: peg$parseCmpOp, ExtendedVersion: peg$parseExtendedVersion, EmverVersionRange: peg$parseEmverVersionRange, EmverVersionRangeAtom: peg$parseEmverVersionRangeAtom, EmverParens: peg$parseEmverParens, EmverAnchor: peg$parseEmverAnchor, EmverNot: peg$parseEmverNot, Emver: peg$parseEmver, Flavor: peg$parseFlavor, FlavorString: peg$parseFlavorString, String: peg$parseString, Version: peg$parseVersion, PreRelease: peg$parsePreRelease, PreReleaseSegment: peg$parsePreReleaseSegment, VersionNumber: peg$parseVersionNumber, Digit: peg$parseDigit, _: peg$parse_ }; +// @ts-ignore + var peg$startRuleFunction = peg$parseVersionRange; + +// @ts-ignore + var peg$c0 = "||"; + var peg$c1 = "&&"; + var peg$c2 = "("; + var peg$c3 = ")"; + var peg$c4 = ":"; + var peg$c5 = "#"; + var peg$c6 = "!"; + var peg$c7 = "*"; + var peg$c8 = ">="; + var peg$c9 = "<="; + var peg$c10 = ">"; + var peg$c11 = "<"; + var peg$c12 = "="; + var peg$c13 = "!="; + var peg$c14 = "^"; + var peg$c15 = "~"; + var peg$c16 = "."; + var peg$c17 = "-"; + + var peg$r0 = /^[a-z]/; + var peg$r1 = /^[a-zA-Z]/; + var peg$r2 = /^[0-9]/; + var peg$r3 = /^[ \t\n\r]/; + + var peg$e0 = peg$literalExpectation("||", false); + var peg$e1 = peg$literalExpectation("&&", false); + var peg$e2 = peg$literalExpectation("(", false); + var peg$e3 = peg$literalExpectation(")", false); + var peg$e4 = peg$literalExpectation(":", false); + var peg$e5 = peg$literalExpectation("#", false); + var peg$e6 = peg$literalExpectation("!", false); + var peg$e7 = peg$literalExpectation("*", false); + var peg$e8 = peg$literalExpectation(">=", false); + var peg$e9 = peg$literalExpectation("<=", false); + var peg$e10 = peg$literalExpectation(">", false); + var peg$e11 = peg$literalExpectation("<", false); + var peg$e12 = peg$literalExpectation("=", false); + var peg$e13 = peg$literalExpectation("!=", false); + var peg$e14 = peg$literalExpectation("^", false); + var peg$e15 = peg$literalExpectation("~", false); + var peg$e16 = peg$literalExpectation(".", false); + var peg$e17 = peg$classExpectation([["a", "z"]], false, false); + var peg$e18 = peg$classExpectation([["a", "z"], ["A", "Z"]], false, false); + var peg$e19 = peg$literalExpectation("-", false); + var peg$e20 = peg$classExpectation([["0", "9"]], false, false); + var peg$e21 = peg$otherExpectation("whitespace"); + var peg$e22 = peg$classExpectation([" ", "\t", "\n", "\r"], false, false); +// @ts-ignore + + var peg$f0 = function(expr) {// @ts-ignore + return { type: "Parens", expr } };// @ts-ignore + + var peg$f1 = function(operator, version) {// @ts-ignore + return { type: "Anchor", operator, version } };// @ts-ignore + + var peg$f2 = function(flavor, upstream, downstream) {// @ts-ignore + return { flavor: flavor || null, upstream, downstream: downstream ? downstream[1] : { number: [0], prerelease: [] } } };// @ts-ignore + + var peg$f3 = function(flavor) {// @ts-ignore + return { type: "Flavor", flavor: flavor } };// @ts-ignore + + var peg$f4 = function(value) {// @ts-ignore + return { type: "Not", value: value }};// @ts-ignore + + var peg$f5 = function() {// @ts-ignore + return { type: "Any" } };// @ts-ignore + + var peg$f6 = function() {// @ts-ignore + return { type: "None" } };// @ts-ignore + + var peg$f7 = function() {// @ts-ignore + return ">="; };// @ts-ignore + + var peg$f8 = function() {// @ts-ignore + return "<="; };// @ts-ignore + + var peg$f9 = function() {// @ts-ignore + return ">"; };// @ts-ignore + + var peg$f10 = function() {// @ts-ignore + return "<"; };// @ts-ignore + + var peg$f11 = function() {// @ts-ignore + return "="; };// @ts-ignore + + var peg$f12 = function() {// @ts-ignore + return "!="; };// @ts-ignore + + var peg$f13 = function() {// @ts-ignore + return "^"; };// @ts-ignore + + var peg$f14 = function() {// @ts-ignore + return "~"; };// @ts-ignore + + var peg$f15 = function(flavor, upstream, downstream) { +// @ts-ignore + return { flavor: flavor || null, upstream, downstream } + };// @ts-ignore + + var peg$f16 = function(expr) {// @ts-ignore + return { type: "Parens", expr } };// @ts-ignore + + var peg$f17 = function(operator, version) {// @ts-ignore + return { type: "Anchor", operator, version } };// @ts-ignore + + var peg$f18 = function(value) {// @ts-ignore + return { type: "Not", value: value }};// @ts-ignore + + var peg$f19 = function(major, minor, patch, revision) {// @ts-ignore + return revision };// @ts-ignore + + var peg$f20 = function(major, minor, patch, revision) { +// @ts-ignore return { - SyntaxError: peg$SyntaxError, - parse: peg$parse, +// @ts-ignore + flavor: null, +// @ts-ignore + upstream: { +// @ts-ignore + number: [major, minor, patch], +// @ts-ignore + prerelease: [], + }, +// @ts-ignore + downstream: { +// @ts-ignore + number: [revision || 0], +// @ts-ignore + prerelease: [], + }, } - })() + };// @ts-ignore + + var peg$f21 = function(flavor) {// @ts-ignore + return flavor };// @ts-ignore + + var peg$f22 = function() {// @ts-ignore + return text() };// @ts-ignore + + var peg$f23 = function() {// @ts-ignore + return text(); };// @ts-ignore + + var peg$f24 = function(number, prerelease) { +// @ts-ignore + return { +// @ts-ignore + number, +// @ts-ignore + prerelease: prerelease || [] + }; + };// @ts-ignore + + var peg$f25 = function(first, rest) { +// @ts-ignore + return [first].concat(rest.map(r => r[1])); + };// @ts-ignore + + var peg$f26 = function(segment) { +// @ts-ignore + return segment; + };// @ts-ignore + + var peg$f27 = function(first, rest) { +// @ts-ignore + return [first].concat(rest.map(r => r[1])); + };// @ts-ignore + + var peg$f28 = function() {// @ts-ignore + return parseInt(text(), 10); }; +// @ts-ignore + var peg$currPos = 0; +// @ts-ignore + var peg$savedPos = 0; +// @ts-ignore + var peg$posDetailsCache = [{ line: 1, column: 1 }]; +// @ts-ignore + var peg$maxFailPos = 0; +// @ts-ignore + var peg$maxFailExpected = []; +// @ts-ignore + var peg$silentFails = 0; + +// @ts-ignore + var peg$result; + +// @ts-ignore + if ("startRule" in options) { +// @ts-ignore + if (!(options.startRule in peg$startRuleFunctions)) { +// @ts-ignore + throw new Error("Can't start parsing from rule \"" + options.startRule + "\"."); + } + +// @ts-ignore + peg$startRuleFunction = peg$startRuleFunctions[options.startRule]; + } + +// @ts-ignore + function text() { +// @ts-ignore + return input.substring(peg$savedPos, peg$currPos); + } + +// @ts-ignore + function offset() { +// @ts-ignore + return peg$savedPos; + } + +// @ts-ignore + function range() { +// @ts-ignore + return { +// @ts-ignore + source: peg$source, +// @ts-ignore + start: peg$savedPos, +// @ts-ignore + end: peg$currPos + }; + } + +// @ts-ignore + function location() { +// @ts-ignore + return peg$computeLocation(peg$savedPos, peg$currPos); + } + +// @ts-ignore + function expected(description, location) { +// @ts-ignore + location = location !== undefined +// @ts-ignore + ? location +// @ts-ignore + : peg$computeLocation(peg$savedPos, peg$currPos); + +// @ts-ignore + throw peg$buildStructuredError( +// @ts-ignore + [peg$otherExpectation(description)], +// @ts-ignore + input.substring(peg$savedPos, peg$currPos), +// @ts-ignore + location + ); + } + +// @ts-ignore + function error(message, location) { +// @ts-ignore + location = location !== undefined +// @ts-ignore + ? location +// @ts-ignore + : peg$computeLocation(peg$savedPos, peg$currPos); + +// @ts-ignore + throw peg$buildSimpleError(message, location); + } + +// @ts-ignore + function peg$literalExpectation(text, ignoreCase) { +// @ts-ignore + return { type: "literal", text: text, ignoreCase: ignoreCase }; + } + +// @ts-ignore + function peg$classExpectation(parts, inverted, ignoreCase) { +// @ts-ignore + return { type: "class", parts: parts, inverted: inverted, ignoreCase: ignoreCase }; + } + +// @ts-ignore + function peg$anyExpectation() { +// @ts-ignore + return { type: "any" }; + } + +// @ts-ignore + function peg$endExpectation() { +// @ts-ignore + return { type: "end" }; + } + +// @ts-ignore + function peg$otherExpectation(description) { +// @ts-ignore + return { type: "other", description: description }; + } + +// @ts-ignore + function peg$computePosDetails(pos) { +// @ts-ignore + var details = peg$posDetailsCache[pos]; +// @ts-ignore + var p; + +// @ts-ignore + if (details) { +// @ts-ignore + return details; +// @ts-ignore + } else { +// @ts-ignore + p = pos - 1; +// @ts-ignore + while (!peg$posDetailsCache[p]) { +// @ts-ignore + p--; + } + +// @ts-ignore + details = peg$posDetailsCache[p]; +// @ts-ignore + details = { +// @ts-ignore + line: details.line, +// @ts-ignore + column: details.column + }; + +// @ts-ignore + while (p < pos) { +// @ts-ignore + if (input.charCodeAt(p) === 10) { +// @ts-ignore + details.line++; +// @ts-ignore + details.column = 1; +// @ts-ignore + } else { +// @ts-ignore + details.column++; + } + +// @ts-ignore + p++; + } + +// @ts-ignore + peg$posDetailsCache[pos] = details; + +// @ts-ignore + return details; + } + } + +// @ts-ignore + function peg$computeLocation(startPos, endPos, offset) { +// @ts-ignore + var startPosDetails = peg$computePosDetails(startPos); +// @ts-ignore + var endPosDetails = peg$computePosDetails(endPos); + +// @ts-ignore + var res = { +// @ts-ignore + source: peg$source, +// @ts-ignore + start: { +// @ts-ignore + offset: startPos, +// @ts-ignore + line: startPosDetails.line, +// @ts-ignore + column: startPosDetails.column + }, +// @ts-ignore + end: { +// @ts-ignore + offset: endPos, +// @ts-ignore + line: endPosDetails.line, +// @ts-ignore + column: endPosDetails.column + } + }; +// @ts-ignore + if (offset && peg$source && (typeof peg$source.offset === "function")) { +// @ts-ignore + res.start = peg$source.offset(res.start); +// @ts-ignore + res.end = peg$source.offset(res.end); + } +// @ts-ignore + return res; + } + +// @ts-ignore + function peg$fail(expected) { +// @ts-ignore + if (peg$currPos < peg$maxFailPos) { return; } + +// @ts-ignore + if (peg$currPos > peg$maxFailPos) { +// @ts-ignore + peg$maxFailPos = peg$currPos; +// @ts-ignore + peg$maxFailExpected = []; + } + +// @ts-ignore + peg$maxFailExpected.push(expected); + } + +// @ts-ignore + function peg$buildSimpleError(message, location) { +// @ts-ignore + return new peg$SyntaxError(message, null, null, location); + } + +// @ts-ignore + function peg$buildStructuredError(expected, found, location) { +// @ts-ignore + return new peg$SyntaxError( +// @ts-ignore + peg$SyntaxError.buildMessage(expected, found), +// @ts-ignore + expected, +// @ts-ignore + found, +// @ts-ignore + location + ); + } + +// @ts-ignore + function // @ts-ignore +peg$parseVersionRange() { +// @ts-ignore + var s0, s1, s2, s3, s4, s5, s6, s7; + +// @ts-ignore + s0 = peg$currPos; +// @ts-ignore + s1 = peg$parseVersionRangeAtom(); +// @ts-ignore + if (s1 !== peg$FAILED) { +// @ts-ignore + s2 = []; +// @ts-ignore + s3 = peg$currPos; +// @ts-ignore + s4 = peg$parse_(); +// @ts-ignore + s5 = peg$currPos; +// @ts-ignore + s6 = peg$parseOr(); +// @ts-ignore + if (s6 === peg$FAILED) { +// @ts-ignore + s6 = peg$parseAnd(); + } +// @ts-ignore + if (s6 !== peg$FAILED) { +// @ts-ignore + s7 = peg$parse_(); +// @ts-ignore + s6 = [s6, s7]; +// @ts-ignore + s5 = s6; +// @ts-ignore + } else { +// @ts-ignore + peg$currPos = s5; +// @ts-ignore + s5 = peg$FAILED; + } +// @ts-ignore + if (s5 === peg$FAILED) { +// @ts-ignore + s5 = null; + } +// @ts-ignore + s6 = peg$parseVersionRangeAtom(); +// @ts-ignore + if (s6 !== peg$FAILED) { +// @ts-ignore + s4 = [s4, s5, s6]; +// @ts-ignore + s3 = s4; +// @ts-ignore + } else { +// @ts-ignore + peg$currPos = s3; +// @ts-ignore + s3 = peg$FAILED; + } +// @ts-ignore + while (s3 !== peg$FAILED) { +// @ts-ignore + s2.push(s3); +// @ts-ignore + s3 = peg$currPos; +// @ts-ignore + s4 = peg$parse_(); +// @ts-ignore + s5 = peg$currPos; +// @ts-ignore + s6 = peg$parseOr(); +// @ts-ignore + if (s6 === peg$FAILED) { +// @ts-ignore + s6 = peg$parseAnd(); + } +// @ts-ignore + if (s6 !== peg$FAILED) { +// @ts-ignore + s7 = peg$parse_(); +// @ts-ignore + s6 = [s6, s7]; +// @ts-ignore + s5 = s6; +// @ts-ignore + } else { +// @ts-ignore + peg$currPos = s5; +// @ts-ignore + s5 = peg$FAILED; + } +// @ts-ignore + if (s5 === peg$FAILED) { +// @ts-ignore + s5 = null; + } +// @ts-ignore + s6 = peg$parseVersionRangeAtom(); +// @ts-ignore + if (s6 !== peg$FAILED) { +// @ts-ignore + s4 = [s4, s5, s6]; +// @ts-ignore + s3 = s4; +// @ts-ignore + } else { +// @ts-ignore + peg$currPos = s3; +// @ts-ignore + s3 = peg$FAILED; + } + } +// @ts-ignore + s1 = [s1, s2]; +// @ts-ignore + s0 = s1; +// @ts-ignore + } else { +// @ts-ignore + peg$currPos = s0; +// @ts-ignore + s0 = peg$FAILED; + } + +// @ts-ignore + return s0; + } + +// @ts-ignore + function // @ts-ignore +peg$parseOr() { +// @ts-ignore + var s0; + +// @ts-ignore + if (input.substr(peg$currPos, 2) === peg$c0) { +// @ts-ignore + s0 = peg$c0; +// @ts-ignore + peg$currPos += 2; +// @ts-ignore + } else { +// @ts-ignore + s0 = peg$FAILED; +// @ts-ignore + if (peg$silentFails === 0) { peg$fail(peg$e0); } + } + +// @ts-ignore + return s0; + } + +// @ts-ignore + function // @ts-ignore +peg$parseAnd() { +// @ts-ignore + var s0; + +// @ts-ignore + if (input.substr(peg$currPos, 2) === peg$c1) { +// @ts-ignore + s0 = peg$c1; +// @ts-ignore + peg$currPos += 2; +// @ts-ignore + } else { +// @ts-ignore + s0 = peg$FAILED; +// @ts-ignore + if (peg$silentFails === 0) { peg$fail(peg$e1); } + } + +// @ts-ignore + return s0; + } + +// @ts-ignore + function // @ts-ignore +peg$parseVersionRangeAtom() { +// @ts-ignore + var s0; + +// @ts-ignore + s0 = peg$parseParens(); +// @ts-ignore + if (s0 === peg$FAILED) { +// @ts-ignore + s0 = peg$parseAnchor(); +// @ts-ignore + if (s0 === peg$FAILED) { +// @ts-ignore + s0 = peg$parseNot(); +// @ts-ignore + if (s0 === peg$FAILED) { +// @ts-ignore + s0 = peg$parseAny(); +// @ts-ignore + if (s0 === peg$FAILED) { +// @ts-ignore + s0 = peg$parseNone(); +// @ts-ignore + if (s0 === peg$FAILED) { +// @ts-ignore + s0 = peg$parseFlavorAtom(); + } + } + } + } + } + +// @ts-ignore + return s0; + } + +// @ts-ignore + function // @ts-ignore +peg$parseParens() { +// @ts-ignore + var s0, s1, s2, s3, s4, s5; + +// @ts-ignore + s0 = peg$currPos; +// @ts-ignore + if (input.charCodeAt(peg$currPos) === 40) { +// @ts-ignore + s1 = peg$c2; +// @ts-ignore + peg$currPos++; +// @ts-ignore + } else { +// @ts-ignore + s1 = peg$FAILED; +// @ts-ignore + if (peg$silentFails === 0) { peg$fail(peg$e2); } + } +// @ts-ignore + if (s1 !== peg$FAILED) { +// @ts-ignore + s2 = peg$parse_(); +// @ts-ignore + s3 = peg$parseVersionRange(); +// @ts-ignore + if (s3 !== peg$FAILED) { +// @ts-ignore + s4 = peg$parse_(); +// @ts-ignore + if (input.charCodeAt(peg$currPos) === 41) { +// @ts-ignore + s5 = peg$c3; +// @ts-ignore + peg$currPos++; +// @ts-ignore + } else { +// @ts-ignore + s5 = peg$FAILED; +// @ts-ignore + if (peg$silentFails === 0) { peg$fail(peg$e3); } + } +// @ts-ignore + if (s5 !== peg$FAILED) { +// @ts-ignore + peg$savedPos = s0; +// @ts-ignore + s0 = peg$f0(s3); +// @ts-ignore + } else { +// @ts-ignore + peg$currPos = s0; +// @ts-ignore + s0 = peg$FAILED; + } +// @ts-ignore + } else { +// @ts-ignore + peg$currPos = s0; +// @ts-ignore + s0 = peg$FAILED; + } +// @ts-ignore + } else { +// @ts-ignore + peg$currPos = s0; +// @ts-ignore + s0 = peg$FAILED; + } + +// @ts-ignore + return s0; + } + +// @ts-ignore + function // @ts-ignore +peg$parseAnchor() { +// @ts-ignore + var s0, s1, s2, s3; + +// @ts-ignore + s0 = peg$currPos; +// @ts-ignore + s1 = peg$parseCmpOp(); +// @ts-ignore + if (s1 === peg$FAILED) { +// @ts-ignore + s1 = null; + } +// @ts-ignore + s2 = peg$parse_(); +// @ts-ignore + s3 = peg$parseVersionSpec(); +// @ts-ignore + if (s3 !== peg$FAILED) { +// @ts-ignore + peg$savedPos = s0; +// @ts-ignore + s0 = peg$f1(s1, s3); +// @ts-ignore + } else { +// @ts-ignore + peg$currPos = s0; +// @ts-ignore + s0 = peg$FAILED; + } + +// @ts-ignore + return s0; + } + +// @ts-ignore + function // @ts-ignore +peg$parseVersionSpec() { +// @ts-ignore + var s0, s1, s2, s3, s4, s5; + +// @ts-ignore + s0 = peg$currPos; +// @ts-ignore + s1 = peg$parseFlavor(); +// @ts-ignore + if (s1 === peg$FAILED) { +// @ts-ignore + s1 = null; + } +// @ts-ignore + s2 = peg$parseVersion(); +// @ts-ignore + if (s2 !== peg$FAILED) { +// @ts-ignore + s3 = peg$currPos; +// @ts-ignore + if (input.charCodeAt(peg$currPos) === 58) { +// @ts-ignore + s4 = peg$c4; +// @ts-ignore + peg$currPos++; +// @ts-ignore + } else { +// @ts-ignore + s4 = peg$FAILED; +// @ts-ignore + if (peg$silentFails === 0) { peg$fail(peg$e4); } + } +// @ts-ignore + if (s4 !== peg$FAILED) { +// @ts-ignore + s5 = peg$parseVersion(); +// @ts-ignore + if (s5 !== peg$FAILED) { +// @ts-ignore + s4 = [s4, s5]; +// @ts-ignore + s3 = s4; +// @ts-ignore + } else { +// @ts-ignore + peg$currPos = s3; +// @ts-ignore + s3 = peg$FAILED; + } +// @ts-ignore + } else { +// @ts-ignore + peg$currPos = s3; +// @ts-ignore + s3 = peg$FAILED; + } +// @ts-ignore + if (s3 === peg$FAILED) { +// @ts-ignore + s3 = null; + } +// @ts-ignore + peg$savedPos = s0; +// @ts-ignore + s0 = peg$f2(s1, s2, s3); +// @ts-ignore + } else { +// @ts-ignore + peg$currPos = s0; +// @ts-ignore + s0 = peg$FAILED; + } + +// @ts-ignore + return s0; + } + +// @ts-ignore + function // @ts-ignore +peg$parseFlavorAtom() { +// @ts-ignore + var s0, s1, s2; + +// @ts-ignore + s0 = peg$currPos; +// @ts-ignore + if (input.charCodeAt(peg$currPos) === 35) { +// @ts-ignore + s1 = peg$c5; +// @ts-ignore + peg$currPos++; +// @ts-ignore + } else { +// @ts-ignore + s1 = peg$FAILED; +// @ts-ignore + if (peg$silentFails === 0) { peg$fail(peg$e5); } + } +// @ts-ignore + if (s1 !== peg$FAILED) { +// @ts-ignore + s2 = peg$parseFlavorString(); +// @ts-ignore + peg$savedPos = s0; +// @ts-ignore + s0 = peg$f3(s2); +// @ts-ignore + } else { +// @ts-ignore + peg$currPos = s0; +// @ts-ignore + s0 = peg$FAILED; + } + +// @ts-ignore + return s0; + } + +// @ts-ignore + function // @ts-ignore +peg$parseNot() { +// @ts-ignore + var s0, s1, s2, s3; + +// @ts-ignore + s0 = peg$currPos; +// @ts-ignore + if (input.charCodeAt(peg$currPos) === 33) { +// @ts-ignore + s1 = peg$c6; +// @ts-ignore + peg$currPos++; +// @ts-ignore + } else { +// @ts-ignore + s1 = peg$FAILED; +// @ts-ignore + if (peg$silentFails === 0) { peg$fail(peg$e6); } + } +// @ts-ignore + if (s1 !== peg$FAILED) { +// @ts-ignore + s2 = peg$parse_(); +// @ts-ignore + s3 = peg$parseVersionRangeAtom(); +// @ts-ignore + if (s3 !== peg$FAILED) { +// @ts-ignore + peg$savedPos = s0; +// @ts-ignore + s0 = peg$f4(s3); +// @ts-ignore + } else { +// @ts-ignore + peg$currPos = s0; +// @ts-ignore + s0 = peg$FAILED; + } +// @ts-ignore + } else { +// @ts-ignore + peg$currPos = s0; +// @ts-ignore + s0 = peg$FAILED; + } + +// @ts-ignore + return s0; + } + +// @ts-ignore + function // @ts-ignore +peg$parseAny() { +// @ts-ignore + var s0, s1; + +// @ts-ignore + s0 = peg$currPos; +// @ts-ignore + if (input.charCodeAt(peg$currPos) === 42) { +// @ts-ignore + s1 = peg$c7; +// @ts-ignore + peg$currPos++; +// @ts-ignore + } else { +// @ts-ignore + s1 = peg$FAILED; +// @ts-ignore + if (peg$silentFails === 0) { peg$fail(peg$e7); } + } +// @ts-ignore + if (s1 !== peg$FAILED) { +// @ts-ignore + peg$savedPos = s0; +// @ts-ignore + s1 = peg$f5(); + } +// @ts-ignore + s0 = s1; + +// @ts-ignore + return s0; + } + +// @ts-ignore + function // @ts-ignore +peg$parseNone() { +// @ts-ignore + var s0, s1; + +// @ts-ignore + s0 = peg$currPos; +// @ts-ignore + if (input.charCodeAt(peg$currPos) === 33) { +// @ts-ignore + s1 = peg$c6; +// @ts-ignore + peg$currPos++; +// @ts-ignore + } else { +// @ts-ignore + s1 = peg$FAILED; +// @ts-ignore + if (peg$silentFails === 0) { peg$fail(peg$e6); } + } +// @ts-ignore + if (s1 !== peg$FAILED) { +// @ts-ignore + peg$savedPos = s0; +// @ts-ignore + s1 = peg$f6(); + } +// @ts-ignore + s0 = s1; + +// @ts-ignore + return s0; + } + +// @ts-ignore + function // @ts-ignore +peg$parseCmpOp() { +// @ts-ignore + var s0, s1; + +// @ts-ignore + s0 = peg$currPos; +// @ts-ignore + if (input.substr(peg$currPos, 2) === peg$c8) { +// @ts-ignore + s1 = peg$c8; +// @ts-ignore + peg$currPos += 2; +// @ts-ignore + } else { +// @ts-ignore + s1 = peg$FAILED; +// @ts-ignore + if (peg$silentFails === 0) { peg$fail(peg$e8); } + } +// @ts-ignore + if (s1 !== peg$FAILED) { +// @ts-ignore + peg$savedPos = s0; +// @ts-ignore + s1 = peg$f7(); + } +// @ts-ignore + s0 = s1; +// @ts-ignore + if (s0 === peg$FAILED) { +// @ts-ignore + s0 = peg$currPos; +// @ts-ignore + if (input.substr(peg$currPos, 2) === peg$c9) { +// @ts-ignore + s1 = peg$c9; +// @ts-ignore + peg$currPos += 2; +// @ts-ignore + } else { +// @ts-ignore + s1 = peg$FAILED; +// @ts-ignore + if (peg$silentFails === 0) { peg$fail(peg$e9); } + } +// @ts-ignore + if (s1 !== peg$FAILED) { +// @ts-ignore + peg$savedPos = s0; +// @ts-ignore + s1 = peg$f8(); + } +// @ts-ignore + s0 = s1; +// @ts-ignore + if (s0 === peg$FAILED) { +// @ts-ignore + s0 = peg$currPos; +// @ts-ignore + if (input.charCodeAt(peg$currPos) === 62) { +// @ts-ignore + s1 = peg$c10; +// @ts-ignore + peg$currPos++; +// @ts-ignore + } else { +// @ts-ignore + s1 = peg$FAILED; +// @ts-ignore + if (peg$silentFails === 0) { peg$fail(peg$e10); } + } +// @ts-ignore + if (s1 !== peg$FAILED) { +// @ts-ignore + peg$savedPos = s0; +// @ts-ignore + s1 = peg$f9(); + } +// @ts-ignore + s0 = s1; +// @ts-ignore + if (s0 === peg$FAILED) { +// @ts-ignore + s0 = peg$currPos; +// @ts-ignore + if (input.charCodeAt(peg$currPos) === 60) { +// @ts-ignore + s1 = peg$c11; +// @ts-ignore + peg$currPos++; +// @ts-ignore + } else { +// @ts-ignore + s1 = peg$FAILED; +// @ts-ignore + if (peg$silentFails === 0) { peg$fail(peg$e11); } + } +// @ts-ignore + if (s1 !== peg$FAILED) { +// @ts-ignore + peg$savedPos = s0; +// @ts-ignore + s1 = peg$f10(); + } +// @ts-ignore + s0 = s1; +// @ts-ignore + if (s0 === peg$FAILED) { +// @ts-ignore + s0 = peg$currPos; +// @ts-ignore + if (input.charCodeAt(peg$currPos) === 61) { +// @ts-ignore + s1 = peg$c12; +// @ts-ignore + peg$currPos++; +// @ts-ignore + } else { +// @ts-ignore + s1 = peg$FAILED; +// @ts-ignore + if (peg$silentFails === 0) { peg$fail(peg$e12); } + } +// @ts-ignore + if (s1 !== peg$FAILED) { +// @ts-ignore + peg$savedPos = s0; +// @ts-ignore + s1 = peg$f11(); + } +// @ts-ignore + s0 = s1; +// @ts-ignore + if (s0 === peg$FAILED) { +// @ts-ignore + s0 = peg$currPos; +// @ts-ignore + if (input.substr(peg$currPos, 2) === peg$c13) { +// @ts-ignore + s1 = peg$c13; +// @ts-ignore + peg$currPos += 2; +// @ts-ignore + } else { +// @ts-ignore + s1 = peg$FAILED; +// @ts-ignore + if (peg$silentFails === 0) { peg$fail(peg$e13); } + } +// @ts-ignore + if (s1 !== peg$FAILED) { +// @ts-ignore + peg$savedPos = s0; +// @ts-ignore + s1 = peg$f12(); + } +// @ts-ignore + s0 = s1; +// @ts-ignore + if (s0 === peg$FAILED) { +// @ts-ignore + s0 = peg$currPos; +// @ts-ignore + if (input.charCodeAt(peg$currPos) === 94) { +// @ts-ignore + s1 = peg$c14; +// @ts-ignore + peg$currPos++; +// @ts-ignore + } else { +// @ts-ignore + s1 = peg$FAILED; +// @ts-ignore + if (peg$silentFails === 0) { peg$fail(peg$e14); } + } +// @ts-ignore + if (s1 !== peg$FAILED) { +// @ts-ignore + peg$savedPos = s0; +// @ts-ignore + s1 = peg$f13(); + } +// @ts-ignore + s0 = s1; +// @ts-ignore + if (s0 === peg$FAILED) { +// @ts-ignore + s0 = peg$currPos; +// @ts-ignore + if (input.charCodeAt(peg$currPos) === 126) { +// @ts-ignore + s1 = peg$c15; +// @ts-ignore + peg$currPos++; +// @ts-ignore + } else { +// @ts-ignore + s1 = peg$FAILED; +// @ts-ignore + if (peg$silentFails === 0) { peg$fail(peg$e15); } + } +// @ts-ignore + if (s1 !== peg$FAILED) { +// @ts-ignore + peg$savedPos = s0; +// @ts-ignore + s1 = peg$f14(); + } +// @ts-ignore + s0 = s1; + } + } + } + } + } + } + } + +// @ts-ignore + return s0; + } + +// @ts-ignore + function // @ts-ignore +peg$parseExtendedVersion() { +// @ts-ignore + var s0, s1, s2, s3, s4; + +// @ts-ignore + s0 = peg$currPos; +// @ts-ignore + s1 = peg$parseFlavor(); +// @ts-ignore + if (s1 === peg$FAILED) { +// @ts-ignore + s1 = null; + } +// @ts-ignore + s2 = peg$parseVersion(); +// @ts-ignore + if (s2 !== peg$FAILED) { +// @ts-ignore + if (input.charCodeAt(peg$currPos) === 58) { +// @ts-ignore + s3 = peg$c4; +// @ts-ignore + peg$currPos++; +// @ts-ignore + } else { +// @ts-ignore + s3 = peg$FAILED; +// @ts-ignore + if (peg$silentFails === 0) { peg$fail(peg$e4); } + } +// @ts-ignore + if (s3 !== peg$FAILED) { +// @ts-ignore + s4 = peg$parseVersion(); +// @ts-ignore + if (s4 !== peg$FAILED) { +// @ts-ignore + peg$savedPos = s0; +// @ts-ignore + s0 = peg$f15(s1, s2, s4); +// @ts-ignore + } else { +// @ts-ignore + peg$currPos = s0; +// @ts-ignore + s0 = peg$FAILED; + } +// @ts-ignore + } else { +// @ts-ignore + peg$currPos = s0; +// @ts-ignore + s0 = peg$FAILED; + } +// @ts-ignore + } else { +// @ts-ignore + peg$currPos = s0; +// @ts-ignore + s0 = peg$FAILED; + } + +// @ts-ignore + return s0; + } + +// @ts-ignore + function // @ts-ignore +peg$parseEmverVersionRange() { +// @ts-ignore + var s0, s1, s2, s3, s4, s5, s6, s7; + +// @ts-ignore + s0 = peg$currPos; +// @ts-ignore + s1 = peg$parseEmverVersionRangeAtom(); +// @ts-ignore + if (s1 !== peg$FAILED) { +// @ts-ignore + s2 = []; +// @ts-ignore + s3 = peg$currPos; +// @ts-ignore + s4 = peg$parse_(); +// @ts-ignore + s5 = peg$currPos; +// @ts-ignore + s6 = peg$parseOr(); +// @ts-ignore + if (s6 === peg$FAILED) { +// @ts-ignore + s6 = peg$parseAnd(); + } +// @ts-ignore + if (s6 !== peg$FAILED) { +// @ts-ignore + s7 = peg$parse_(); +// @ts-ignore + s6 = [s6, s7]; +// @ts-ignore + s5 = s6; +// @ts-ignore + } else { +// @ts-ignore + peg$currPos = s5; +// @ts-ignore + s5 = peg$FAILED; + } +// @ts-ignore + if (s5 === peg$FAILED) { +// @ts-ignore + s5 = null; + } +// @ts-ignore + s6 = peg$parseEmverVersionRangeAtom(); +// @ts-ignore + if (s6 !== peg$FAILED) { +// @ts-ignore + s4 = [s4, s5, s6]; +// @ts-ignore + s3 = s4; +// @ts-ignore + } else { +// @ts-ignore + peg$currPos = s3; +// @ts-ignore + s3 = peg$FAILED; + } +// @ts-ignore + while (s3 !== peg$FAILED) { +// @ts-ignore + s2.push(s3); +// @ts-ignore + s3 = peg$currPos; +// @ts-ignore + s4 = peg$parse_(); +// @ts-ignore + s5 = peg$currPos; +// @ts-ignore + s6 = peg$parseOr(); +// @ts-ignore + if (s6 === peg$FAILED) { +// @ts-ignore + s6 = peg$parseAnd(); + } +// @ts-ignore + if (s6 !== peg$FAILED) { +// @ts-ignore + s7 = peg$parse_(); +// @ts-ignore + s6 = [s6, s7]; +// @ts-ignore + s5 = s6; +// @ts-ignore + } else { +// @ts-ignore + peg$currPos = s5; +// @ts-ignore + s5 = peg$FAILED; + } +// @ts-ignore + if (s5 === peg$FAILED) { +// @ts-ignore + s5 = null; + } +// @ts-ignore + s6 = peg$parseEmverVersionRangeAtom(); +// @ts-ignore + if (s6 !== peg$FAILED) { +// @ts-ignore + s4 = [s4, s5, s6]; +// @ts-ignore + s3 = s4; +// @ts-ignore + } else { +// @ts-ignore + peg$currPos = s3; +// @ts-ignore + s3 = peg$FAILED; + } + } +// @ts-ignore + s1 = [s1, s2]; +// @ts-ignore + s0 = s1; +// @ts-ignore + } else { +// @ts-ignore + peg$currPos = s0; +// @ts-ignore + s0 = peg$FAILED; + } + +// @ts-ignore + return s0; + } + +// @ts-ignore + function // @ts-ignore +peg$parseEmverVersionRangeAtom() { +// @ts-ignore + var s0; + +// @ts-ignore + s0 = peg$parseEmverParens(); +// @ts-ignore + if (s0 === peg$FAILED) { +// @ts-ignore + s0 = peg$parseEmverAnchor(); +// @ts-ignore + if (s0 === peg$FAILED) { +// @ts-ignore + s0 = peg$parseEmverNot(); +// @ts-ignore + if (s0 === peg$FAILED) { +// @ts-ignore + s0 = peg$parseAny(); +// @ts-ignore + if (s0 === peg$FAILED) { +// @ts-ignore + s0 = peg$parseNone(); + } + } + } + } + +// @ts-ignore + return s0; + } + +// @ts-ignore + function // @ts-ignore +peg$parseEmverParens() { +// @ts-ignore + var s0, s1, s2, s3, s4, s5; + +// @ts-ignore + s0 = peg$currPos; +// @ts-ignore + if (input.charCodeAt(peg$currPos) === 40) { +// @ts-ignore + s1 = peg$c2; +// @ts-ignore + peg$currPos++; +// @ts-ignore + } else { +// @ts-ignore + s1 = peg$FAILED; +// @ts-ignore + if (peg$silentFails === 0) { peg$fail(peg$e2); } + } +// @ts-ignore + if (s1 !== peg$FAILED) { +// @ts-ignore + s2 = peg$parse_(); +// @ts-ignore + s3 = peg$parseEmverVersionRange(); +// @ts-ignore + if (s3 !== peg$FAILED) { +// @ts-ignore + s4 = peg$parse_(); +// @ts-ignore + if (input.charCodeAt(peg$currPos) === 41) { +// @ts-ignore + s5 = peg$c3; +// @ts-ignore + peg$currPos++; +// @ts-ignore + } else { +// @ts-ignore + s5 = peg$FAILED; +// @ts-ignore + if (peg$silentFails === 0) { peg$fail(peg$e3); } + } +// @ts-ignore + if (s5 !== peg$FAILED) { +// @ts-ignore + peg$savedPos = s0; +// @ts-ignore + s0 = peg$f16(s3); +// @ts-ignore + } else { +// @ts-ignore + peg$currPos = s0; +// @ts-ignore + s0 = peg$FAILED; + } +// @ts-ignore + } else { +// @ts-ignore + peg$currPos = s0; +// @ts-ignore + s0 = peg$FAILED; + } +// @ts-ignore + } else { +// @ts-ignore + peg$currPos = s0; +// @ts-ignore + s0 = peg$FAILED; + } + +// @ts-ignore + return s0; + } + +// @ts-ignore + function // @ts-ignore +peg$parseEmverAnchor() { +// @ts-ignore + var s0, s1, s2, s3; + +// @ts-ignore + s0 = peg$currPos; +// @ts-ignore + s1 = peg$parseCmpOp(); +// @ts-ignore + if (s1 === peg$FAILED) { +// @ts-ignore + s1 = null; + } +// @ts-ignore + s2 = peg$parse_(); +// @ts-ignore + s3 = peg$parseEmver(); +// @ts-ignore + if (s3 !== peg$FAILED) { +// @ts-ignore + peg$savedPos = s0; +// @ts-ignore + s0 = peg$f17(s1, s3); +// @ts-ignore + } else { +// @ts-ignore + peg$currPos = s0; +// @ts-ignore + s0 = peg$FAILED; + } + +// @ts-ignore + return s0; + } + +// @ts-ignore + function // @ts-ignore +peg$parseEmverNot() { +// @ts-ignore + var s0, s1, s2, s3; + +// @ts-ignore + s0 = peg$currPos; +// @ts-ignore + if (input.charCodeAt(peg$currPos) === 33) { +// @ts-ignore + s1 = peg$c6; +// @ts-ignore + peg$currPos++; +// @ts-ignore + } else { +// @ts-ignore + s1 = peg$FAILED; +// @ts-ignore + if (peg$silentFails === 0) { peg$fail(peg$e6); } + } +// @ts-ignore + if (s1 !== peg$FAILED) { +// @ts-ignore + s2 = peg$parse_(); +// @ts-ignore + s3 = peg$parseEmverVersionRangeAtom(); +// @ts-ignore + if (s3 !== peg$FAILED) { +// @ts-ignore + peg$savedPos = s0; +// @ts-ignore + s0 = peg$f18(s3); +// @ts-ignore + } else { +// @ts-ignore + peg$currPos = s0; +// @ts-ignore + s0 = peg$FAILED; + } +// @ts-ignore + } else { +// @ts-ignore + peg$currPos = s0; +// @ts-ignore + s0 = peg$FAILED; + } + +// @ts-ignore + return s0; + } + +// @ts-ignore + function // @ts-ignore +peg$parseEmver() { +// @ts-ignore + var s0, s1, s2, s3, s4, s5, s6, s7, s8; + +// @ts-ignore + s0 = peg$currPos; +// @ts-ignore + s1 = peg$parseDigit(); +// @ts-ignore + if (s1 !== peg$FAILED) { +// @ts-ignore + if (input.charCodeAt(peg$currPos) === 46) { +// @ts-ignore + s2 = peg$c16; +// @ts-ignore + peg$currPos++; +// @ts-ignore + } else { +// @ts-ignore + s2 = peg$FAILED; +// @ts-ignore + if (peg$silentFails === 0) { peg$fail(peg$e16); } + } +// @ts-ignore + if (s2 !== peg$FAILED) { +// @ts-ignore + s3 = peg$parseDigit(); +// @ts-ignore + if (s3 !== peg$FAILED) { +// @ts-ignore + if (input.charCodeAt(peg$currPos) === 46) { +// @ts-ignore + s4 = peg$c16; +// @ts-ignore + peg$currPos++; +// @ts-ignore + } else { +// @ts-ignore + s4 = peg$FAILED; +// @ts-ignore + if (peg$silentFails === 0) { peg$fail(peg$e16); } + } +// @ts-ignore + if (s4 !== peg$FAILED) { +// @ts-ignore + s5 = peg$parseDigit(); +// @ts-ignore + if (s5 !== peg$FAILED) { +// @ts-ignore + s6 = peg$currPos; +// @ts-ignore + if (input.charCodeAt(peg$currPos) === 46) { +// @ts-ignore + s7 = peg$c16; +// @ts-ignore + peg$currPos++; +// @ts-ignore + } else { +// @ts-ignore + s7 = peg$FAILED; +// @ts-ignore + if (peg$silentFails === 0) { peg$fail(peg$e16); } + } +// @ts-ignore + if (s7 !== peg$FAILED) { +// @ts-ignore + s8 = peg$parseDigit(); +// @ts-ignore + if (s8 !== peg$FAILED) { +// @ts-ignore + peg$savedPos = s6; +// @ts-ignore + s6 = peg$f19(s1, s3, s5, s8); +// @ts-ignore + } else { +// @ts-ignore + peg$currPos = s6; +// @ts-ignore + s6 = peg$FAILED; + } +// @ts-ignore + } else { +// @ts-ignore + peg$currPos = s6; +// @ts-ignore + s6 = peg$FAILED; + } +// @ts-ignore + if (s6 === peg$FAILED) { +// @ts-ignore + s6 = null; + } +// @ts-ignore + peg$savedPos = s0; +// @ts-ignore + s0 = peg$f20(s1, s3, s5, s6); +// @ts-ignore + } else { +// @ts-ignore + peg$currPos = s0; +// @ts-ignore + s0 = peg$FAILED; + } +// @ts-ignore + } else { +// @ts-ignore + peg$currPos = s0; +// @ts-ignore + s0 = peg$FAILED; + } +// @ts-ignore + } else { +// @ts-ignore + peg$currPos = s0; +// @ts-ignore + s0 = peg$FAILED; + } +// @ts-ignore + } else { +// @ts-ignore + peg$currPos = s0; +// @ts-ignore + s0 = peg$FAILED; + } +// @ts-ignore + } else { +// @ts-ignore + peg$currPos = s0; +// @ts-ignore + s0 = peg$FAILED; + } + +// @ts-ignore + return s0; + } + +// @ts-ignore + function // @ts-ignore +peg$parseFlavor() { +// @ts-ignore + var s0, s1, s2, s3; + +// @ts-ignore + s0 = peg$currPos; +// @ts-ignore + if (input.charCodeAt(peg$currPos) === 35) { +// @ts-ignore + s1 = peg$c5; +// @ts-ignore + peg$currPos++; +// @ts-ignore + } else { +// @ts-ignore + s1 = peg$FAILED; +// @ts-ignore + if (peg$silentFails === 0) { peg$fail(peg$e5); } + } +// @ts-ignore + if (s1 !== peg$FAILED) { +// @ts-ignore + s2 = peg$parseFlavorString(); +// @ts-ignore + if (input.charCodeAt(peg$currPos) === 58) { +// @ts-ignore + s3 = peg$c4; +// @ts-ignore + peg$currPos++; +// @ts-ignore + } else { +// @ts-ignore + s3 = peg$FAILED; +// @ts-ignore + if (peg$silentFails === 0) { peg$fail(peg$e4); } + } +// @ts-ignore + if (s3 !== peg$FAILED) { +// @ts-ignore + peg$savedPos = s0; +// @ts-ignore + s0 = peg$f21(s2); +// @ts-ignore + } else { +// @ts-ignore + peg$currPos = s0; +// @ts-ignore + s0 = peg$FAILED; + } +// @ts-ignore + } else { +// @ts-ignore + peg$currPos = s0; +// @ts-ignore + s0 = peg$FAILED; + } + +// @ts-ignore + return s0; + } + +// @ts-ignore + function // @ts-ignore +peg$parseFlavorString() { +// @ts-ignore + var s0, s1, s2; + +// @ts-ignore + s0 = peg$currPos; +// @ts-ignore + s1 = []; +// @ts-ignore + if (peg$r0.test(input.charAt(peg$currPos))) { +// @ts-ignore + s2 = input.charAt(peg$currPos); +// @ts-ignore + peg$currPos++; +// @ts-ignore + } else { +// @ts-ignore + s2 = peg$FAILED; +// @ts-ignore + if (peg$silentFails === 0) { peg$fail(peg$e17); } + } +// @ts-ignore + while (s2 !== peg$FAILED) { +// @ts-ignore + s1.push(s2); +// @ts-ignore + if (peg$r0.test(input.charAt(peg$currPos))) { +// @ts-ignore + s2 = input.charAt(peg$currPos); +// @ts-ignore + peg$currPos++; +// @ts-ignore + } else { +// @ts-ignore + s2 = peg$FAILED; +// @ts-ignore + if (peg$silentFails === 0) { peg$fail(peg$e17); } + } + } +// @ts-ignore + peg$savedPos = s0; +// @ts-ignore + s1 = peg$f22(); +// @ts-ignore + s0 = s1; + +// @ts-ignore + return s0; + } + +// @ts-ignore + function // @ts-ignore +peg$parseString() { +// @ts-ignore + var s0, s1, s2; + +// @ts-ignore + s0 = peg$currPos; +// @ts-ignore + s1 = []; +// @ts-ignore + if (peg$r1.test(input.charAt(peg$currPos))) { +// @ts-ignore + s2 = input.charAt(peg$currPos); +// @ts-ignore + peg$currPos++; +// @ts-ignore + } else { +// @ts-ignore + s2 = peg$FAILED; +// @ts-ignore + if (peg$silentFails === 0) { peg$fail(peg$e18); } + } +// @ts-ignore + if (s2 !== peg$FAILED) { +// @ts-ignore + while (s2 !== peg$FAILED) { +// @ts-ignore + s1.push(s2); +// @ts-ignore + if (peg$r1.test(input.charAt(peg$currPos))) { +// @ts-ignore + s2 = input.charAt(peg$currPos); +// @ts-ignore + peg$currPos++; +// @ts-ignore + } else { +// @ts-ignore + s2 = peg$FAILED; +// @ts-ignore + if (peg$silentFails === 0) { peg$fail(peg$e18); } + } + } +// @ts-ignore + } else { +// @ts-ignore + s1 = peg$FAILED; + } +// @ts-ignore + if (s1 !== peg$FAILED) { +// @ts-ignore + peg$savedPos = s0; +// @ts-ignore + s1 = peg$f23(); + } +// @ts-ignore + s0 = s1; + +// @ts-ignore + return s0; + } + +// @ts-ignore + function // @ts-ignore +peg$parseVersion() { +// @ts-ignore + var s0, s1, s2; + +// @ts-ignore + s0 = peg$currPos; +// @ts-ignore + s1 = peg$parseVersionNumber(); +// @ts-ignore + if (s1 !== peg$FAILED) { +// @ts-ignore + s2 = peg$parsePreRelease(); +// @ts-ignore + if (s2 === peg$FAILED) { +// @ts-ignore + s2 = null; + } +// @ts-ignore + peg$savedPos = s0; +// @ts-ignore + s0 = peg$f24(s1, s2); +// @ts-ignore + } else { +// @ts-ignore + peg$currPos = s0; +// @ts-ignore + s0 = peg$FAILED; + } + +// @ts-ignore + return s0; + } + +// @ts-ignore + function // @ts-ignore +peg$parsePreRelease() { +// @ts-ignore + var s0, s1, s2, s3, s4, s5, s6; + +// @ts-ignore + s0 = peg$currPos; +// @ts-ignore + if (input.charCodeAt(peg$currPos) === 45) { +// @ts-ignore + s1 = peg$c17; +// @ts-ignore + peg$currPos++; +// @ts-ignore + } else { +// @ts-ignore + s1 = peg$FAILED; +// @ts-ignore + if (peg$silentFails === 0) { peg$fail(peg$e19); } + } +// @ts-ignore + if (s1 !== peg$FAILED) { +// @ts-ignore + s2 = peg$parsePreReleaseSegment(); +// @ts-ignore + if (s2 !== peg$FAILED) { +// @ts-ignore + s3 = []; +// @ts-ignore + s4 = peg$currPos; +// @ts-ignore + if (input.charCodeAt(peg$currPos) === 46) { +// @ts-ignore + s5 = peg$c16; +// @ts-ignore + peg$currPos++; +// @ts-ignore + } else { +// @ts-ignore + s5 = peg$FAILED; +// @ts-ignore + if (peg$silentFails === 0) { peg$fail(peg$e16); } + } +// @ts-ignore + if (s5 !== peg$FAILED) { +// @ts-ignore + s6 = peg$parsePreReleaseSegment(); +// @ts-ignore + if (s6 !== peg$FAILED) { +// @ts-ignore + s5 = [s5, s6]; +// @ts-ignore + s4 = s5; +// @ts-ignore + } else { +// @ts-ignore + peg$currPos = s4; +// @ts-ignore + s4 = peg$FAILED; + } +// @ts-ignore + } else { +// @ts-ignore + peg$currPos = s4; +// @ts-ignore + s4 = peg$FAILED; + } +// @ts-ignore + while (s4 !== peg$FAILED) { +// @ts-ignore + s3.push(s4); +// @ts-ignore + s4 = peg$currPos; +// @ts-ignore + if (input.charCodeAt(peg$currPos) === 46) { +// @ts-ignore + s5 = peg$c16; +// @ts-ignore + peg$currPos++; +// @ts-ignore + } else { +// @ts-ignore + s5 = peg$FAILED; +// @ts-ignore + if (peg$silentFails === 0) { peg$fail(peg$e16); } + } +// @ts-ignore + if (s5 !== peg$FAILED) { +// @ts-ignore + s6 = peg$parsePreReleaseSegment(); +// @ts-ignore + if (s6 !== peg$FAILED) { +// @ts-ignore + s5 = [s5, s6]; +// @ts-ignore + s4 = s5; +// @ts-ignore + } else { +// @ts-ignore + peg$currPos = s4; +// @ts-ignore + s4 = peg$FAILED; + } +// @ts-ignore + } else { +// @ts-ignore + peg$currPos = s4; +// @ts-ignore + s4 = peg$FAILED; + } + } +// @ts-ignore + peg$savedPos = s0; +// @ts-ignore + s0 = peg$f25(s2, s3); +// @ts-ignore + } else { +// @ts-ignore + peg$currPos = s0; +// @ts-ignore + s0 = peg$FAILED; + } +// @ts-ignore + } else { +// @ts-ignore + peg$currPos = s0; +// @ts-ignore + s0 = peg$FAILED; + } + +// @ts-ignore + return s0; + } + +// @ts-ignore + function // @ts-ignore +peg$parsePreReleaseSegment() { +// @ts-ignore + var s0, s1, s2; + +// @ts-ignore + s0 = peg$currPos; +// @ts-ignore + if (input.charCodeAt(peg$currPos) === 46) { +// @ts-ignore + s1 = peg$c16; +// @ts-ignore + peg$currPos++; +// @ts-ignore + } else { +// @ts-ignore + s1 = peg$FAILED; +// @ts-ignore + if (peg$silentFails === 0) { peg$fail(peg$e16); } + } +// @ts-ignore + if (s1 === peg$FAILED) { +// @ts-ignore + s1 = null; + } +// @ts-ignore + s2 = peg$parseDigit(); +// @ts-ignore + if (s2 === peg$FAILED) { +// @ts-ignore + s2 = peg$parseString(); + } +// @ts-ignore + if (s2 !== peg$FAILED) { +// @ts-ignore + peg$savedPos = s0; +// @ts-ignore + s0 = peg$f26(s2); +// @ts-ignore + } else { +// @ts-ignore + peg$currPos = s0; +// @ts-ignore + s0 = peg$FAILED; + } + +// @ts-ignore + return s0; + } + +// @ts-ignore + function // @ts-ignore +peg$parseVersionNumber() { +// @ts-ignore + var s0, s1, s2, s3, s4, s5; + +// @ts-ignore + s0 = peg$currPos; +// @ts-ignore + s1 = peg$parseDigit(); +// @ts-ignore + if (s1 !== peg$FAILED) { +// @ts-ignore + s2 = []; +// @ts-ignore + s3 = peg$currPos; +// @ts-ignore + if (input.charCodeAt(peg$currPos) === 46) { +// @ts-ignore + s4 = peg$c16; +// @ts-ignore + peg$currPos++; +// @ts-ignore + } else { +// @ts-ignore + s4 = peg$FAILED; +// @ts-ignore + if (peg$silentFails === 0) { peg$fail(peg$e16); } + } +// @ts-ignore + if (s4 !== peg$FAILED) { +// @ts-ignore + s5 = peg$parseDigit(); +// @ts-ignore + if (s5 !== peg$FAILED) { +// @ts-ignore + s4 = [s4, s5]; +// @ts-ignore + s3 = s4; +// @ts-ignore + } else { +// @ts-ignore + peg$currPos = s3; +// @ts-ignore + s3 = peg$FAILED; + } +// @ts-ignore + } else { +// @ts-ignore + peg$currPos = s3; +// @ts-ignore + s3 = peg$FAILED; + } +// @ts-ignore + while (s3 !== peg$FAILED) { +// @ts-ignore + s2.push(s3); +// @ts-ignore + s3 = peg$currPos; +// @ts-ignore + if (input.charCodeAt(peg$currPos) === 46) { +// @ts-ignore + s4 = peg$c16; +// @ts-ignore + peg$currPos++; +// @ts-ignore + } else { +// @ts-ignore + s4 = peg$FAILED; +// @ts-ignore + if (peg$silentFails === 0) { peg$fail(peg$e16); } + } +// @ts-ignore + if (s4 !== peg$FAILED) { +// @ts-ignore + s5 = peg$parseDigit(); +// @ts-ignore + if (s5 !== peg$FAILED) { +// @ts-ignore + s4 = [s4, s5]; +// @ts-ignore + s3 = s4; +// @ts-ignore + } else { +// @ts-ignore + peg$currPos = s3; +// @ts-ignore + s3 = peg$FAILED; + } +// @ts-ignore + } else { +// @ts-ignore + peg$currPos = s3; +// @ts-ignore + s3 = peg$FAILED; + } + } +// @ts-ignore + peg$savedPos = s0; +// @ts-ignore + s0 = peg$f27(s1, s2); +// @ts-ignore + } else { +// @ts-ignore + peg$currPos = s0; +// @ts-ignore + s0 = peg$FAILED; + } + +// @ts-ignore + return s0; + } + +// @ts-ignore + function // @ts-ignore +peg$parseDigit() { +// @ts-ignore + var s0, s1, s2; + +// @ts-ignore + s0 = peg$currPos; +// @ts-ignore + s1 = []; +// @ts-ignore + if (peg$r2.test(input.charAt(peg$currPos))) { +// @ts-ignore + s2 = input.charAt(peg$currPos); +// @ts-ignore + peg$currPos++; +// @ts-ignore + } else { +// @ts-ignore + s2 = peg$FAILED; +// @ts-ignore + if (peg$silentFails === 0) { peg$fail(peg$e20); } + } +// @ts-ignore + if (s2 !== peg$FAILED) { +// @ts-ignore + while (s2 !== peg$FAILED) { +// @ts-ignore + s1.push(s2); +// @ts-ignore + if (peg$r2.test(input.charAt(peg$currPos))) { +// @ts-ignore + s2 = input.charAt(peg$currPos); +// @ts-ignore + peg$currPos++; +// @ts-ignore + } else { +// @ts-ignore + s2 = peg$FAILED; +// @ts-ignore + if (peg$silentFails === 0) { peg$fail(peg$e20); } + } + } +// @ts-ignore + } else { +// @ts-ignore + s1 = peg$FAILED; + } +// @ts-ignore + if (s1 !== peg$FAILED) { +// @ts-ignore + peg$savedPos = s0; +// @ts-ignore + s1 = peg$f28(); + } +// @ts-ignore + s0 = s1; + +// @ts-ignore + return s0; + } + +// @ts-ignore + function // @ts-ignore +peg$parse_() { +// @ts-ignore + var s0, s1; + +// @ts-ignore + peg$silentFails++; +// @ts-ignore + s0 = []; +// @ts-ignore + if (peg$r3.test(input.charAt(peg$currPos))) { +// @ts-ignore + s1 = input.charAt(peg$currPos); +// @ts-ignore + peg$currPos++; +// @ts-ignore + } else { +// @ts-ignore + s1 = peg$FAILED; +// @ts-ignore + if (peg$silentFails === 0) { peg$fail(peg$e22); } + } +// @ts-ignore + while (s1 !== peg$FAILED) { +// @ts-ignore + s0.push(s1); +// @ts-ignore + if (peg$r3.test(input.charAt(peg$currPos))) { +// @ts-ignore + s1 = input.charAt(peg$currPos); +// @ts-ignore + peg$currPos++; +// @ts-ignore + } else { +// @ts-ignore + s1 = peg$FAILED; +// @ts-ignore + if (peg$silentFails === 0) { peg$fail(peg$e22); } + } + } +// @ts-ignore + peg$silentFails--; +// @ts-ignore + s1 = peg$FAILED; +// @ts-ignore + if (peg$silentFails === 0) { peg$fail(peg$e21); } + +// @ts-ignore + return s0; + } + +// @ts-ignore + peg$result = peg$startRuleFunction(); + +// @ts-ignore + if (peg$result !== peg$FAILED && peg$currPos === input.length) { +// @ts-ignore + return peg$result; +// @ts-ignore + } else { +// @ts-ignore + if (peg$result !== peg$FAILED && peg$currPos < input.length) { +// @ts-ignore + peg$fail(peg$endExpectation()); + } + +// @ts-ignore + throw peg$buildStructuredError( +// @ts-ignore + peg$maxFailExpected, +// @ts-ignore + peg$maxFailPos < input.length ? input.charAt(peg$maxFailPos) : null, +// @ts-ignore + peg$maxFailPos < input.length +// @ts-ignore + ? peg$computeLocation(peg$maxFailPos, peg$maxFailPos + 1) +// @ts-ignore + : peg$computeLocation(peg$maxFailPos, peg$maxFailPos) + ); + } +} + +// @ts-ignore + return { + SyntaxError: peg$SyntaxError, + parse: peg$parse + }; +})() export interface FilePosition { - offset: number - line: number - column: number + offset: number; + line: number; + column: number; } export interface FileRange { - start: FilePosition - end: FilePosition - source: string + start: FilePosition; + end: FilePosition; + source: string; } export interface LiteralExpectation { - type: 'literal' - text: string - ignoreCase: boolean + type: "literal"; + text: string; + ignoreCase: boolean; } export interface ClassParts extends Array {} export interface ClassExpectation { - type: 'class' - parts: ClassParts - inverted: boolean - ignoreCase: boolean + type: "class"; + parts: ClassParts; + inverted: boolean; + ignoreCase: boolean; } export interface AnyExpectation { - type: 'any' + type: "any"; } export interface EndExpectation { - type: 'end' + type: "end"; } export interface OtherExpectation { - type: 'other' - description: string + type: "other"; + description: string; } -export type Expectation = - | LiteralExpectation - | ClassExpectation - | AnyExpectation - | EndExpectation - | OtherExpectation +export type Expectation = LiteralExpectation | ClassExpectation | AnyExpectation | EndExpectation | OtherExpectation; declare class _PeggySyntaxError extends Error { - public static buildMessage( - expected: Expectation[], - found: string | null, - ): string - public message: string - public expected: Expectation[] - public found: string | null - public location: FileRange - public name: string - constructor( - message: string, - expected: Expectation[], - found: string | null, - location: FileRange, - ) - format( - sources: { - source?: any - text: string - }[], - ): string + public static buildMessage(expected: Expectation[], found: string | null): string; + public message: string; + public expected: Expectation[]; + public found: string | null; + public location: FileRange; + public name: string; + constructor(message: string, expected: Expectation[], found: string | null, location: FileRange); + format(sources: { + source?: any; + text: string; + }[]): string; } export interface TraceEvent { - type: string - rule: string - result?: any - location: FileRange -} + type: string; + rule: string; + result?: any; + location: FileRange; + } declare class _DefaultTracer { - private indentLevel: number - public trace(event: TraceEvent): void + private indentLevel: number; + public trace(event: TraceEvent): void; } -peggyParser.SyntaxError.prototype.name = 'PeggySyntaxError' +peggyParser.SyntaxError.prototype.name = "PeggySyntaxError"; export interface ParseOptions { - filename?: string - startRule?: - | 'VersionRange' - | 'Or' - | 'And' - | 'VersionRangeAtom' - | 'Parens' - | 'Anchor' - | 'VersionSpec' - | 'FlavorAtom' - | 'Not' - | 'Any' - | 'None' - | 'CmpOp' - | 'ExtendedVersion' - | 'EmverVersionRange' - | 'EmverVersionRangeAtom' - | 'EmverParens' - | 'EmverAnchor' - | 'EmverNot' - | 'Emver' - | 'Flavor' - | 'FlavorString' - | 'String' - | 'Version' - | 'PreRelease' - | 'PreReleaseSegment' - | 'VersionNumber' - | 'Digit' - | '_' - tracer?: any - [key: string]: any + filename?: string; + startRule?: "VersionRange" | "Or" | "And" | "VersionRangeAtom" | "Parens" | "Anchor" | "VersionSpec" | "FlavorAtom" | "Not" | "Any" | "None" | "CmpOp" | "ExtendedVersion" | "EmverVersionRange" | "EmverVersionRangeAtom" | "EmverParens" | "EmverAnchor" | "EmverNot" | "Emver" | "Flavor" | "FlavorString" | "String" | "Version" | "PreRelease" | "PreReleaseSegment" | "VersionNumber" | "Digit" | "_"; + tracer?: any; + [key: string]: any; } export type ParseFunction = ( - input: string, - options?: Options, -) => Options extends { startRule: infer StartRule } - ? StartRule extends 'VersionRange' - ? VersionRange - : StartRule extends 'Or' - ? Or - : StartRule extends 'And' - ? And - : StartRule extends 'VersionRangeAtom' - ? VersionRangeAtom - : StartRule extends 'Parens' - ? Parens - : StartRule extends 'Anchor' - ? Anchor - : StartRule extends 'VersionSpec' - ? VersionSpec - : StartRule extends 'FlavorAtom' - ? FlavorAtom - : StartRule extends 'Not' - ? Not - : StartRule extends 'Any' - ? Any - : StartRule extends 'None' - ? None - : StartRule extends 'CmpOp' - ? CmpOp - : StartRule extends 'ExtendedVersion' - ? ExtendedVersion - : StartRule extends 'EmverVersionRange' - ? EmverVersionRange - : StartRule extends 'EmverVersionRangeAtom' - ? EmverVersionRangeAtom - : StartRule extends 'EmverParens' - ? EmverParens - : StartRule extends 'EmverAnchor' - ? EmverAnchor - : StartRule extends 'EmverNot' - ? EmverNot - : StartRule extends 'Emver' - ? Emver - : StartRule extends 'Flavor' - ? Flavor - : StartRule extends 'FlavorString' - ? FlavorString - : StartRule extends 'String' - ? String_1 - : StartRule extends 'Version' - ? Version - : StartRule extends 'PreRelease' - ? PreRelease - : StartRule extends 'PreReleaseSegment' - ? PreReleaseSegment - : StartRule extends 'VersionNumber' - ? VersionNumber - : StartRule extends 'Digit' - ? Digit - : StartRule extends '_' - ? _ - : VersionRange - : VersionRange -export const parse: ParseFunction = peggyParser.parse + input: string, + options?: Options + ) => Options extends { startRule: infer StartRule } ? + StartRule extends "VersionRange" ? VersionRange : + StartRule extends "Or" ? Or : + StartRule extends "And" ? And : + StartRule extends "VersionRangeAtom" ? VersionRangeAtom : + StartRule extends "Parens" ? Parens : + StartRule extends "Anchor" ? Anchor : + StartRule extends "VersionSpec" ? VersionSpec : + StartRule extends "FlavorAtom" ? FlavorAtom : + StartRule extends "Not" ? Not : + StartRule extends "Any" ? Any : + StartRule extends "None" ? None : + StartRule extends "CmpOp" ? CmpOp : + StartRule extends "ExtendedVersion" ? ExtendedVersion : + StartRule extends "EmverVersionRange" ? EmverVersionRange : + StartRule extends "EmverVersionRangeAtom" ? EmverVersionRangeAtom : + StartRule extends "EmverParens" ? EmverParens : + StartRule extends "EmverAnchor" ? EmverAnchor : + StartRule extends "EmverNot" ? EmverNot : + StartRule extends "Emver" ? Emver : + StartRule extends "Flavor" ? Flavor : + StartRule extends "FlavorString" ? FlavorString : + StartRule extends "String" ? String_1 : + StartRule extends "Version" ? Version : + StartRule extends "PreRelease" ? PreRelease : + StartRule extends "PreReleaseSegment" ? PreReleaseSegment : + StartRule extends "VersionNumber" ? VersionNumber : + StartRule extends "Digit" ? Digit : + StartRule extends "_" ? _ : VersionRange + : VersionRange; +export const parse: ParseFunction = peggyParser.parse; -export const PeggySyntaxError = - peggyParser.SyntaxError as typeof _PeggySyntaxError +export const PeggySyntaxError = peggyParser.SyntaxError as typeof _PeggySyntaxError; -export type PeggySyntaxError = _PeggySyntaxError +export type PeggySyntaxError = _PeggySyntaxError; // These types were autogenerated by ts-pegjs export type VersionRange = [ VersionRangeAtom, - [_, [Or | And, _] | null, VersionRangeAtom][], -] -export type Or = '||' -export type And = '&&' -export type VersionRangeAtom = Parens | Anchor | Not | Any | None | FlavorAtom -export type Parens = { type: 'Parens'; expr: VersionRange } + [_, [Or | And, _] | null, VersionRangeAtom][] +]; +export type Or = "||"; +export type And = "&&"; +export type VersionRangeAtom = Parens | Anchor | Not | Any | None | FlavorAtom; +export type Parens = { type: "Parens"; expr: VersionRange }; export type Anchor = { - type: 'Anchor' - operator: CmpOp | null - version: VersionSpec -} + type: "Anchor"; + operator: CmpOp | null; + version: VersionSpec; +}; export type VersionSpec = { - flavor: NonNullable | null - upstream: Version - downstream: any -} -export type FlavorAtom = { type: 'Flavor'; flavor: FlavorString } -export type Not = { type: 'Not'; value: VersionRangeAtom } -export type Any = { type: 'Any' } -export type None = { type: 'None' } -export type CmpOp = '>=' | '<=' | '>' | '<' | '=' | '!=' | '^' | '~' + flavor: NonNullable | null; + upstream: Version; + downstream: any; +}; +export type FlavorAtom = { type: "Flavor"; flavor: FlavorString }; +export type Not = { type: "Not"; value: VersionRangeAtom }; +export type Any = { type: "Any" }; +export type None = { type: "None" }; +export type CmpOp = ">=" | "<=" | ">" | "<" | "=" | "!=" | "^" | "~"; export type ExtendedVersion = { - flavor: NonNullable | null - upstream: Version - downstream: Version -} + flavor: NonNullable | null; + upstream: Version; + downstream: Version; +}; export type EmverVersionRange = [ EmverVersionRangeAtom, - [_, [Or | And, _] | null, EmverVersionRangeAtom][], -] + [_, [Or | And, _] | null, EmverVersionRangeAtom][] +]; export type EmverVersionRangeAtom = | EmverParens | EmverAnchor | EmverNot | Any - | None -export type EmverParens = { type: 'Parens'; expr: EmverVersionRange } + | None; +export type EmverParens = { type: "Parens"; expr: EmverVersionRange }; export type EmverAnchor = { - type: 'Anchor' - operator: CmpOp | null - version: Emver -} -export type EmverNot = { type: 'Not'; value: EmverVersionRangeAtom } + type: "Anchor"; + operator: CmpOp | null; + version: Emver; +}; +export type EmverNot = { type: "Not"; value: EmverVersionRangeAtom }; export type Emver = { - flavor: null - upstream: { number: [Digit, Digit, Digit]; prerelease: [] } - downstream: { number: [0 | NonNullable]; prerelease: [] } -} -export type Flavor = FlavorString -export type FlavorString = string -export type String_1 = string + flavor: null; + upstream: { number: [Digit, Digit, Digit]; prerelease: [] }; + downstream: { number: [0 | NonNullable]; prerelease: [] }; +}; +export type Flavor = FlavorString; +export type FlavorString = string; +export type String_1 = string; export type Version = { - number: VersionNumber - prerelease: never[] | NonNullable -} -export type PreRelease = PreReleaseSegment[] -export type PreReleaseSegment = Digit | String_1 -export type VersionNumber = Digit[] -export type Digit = number -export type _ = string[] + number: VersionNumber; + prerelease: never[] | NonNullable; +}; +export type PreRelease = PreReleaseSegment[]; +export type PreReleaseSegment = Digit | String_1; +export type VersionNumber = Digit[]; +export type Digit = number; +export type _ = string[]; diff --git a/sdk/base/lib/osBindings/BindInfo.ts b/sdk/base/lib/osBindings/BindInfo.ts index eba1fe446..d83c6948f 100644 --- a/sdk/base/lib/osBindings/BindInfo.ts +++ b/sdk/base/lib/osBindings/BindInfo.ts @@ -1,5 +1,11 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. import type { BindOptions } from './BindOptions' +import type { DerivedAddressInfo } from './DerivedAddressInfo' import type { NetInfo } from './NetInfo' -export type BindInfo = { enabled: boolean; options: BindOptions; net: NetInfo } +export type BindInfo = { + enabled: boolean + options: BindOptions + net: NetInfo + addresses: DerivedAddressInfo +} diff --git a/sdk/base/lib/osBindings/BindingGatewaySetEnabledParams.ts b/sdk/base/lib/osBindings/BindingSetAddressEnabledParams.ts similarity index 58% rename from sdk/base/lib/osBindings/BindingGatewaySetEnabledParams.ts rename to sdk/base/lib/osBindings/BindingSetAddressEnabledParams.ts index fb90cdaa7..2dfeff757 100644 --- a/sdk/base/lib/osBindings/BindingGatewaySetEnabledParams.ts +++ b/sdk/base/lib/osBindings/BindingSetAddressEnabledParams.ts @@ -1,8 +1,7 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. -import type { GatewayId } from './GatewayId' -export type BindingGatewaySetEnabledParams = { +export type BindingSetAddressEnabledParams = { internalPort: number - gateway: GatewayId + address: string enabled: boolean | null } diff --git a/sdk/base/lib/osBindings/OnionHostname.ts b/sdk/base/lib/osBindings/Bindings.ts similarity index 53% rename from sdk/base/lib/osBindings/OnionHostname.ts rename to sdk/base/lib/osBindings/Bindings.ts index 0bea8245e..ef921868e 100644 --- a/sdk/base/lib/osBindings/OnionHostname.ts +++ b/sdk/base/lib/osBindings/Bindings.ts @@ -1,7 +1,4 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { BindInfo } from './BindInfo' -export type OnionHostname = { - value: string - port: number | null - sslPort: number | null -} +export type Bindings = { [key: number]: BindInfo } diff --git a/sdk/base/lib/osBindings/DerivedAddressInfo.ts b/sdk/base/lib/osBindings/DerivedAddressInfo.ts new file mode 100644 index 000000000..2c83191fd --- /dev/null +++ b/sdk/base/lib/osBindings/DerivedAddressInfo.ts @@ -0,0 +1,17 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { HostnameInfo } from './HostnameInfo' + +export type DerivedAddressInfo = { + /** + * User-controlled: private-gateway addresses the user has disabled + */ + privateDisabled: Array + /** + * User-controlled: public-gateway addresses the user has enabled + */ + publicEnabled: Array + /** + * COMPUTED: NetServiceData::update — all possible addresses for this binding + */ + possible: Array +} diff --git a/sdk/base/lib/osBindings/ErrorData.ts b/sdk/base/lib/osBindings/ErrorData.ts index 3485b2f8b..fc2fa1e9a 100644 --- a/sdk/base/lib/osBindings/ErrorData.ts +++ b/sdk/base/lib/osBindings/ErrorData.ts @@ -1,3 +1,3 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. -export type ErrorData = { details: string; debug: string } +export type ErrorData = { details: string; debug: string; info: unknown } diff --git a/sdk/base/lib/osBindings/Host.ts b/sdk/base/lib/osBindings/Host.ts index e34af2ae8..cecc24475 100644 --- a/sdk/base/lib/osBindings/Host.ts +++ b/sdk/base/lib/osBindings/Host.ts @@ -1,15 +1,9 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. -import type { BindInfo } from './BindInfo' -import type { HostnameInfo } from './HostnameInfo' +import type { Bindings } from './Bindings' import type { PublicDomainConfig } from './PublicDomainConfig' export type Host = { - bindings: { [key: number]: BindInfo } - onions: string[] + bindings: Bindings publicDomains: { [key: string]: PublicDomainConfig } privateDomains: Array - /** - * COMPUTED: NetService::update - */ - hostnameInfo: { [key: number]: Array } } diff --git a/sdk/base/lib/osBindings/HostnameInfo.ts b/sdk/base/lib/osBindings/HostnameInfo.ts index f2bb5e226..4d80dd43f 100644 --- a/sdk/base/lib/osBindings/HostnameInfo.ts +++ b/sdk/base/lib/osBindings/HostnameInfo.ts @@ -1,8 +1,9 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. import type { GatewayInfo } from './GatewayInfo' import type { IpHostname } from './IpHostname' -import type { OnionHostname } from './OnionHostname' -export type HostnameInfo = - | { kind: 'ip'; gateway: GatewayInfo; public: boolean; hostname: IpHostname } - | { kind: 'onion'; hostname: OnionHostname } +export type HostnameInfo = { + gateway: GatewayInfo + public: boolean + hostname: IpHostname +} diff --git a/sdk/base/lib/osBindings/NetInfo.ts b/sdk/base/lib/osBindings/NetInfo.ts index 4483f81b8..90abe2cd8 100644 --- a/sdk/base/lib/osBindings/NetInfo.ts +++ b/sdk/base/lib/osBindings/NetInfo.ts @@ -1,9 +1,6 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. -import type { GatewayId } from './GatewayId' export type NetInfo = { - privateDisabled: Array - publicEnabled: Array assignedPort: number | null assignedSslPort: number | null } diff --git a/sdk/base/lib/osBindings/index.ts b/sdk/base/lib/osBindings/index.ts index 4d9049856..18cca9c9a 100644 --- a/sdk/base/lib/osBindings/index.ts +++ b/sdk/base/lib/osBindings/index.ts @@ -36,7 +36,8 @@ export { BackupTargetFS } from './BackupTargetFS' export { Base64 } from './Base64' export { BindId } from './BindId' export { BindInfo } from './BindInfo' -export { BindingGatewaySetEnabledParams } from './BindingGatewaySetEnabledParams' +export { BindingSetAddressEnabledParams } from './BindingSetAddressEnabledParams' +export { Bindings } from './Bindings' export { BindOptions } from './BindOptions' export { BindParams } from './BindParams' export { Blake3Commitment } from './Blake3Commitment' @@ -64,6 +65,7 @@ export { Dependencies } from './Dependencies' export { DependencyMetadata } from './DependencyMetadata' export { DependencyRequirement } from './DependencyRequirement' export { DepInfo } from './DepInfo' +export { DerivedAddressInfo } from './DerivedAddressInfo' export { Description } from './Description' export { DesiredStatus } from './DesiredStatus' export { DestroySubcontainerFsParams } from './DestroySubcontainerFsParams' @@ -149,7 +151,6 @@ export { NetInfo } from './NetInfo' export { NetworkInfo } from './NetworkInfo' export { NetworkInterfaceInfo } from './NetworkInterfaceInfo' export { NetworkInterfaceType } from './NetworkInterfaceType' -export { OnionHostname } from './OnionHostname' export { OsIndex } from './OsIndex' export { OsVersionInfoMap } from './OsVersionInfoMap' export { OsVersionInfo } from './OsVersionInfo' diff --git a/sdk/base/lib/util/getServiceInterface.ts b/sdk/base/lib/util/getServiceInterface.ts index 17186b7d7..bc22e70c6 100644 --- a/sdk/base/lib/util/getServiceInterface.ts +++ b/sdk/base/lib/util/getServiceInterface.ts @@ -1,6 +1,12 @@ import { PackageId, ServiceInterfaceId, ServiceInterfaceType } from '../types' import { knownProtocols } from '../interfaces/Host' -import { AddressInfo, Host, Hostname, HostnameInfo } from '../types' +import { + AddressInfo, + DerivedAddressInfo, + Host, + Hostname, + HostnameInfo, +} from '../types' import { Effects } from '../Effects' import { DropGenerator, DropPromise } from './Drop' import { IpAddress, IPV6_LINK_LOCAL } from './ip' @@ -20,7 +26,6 @@ export const getHostname = (url: string): Hostname | null => { } type FilterKinds = - | 'onion' | 'mdns' | 'domain' | 'ip' @@ -42,27 +47,25 @@ type VisibilityFilter = V extends 'public' | (HostnameInfo & { public: false }) | VisibilityFilter> : never -type KindFilter = K extends 'onion' - ? (HostnameInfo & { kind: 'onion' }) | KindFilter> - : K extends 'mdns' +type KindFilter = K extends 'mdns' + ? + | (HostnameInfo & { hostname: { kind: 'local' } }) + | KindFilter> + : K extends 'domain' ? - | (HostnameInfo & { kind: 'ip'; hostname: { kind: 'local' } }) - | KindFilter> - : K extends 'domain' + | (HostnameInfo & { hostname: { kind: 'domain' } }) + | KindFilter> + : K extends 'ipv4' ? - | (HostnameInfo & { kind: 'ip'; hostname: { kind: 'domain' } }) - | KindFilter> - : K extends 'ipv4' + | (HostnameInfo & { hostname: { kind: 'ipv4' } }) + | KindFilter> + : K extends 'ipv6' ? - | (HostnameInfo & { kind: 'ip'; hostname: { kind: 'ipv4' } }) - | KindFilter> - : K extends 'ipv6' - ? - | (HostnameInfo & { kind: 'ip'; hostname: { kind: 'ipv6' } }) - | KindFilter> - : K extends 'ip' - ? KindFilter | 'ipv4' | 'ipv6'> - : never + | (HostnameInfo & { hostname: { kind: 'ipv6' } }) + | KindFilter> + : K extends 'ip' + ? KindFilter | 'ipv4' | 'ipv6'> + : never type FilterReturnTy = F extends { visibility: infer V extends 'public' | 'private' @@ -90,10 +93,6 @@ const nonLocalFilter = { const publicFilter = { visibility: 'public', } as const -const onionFilter = { - kind: 'onion', -} as const - type Formats = 'hostname-info' | 'urlstring' | 'url' type FormatReturnTy< F extends Filter, @@ -124,7 +123,6 @@ export type Filled = { nonLocal: Filled public: Filled - onion: Filled } export type FilledAddressInfo = AddressInfo & Filled export type ServiceInterfaceFilled = { @@ -162,18 +160,14 @@ export const addressHostToUrl = ( scheme in knownProtocols && port === knownProtocols[scheme as keyof typeof knownProtocols].defaultPort let hostname - if (host.kind === 'onion') { + if (host.hostname.kind === 'domain') { + hostname = host.hostname.value + } else if (host.hostname.kind === 'ipv6') { + hostname = IPV6_LINK_LOCAL.contains(host.hostname.value) + ? `[${host.hostname.value}%${host.hostname.scopeId}]` + : `[${host.hostname.value}]` + } else { hostname = host.hostname.value - } else if (host.kind === 'ip') { - if (host.hostname.kind === 'domain') { - hostname = host.hostname.value - } else if (host.hostname.kind === 'ipv6') { - hostname = IPV6_LINK_LOCAL.contains(host.hostname.value) - ? `[${host.hostname.value}%${host.hostname.scopeId}]` - : `[${host.hostname.value}]` - } else { - hostname = host.hostname.value - } } return `${scheme ? `${scheme}://` : ''}${ username ? `${username}@` : '' @@ -201,13 +195,9 @@ function filterRec( hostnames = hostnames.filter((h) => invert !== pred(h)) } if (filter.visibility === 'public') - hostnames = hostnames.filter( - (h) => invert !== (h.kind === 'onion' || h.public), - ) + hostnames = hostnames.filter((h) => invert !== h.public) if (filter.visibility === 'private') - hostnames = hostnames.filter( - (h) => invert !== (h.kind !== 'onion' && !h.public), - ) + hostnames = hostnames.filter((h) => invert !== !h.public) if (filter.kind) { const kind = new Set( Array.isArray(filter.kind) ? filter.kind : [filter.kind], @@ -219,19 +209,13 @@ function filterRec( hostnames = hostnames.filter( (h) => invert !== - ((kind.has('onion') && h.kind === 'onion') || - (kind.has('mdns') && - h.kind === 'ip' && - h.hostname.kind === 'local') || - (kind.has('domain') && - h.kind === 'ip' && - h.hostname.kind === 'domain') || - (kind.has('ipv4') && h.kind === 'ip' && h.hostname.kind === 'ipv4') || - (kind.has('ipv6') && h.kind === 'ip' && h.hostname.kind === 'ipv6') || + ((kind.has('mdns') && h.hostname.kind === 'local') || + (kind.has('domain') && h.hostname.kind === 'domain') || + (kind.has('ipv4') && h.hostname.kind === 'ipv4') || + (kind.has('ipv6') && h.hostname.kind === 'ipv6') || (kind.has('localhost') && ['localhost', '127.0.0.1', '::1'].includes(h.hostname.value)) || (kind.has('link-local') && - h.kind === 'ip' && h.hostname.kind === 'ipv6' && IPV6_LINK_LOCAL.contains(IpAddress.parse(h.hostname.value)))), ) @@ -242,6 +226,14 @@ function filterRec( return hostnames } +function enabledAddresses(addr: DerivedAddressInfo): HostnameInfo[] { + return addr.possible.filter((h) => + h.public + ? addr.publicEnabled.some((e) => deepEqual(e, h)) + : !addr.privateDisabled.some((d) => deepEqual(d, h)), + ) +} + export const filledAddress = ( host: Host, addressInfo: AddressInfo, @@ -251,7 +243,8 @@ export const filledAddress = ( const u = toUrls(h) return [u.url, u.sslUrl].filter((u) => u !== null) } - const hostnames = host.hostnameInfo[addressInfo.internalPort] ?? [] + const binding = host.bindings[addressInfo.internalPort] + const hostnames = binding ? enabledAddresses(binding.addresses) : [] function filledAddressFromHostnames( hostnames: HostnameInfo[], @@ -266,11 +259,6 @@ export const filledAddress = ( filterRec(hostnames, publicFilter, false), ), ) - const getOnion = once(() => - filledAddressFromHostnames( - filterRec(hostnames, onionFilter, false), - ), - ) return { ...addressInfo, hostnames, @@ -294,9 +282,6 @@ export const filledAddress = ( get public(): Filled { return getPublic() }, - get onion(): Filled { - return getOnion() - }, } } diff --git a/sdk/base/lib/util/patterns.ts b/sdk/base/lib/util/patterns.ts index 59f7a863b..b1f54c44d 100644 --- a/sdk/base/lib/util/patterns.ts +++ b/sdk/base/lib/util/patterns.ts @@ -21,11 +21,6 @@ export const localHostname: Pattern = { description: 'Must be a valid ".local" hostname', } -export const torHostname: Pattern = { - regex: regexes.torHostname.matches(), - description: 'Must be a valid Tor (".onion") hostname', -} - export const url: Pattern = { regex: regexes.url.matches(), description: 'Must be a valid URL', @@ -36,11 +31,6 @@ export const localUrl: Pattern = { description: 'Must be a valid ".local" URL', } -export const torUrl: Pattern = { - regex: regexes.torUrl.matches(), - description: 'Must be a valid Tor (".onion") URL', -} - export const ascii: Pattern = { regex: regexes.ascii.matches(), description: diff --git a/sdk/base/lib/util/regexes.ts b/sdk/base/lib/util/regexes.ts index 65213a7b3..c5a78b2bb 100644 --- a/sdk/base/lib/util/regexes.ts +++ b/sdk/base/lib/util/regexes.ts @@ -39,10 +39,6 @@ export const localHostname = new ComposableRegex( /[-a-zA-Z0-9@:%._\+~#=]{1,256}\.local/, ) -export const torHostname = new ComposableRegex( - /[-a-zA-Z0-9@:%._\+~#=]{1,256}\.onion/, -) - // https://ihateregex.io/expr/url/ export const url = new ComposableRegex( /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()!@:%_\+.~#?&\/\/=]*)/, @@ -52,10 +48,6 @@ export const localUrl = new ComposableRegex( /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.local\b([-a-zA-Z0-9()!@:%_\+.~#?&\/\/=]*)/, ) -export const torUrl = new ComposableRegex( - /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.onion\b([-a-zA-Z0-9()!@:%_\+.~#?&\/\/=]*)/, -) - // https://ihateregex.io/expr/ascii/ export const ascii = new ComposableRegex(/[ -~]*/) diff --git a/sdk/package/lib/StartSdk.ts b/sdk/package/lib/StartSdk.ts index 9d1dc0164..3855d594d 100644 --- a/sdk/package/lib/StartSdk.ts +++ b/sdk/package/lib/StartSdk.ts @@ -67,7 +67,7 @@ import { import { getOwnServiceInterfaces } from '../../base/lib/util/getServiceInterfaces' import { Volumes, createVolumes } from './util/Volume' -export const OSVersion = testTypeVersion('0.4.0-alpha.19') +export const OSVersion = testTypeVersion('0.4.0-alpha.20') // prettier-ignore type AnyNeverCond = diff --git a/sdk/package/scripts/oldSpecToBuilder.ts b/sdk/package/scripts/oldSpecToBuilder.ts index c8a275d57..22d377083 100644 --- a/sdk/package/scripts/oldSpecToBuilder.ts +++ b/sdk/package/scripts/oldSpecToBuilder.ts @@ -1,9 +1,9 @@ -import * as fs from "fs" +import * as fs from 'fs' // https://stackoverflow.com/questions/2970525/converting-any-string-into-camel-case export function camelCase(value: string) { return value - .replace(/([\(\)\[\]])/g, "") + .replace(/([\(\)\[\]])/g, '') .replace(/^([A-Z])|[\s-_](\w)/g, function (match, p1, p2, offset) { if (p2) return p2.toUpperCase() return p1.toLowerCase() @@ -23,12 +23,12 @@ export async function oldSpecToBuilder( } function isString(x: unknown): x is string { - return typeof x === "string" + return typeof x === 'string' } export default async function makeFileContentFromOld( inputData: Promise | any, - { StartSdk = "start-sdk", nested = true } = {}, + { StartSdk = 'start-sdk', nested = true } = {}, ) { const outputLines: string[] = [] outputLines.push(` @@ -37,16 +37,16 @@ const {InputSpec, List, Value, Variants} = sdk `) const data = await inputData - const namedConsts = new Set(["InputSpec", "Value", "List"]) - const inputSpecName = newConst("inputSpecSpec", convertInputSpec(data)) + const namedConsts = new Set(['InputSpec', 'Value', 'List']) + const inputSpecName = newConst('inputSpecSpec', convertInputSpec(data)) outputLines.push(`export type InputSpecSpec = typeof ${inputSpecName}._TYPE;`) - return outputLines.join("\n") + return outputLines.join('\n') function newConst(key: string, data: string, type?: string) { const variableName = getNextConstName(camelCase(key)) outputLines.push( - `export const ${variableName}${!type ? "" : `: ${type}`} = ${data};`, + `export const ${variableName}${!type ? '' : `: ${type}`} = ${data};`, ) return variableName } @@ -55,7 +55,7 @@ const {InputSpec, List, Value, Variants} = sdk return newConst(key, data) } function convertInputSpecInner(data: any) { - let answer = "{" + let answer = '{' for (const [key, value] of Object.entries(data)) { const variableName = maybeNewConst(key, convertValueSpec(value)) @@ -69,7 +69,7 @@ const {InputSpec, List, Value, Variants} = sdk } function convertValueSpec(value: any): string { switch (value.type) { - case "string": { + case 'string': { if (value.textarea) { return `${rangeToTodoComment( value?.range, @@ -99,12 +99,12 @@ const {InputSpec, List, Value, Variants} = sdk warning: value.warning || null, masked: value.masked || false, placeholder: value.placeholder || null, - inputmode: "text", + inputmode: 'text', patterns: value.pattern ? [ { regex: value.pattern, - description: value["pattern-description"], + description: value['pattern-description'], }, ] : [], @@ -115,7 +115,7 @@ const {InputSpec, List, Value, Variants} = sdk 2, )})` } - case "number": { + case 'number': { return `${rangeToTodoComment( value?.range, )}Value.number(${JSON.stringify( @@ -136,7 +136,7 @@ const {InputSpec, List, Value, Variants} = sdk 2, )})` } - case "boolean": { + case 'boolean': { return `Value.toggle(${JSON.stringify( { name: value.name || null, @@ -148,15 +148,15 @@ const {InputSpec, List, Value, Variants} = sdk 2, )})` } - case "enum": { + case 'enum': { const allValueNames = new Set([ - ...(value?.["values"] || []), - ...Object.keys(value?.["value-names"] || {}), + ...(value?.['values'] || []), + ...Object.keys(value?.['value-names'] || {}), ]) const values = Object.fromEntries( Array.from(allValueNames) .filter(isString) - .map((key) => [key, value?.spec?.["value-names"]?.[key] || key]), + .map((key) => [key, value?.spec?.['value-names']?.[key] || key]), ) return `Value.select(${JSON.stringify( { @@ -170,9 +170,9 @@ const {InputSpec, List, Value, Variants} = sdk 2, )} as const)` } - case "object": { + case 'object': { const specName = maybeNewConst( - value.name + "_spec", + value.name + '_spec', convertInputSpec(value.spec), ) return `Value.object({ @@ -180,10 +180,10 @@ const {InputSpec, List, Value, Variants} = sdk description: ${JSON.stringify(value.description || null)}, }, ${specName})` } - case "union": { + case 'union': { const variants = maybeNewConst( - value.name + "_variants", - convertVariants(value.variants, value.tag["variant-names"] || {}), + value.name + '_variants', + convertVariants(value.variants, value.tag['variant-names'] || {}), ) return `Value.union({ @@ -194,18 +194,18 @@ const {InputSpec, List, Value, Variants} = sdk variants: ${variants}, })` } - case "list": { - if (value.subtype === "enum") { + case 'list': { + if (value.subtype === 'enum') { const allValueNames = new Set([ - ...(value?.spec?.["values"] || []), - ...Object.keys(value?.spec?.["value-names"] || {}), + ...(value?.spec?.['values'] || []), + ...Object.keys(value?.spec?.['value-names'] || {}), ]) const values = Object.fromEntries( Array.from(allValueNames) .filter(isString) .map((key: string) => [ key, - value?.spec?.["value-names"]?.[key] ?? key, + value?.spec?.['value-names']?.[key] ?? key, ]), ) return `Value.multiselect(${JSON.stringify( @@ -222,10 +222,10 @@ const {InputSpec, List, Value, Variants} = sdk 2, )})` } - const list = maybeNewConst(value.name + "_list", convertList(value)) + const list = maybeNewConst(value.name + '_list', convertList(value)) return `Value.list(${list})` } - case "pointer": { + case 'pointer': { return `/* TODO deal with point removed point "${value.name}" */null as any` } } @@ -234,7 +234,7 @@ const {InputSpec, List, Value, Variants} = sdk function convertList(value: any) { switch (value.subtype) { - case "string": { + case 'string': { return `${rangeToTodoComment(value?.range)}List.text(${JSON.stringify( { name: value.name || null, @@ -253,7 +253,7 @@ const {InputSpec, List, Value, Variants} = sdk ? [ { regex: value.spec.pattern, - description: value?.spec?.["pattern-description"], + description: value?.spec?.['pattern-description'], }, ] : [], @@ -281,12 +281,12 @@ const {InputSpec, List, Value, Variants} = sdk // placeholder: value?.spec?.placeholder || null, // })})` // } - case "enum": { - return "/* error!! list.enum */" + case 'enum': { + return '/* error!! list.enum */' } - case "object": { + case 'object': { const specName = maybeNewConst( - value.name + "_spec", + value.name + '_spec', convertInputSpec(value.spec.spec), ) return `${rangeToTodoComment(value?.range)}List.obj({ @@ -297,20 +297,20 @@ const {InputSpec, List, Value, Variants} = sdk description: ${JSON.stringify(value.description || null)}, }, { spec: ${specName}, - displayAs: ${JSON.stringify(value?.spec?.["display-as"] || null)}, - uniqueBy: ${JSON.stringify(value?.spec?.["unique-by"] || null)}, + displayAs: ${JSON.stringify(value?.spec?.['display-as'] || null)}, + uniqueBy: ${JSON.stringify(value?.spec?.['unique-by'] || null)}, })` } - case "union": { + case 'union': { const variants = maybeNewConst( - value.name + "_variants", + value.name + '_variants', convertVariants( value.spec.variants, - value.spec["variant-names"] || {}, + value.spec['variant-names'] || {}, ), ) const unionValueName = maybeNewConst( - value.name + "_union", + value.name + '_union', `${rangeToTodoComment(value?.range)} Value.union({ name: ${JSON.stringify(value?.spec?.tag?.name || null)}, @@ -324,7 +324,7 @@ const {InputSpec, List, Value, Variants} = sdk `, ) const listInputSpec = maybeNewConst( - value.name + "_list_inputSpec", + value.name + '_list_inputSpec', ` InputSpec.of({ "union": ${unionValueName} @@ -340,8 +340,8 @@ const {InputSpec, List, Value, Variants} = sdk warning: ${JSON.stringify(value.warning || null)}, }, { spec: ${listInputSpec}, - displayAs: ${JSON.stringify(value?.spec?.["display-as"] || null)}, - uniqueBy: ${JSON.stringify(value?.spec?.["unique-by"] || null)}, + displayAs: ${JSON.stringify(value?.spec?.['display-as'] || null)}, + uniqueBy: ${JSON.stringify(value?.spec?.['unique-by'] || null)}, })` } } @@ -352,7 +352,7 @@ const {InputSpec, List, Value, Variants} = sdk variants: Record, variantNames: Record, ): string { - let answer = "Variants.of({" + let answer = 'Variants.of({' for (const [key, value] of Object.entries(variants)) { const variantSpec = maybeNewConst(key, convertInputSpec(value)) answer += `"${key}": {name: "${ @@ -373,7 +373,7 @@ const {InputSpec, List, Value, Variants} = sdk } function rangeToTodoComment(range: string | undefined) { - if (!range) return "" + if (!range) return '' return `/* TODO: Convert range for this value (${range})*/` } diff --git a/web/package-lock.json b/web/package-lock.json index bb0aca4f2..8c72087c8 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -1,12 +1,12 @@ { "name": "startos-ui", - "version": "0.4.0-alpha.19", + "version": "0.4.0-alpha.20", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "startos-ui", - "version": "0.4.0-alpha.19", + "version": "0.4.0-alpha.20", "license": "MIT", "dependencies": { "@angular/animations": "^20.3.0", diff --git a/web/package.json b/web/package.json index 49fc3a76d..6d4e17883 100644 --- a/web/package.json +++ b/web/package.json @@ -1,6 +1,6 @@ { "name": "startos-ui", - "version": "0.4.0-alpha.19", + "version": "0.4.0-alpha.20", "author": "Start9 Labs, Inc", "homepage": "https://start9.com/", "license": "MIT", diff --git a/web/projects/shared/src/types/workspace-config.ts b/web/projects/shared/src/types/workspace-config.ts index ebc678ccd..a00d66050 100644 --- a/web/projects/shared/src/types/workspace-config.ts +++ b/web/projects/shared/src/types/workspace-config.ts @@ -1,5 +1,4 @@ export type AccessType = - | 'tor' | 'mdns' | 'localhost' | 'ipv4' diff --git a/web/projects/ui/src/app/routes/portal/components/interfaces/gateways.component.ts b/web/projects/ui/src/app/routes/portal/components/interfaces/gateways.component.ts index 077fa87bd..1a3a4d632 100644 --- a/web/projects/ui/src/app/routes/portal/components/interfaces/gateways.component.ts +++ b/web/projects/ui/src/app/routes/portal/components/interfaces/gateways.component.ts @@ -83,32 +83,8 @@ export class InterfaceGatewaysComponent { readonly gateways = input.required() - async onToggle(gateway: InterfaceGateway) { - const addressInfo = this.interface.value()!.addressInfo - const pkgId = this.interface.packageId() - - const loader = this.loader.open().subscribe() - - try { - if (pkgId) { - await this.api.pkgBindingToggleGateway({ - gateway: gateway.id, - enabled: !gateway.enabled, - internalPort: addressInfo.internalPort, - host: addressInfo.hostId, - package: pkgId, - }) - } else { - await this.api.serverBindingToggleGateway({ - gateway: gateway.id, - enabled: !gateway.enabled, - internalPort: 80, - }) - } - } catch (e: any) { - this.errorService.handleError(e) - } finally { - loader.unsubscribe() - } + async onToggle(_gateway: InterfaceGateway) { + // TODO: Replace with per-address toggle UI (Section 6 frontend overhaul). + // Gateway-level toggle replaced by set-address-enabled RPC. } } diff --git a/web/projects/ui/src/app/routes/portal/components/interfaces/interface.component.ts b/web/projects/ui/src/app/routes/portal/components/interfaces/interface.component.ts index a1deaf339..1284e1396 100644 --- a/web/projects/ui/src/app/routes/portal/components/interfaces/interface.component.ts +++ b/web/projects/ui/src/app/routes/portal/components/interfaces/interface.component.ts @@ -2,7 +2,6 @@ import { ChangeDetectionStrategy, Component, input } from '@angular/core' import { tuiButtonOptionsProvider } from '@taiga-ui/core' import { MappedServiceInterface } from './interface.service' import { InterfaceGatewaysComponent } from './gateways.component' -import { InterfaceTorDomainsComponent } from './tor-domains.component' import { PublicDomainsComponent } from './public-domains/pd.component' import { InterfacePrivateDomainsComponent } from './private-domains.component' import { InterfaceAddressesComponent } from './addresses/addresses.component' @@ -16,7 +15,6 @@ import { InterfaceAddressesComponent } from './addresses/addresses.component' [publicDomains]="value()?.publicDomains" [addSsl]="value()?.addSsl || false" > -

@@ -52,7 +50,6 @@ import { InterfaceAddressesComponent } from './addresses/addresses.component' providers: [tuiButtonOptionsProvider({ size: 'xs' })], imports: [ InterfaceGatewaysComponent, - InterfaceTorDomainsComponent, PublicDomainsComponent, InterfacePrivateDomainsComponent, InterfaceAddressesComponent, diff --git a/web/projects/ui/src/app/routes/portal/components/interfaces/interface.service.ts b/web/projects/ui/src/app/routes/portal/components/interfaces/interface.service.ts index 65e3055bb..6e05c02d9 100644 --- a/web/projects/ui/src/app/routes/portal/components/interfaces/interface.service.ts +++ b/web/projects/ui/src/app/routes/portal/components/interfaces/interface.service.ts @@ -27,17 +27,9 @@ function cmpWithRankedPredicates( return 0 } -type TorAddress = AddressWithInfo & { info: { kind: 'onion' } } -function filterTor(a: AddressWithInfo): a is TorAddress { - return a.info.kind === 'onion' -} -function cmpTor(a: TorAddress, b: TorAddress): -1 | 0 | 1 { - return cmpWithRankedPredicates(a, b, [x => !x.showSsl]) -} - -type LanAddress = AddressWithInfo & { info: { kind: 'ip'; public: false } } +type LanAddress = AddressWithInfo & { info: { public: false } } function filterLan(a: AddressWithInfo): a is LanAddress { - return a.info.kind === 'ip' && !a.info.public + return !a.info.public } function cmpLan(host: T.Host, a: LanAddress, b: LanAddress): -1 | 0 | 1 { return cmpWithRankedPredicates(a, b, [ @@ -53,15 +45,12 @@ function cmpLan(host: T.Host, a: LanAddress, b: LanAddress): -1 | 0 | 1 { type VpnAddress = AddressWithInfo & { info: { - kind: 'ip' public: false hostname: { kind: 'ipv4' | 'ipv6' | 'domain' } } } function filterVpn(a: AddressWithInfo): a is VpnAddress { - return ( - a.info.kind === 'ip' && !a.info.public && a.info.hostname.kind !== 'local' - ) + return !a.info.public && a.info.hostname.kind !== 'local' } function cmpVpn(host: T.Host, a: VpnAddress, b: VpnAddress): -1 | 0 | 1 { return cmpWithRankedPredicates(a, b, [ @@ -76,13 +65,12 @@ function cmpVpn(host: T.Host, a: VpnAddress, b: VpnAddress): -1 | 0 | 1 { type ClearnetAddress = AddressWithInfo & { info: { - kind: 'ip' public: true hostname: { kind: 'ipv4' | 'ipv6' | 'domain' } } } function filterClearnet(a: AddressWithInfo): a is ClearnetAddress { - return a.info.kind === 'ip' && a.info.public + return a.info.public } function cmpClearnet( host: T.Host, @@ -142,10 +130,7 @@ export class InterfaceService { h, ) const info = h - const gateway = - h.kind === 'ip' - ? gateways.find(g => h.gateway.id === g.id) - : undefined + const gateway = gateways.find(g => h.gateway.id === g.id) const res = [] if (url) { res.push({ @@ -171,7 +156,6 @@ export class InterfaceService { }, ) - const torAddrs = allAddressesWithInfo.filter(filterTor).sort(cmpTor) const lanAddrs = allAddressesWithInfo .filter(filterLan) .sort((a, b) => cmpLan(host, a, b)) @@ -188,7 +172,6 @@ export class InterfaceService { clearnetAddrs[0], lanAddrs[0], vpnAddrs[0], - torAddrs[0], ] .filter(a => !!a) .reduce((acc, x) => { @@ -214,9 +197,8 @@ export class InterfaceService { kind: 'domain', visibility: 'public', }) - const tor = addresses.filter({ kind: 'onion' }) const wanIp = addresses.filter({ kind: 'ipv4', visibility: 'public' }) - const bestPublic = [publicDomains, tor, wanIp].flatMap(h => + const bestPublic = [publicDomains, wanIp].flatMap(h => h.format('urlstring'), )[0] const privateDomains = addresses.filter({ @@ -254,9 +236,6 @@ export class InterfaceService { .format('urlstring')[0] onLan = true break - case 'tor': - matching = tor.format('urlstring')[0] - break case 'mdns': matching = mdns.format('urlstring')[0] onLan = true @@ -273,19 +252,23 @@ export class InterfaceService { serviceInterface: T.ServiceInterface, host: T.Host, ): T.HostnameInfo[] { - let hostnameInfo = - host.hostnameInfo[serviceInterface.addressInfo.internalPort] - return ( - hostnameInfo?.filter( - h => - this.config.accessType === 'localhost' || - !( - h.kind === 'ip' && - ((h.hostname.kind === 'ipv6' && - utils.IPV6_LINK_LOCAL.contains(h.hostname.value)) || - h.gateway.id === 'lo') - ), - ) || [] + const binding = + host.bindings[serviceInterface.addressInfo.internalPort] + if (!binding) return [] + const addr = binding.addresses + const enabled = addr.possible.filter(h => + h.public + ? addr.publicEnabled.some(e => utils.deepEqual(e, h)) + : !addr.privateDisabled.some(d => utils.deepEqual(d, h)), + ) + return enabled.filter( + h => + this.config.accessType === 'localhost' || + !( + (h.hostname.kind === 'ipv6' && + utils.IPV6_LINK_LOCAL.contains(h.hostname.value)) || + h.gateway.id === 'lo' + ), ) } @@ -302,31 +285,7 @@ export class InterfaceService { "Requires trusting your server's Root CA", ) - // ** Tor ** - if (info.kind === 'onion') { - access = null - gatewayName = null - type = 'Tor' - bullets = [ - this.i18n.transform('Connections can be slow or unreliable at times'), - this.i18n.transform( - 'Public if you share the address publicly, otherwise private', - ), - this.i18n.transform('Requires using a Tor-enabled device or browser'), - ] - // Tor (SSL) - if (showSsl) { - bullets = [rootCaRequired, ...bullets] - // Tor (NON-SSL) - } else { - bullets.unshift( - this.i18n.transform( - 'Ideal for anonymous, censorship-resistant hosting and remote access', - ), - ) - } - // ** Not Tor ** - } else { + { const port = info.hostname.sslPort || info.hostname.port gatewayName = info.gateway.name @@ -479,7 +438,6 @@ export class InterfaceService { export type MappedServiceInterface = T.ServiceInterface & { gateways: InterfaceGateway[] - torDomains: string[] publicDomains: PublicDomain[] privateDomains: string[] addresses: { diff --git a/web/projects/ui/src/app/routes/portal/components/interfaces/tor-domains.component.ts b/web/projects/ui/src/app/routes/portal/components/interfaces/tor-domains.component.ts deleted file mode 100644 index 001bcd71a..000000000 --- a/web/projects/ui/src/app/routes/portal/components/interfaces/tor-domains.component.ts +++ /dev/null @@ -1,193 +0,0 @@ -import { - ChangeDetectionStrategy, - Component, - inject, - input, -} from '@angular/core' -import { - DialogService, - DocsLinkDirective, - ErrorService, - i18nPipe, - LoadingService, -} from '@start9labs/shared' -import { ISB, utils } from '@start9labs/start-sdk' -import { TuiButton, TuiTitle } from '@taiga-ui/core' -import { TuiSkeleton } from '@taiga-ui/kit' -import { TuiCell } from '@taiga-ui/layout' -import { filter } from 'rxjs' -import { - FormComponent, - FormContext, -} from 'src/app/routes/portal/components/form.component' -import { PlaceholderComponent } from 'src/app/routes/portal/components/placeholder.component' -import { ApiService } from 'src/app/services/api/embassy-api.service' -import { FormDialogService } from 'src/app/services/form-dialog.service' -import { configBuilderToSpec } from 'src/app/utils/configBuilderToSpec' - -import { InterfaceComponent } from './interface.component' - -type OnionForm = { - key: string -} - -@Component({ - selector: 'section[torDomains]', - template: ` -
- {{ 'Tor Domains' | i18n }} - - {{ 'Documentation' | i18n }} - - -
- @for (domain of torDomains(); track domain) { -
- {{ domain }} - -
- } @empty { - @if (torDomains()) { - - {{ 'No Tor domains' | i18n }} - - } @else { - @for (_ of [0, 1]; track $index) { - - } - } - } - `, - styles: ` - :host { - grid-column: span 6; - overflow-wrap: break-word; - } - `, - host: { class: 'g-card' }, - imports: [ - TuiCell, - TuiTitle, - TuiButton, - PlaceholderComponent, - i18nPipe, - DocsLinkDirective, - TuiSkeleton, - ], - changeDetection: ChangeDetectionStrategy.OnPush, -}) -export class InterfaceTorDomainsComponent { - private readonly dialog = inject(DialogService) - private readonly formDialog = inject(FormDialogService) - private readonly loader = inject(LoadingService) - private readonly errorService = inject(ErrorService) - private readonly api = inject(ApiService) - private readonly interface = inject(InterfaceComponent) - private readonly i18n = inject(i18nPipe) - - readonly torDomains = input.required() - - async remove(onion: string) { - this.dialog - .openConfirm({ label: 'Are you sure?', size: 's' }) - .pipe(filter(Boolean)) - .subscribe(async () => { - const loader = this.loader.open('Removing').subscribe() - const params = { onion } - - try { - if (this.interface.packageId()) { - await this.api.pkgRemoveOnion({ - ...params, - package: this.interface.packageId(), - host: this.interface.value()?.addressInfo.hostId || '', - }) - } else { - await this.api.serverRemoveOnion(params) - } - return true - } catch (e: any) { - this.errorService.handleError(e) - return false - } finally { - loader.unsubscribe() - } - }) - } - - async add() { - this.formDialog.open>(FormComponent, { - label: 'New Tor domain', - data: { - spec: await configBuilderToSpec( - ISB.InputSpec.of({ - key: ISB.Value.text({ - name: this.i18n.transform('Private Key (optional)')!, - description: this.i18n.transform( - 'Optionally provide a base64-encoded ed25519 private key for generating the Tor V3 (.onion) domain. If not provided, a random key will be generated.', - ), - required: false, - default: null, - patterns: [utils.Patterns.base64], - }), - }), - ), - buttons: [ - { - text: this.i18n.transform('Save')!, - handler: async value => this.save(value.key), - }, - ], - }, - }) - } - - private async save(key?: string): Promise { - const loader = this.loader.open('Saving').subscribe() - - try { - const onion = key - ? await this.api.addTorKey({ key }) - : await this.api.generateTorKey({}) - - if (this.interface.packageId()) { - await this.api.pkgAddOnion({ - onion, - package: this.interface.packageId(), - host: this.interface.value()?.addressInfo.hostId || '', - }) - } else { - await this.api.serverAddOnion({ onion }) - } - return true - } catch (e: any) { - this.errorService.handleError(e) - return false - } finally { - loader.unsubscribe() - } - } -} diff --git a/web/projects/ui/src/app/routes/portal/routes/logs/logs.routes.ts b/web/projects/ui/src/app/routes/portal/routes/logs/logs.routes.ts index 281683fcd..c42623ea9 100644 --- a/web/projects/ui/src/app/routes/portal/routes/logs/logs.routes.ts +++ b/web/projects/ui/src/app/routes/portal/routes/logs/logs.routes.ts @@ -13,10 +13,6 @@ export const ROUTES: Routes = [ path: 'os', loadComponent: () => import('./routes/os.component'), }, - { - path: 'tor', - loadComponent: () => import('./routes/tor.component'), - }, ] export default ROUTES diff --git a/web/projects/ui/src/app/routes/portal/routes/logs/routes/outlet.component.ts b/web/projects/ui/src/app/routes/portal/routes/logs/routes/outlet.component.ts index 97d3f69cd..524a997da 100644 --- a/web/projects/ui/src/app/routes/portal/routes/logs/routes/outlet.component.ts +++ b/web/projects/ui/src/app/routes/portal/routes/logs/routes/outlet.component.ts @@ -79,12 +79,6 @@ export default class SystemLogsComponent { subtitle: 'Raw, unfiltered operating system logs', icon: '@tui.square-dashed-bottom-code', }, - { - link: 'tor', - title: 'Tor Logs', - subtitle: 'Diagnostics for the Tor daemon on this server', - icon: '@tui.target', - }, { link: 'kernel', title: 'Kernel Logs', diff --git a/web/projects/ui/src/app/routes/portal/routes/logs/routes/tor.component.ts b/web/projects/ui/src/app/routes/portal/routes/logs/routes/tor.component.ts deleted file mode 100644 index 45b711fa5..000000000 --- a/web/projects/ui/src/app/routes/portal/routes/logs/routes/tor.component.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { ChangeDetectionStrategy, Component, inject } from '@angular/core' -import { i18nPipe } from '@start9labs/shared' -import { LogsComponent } from 'src/app/routes/portal/components/logs/logs.component' -import { LogsHeaderComponent } from 'src/app/routes/portal/routes/logs/components/header.component' -import { RR } from 'src/app/services/api/api.types' -import { ApiService } from 'src/app/services/api/embassy-api.service' - -@Component({ - template: ` - - {{ 'Diagnostics for the Tor daemon on this server' | i18n }} - - - `, - styles: ` - :host { - padding: 1rem; - } - `, - changeDetection: ChangeDetectionStrategy.OnPush, - imports: [LogsComponent, LogsHeaderComponent, i18nPipe], - host: { class: 'g-page' }, -}) -export default class SystemTorComponent { - private readonly api = inject(ApiService) - - protected readonly follow = (params: RR.FollowServerLogsReq) => - this.api.followTorLogs(params) - - protected readonly fetch = (params: RR.GetServerLogsReq) => - this.api.getTorLogs(params) -} diff --git a/web/projects/ui/src/app/routes/portal/routes/services/routes/interface.component.ts b/web/projects/ui/src/app/routes/portal/routes/services/routes/interface.component.ts index 2b42dacc9..03ec3d3ee 100644 --- a/web/projects/ui/src/app/routes/portal/routes/services/routes/interface.component.ts +++ b/web/projects/ui/src/app/routes/portal/routes/services/routes/interface.component.ts @@ -135,11 +135,10 @@ export default class ServiceInterfaceRoute { gateways.map(g => ({ enabled: (g.public - ? binding?.net.publicEnabled.includes(g.id) - : !binding?.net.privateDisabled.includes(g.id)) ?? false, + ? binding?.addresses.publicEnabled.some(a => a.gateway.id === g.id) + : !binding?.addresses.privateDisabled.some(a => a.gateway.id === g.id)) ?? false, ...g, })) || [], - torDomains: host.onions, publicDomains: getPublicDomains(host.publicDomains, gateways), privateDomains: host.privateDomains, addSsl: !!binding?.options.addSsl, diff --git a/web/projects/ui/src/app/routes/portal/routes/sideload/sideload.component.ts b/web/projects/ui/src/app/routes/portal/routes/sideload/sideload.component.ts index 8f2055787..ebf7b2587 100644 --- a/web/projects/ui/src/app/routes/portal/routes/sideload/sideload.component.ts +++ b/web/projects/ui/src/app/routes/portal/routes/sideload/sideload.component.ts @@ -13,7 +13,6 @@ import { TuiFiles, tuiInputFilesOptionsProvider, } from '@taiga-ui/kit' -import { ConfigService } from 'src/app/services/config.service' import { TitleDirective } from 'src/app/services/title.service' import { SideloadPackageComponent } from './package.component' @@ -55,11 +54,6 @@ import { MarketplacePkgSideload, validateS9pk } from './sideload.utils'

{{ 'Upload .s9pk package file' | i18n }}

- @if (isTor) { -

- {{ 'Warning: package upload will be slow over Tor.' | i18n }} -

- }
} @@ -92,8 +86,6 @@ import { MarketplacePkgSideload, validateS9pk } from './sideload.utils' ], }) export default class SideloadComponent { - readonly isTor = inject(ConfigService).accessType === 'tor' - file: File | null = null readonly package = signal(null) readonly error = signal(null) diff --git a/web/projects/ui/src/app/routes/portal/routes/system/routes/general/general.component.ts b/web/projects/ui/src/app/routes/portal/routes/system/routes/general/general.component.ts index aaff817d0..cdf664e86 100644 --- a/web/projects/ui/src/app/routes/portal/routes/system/routes/general/general.component.ts +++ b/web/projects/ui/src/app/routes/portal/routes/system/routes/general/general.component.ts @@ -47,13 +47,11 @@ import { PolymorpheusComponent } from '@taiga-ui/polymorpheus' import { PatchDB } from 'patch-db-client' import { filter } from 'rxjs' import { ApiService } from 'src/app/services/api/embassy-api.service' -import { ConfigService } from 'src/app/services/config.service' import { OSService } from 'src/app/services/os.service' import { DataModel } from 'src/app/services/patch-db/data-model' import { TitleDirective } from 'src/app/services/title.service' import { SnekDirective } from './snek.directive' import { UPDATE } from './update.component' -import { SystemWipeComponent } from './wipe.component' import { KeyboardSelectComponent } from './keyboard-select.component' @Component({ @@ -66,7 +64,7 @@ import { KeyboardSelectComponent } from './keyboard-select.component' @if (server(); as server) {
- + {{ 'Software Update' | i18n }} @@ -178,18 +176,6 @@ import { KeyboardSelectComponent } from './keyboard-select.component' }
-
- - - {{ 'Restart Tor' | i18n }} - - {{ 'Restart the Tor daemon on your server' | i18n }} - - - -
@if (count > 4) {
@@ -283,12 +269,10 @@ export default class SystemGeneralComponent { private readonly errorService = inject(ErrorService) private readonly patch = inject>(PatchDB) private readonly api = inject(ApiService) - private readonly isTor = inject(ConfigService).accessType === 'tor' private readonly dialog = inject(DialogService) private readonly i18n = inject(i18nPipe) private readonly injector = inject(INJECTOR) - wipe = false count = 0 readonly server = toSignal(this.patch.watch$('serverInfo')) @@ -392,24 +376,6 @@ export default class SystemGeneralComponent { }) } - onTorRestart() { - this.wipe = false - this.dialog - .openConfirm({ - label: this.isTor ? 'Warning' : 'Confirm', - data: { - content: new PolymorpheusComponent( - SystemWipeComponent, - this.injector, - ), - yes: 'Restart', - no: 'Cancel', - }, - }) - .pipe(filter(Boolean)) - .subscribe(() => this.resetTor(this.wipe)) - } - async onRepair() { this.dialog .openConfirm({ @@ -532,19 +498,6 @@ export default class SystemGeneralComponent { .subscribe(() => this.restart()) } - private async resetTor(wipeState: boolean) { - const loader = this.loader.open().subscribe() - - try { - await this.api.resetTor({ wipeState, reason: 'User triggered' }) - this.dialog.openAlert('Tor restart in progress').subscribe() - } catch (e: any) { - this.errorService.handleError(e) - } finally { - loader.unsubscribe() - } - } - private update() { this.dialogs .open(UPDATE, { diff --git a/web/projects/ui/src/app/routes/portal/routes/system/routes/general/wipe.component.ts b/web/projects/ui/src/app/routes/portal/routes/system/routes/general/wipe.component.ts deleted file mode 100644 index 0456cbed3..000000000 --- a/web/projects/ui/src/app/routes/portal/routes/system/routes/general/wipe.component.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { ChangeDetectionStrategy, Component, inject } from '@angular/core' -import { FormsModule } from '@angular/forms' -import { TuiLabel } from '@taiga-ui/core' -import { TuiCheckbox } from '@taiga-ui/kit' -import { ConfigService } from 'src/app/services/config.service' -import SystemGeneralComponent from './general.component' -import { i18nPipe } from '@start9labs/shared' - -@Component({ - template: ` - @if (isTor) { -

- {{ - 'You are currently connected over Tor. If you restart the Tor daemon, you will lose connectivity until it comes back online.' - | i18n - }} -

- } -

- {{ - 'Optionally wipe state to forcibly acquire new guard nodes. It is recommended to try without wiping state first.' - | i18n - }} -

- - `, - changeDetection: ChangeDetectionStrategy.OnPush, - imports: [TuiLabel, FormsModule, TuiCheckbox, i18nPipe], -}) -export class SystemWipeComponent { - readonly isTor = inject(ConfigService).accessType === 'tor' - readonly component = inject(SystemGeneralComponent) -} diff --git a/web/projects/ui/src/app/routes/portal/routes/system/routes/startos-ui/startos-ui.component.ts b/web/projects/ui/src/app/routes/portal/routes/system/routes/startos-ui/startos-ui.component.ts index 524eb92dc..6d2246f98 100644 --- a/web/projects/ui/src/app/routes/portal/routes/system/routes/startos-ui/startos-ui.component.ts +++ b/web/projects/ui/src/app/routes/portal/routes/system/routes/startos-ui/startos-ui.component.ts @@ -96,11 +96,10 @@ export default class StartOsUiComponent { gateways: gateways.map(g => ({ enabled: (g.public - ? binding?.net.publicEnabled.includes(g.id) - : !binding?.net.privateDisabled.includes(g.id)) ?? false, + ? binding?.addresses.publicEnabled.some(a => a.gateway.id === g.id) + : !binding?.addresses.privateDisabled.some(a => a.gateway.id === g.id)) ?? false, ...g, })), - torDomains: network.host.onions, publicDomains: getPublicDomains(network.host.publicDomains, gateways), privateDomains: network.host.privateDomains, addSsl: true, diff --git a/web/projects/ui/src/app/services/api/api.fixures.ts b/web/projects/ui/src/app/services/api/api.fixures.ts index 5be5003a3..7e092ef80 100644 --- a/web/projects/ui/src/app/services/api/api.fixures.ts +++ b/web/projects/ui/src/app/services/api/api.fixures.ts @@ -2126,8 +2126,74 @@ export namespace Mock { net: { assignedPort: 80, assignedSslPort: 443, - publicEnabled: [], + }, + addresses: { privateDisabled: [], + publicEnabled: [], + possible: [ + { + gateway: { id: 'eth0', name: 'Ethernet', public: false }, + public: false, + hostname: { + kind: 'local', + value: 'adjective-noun.local', + port: null, + sslPort: 1234, + }, + }, + { + gateway: { id: 'wlan0', name: 'Wireless', public: false }, + public: false, + hostname: { + kind: 'local', + value: 'adjective-noun.local', + port: null, + sslPort: 1234, + }, + }, + { + gateway: { id: 'wlan0', name: 'Wireless', public: false }, + public: false, + hostname: { + kind: 'ipv4', + value: '192.168.10.11', + port: null, + sslPort: 1234, + }, + }, + { + gateway: { id: 'wlan0', name: 'Wireless', public: false }, + public: false, + hostname: { + kind: 'ipv4', + value: '10.0.0.2', + port: null, + sslPort: 1234, + }, + }, + { + gateway: { id: 'eth0', name: 'Ethernet', public: false }, + public: false, + hostname: { + kind: 'ipv6', + value: '[fe80:cd00:0000:0cde:1257:0000:211e:72cd]', + scopeId: 2, + port: null, + sslPort: 1234, + }, + }, + { + gateway: { id: 'wlan0', name: 'Wireless', public: false }, + public: false, + hostname: { + kind: 'ipv6', + value: '[fe80:cd00:0000:0cde:1257:0000:211e:1234]', + scopeId: 3, + port: null, + sslPort: 1234, + }, + }, + ], }, options: { addSsl: null, @@ -2138,87 +2204,6 @@ export namespace Mock { }, publicDomains: {}, privateDomains: [], - onions: [], - hostnameInfo: { - 80: [ - { - kind: 'ip', - gateway: { id: 'eth0', name: 'Ethernet', public: false }, - public: false, - hostname: { - kind: 'local', - value: 'adjective-noun.local', - port: null, - sslPort: 1234, - }, - }, - { - kind: 'ip', - gateway: { id: 'wlan0', name: 'Wireless', public: false }, - public: false, - hostname: { - kind: 'local', - value: 'adjective-noun.local', - port: null, - sslPort: 1234, - }, - }, - { - kind: 'ip', - gateway: { id: 'wlan0', name: 'Wireless', public: false }, - public: false, - hostname: { - kind: 'ipv4', - value: '192.168.10.11', - port: null, - sslPort: 1234, - }, - }, - { - kind: 'ip', - gateway: { id: 'wlan0', name: 'Wireless', public: false }, - public: false, - hostname: { - kind: 'ipv4', - value: '10.0.0.2', - port: null, - sslPort: 1234, - }, - }, - { - kind: 'ip', - gateway: { id: 'eth0', name: 'Ethernet', public: false }, - public: false, - hostname: { - kind: 'ipv6', - value: '[fe80:cd00:0000:0cde:1257:0000:211e:72cd]', - scopeId: 2, - port: null, - sslPort: 1234, - }, - }, - { - kind: 'ip', - gateway: { id: 'wlan0', name: 'Wireless', public: false }, - public: false, - hostname: { - kind: 'ipv6', - value: '[fe80:cd00:0000:0cde:1257:0000:211e:1234]', - scopeId: 3, - port: null, - sslPort: 1234, - }, - }, - { - kind: 'onion', - hostname: { - value: 'bitcoin-p2p.onion', - port: 80, - sslPort: 443, - }, - }, - ], - }, }, bcdefgh: { bindings: { @@ -2227,8 +2212,11 @@ export namespace Mock { net: { assignedPort: 8332, assignedSslPort: null, - publicEnabled: [], + }, + addresses: { privateDisabled: [], + publicEnabled: [], + possible: [], }, options: { addSsl: null, @@ -2239,10 +2227,6 @@ export namespace Mock { }, publicDomains: {}, privateDomains: [], - onions: [], - hostnameInfo: { - 8332: [], - }, }, cdefghi: { bindings: { @@ -2251,8 +2235,11 @@ export namespace Mock { net: { assignedPort: 8333, assignedSslPort: null, - publicEnabled: [], + }, + addresses: { privateDisabled: [], + publicEnabled: [], + possible: [], }, options: { addSsl: null, @@ -2263,10 +2250,6 @@ export namespace Mock { }, publicDomains: {}, privateDomains: [], - onions: [], - hostnameInfo: { - 8333: [], - }, }, }, storeExposedDependents: [], diff --git a/web/projects/ui/src/app/services/api/api.types.ts b/web/projects/ui/src/app/services/api/api.types.ts index 1267d57d8..76a96488f 100644 --- a/web/projects/ui/src/app/services/api/api.types.ts +++ b/web/projects/ui/src/app/services/api/api.types.ts @@ -79,14 +79,14 @@ export namespace RR { uptime: number // seconds } - export type GetServerLogsReq = FetchLogsReq // server.logs & server.kernel-logs & net.tor.logs + export type GetServerLogsReq = FetchLogsReq // server.logs & server.kernel-logs export type GetServerLogsRes = FetchLogsRes export type FollowServerLogsReq = { limit?: number // (optional) default is 50. Ignored if cursor provided boot?: number | string | null // (optional) number is offset (0: current, -1 prev, +1 first), string is a specific boot id, null is all. Default is undefined cursor?: string // the last known log. Websocket will return all logs since this log - } // server.logs.follow & server.kernel-logs.follow & net.tor.follow-logs + } // server.logs.follow & server.kernel-logs.follow export type FollowServerLogsRes = { startCursor: string guid: string @@ -120,12 +120,6 @@ export namespace RR { } // net.dns.query export type QueryDnsRes = string | null - export type ResetTorReq = { - wipeState: boolean - reason: string - } // net.tor.reset - export type ResetTorRes = null - export type SetKeyboardReq = FullKeyboard // server.set-keyboard export type SetKeyboardRes = null @@ -301,29 +295,13 @@ export namespace RR { } export type RemoveAcmeRes = null - export type AddTorKeyReq = { - // net.tor.key.add - key: string - } - export type GenerateTorKeyReq = {} // net.tor.key.generate - export type AddTorKeyRes = string // onion address *with* .onion suffix - - export type ServerBindingToggleGatewayReq = { - // server.host.binding.set-gateway-enabled - gateway: T.GatewayId + export type ServerBindingSetAddressEnabledReq = { + // server.host.binding.set-address-enabled internalPort: 80 - enabled: boolean + address: string // JSON-serialized HostnameInfo + enabled: boolean | null // null = reset to default } - export type ServerBindingToggleGatewayRes = null - - export type ServerAddOnionReq = { - // server.host.address.onion.add - onion: string // address *with* .onion suffix - } - export type AddOnionRes = null - - export type ServerRemoveOnionReq = ServerAddOnionReq // server.host.address.onion.remove - export type RemoveOnionRes = null + export type ServerBindingSetAddressEnabledRes = null export type OsUiAddPublicDomainReq = { // server.host.address.domain.public.add @@ -351,23 +329,16 @@ export namespace RR { } export type OsUiRemovePrivateDomainRes = null - export type PkgBindingToggleGatewayReq = Omit< - ServerBindingToggleGatewayReq, + export type PkgBindingSetAddressEnabledReq = Omit< + ServerBindingSetAddressEnabledReq, 'internalPort' > & { - // package.host.binding.set-gateway-enabled + // package.host.binding.set-address-enabled internalPort: number package: T.PackageId // string host: T.HostId // string } - export type PkgBindingToggleGatewayRes = null - - export type PkgAddOnionReq = ServerAddOnionReq & { - // package.host.address.onion.add - package: T.PackageId // string - host: T.HostId // string - } - export type PkgRemoveOnionReq = PkgAddOnionReq // package.host.address.onion.remove + export type PkgBindingSetAddressEnabledRes = null export type PkgAddPublicDomainReq = OsUiAddPublicDomainReq & { // package.host.address.domain.public.add diff --git a/web/projects/ui/src/app/services/api/embassy-api.service.ts b/web/projects/ui/src/app/services/api/embassy-api.service.ts index 173b6da48..7e84ffabe 100644 --- a/web/projects/ui/src/app/services/api/embassy-api.service.ts +++ b/web/projects/ui/src/app/services/api/embassy-api.service.ts @@ -81,8 +81,6 @@ export abstract class ApiService { params: RR.GetServerLogsReq, ): Promise - abstract getTorLogs(params: RR.GetServerLogsReq): Promise - abstract getKernelLogs( params: RR.GetServerLogsReq, ): Promise @@ -91,10 +89,6 @@ export abstract class ApiService { params: RR.FollowServerLogsReq, ): Promise - abstract followTorLogs( - params: RR.FollowServerLogsReq, - ): Promise - abstract followKernelLogs( params: RR.FollowServerLogsReq, ): Promise @@ -125,8 +119,6 @@ export abstract class ApiService { abstract queryDns(params: RR.QueryDnsReq): Promise - abstract resetTor(params: RR.ResetTorReq): Promise - // smtp abstract setSmtp(params: RR.SetSMTPReq): Promise @@ -352,21 +344,9 @@ export abstract class ApiService { abstract removeAcme(params: RR.RemoveAcmeReq): Promise - abstract addTorKey(params: RR.AddTorKeyReq): Promise - - abstract generateTorKey( - params: RR.GenerateTorKeyReq, - ): Promise - - abstract serverBindingToggleGateway( - params: RR.ServerBindingToggleGatewayReq, - ): Promise - - abstract serverAddOnion(params: RR.ServerAddOnionReq): Promise - - abstract serverRemoveOnion( - params: RR.ServerRemoveOnionReq, - ): Promise + abstract serverBindingSetAddressEnabled( + params: RR.ServerBindingSetAddressEnabledReq, + ): Promise abstract osUiAddPublicDomain( params: RR.OsUiAddPublicDomainReq, @@ -384,15 +364,9 @@ export abstract class ApiService { params: RR.OsUiRemovePrivateDomainReq, ): Promise - abstract pkgBindingToggleGateway( - params: RR.PkgBindingToggleGatewayReq, - ): Promise - - abstract pkgAddOnion(params: RR.PkgAddOnionReq): Promise - - abstract pkgRemoveOnion( - params: RR.PkgRemoveOnionReq, - ): Promise + abstract pkgBindingSetAddressEnabled( + params: RR.PkgBindingSetAddressEnabledReq, + ): Promise abstract pkgAddPublicDomain( params: RR.PkgAddPublicDomainReq, diff --git a/web/projects/ui/src/app/services/api/embassy-live-api.service.ts b/web/projects/ui/src/app/services/api/embassy-live-api.service.ts index 8201b4908..2b4d2a056 100644 --- a/web/projects/ui/src/app/services/api/embassy-live-api.service.ts +++ b/web/projects/ui/src/app/services/api/embassy-live-api.service.ts @@ -195,10 +195,6 @@ export class LiveApiService extends ApiService { return this.rpcRequest({ method: 'server.logs', params }) } - async getTorLogs(params: RR.GetServerLogsReq): Promise { - return this.rpcRequest({ method: 'net.tor.logs', params }) - } - async getKernelLogs( params: RR.GetServerLogsReq, ): Promise { @@ -211,12 +207,6 @@ export class LiveApiService extends ApiService { return this.rpcRequest({ method: 'server.logs.follow', params }) } - async followTorLogs( - params: RR.FollowServerLogsReq, - ): Promise { - return this.rpcRequest({ method: 'net.tor.logs.follow', params }) - } - async followKernelLogs( params: RR.FollowServerLogsReq, ): Promise { @@ -278,10 +268,6 @@ export class LiveApiService extends ApiService { }) } - async resetTor(params: RR.ResetTorReq): Promise { - return this.rpcRequest({ method: 'net.tor.reset', params }) - } - // marketplace URLs async checkOSUpdate( @@ -633,41 +619,11 @@ export class LiveApiService extends ApiService { }) } - async addTorKey(params: RR.AddTorKeyReq): Promise { + async serverBindingSetAddressEnabled( + params: RR.ServerBindingSetAddressEnabledReq, + ): Promise { return this.rpcRequest({ - method: 'net.tor.key.add', - params, - }) - } - - async generateTorKey(params: RR.GenerateTorKeyReq): Promise { - return this.rpcRequest({ - method: 'net.tor.key.generate', - params, - }) - } - - async serverBindingToggleGateway( - params: RR.ServerBindingToggleGatewayReq, - ): Promise { - return this.rpcRequest({ - method: 'server.host.binding.set-gateway-enabled', - params, - }) - } - - async serverAddOnion(params: RR.ServerAddOnionReq): Promise { - return this.rpcRequest({ - method: 'server.host.address.onion.add', - params, - }) - } - - async serverRemoveOnion( - params: RR.ServerRemoveOnionReq, - ): Promise { - return this.rpcRequest({ - method: 'server.host.address.onion.remove', + method: 'server.host.binding.set-address-enabled', params, }) } @@ -708,27 +664,11 @@ export class LiveApiService extends ApiService { }) } - async pkgBindingToggleGateway( - params: RR.PkgBindingToggleGatewayReq, - ): Promise { + async pkgBindingSetAddressEnabled( + params: RR.PkgBindingSetAddressEnabledReq, + ): Promise { return this.rpcRequest({ - method: 'package.host.binding.set-gateway-enabled', - params, - }) - } - - async pkgAddOnion(params: RR.PkgAddOnionReq): Promise { - return this.rpcRequest({ - method: 'package.host.address.onion.add', - params, - }) - } - - async pkgRemoveOnion( - params: RR.PkgRemoveOnionReq, - ): Promise { - return this.rpcRequest({ - method: 'package.host.address.onion.remove', + method: 'package.host.binding.set-address-enabled', params, }) } diff --git a/web/projects/ui/src/app/services/api/embassy-mock-api.service.ts b/web/projects/ui/src/app/services/api/embassy-mock-api.service.ts index 7b4b8122e..f14e0ac20 100644 --- a/web/projects/ui/src/app/services/api/embassy-mock-api.service.ts +++ b/web/projects/ui/src/app/services/api/embassy-mock-api.service.ts @@ -281,17 +281,6 @@ export class MockApiService extends ApiService { } } - async getTorLogs(params: RR.GetServerLogsReq): Promise { - await pauseFor(2000) - const entries = this.randomLogs(params.limit) - - return { - entries, - startCursor: 'start-cursor', - endCursor: 'end-cursor', - } - } - async getKernelLogs( params: RR.GetServerLogsReq, ): Promise { @@ -315,16 +304,6 @@ export class MockApiService extends ApiService { } } - async followTorLogs( - params: RR.FollowServerLogsReq, - ): Promise { - await pauseFor(2000) - return { - startCursor: 'start-cursor', - guid: 'logs-guid', - } - } - async followKernelLogs( params: RR.FollowServerLogsReq, ): Promise { @@ -504,11 +483,6 @@ export class MockApiService extends ApiService { return null } - async resetTor(params: RR.ResetTorReq): Promise { - await pauseFor(2000) - return null - } - // marketplace URLs async checkOSUpdate( @@ -1415,77 +1389,12 @@ export class MockApiService extends ApiService { return null } - async addTorKey(params: RR.AddTorKeyReq): Promise { - await pauseFor(2000) - return 'vanityabcdefghijklmnop.onion' - } - - async generateTorKey(params: RR.GenerateTorKeyReq): Promise { - await pauseFor(2000) - return 'abcdefghijklmnopqrstuv.onion' - } - - async serverBindingToggleGateway( - params: RR.ServerBindingToggleGatewayReq, - ): Promise { + async serverBindingSetAddressEnabled( + params: RR.ServerBindingSetAddressEnabledReq, + ): Promise { await pauseFor(2000) - const patch = [ - { - op: PatchOp.REPLACE, - path: `/serverInfo/network/host/bindings/${params.internalPort}/net/publicEnabled`, - value: params.enabled ? [params.gateway] : [], - }, - ] - this.mockRevision(patch) - - return null - } - - async serverAddOnion(params: RR.ServerAddOnionReq): Promise { - await pauseFor(2000) - - const patch: Operation[] = [ - { - op: PatchOp.ADD, - path: `/serverInfo/host/onions/0`, - value: params.onion, - }, - { - op: PatchOp.ADD, - path: `/serverInfo/host/hostnameInfo/80/0`, - value: { - kind: 'onion', - hostname: { - port: 80, - sslPort: 443, - value: params.onion, - }, - }, - }, - ] - this.mockRevision(patch) - - return null - } - - async serverRemoveOnion( - params: RR.ServerRemoveOnionReq, - ): Promise { - await pauseFor(2000) - - const patch: RemoveOperation[] = [ - { - op: PatchOp.REMOVE, - path: `/serverInfo/host/onions/0`, - }, - { - op: PatchOp.REMOVE, - path: `/serverInfo/host/hostnameInfo/80/-1`, - }, - ] - this.mockRevision(patch) - + // Mock: no-op since address enable/disable modifies DerivedAddressInfo sets return null } @@ -1504,10 +1413,9 @@ export class MockApiService extends ApiService { }, { op: PatchOp.ADD, - path: `/serverInfo/host/hostnameInfo/80/0`, + path: `/serverInfo/network/host/bindings/80/addresses/possible/0`, value: { - kind: 'ip', - gatewayId: 'eth0', + gateway: { id: 'eth0', name: 'Ethernet', public: false }, public: true, hostname: { kind: 'domain', @@ -1536,7 +1444,7 @@ export class MockApiService extends ApiService { }, { op: PatchOp.REMOVE, - path: `/serverInfo/host/hostnameInfo/80/0`, + path: `/serverInfo/network/host/bindings/80/addresses/possible/0`, }, ] this.mockRevision(patch) @@ -1557,10 +1465,9 @@ export class MockApiService extends ApiService { }, { op: PatchOp.ADD, - path: `/serverInfo/host/hostnameInfo/80/0`, + path: `/serverInfo/network/host/bindings/80/addresses/possible/0`, value: { - kind: 'ip', - gatewayId: 'eth0', + gateway: { id: 'eth0', name: 'Ethernet', public: false }, public: false, hostname: { kind: 'domain', @@ -1590,7 +1497,7 @@ export class MockApiService extends ApiService { }, { op: PatchOp.REMOVE, - path: `/serverInfo/host/hostnameInfo/80/0`, + path: `/serverInfo/network/host/bindings/80/addresses/possible/0`, }, ] this.mockRevision(patch) @@ -1598,67 +1505,12 @@ export class MockApiService extends ApiService { return null } - async pkgBindingToggleGateway( - params: RR.PkgBindingToggleGatewayReq, - ): Promise { + async pkgBindingSetAddressEnabled( + params: RR.PkgBindingSetAddressEnabledReq, + ): Promise { await pauseFor(2000) - const patch = [ - { - op: PatchOp.REPLACE, - path: `/packageData/${params.package}/hosts/${params.host}/bindings/${params.internalPort}/net/privateDisabled`, - value: params.enabled ? [] : [params.gateway], - }, - ] - this.mockRevision(patch) - - return null - } - - async pkgAddOnion(params: RR.PkgAddOnionReq): Promise { - await pauseFor(2000) - - const patch: Operation[] = [ - { - op: PatchOp.ADD, - path: `/packageData/${params.package}/hosts/${params.host}/onions/0`, - value: params.onion, - }, - { - op: PatchOp.ADD, - path: `/packageData/${params.package}/hosts/${params.host}/hostnameInfo/80/0`, - value: { - kind: 'onion', - hostname: { - port: 80, - sslPort: 443, - value: params.onion, - }, - }, - }, - ] - this.mockRevision(patch) - - return null - } - - async pkgRemoveOnion( - params: RR.PkgRemoveOnionReq, - ): Promise { - await pauseFor(2000) - - const patch: RemoveOperation[] = [ - { - op: PatchOp.REMOVE, - path: `/packageData/${params.package}/hosts/${params.host}/onions/0`, - }, - { - op: PatchOp.REMOVE, - path: `/packageData/${params.package}/hosts/${params.host}/hostnameInfo/80/0`, - }, - ] - this.mockRevision(patch) - + // Mock: no-op since address enable/disable modifies DerivedAddressInfo sets return null } @@ -1677,10 +1529,9 @@ export class MockApiService extends ApiService { }, { op: PatchOp.ADD, - path: `/packageData/${params.package}/hosts/${params.host}/hostnameInfo/80/0`, + path: `/packageData/${params.package}/hosts/${params.host}/bindings/80/addresses/possible/0`, value: { - kind: 'ip', - gatewayId: 'eth0', + gateway: { id: 'eth0', name: 'Ethernet', public: false }, public: true, hostname: { kind: 'domain', @@ -1709,7 +1560,7 @@ export class MockApiService extends ApiService { }, { op: PatchOp.REMOVE, - path: `/packageData/${params.package}/hosts/${params.host}/hostnameInfo/80/0`, + path: `/packageData/${params.package}/hosts/${params.host}/bindings/80/addresses/possible/0`, }, ] this.mockRevision(patch) @@ -1730,10 +1581,9 @@ export class MockApiService extends ApiService { }, { op: PatchOp.ADD, - path: `/packageData/${params.package}/hosts/${params.host}/hostnameInfo/80/0`, + path: `/packageData/${params.package}/hosts/${params.host}/bindings/80/addresses/possible/0`, value: { - kind: 'ip', - gatewayId: 'eth0', + gateway: { id: 'eth0', name: 'Ethernet', public: false }, public: false, hostname: { kind: 'domain', @@ -1763,7 +1613,7 @@ export class MockApiService extends ApiService { }, { op: PatchOp.REMOVE, - path: `/packageData/${params.package}/hosts/${params.host}/hostnameInfo/80/0`, + path: `/packageData/${params.package}/hosts/${params.host}/bindings/80/addresses/possible/0`, }, ] this.mockRevision(patch) diff --git a/web/projects/ui/src/app/services/api/mock-patch.ts b/web/projects/ui/src/app/services/api/mock-patch.ts index af26a5855..d3484fefc 100644 --- a/web/projects/ui/src/app/services/api/mock-patch.ts +++ b/web/projects/ui/src/app/services/api/mock-patch.ts @@ -38,8 +38,74 @@ export const mockPatchData: DataModel = { net: { assignedPort: null, assignedSslPort: 443, - publicEnabled: [], + }, + addresses: { privateDisabled: [], + publicEnabled: [], + possible: [ + { + gateway: { id: 'eth0', name: 'Ethernet', public: false }, + public: false, + hostname: { + kind: 'local', + value: 'adjective-noun.local', + port: null, + sslPort: 443, + }, + }, + { + gateway: { id: 'wlan0', name: 'Wireless', public: false }, + public: false, + hostname: { + kind: 'local', + value: 'adjective-noun.local', + port: null, + sslPort: 443, + }, + }, + { + gateway: { id: 'eth0', name: 'Ethernet', public: false }, + public: false, + hostname: { + kind: 'ipv4', + value: '10.0.0.1', + port: null, + sslPort: 443, + }, + }, + { + gateway: { id: 'wlan0', name: 'Wireless', public: false }, + public: false, + hostname: { + kind: 'ipv4', + value: '10.0.0.2', + port: null, + sslPort: 443, + }, + }, + { + gateway: { id: 'eth0', name: 'Ethernet', public: false }, + public: false, + hostname: { + kind: 'ipv6', + value: 'fe80::cd00:0000:0cde:1257:0000:211e:72cd', + scopeId: 2, + port: null, + sslPort: 443, + }, + }, + { + gateway: { id: 'wlan0', name: 'Wireless', public: false }, + public: false, + hostname: { + kind: 'ipv6', + value: 'fe80::cd00:0000:0cde:1257:0000:211e:1234', + scopeId: 3, + port: null, + sslPort: 443, + }, + }, + ], }, options: { preferredExternalPort: 80, @@ -54,87 +120,6 @@ export const mockPatchData: DataModel = { }, publicDomains: {}, privateDomains: [], - onions: ['myveryownspecialtoraddress'], - hostnameInfo: { - 80: [ - { - kind: 'ip', - gateway: { id: 'eth0', name: 'Ethernet', public: false }, - public: false, - hostname: { - kind: 'local', - value: 'adjective-noun.local', - port: null, - sslPort: 443, - }, - }, - { - kind: 'ip', - gateway: { id: 'wlan0', name: 'Wireless', public: false }, - public: false, - hostname: { - kind: 'local', - value: 'adjective-noun.local', - port: null, - sslPort: 443, - }, - }, - { - kind: 'ip', - gateway: { id: 'eth0', name: 'Ethernet', public: false }, - public: false, - hostname: { - kind: 'ipv4', - value: '10.0.0.1', - port: null, - sslPort: 443, - }, - }, - { - kind: 'ip', - gateway: { id: 'wlan0', name: 'Wireless', public: false }, - public: false, - hostname: { - kind: 'ipv4', - value: '10.0.0.2', - port: null, - sslPort: 443, - }, - }, - { - kind: 'ip', - gateway: { id: 'eth0', name: 'Ethernet', public: false }, - public: false, - hostname: { - kind: 'ipv6', - value: 'fe80::cd00:0000:0cde:1257:0000:211e:72cd', - scopeId: 2, - port: null, - sslPort: 443, - }, - }, - { - kind: 'ip', - gateway: { id: 'wlan0', name: 'Wireless', public: false }, - public: false, - hostname: { - kind: 'ipv6', - value: 'fe80::cd00:0000:0cde:1257:0000:211e:1234', - scopeId: 3, - port: null, - sslPort: 443, - }, - }, - { - kind: 'onion', - hostname: { - value: 'myveryownspecialtoraddress.onion', - port: 80, - sslPort: 443, - }, - }, - ], - }, }, gateways: { eth0: { @@ -529,8 +514,74 @@ export const mockPatchData: DataModel = { net: { assignedPort: 80, assignedSslPort: 443, - publicEnabled: [], + }, + addresses: { privateDisabled: [], + publicEnabled: [], + possible: [ + { + gateway: { id: 'eth0', name: 'Ethernet', public: false }, + public: false, + hostname: { + kind: 'local', + value: 'adjective-noun.local', + port: null, + sslPort: 1234, + }, + }, + { + gateway: { id: 'wlan0', name: 'Wireless', public: false }, + public: false, + hostname: { + kind: 'local', + value: 'adjective-noun.local', + port: null, + sslPort: 1234, + }, + }, + { + gateway: { id: 'eth0', name: 'Ethernet', public: false }, + public: false, + hostname: { + kind: 'ipv4', + value: '10.0.0.1', + port: null, + sslPort: 1234, + }, + }, + { + gateway: { id: 'wlan0', name: 'Wireless', public: false }, + public: false, + hostname: { + kind: 'ipv4', + value: '10.0.0.2', + port: null, + sslPort: 1234, + }, + }, + { + gateway: { id: 'eth0', name: 'Ethernet', public: false }, + public: false, + hostname: { + kind: 'ipv6', + value: 'fe80::cd00:0000:0cde:1257:0000:211e:72cd', + scopeId: 2, + port: null, + sslPort: 1234, + }, + }, + { + gateway: { id: 'wlan0', name: 'Wireless', public: false }, + public: false, + hostname: { + kind: 'ipv6', + value: 'fe80::cd00:0000:0cde:1257:0000:211e:1234', + scopeId: 3, + port: null, + sslPort: 1234, + }, + }, + ], }, options: { addSsl: null, @@ -541,87 +592,6 @@ export const mockPatchData: DataModel = { }, publicDomains: {}, privateDomains: [], - onions: [], - hostnameInfo: { - 80: [ - { - kind: 'ip', - gateway: { id: 'eth0', name: 'Ethernet', public: false }, - public: false, - hostname: { - kind: 'local', - value: 'adjective-noun.local', - port: null, - sslPort: 1234, - }, - }, - { - kind: 'ip', - gateway: { id: 'wlan0', name: 'Wireless', public: false }, - public: false, - hostname: { - kind: 'local', - value: 'adjective-noun.local', - port: null, - sslPort: 1234, - }, - }, - { - kind: 'ip', - gateway: { id: 'eth0', name: 'Ethernet', public: false }, - public: false, - hostname: { - kind: 'ipv4', - value: '10.0.0.1', - port: null, - sslPort: 1234, - }, - }, - { - kind: 'ip', - gateway: { id: 'wlan0', name: 'Wireless', public: false }, - public: false, - hostname: { - kind: 'ipv4', - value: '10.0.0.2', - port: null, - sslPort: 1234, - }, - }, - { - kind: 'ip', - gateway: { id: 'eth0', name: 'Ethernet', public: false }, - public: false, - hostname: { - kind: 'ipv6', - value: 'fe80::cd00:0000:0cde:1257:0000:211e:72cd', - scopeId: 2, - port: null, - sslPort: 1234, - }, - }, - { - kind: 'ip', - gateway: { id: 'wlan0', name: 'Wireless', public: false }, - public: false, - hostname: { - kind: 'ipv6', - value: 'fe80::cd00:0000:0cde:1257:0000:211e:1234', - scopeId: 3, - port: null, - sslPort: 1234, - }, - }, - { - kind: 'onion', - hostname: { - value: 'bitcoin-p2p.onion', - port: 80, - sslPort: 443, - }, - }, - ], - }, }, bcdefgh: { bindings: { @@ -630,8 +600,11 @@ export const mockPatchData: DataModel = { net: { assignedPort: 8332, assignedSslPort: null, - publicEnabled: [], + }, + addresses: { privateDisabled: [], + publicEnabled: [], + possible: [], }, options: { addSsl: null, @@ -642,10 +615,6 @@ export const mockPatchData: DataModel = { }, publicDomains: {}, privateDomains: [], - onions: [], - hostnameInfo: { - 8332: [], - }, }, cdefghi: { bindings: { @@ -654,8 +623,11 @@ export const mockPatchData: DataModel = { net: { assignedPort: 8333, assignedSslPort: null, - publicEnabled: [], + }, + addresses: { privateDisabled: [], + publicEnabled: [], + possible: [], }, options: { addSsl: null, @@ -666,10 +638,6 @@ export const mockPatchData: DataModel = { }, publicDomains: {}, privateDomains: [], - onions: [], - hostnameInfo: { - 8333: [], - }, }, }, storeExposedDependents: [], diff --git a/web/projects/ui/src/app/services/config.service.ts b/web/projects/ui/src/app/services/config.service.ts index 3e9097b54..4e56e8e46 100644 --- a/web/projects/ui/src/app/services/config.service.ts +++ b/web/projects/ui/src/app/services/config.service.ts @@ -32,7 +32,6 @@ export class ConfigService { private getAccessType = utils.once(() => { if (useMocks) return mocks.maskAs if (this.hostname === 'localhost') return 'localhost' - if (this.hostname.endsWith('.onion')) return 'tor' if (this.hostname.endsWith('.local')) return 'mdns' let ip = null try { @@ -51,7 +50,7 @@ export class ConfigService { } isLanHttp(): boolean { - return !this.isHttps() && !['localhost', 'tor'].includes(this.accessType) + return !this.isHttps() && this.accessType !== 'localhost' } isHttps(): boolean {