wire: the ASSOCIATE datagram frame codec — frame + DatagramReader (ADR-003)
- frame(addr, payload): [len: u16 BE][addr + data] over TargetAddr::to_be_bytes() + payload; the joint bound enforced at frame time (checked_add; 65507 + 255-char domain = 65766 > 65535 → Oversize) with addr_encoding_len exposed (7 v4 / 19 v6 / 259 max domain) and MAX_INNER_LEN pinned - DatagramReader: incremental reassembly across chunk boundaries (split frames, batched frames, partial headers), infallible, returning per-frame Results; the addr sub-layout decoded by hand (ATYP-keyed, UTF-8-validated) with typed decode errors — BadAtyp / ShortAddress / DomainNotUtf8 / EmptyFrame — so the producer stage fails the association loud on protocol garbage (udp_undecodable_frame_ends_association, POC #2) - module docs state the framing-once invariant (the egress speaks inner bytes; this layer owns the single length prefix — the double-framing bug class POC #2 caught), the F-2 invariant at payload level (a zero-length payload is a legal empty datagram, never EOF; wire len == 0 is unreachable garbage), and the EOF layering (the channels-level sentinel, outside the codec) - doc-sync: wire.md + bast.md residue corrected — "len == 0 is a legal empty datagram" replaced with the payload-level F-2 statement (len == 0 unreachable since addr >= 7 bytes) - lib.rs re-exports frame, DatagramReader, addr_encoding_len, MAX_INNER_LEN, WireError (convention 15) Verification: cargo test (58 lib + 37 differential), clippy --all-targets -- -D warnings, fmt --check, doc --no-deps, and both wasm checks (check + clippy on wasm32-unknown-unknown) — all clean.
This commit is contained in:
1 parent
ce8148d711
commit
d530eae5c4
5 files changed
+659
-15
No files matched your search
@@ -74,7 +74,7 @@ What this BAST deliberately does **not** cover:
|
||||
{ "name": "addr", "kind": "bytes", "encoding": "length-prefixed",
|
||||
"maxLength": 259, "description": "RFC 1928 address encoding: ATYP + addr + port (TargetAddr::to_be_bytes); v4 = 1 + 4 + 2 = 7, v6 = 1 + 16 + 2 = 19, domain = 1 + 1 + 255 + 2 = 259 (the length byte of the domain form counts)" },
|
||||
{ "name": "data", "kind": "bytes", "encoding": "length-prefixed",
|
||||
"maxLength": 65535, "description": "the datagram payload; len == 0 is a legal empty datagram, NOT EOF" }
|
||||
"maxLength": 65535, "description": "the datagram payload; a zero-length payload is a legal empty datagram, NOT EOF (the F-2 invariant at payload level — len == 0, zero inner bytes, is unreachable from a compliant peer since addr is always >= 7 bytes)" }
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -109,9 +109,11 @@ One frame per datagram, in both directions of the channel
|
||||
`BiStream`, for the whole datagram stage (association lifetime =
|
||||
channel lifetime). Frames are self-delimiting on the byte stream; a
|
||||
decoder is an incremental state machine across chunk boundaries
|
||||
(boundary preservation is mandatory — an empty datagram is a frame
|
||||
with `len == 0`, never EOF; alktunnels F-2's invariant, met
|
||||
structurally).
|
||||
(boundary preservation is mandatory — an empty datagram is a legal
|
||||
zero-length payload, never EOF; alktunnels F-2's invariant, met
|
||||
structurally and stated at payload level: `len == 0` — zero inner
|
||||
bytes — is unreachable from a compliant peer, since `addr` is always
|
||||
≥ 7 bytes).
|
||||
|
||||
### Layering note
|
||||
|
||||
|
||||
@@ -123,9 +123,12 @@ the same channel:
|
||||
- **Frame:** `[len: u16 BE][addr + data]` where `addr + data` is the
|
||||
RFC address encoding (`TargetAddr::to_be_bytes()` — ATYP + addr +
|
||||
port; the RFC UDP header minus RSV/FRAG). One frame per datagram,
|
||||
both directions. `len = 0` is a legal empty datagram (never EOF —
|
||||
the F-2 invariant, shaped on alktunnels ADR-003's codec,
|
||||
re-implemented in first-party `src/wire.rs`). **The length covers
|
||||
both directions. **A zero-length payload is a legal empty datagram
|
||||
(never EOF — the F-2 invariant, shaped on alktunnels ADR-003's
|
||||
codec, re-implemented in first-party `src/wire.rs`); `len == 0` —
|
||||
zero inner bytes — is unreachable from a compliant peer, since
|
||||
`addr` is always ≥ 7 bytes, so the operative invariant lives at
|
||||
payload level.** The length covers
|
||||
`addr + data`, not data alone — the joint bound is the
|
||||
constraint:** a frame is rejected (`Oversize`, at frame time)
|
||||
whenever `len(addr) + len(data) > 65535`. The UDP payload bound
|
||||
@@ -209,7 +212,9 @@ UDP ASSOCIATE (the datagram stage, one direction's view):
|
||||
|
||||
```
|
||||
[len:u16 BE][ATYP + addr + port + data] [len:u16 BE][...] ...
|
||||
len == 0: an empty datagram (legal payload, NOT EOF)
|
||||
zero-length data: an empty datagram (legal payload, NOT EOF —
|
||||
the F-2 invariant at payload level)
|
||||
len == 0 (no addr bytes): unreachable from a compliant peer
|
||||
EOF: the channels-level sentinel, outside the codec
|
||||
```
|
||||
|
||||
|
||||
Reference in new issue
Block a user