docs: remove em dashes from repo prose and forbid them going forward
- Rewrote em dash prose across the whole tree outside docs/reviews/ (the historical reviews stay verbatim, matching the alktunnels and alktty precedent): src doc comments and inline comments (587), docs/architecture (319), docs/research + sdd_process (484), tests (361), tasks (899), fuzz (28), README/CHANGELOG/Cargo.toml (77), AGENTS.md (85), and the .opencode/agents prompts (109) - Fenced content follows the precedent: comments, ASCII-diagram label separators, and template placeholders rewritten; wire data, JSON/BAST documents, byte diagrams, and transcripts survive verbatim; assert, expect, panic, error-display, and format message literals rewritten as prose; no code, fixture, or test data change - src/rfc1928/LICENSE: the two dashes in alksocks' own provenance header rewritten; the MIT legal text untouched - AGENTS.md adds Project Convention 18: no em dashes in prose; varied restructure, no spaced-hyphen substitutes, fenced prose rule, en dash numeric ranges exempt, docs/reviews/ exempt from the greps - tasks/*.md frontmatter: the name values rewritten by the sweep are now quoted (a raw colon inside an unquoted YAML scalar parsed as null); taskgraph validate reports 37 tasks, no issues Verification: cargo test; cargo clippy --all-targets -D warnings; cargo fmt --check; cargo doc --no-deps; fuzz corpus replay (cargo test --manifest-path fuzz/shared/Cargo.toml); wasm check and clippy for wasm32-unknown-unknown with -D warnings; em dash grep over the repo returns matches only in docs/reviews/ and the bast.md BAST JSON data fence
This commit is contained in:
1 parent
9ca8c52601
commit
e196ed43e3
115 files changed
+3593
-3531
No files matched your search
+2
-2
@@ -9,7 +9,7 @@ fast-socks5 v1.0.0 (https://github.com/dizda/fast-socks5) and are
|
||||
carried in-tree by alksocks (extraction per ADR-011,
|
||||
docs/architecture/decisions/011-inline-vendor.md; extraction range
|
||||
git 57b107b..5b18d4d; fork point = the tree as of commit 5b18d4d,
|
||||
the extraction review's gate commit — ownership per ADR-013,
|
||||
the extraction review's gate commit; ownership per ADR-013,
|
||||
docs/architecture/decisions/013-targeted-fork.md). Upstream has no
|
||||
per-file license headers; this bundled notice is the license
|
||||
obligation's carrier. The extraction cut and the fork's deliberate
|
||||
@@ -19,7 +19,7 @@ Modifications after the fork point are Copyright (c) 2026 the
|
||||
alksocks developers, licensed under the crate license
|
||||
(MIT OR Apache-2.0, per alksocks' Cargo.toml); the upstream MIT
|
||||
notice above is preserved per its terms. The upstream project is
|
||||
third-party and unmodified in place — see the ADRs for what was
|
||||
third-party and unmodified in place; see the ADRs for what was
|
||||
changed after the fork point.
|
||||
|
||||
---
|
||||
|
||||
+10
-10
@@ -1,12 +1,12 @@
|
||||
//! Forked from fast-socks5 v1.0.0 (upstream `src/client.rs` —
|
||||
//! Forked from fast-socks5 v1.0.0 (upstream `src/client.rs`;
|
||||
//! extraction; ownership per ADR-013, see the crate root's `mod.rs`).
|
||||
//!
|
||||
//! Extracted region: the `T`-generic
|
||||
//! client session — [`Socks5Stream<S>`] with the `use_stream` path
|
||||
//! client session: [`Socks5Stream<S>`] with the `use_stream` path
|
||||
//! (the version/method greeting, the method-choice read, the
|
||||
//! password-auth sub-handshake, the request encoder `request_header`,
|
||||
//! the reply reader `read_request_reply`, and the socket accessors)
|
||||
//! plus the [`AsyncRead`]/[`AsyncWrite`] impls — the consumer half's
|
||||
//! plus the [`AsyncRead`]/[`AsyncWrite`] impls, the consumer half's
|
||||
//! stream session (ADR-002; POC #1's fit: a channels `BiStream` is
|
||||
//! fed here directly). A `Socks5Stream` is itself a stream, so
|
||||
//! `pump_bidi`-shaped consumers and the front doors can pipe through
|
||||
@@ -14,10 +14,10 @@
|
||||
//!
|
||||
//! Not forked (rfc1928.md's not-forked inventory row 2):
|
||||
//! `Socks5Datagram`
|
||||
//! (struct + impl — the kernel `UdpSocket` associate client; the
|
||||
//! (struct + impl: the kernel `UdpSocket` associate client; the
|
||||
//! crate's associate session is first-party per ADR-003's handle
|
||||
//! swap) and the `Socks5Stream<TcpStream>` convenience impl
|
||||
//! (`connect`/`connect_with_password`/`connect_raw` — the
|
||||
//! (`connect`/`connect_with_password`/`connect_raw`, the
|
||||
//! `tokio::net::TcpStream` dialers; the consumer's transport is a
|
||||
//! channels `BiStream`, not a TCP dial). Upstream
|
||||
//! `Config::connect_timeout` is cut with its only consumer,
|
||||
@@ -26,13 +26,13 @@
|
||||
//!
|
||||
//! Extraction-time adjustments (mechanical, ADR-011 §Amendment):
|
||||
//! `log` calls dropped (the bindings they consumed renamed); `anyhow`
|
||||
//! dropped — errors stay `SocksError`-typed (the missing-target-addr
|
||||
//! dropped; errors stay `SocksError`-typed (the missing-target-addr
|
||||
//! case at upstream `client.rs:285` uses the typed
|
||||
//! [`SocksError::MissingTargetAddr`] variant instead of
|
||||
//! `anyhow::Error::msg(...).into()`); `use_password_auth`'s
|
||||
//! `unreachable!()` (upstream `client.rs:207` — `methods.get(1)`
|
||||
//! `unreachable!()` (upstream `client.rs:207`, where `methods.get(1)`
|
||||
//! being `Some(AuthenticationMethod::None)`) is an error return
|
||||
//! ([`SocksError::AuthenticationRejected`]) — convention 2 forbids
|
||||
//! ([`SocksError::AuthenticationRejected`]); convention 2 forbids
|
||||
//! panics in library code, and the state is reachable only by misuse;
|
||||
//! the `Socks5Datagram` error sites (`SocksError::Other(anyhow!(...))`,
|
||||
//! the `"[::]:0".parse().unwrap()` sites) go with the cut region. No
|
||||
@@ -41,7 +41,7 @@
|
||||
//!
|
||||
//! Tests are first-party (upstream's client test module rides the cut
|
||||
//! net region): raw-byte mock servers over tokio duplex streams, the
|
||||
//! pinned byte sequences written directly — keeping the client's wire
|
||||
//! pinned byte sequences written directly, keeping the client's wire
|
||||
//! expectations uncorrelated from the forked server typestate.
|
||||
|
||||
use crate::rfc1928::util::stream::read_exact;
|
||||
@@ -208,7 +208,7 @@ where
|
||||
ref username,
|
||||
ref password,
|
||||
}) => (username, password),
|
||||
// upstream `unreachable!()` — `methods.get(1)` being
|
||||
// upstream `unreachable!()` (`methods.get(1)` being
|
||||
// `Some(AuthenticationMethod::None)`: reachable only by
|
||||
// misuse; the faithful translation is an error return,
|
||||
// not a panic (convention 2). Same message as upstream's
|
||||
|
||||
+7
-7
@@ -1,12 +1,12 @@
|
||||
//! The RFC 1928 core: a targeted fork of fast-socks5 v1.0.0's
|
||||
//! `T`-generic subset (MIT, third-party upstream —
|
||||
//! `T`-generic subset (MIT, third-party upstream:
|
||||
//! github.com/dizda/fast-socks5; `/workspace/fast-socks5` is the
|
||||
//! read-only reference checkout, kept for differential testing of
|
||||
//! RFC edge cases).
|
||||
//!
|
||||
//! Arrived by extraction (ADR-011 §Amendment, docs/architecture/
|
||||
//! decisions/011-inline-vendor.md): only the code already generic
|
||||
//! over `T: AsyncRead + AsyncWrite + Unpin` was copied in-tree — the
|
||||
//! over `T: AsyncRead + AsyncWrite + Unpin` was copied in-tree; the
|
||||
//! kernel-net handlers, the `socks4` module, and the legacy `Config`/
|
||||
//! `Authentication` layer were **not** copied. The extraction cut,
|
||||
//! the fork's two carried changes (BIND support, `new_reply`
|
||||
@@ -15,7 +15,7 @@
|
||||
//! `docs/architecture/rfc1928.md`.
|
||||
//!
|
||||
//! Ownership (ADR-013, docs/architecture/decisions/013-targeted-fork.md):
|
||||
//! this is fully owned code — project conventions apply
|
||||
//! this is fully owned code, so project conventions apply
|
||||
//! unconditionally (no vendor carve-outs, no verbatim preservation,
|
||||
//! no lint-allow posture). Fork point: the tree as of the
|
||||
//! extraction-review commit (`5b18d4d`); extraction provenance in
|
||||
@@ -32,13 +32,13 @@
|
||||
//! `use_stream` path + the `AsyncRead`/`AsyncWrite` impls) lives in
|
||||
//! [`client`].
|
||||
//!
|
||||
//! License: upstream is MIT with no per-file headers — the bundled
|
||||
//! License: upstream is MIT with no per-file headers; the bundled
|
||||
//! notice (`LICENSE`, this module's directory, v1.0.0 attribution +
|
||||
//! the fork-point statement) is the license obligation's carrier.
|
||||
//!
|
||||
//! Maintenance posture: this code co-evolves with the wrapper and is
|
||||
//! the crate's only RFC 1928 wire vocabulary ("no second codec,
|
||||
//! ever" — ADR-011's rationale, carried). Nothing is absorbed
|
||||
//! ever": ADR-011's rationale, carried). Nothing is absorbed
|
||||
//! silently from upstream; nothing waits on upstream.
|
||||
|
||||
pub mod client;
|
||||
@@ -216,7 +216,7 @@ impl ReplyError {
|
||||
|
||||
/// Map a wire reply code to its variant. Codes outside the RFC
|
||||
/// table (`0x09`–`0xFF`, unassigned per RFC 1928 §6) map to
|
||||
/// `GeneralFailure` — the RFC's catch-all — instead of panicking:
|
||||
/// `GeneralFailure`, the RFC's catch-all, instead of panicking:
|
||||
/// the value comes off the wire (the client session feeds the raw
|
||||
/// reply byte here), so a misbehaving peer must surface as a typed
|
||||
/// error, never a panic. Upstream's `unreachable!()` arm was the
|
||||
@@ -379,7 +379,7 @@ mod tests {
|
||||
}
|
||||
|
||||
/// F-1 (review): unassigned reply codes (`0x09`–`0xFF`, RFC 1928
|
||||
/// §6) surface as `GeneralFailure` — the RFC's catch-all — never
|
||||
/// §6) surface as `GeneralFailure` (the RFC's catch-all), never
|
||||
/// a panic. Upstream's `unreachable!()` arm was removed: the code
|
||||
/// comes off the wire (`read_request_reply` → `from_u8`), so a
|
||||
/// misbehaving peer must not crash the consumer task.
|
||||
|
||||
+16
-16
@@ -1,4 +1,4 @@
|
||||
//! Forked from fast-socks5 v1.0.0 (upstream `src/server.rs` —
|
||||
//! Forked from fast-socks5 v1.0.0 (upstream `src/server.rs`;
|
||||
//! extraction; ownership per ADR-013, see the crate root's `mod.rs`).
|
||||
//!
|
||||
//! Extracted region: the explicit
|
||||
@@ -6,13 +6,13 @@
|
||||
//! negotiation with the `AuthMethod<T>` machinery, `read_command`,
|
||||
//! `reply_success`, `reply_error`, `transfer`, `wait_on_tcp`,
|
||||
//! `ErrorContext<T>`), `SocksServerError`, and the published reply
|
||||
//! encoder `new_reply` (carried change #2 — upstream's is private,
|
||||
//! encoder `new_reply` (carried change #2: upstream's is private,
|
||||
//! `fn new_reply` at `server.rs:1380`; here it is `pub` so the
|
||||
//! BIND reply#2 writer and consumer reply readers reuse the
|
||||
//! 10/22-byte reply form instead of re-encoding it). The cut regions
|
||||
//! (the legacy `Socks5Server`/`Socks5Socket`/`Config`/`Authentication`
|
||||
//! layer, the `run_*` kernel-net handlers, `DnsResolveHelper`, the
|
||||
//! net-only tests) stay in `/workspace/fast-socks5` — the not-forked
|
||||
//! net-only tests) stay in `/workspace/fast-socks5`, the not-forked
|
||||
//! inventory row 1 in `docs/architecture/rfc1928.md`.
|
||||
//!
|
||||
//! Extraction-time adjustments (mechanical, ADR-011 §Amendment):
|
||||
@@ -23,22 +23,22 @@
|
||||
//! `SocksServerError::AuthenticationRejected` are cut; the variant
|
||||
//! stays (the typestate's `accept_password_auth` produces it).
|
||||
//!
|
||||
//! Post-fork owned changes (extend, never replace — convention 17):
|
||||
//! Post-fork owned changes (extend, never replace, per convention 17):
|
||||
//! the keep-or-cut sweep (review 002 N-1) cut the producer-replaced
|
||||
//! machinery — the `auth_method_enums!` macro (upstream's enum
|
||||
//! machinery: the `auth_method_enums!` macro (upstream's enum
|
||||
//! generator) with its generated `StandardAuthentication` enum +
|
||||
//! `allow_no_auth`, `skip_auth_this_is_not_rfc_compliant`, and
|
||||
//! `CheckResult`'s `Option`/`Result` impls (`bool` stays) — none had
|
||||
//! `CheckResult`'s `Option`/`Result` impls (`bool` stays); none had
|
||||
//! an in-tree consumer once the producer drove `negotiate_auth`
|
||||
//! directly. Kept with note (the hand-port map, ADR-013):
|
||||
//! `transfer`/`wait_on_tcp` are differential-suite-consumed (their
|
||||
//! semantics stay pinned there), and the error vocabulary keeps its
|
||||
//! upstream variants. One variant was added, not absorbed:
|
||||
//! `SocksServerError::Refused` — the honest refusal vocabulary for
|
||||
//! `SocksServerError::Refused`: the honest refusal vocabulary for
|
||||
//! the producer's in-stream policy refusals (review 002 N-4; never
|
||||
//! reaches the wire). Recorded in docs/architecture/rfc1928.md.
|
||||
//!
|
||||
//! The BIND note (ADR-007): BIND's driving is producer-side code —
|
||||
//! The BIND note (ADR-007): BIND's driving is producer-side code;
|
||||
//! the typestate already returns `TCPBind` from `read_command()`,
|
||||
//! `reply_success(sock_addr)` carries reply#1's BND.ADDR, and
|
||||
//! reply#2 is written with the published `new_reply`. No BIND code
|
||||
@@ -159,7 +159,7 @@ impl<T> Socks5ServerProtocol<T, states::Opened> {
|
||||
/// Closure-result adapter for [`Socks5ServerProtocol
|
||||
/// ::accept_password_auth`]'s credential checker: `bool` (`is_good`).
|
||||
/// The `Option`/`Result` impls upstream-adjacent shape were cut in
|
||||
/// the keep-or-cut sweep (review 002 N-1) — every in-tree checker
|
||||
/// the keep-or-cut sweep (review 002 N-1); every in-tree checker
|
||||
/// returns `bool`; re-add with a typed-rejection design if a door
|
||||
/// ever wants it.
|
||||
pub trait CheckResult {
|
||||
@@ -240,7 +240,7 @@ pub trait AuthMethodSuccessState<T> {
|
||||
/// Use blank structs for individual protocol implementations;
|
||||
/// `negotiate_auth` takes any slice of `AuthMethod`s, so a custom
|
||||
/// selection is a slice of the method structs (upstream's
|
||||
/// enum-generator macro was cut — review 002 N-1; nothing in-tree
|
||||
/// enum-generator macro was cut (review 002 N-1); nothing in-tree
|
||||
/// drove a multi-method slice).
|
||||
pub trait AuthMethod<T>: Copy {
|
||||
type StartingState;
|
||||
@@ -354,7 +354,7 @@ impl<T: AsyncWrite + Unpin> PasswordAuthenticationImpl<T, password_states::Recei
|
||||
|
||||
/// Notify the client with a "NOT_ACCEPTABLE" reply and drop the socket.
|
||||
///
|
||||
/// The status byte is `0xff` — RFC 1929 requires only that a
|
||||
/// The status byte is `0xff`: RFC 1929 requires only that a
|
||||
/// rejection be any non-`0x00` byte; `0xff` is the convention
|
||||
/// upstream picked (borrowed from the method-negotiation phase's
|
||||
/// `SOCKS5_AUTH_METHOD_NOT_ACCEPTABLE` constant, which is a
|
||||
@@ -530,7 +530,7 @@ impl<T: AsyncRead + AsyncWrite + Unpin> Socks5ServerProtocol<T, states::Authenti
|
||||
}
|
||||
|
||||
/// The RFC reply form's placeholder BND.ADDR for error replies
|
||||
/// (statically constructed — upstream parsed `"0.0.0.0:0"` at runtime).
|
||||
/// (statically constructed; upstream parsed `"0.0.0.0:0"` at runtime).
|
||||
const V4_UNSPECIFIED_ZERO: SocketAddr = SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, 0));
|
||||
|
||||
/// Generate reply code according to the RFC.
|
||||
@@ -606,14 +606,14 @@ mod tests {
|
||||
/// `SocksServerError::to_reply_error`'s full mapping table (review
|
||||
/// 001 L-3): `UnknownCommand` → `CommandNotSupported`,
|
||||
/// `AddrError` delegates to its own table, and every remaining
|
||||
/// variant falls through to `GeneralFailure` — the mapping the
|
||||
/// variant falls through to `GeneralFailure`, the mapping the
|
||||
/// producer's error surface relies on when mapping session errors
|
||||
/// to reply codes (`try_notify!`'s secondary-error write). The
|
||||
/// fall-through arms (`Io`/`FromUtf8`/`EOF`/auth/refusal variants)
|
||||
/// are exercised here directly, since driving each from the wire
|
||||
/// is not one-test-cheap. Upstream `src/server.rs:62-70`. The
|
||||
/// table also pins the vocabulary honesty (review 002 N-4):
|
||||
/// `Bug` and `Refused` are log-vocabulary variants — both fall
|
||||
/// `Bug` and `Refused` are log-vocabulary variants; both fall
|
||||
/// through to `GeneralFailure` (they never reach the wire from
|
||||
/// the typestate; the producer's refusal sites write the RFC
|
||||
/// reply before the session error).
|
||||
@@ -655,13 +655,13 @@ mod tests {
|
||||
}
|
||||
|
||||
/// The refusal vocabulary (review 002 N-4): `Refused` is the
|
||||
/// honest display for the producer's in-stream policy refusals —
|
||||
/// honest display for the producer's in-stream policy refusals:
|
||||
/// not `BUG:` (a working-as-designed refusal is not a bug) and
|
||||
/// not "Authentication rejected" (an ACL refusal is not an auth
|
||||
/// failure). It is log/observability vocabulary: never encoded
|
||||
/// on the wire by the typestate (only `to_reply_error` maps it,
|
||||
/// and the producer's refusal sites write the RFC reply before
|
||||
/// the session error). Upstream has no such variant — the
|
||||
/// the session error). Upstream has no such variant; that is the
|
||||
/// fork's owned addition, recorded in the module doc.
|
||||
#[test]
|
||||
fn refused_variant_display_is_honest_and_wire_invisible() {
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
//! The RFC 1928 core's utility modules (upstream `src/util/` —
|
||||
//! The RFC 1928 core's utility modules (upstream `src/util/`;
|
||||
//! extraction; see the fork statement in the crate root's `mod.rs`
|
||||
//! and ADR-013 for ownership).
|
||||
//!
|
||||
//! Upstream layout: `stream.rs` (the `read_exact!`/`ready!` macros +
|
||||
//! `ConnectError`) and `target_addr.rs` (`TargetAddr` + the address
|
||||
//! codec) — filled by the wire-vocabulary extraction task.
|
||||
//! codec), filled by the wire-vocabulary extraction task.
|
||||
|
||||
pub mod stream;
|
||||
pub mod target_addr;
|
||||
@@ -1,10 +1,10 @@
|
||||
//! Forked from fast-socks5 v1.0.0 (upstream `src/util/stream.rs` —
|
||||
//! Forked from fast-socks5 v1.0.0 (upstream `src/util/stream.rs`;
|
||||
//! extraction; ownership per ADR-013, see the crate root's `mod.rs`).
|
||||
//!
|
||||
//! Upstream carries the `read_exact!`/`ready!` macros, `ConnectError`,
|
||||
//! and the `tcp_connect*` dialers; only the `read_exact!` macro +
|
||||
//! `ConnectError` rode the extraction (the dialers and the `ready!`
|
||||
//! macro are the cut kernel-net region — the crate's local backend is
|
||||
//! macro are the cut kernel-net region: the crate's local backend is
|
||||
//! first-party tokio code, ADR-008, and nothing in the extracted
|
||||
//! region used `ready!`). Extraction-time adjustment: upstream
|
||||
//! exports the macro at the crate root via `#[macro_export]`; here it
|
||||
@@ -13,11 +13,11 @@
|
||||
//! public API).
|
||||
//!
|
||||
//! [`ConnectError`] is currently constructed nowhere in-tree (review
|
||||
//! 001 C-1 — upstream's sole construction site was the cut
|
||||
//! 001 C-1: upstream's sole construction site was the cut
|
||||
//! `tcp_connect` dialer). The keep-or-cut trigger (cut if the
|
||||
//! producer layer landed without consuming it) fired and the
|
||||
//! disposition is **keep** (review 002 N-1): it stays as the
|
||||
//! hand-port map's companion — the dial-error → reply-code mapping
|
||||
//! hand-port map's companion (the dial-error → reply-code mapping
|
||||
//! (`to_reply_error`) is what a future kernel-net port (e.g. the
|
||||
//! dial-callback shape, ADR-004) would reuse, and its mapping is
|
||||
//! pinned by the module's test.
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
//! Forked from fast-socks5 v1.0.0 (upstream `src/util/target_addr.rs`
|
||||
//! — extraction; ownership per ADR-013, see the crate root's
|
||||
//! Forked from fast-socks5 v1.0.0 (upstream `src/util/target_addr.rs`;
|
||||
//! extraction; ownership per ADR-013, see the crate root's
|
||||
//! `mod.rs`).
|
||||
//!
|
||||
//! Upstream carries `TargetAddr`, the address codec (`read_address`),
|
||||
//! and the `resolve_dns` helper; only the codec rode the extraction
|
||||
//! (DNS resolution is the cut net region — `tokio::net::lookup_host`
|
||||
//! (DNS resolution is the cut net region, since
|
||||
//! `tokio::net::lookup_host`
|
||||
//! is not in the wasm-clean graph). The `resolve_dns` method is cut
|
||||
//! with its two `AddrError` variants (`DNSResolutionFailed`,
|
||||
//! `NoDNSRecords`).
|
||||
@@ -110,7 +111,7 @@ impl TargetAddr {
|
||||
|
||||
/// The blocking `ToSocketAddrs` form: IP targets resolve directly;
|
||||
/// domain targets cannot be resolved here (domain resolution is the
|
||||
/// egress's job — ADR-012) and error.
|
||||
/// egress's job, per ADR-012) and error.
|
||||
impl std::net::ToSocketAddrs for TargetAddr {
|
||||
type Iter = IntoIter<SocketAddr>;
|
||||
|
||||
@@ -210,7 +211,7 @@ impl ToTargetAddr for &TargetAddr {
|
||||
pub enum Addr {
|
||||
V4([u8; 4]),
|
||||
V6([u8; 16]),
|
||||
// `String` — validated UTF-8 at the codec boundary (`read_address`
|
||||
// `String`, validated UTF-8 at the codec boundary (`read_address`
|
||||
// converts before constructing).
|
||||
Domain(String),
|
||||
}
|
||||
@@ -218,7 +219,7 @@ pub enum Addr {
|
||||
/// This function is used by the client & the server.
|
||||
///
|
||||
/// The domain arm's allocation is bounded by construction: the wire
|
||||
/// length field is one byte, so the claimed length is at most 255 —
|
||||
/// length field is one byte, so the claimed length is at most 255;
|
||||
/// the attacker-controlled byte cannot ask for more. Any change that
|
||||
/// widens that field (an FRAG-style extension) requires an explicit
|
||||
/// cap here before the allocation.
|
||||
|
||||
Reference in new issue
Block a user