1fc9ef0050
ci / web (push) Successful in 49s
ci / docs-site (push) Successful in 52s
decky / build-publish (push) Successful in 18s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 9s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 9s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 8s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 10s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 44s
ci / bench (push) Successful in 5m56s
flatpak / build-publish (push) Successful in 6m41s
docker / deploy-docs (push) Successful in 27s
apple / swift (push) Successful in 4m40s
deb / build-publish (push) Successful in 12m7s
arch / build-publish (push) Successful in 14m10s
android / android (push) Successful in 17m16s
ci / rust (push) Successful in 17m57s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 16m50s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 15m15s
windows-host / package (push) Successful in 14m33s
windows-msix / package (arm64, C:\Users\Public\ffmpeg-arm64, --no-default-features, aarch64-pc-windows-msvc, C:\t-a64) (push) Successful in 3m55s
release / apple (push) Successful in 25m31s
windows-msix / package (x64, C:\Users\Public\ffmpeg, , x86_64-pc-windows-msvc, C:\t) (push) Successful in 3m49s
windows / build (aarch64-pc-windows-msvc) (push) Successful in 5m11s
windows / build (x86_64-pc-windows-msvc) (push) Successful in 6m9s
apple / screenshots (push) Successful in 19m41s
A host's pairing-gate rejections (not armed / bound to another device /
rate-limited / identity required / denied / approval timeout / superseded /
wire-version mismatch) used to drop the connection with a bare code-0 close,
and every client collapsed that — plus plain unreachability — into one
"wrong PIN / not accepted" message. A dead network path, a disarmed host,
and an operator denial were indistinguishable, which is exactly the
misdiagnosis behind the recent Android pairing support thread.
- core: new ungated `reject` module — shared close-code block 0x60–0x67
(+ 0x42 busy promoted from the host), `RejectReason`, and
`PunktfunkError::Rejected`; `pair()`/`connect()` decode the host's
ApplicationClosed code into `Rejected` instead of a generic Io error.
C ABI v7: status block −20…−28 and `punktfunk_connect_ex8` (`status_out`
reports the failure cause; NULL-return alone can't). Wire unchanged —
old peers see exactly the old bare close.
- host: every gate rejection `conn.close()`s with its typed code (and the
human reason as close bytes) before erroring out of the session task.
- pf-client-core: shared `pair_error_message`/`connect_reject_message`
wording consumed by the Windows + Linux + console-UI + CLI surfaces; a
connect failure now renders the host's stated reason.
- android: `nativeTakeLastError()` JNI token + `ConnectErrors.kt` — a
network timeout is no longer reported as "wrong PIN, or the host isn't
armed", and a typed rejection skips the wake-and-wait fallback (the host
is demonstrably awake).
- apple: `HostRejection` + `.rejected`; the pair sheet and session alerts
show the stated reason; connect moves to `ex8`.
Completes the cross-client half of the hunks that rode along in 12148243
(client.rs / trust.rs / punktfunk1.rs) — main did not build without this.
Validated: workspace clippy -D warnings + full test suite green on .21
(EXIT=0, 309 host / 148 core suites); macOS core 147+c_abi green; swift
build green; Android Kotlin + native crate green.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
97 lines
3.7 KiB
Rust
97 lines
3.7 KiB
Rust
//! Error type and the stable C ABI status codes it maps to.
|
|
|
|
use thiserror::Error;
|
|
|
|
/// The core's internal error type. Crosses the C ABI as a [`PunktfunkStatus`] code.
|
|
#[derive(Debug, Error)]
|
|
pub enum PunktfunkError {
|
|
#[error("invalid argument: {0}")]
|
|
InvalidArg(&'static str),
|
|
#[error("fec error: {0}")]
|
|
Fec(#[from] crate::fec::FecError),
|
|
#[error("crypto seal/open failed")]
|
|
Crypto,
|
|
#[error("malformed packet")]
|
|
BadPacket,
|
|
#[error("no complete frame available yet")]
|
|
NoFrame,
|
|
#[error("unsupported: {0}")]
|
|
Unsupported(&'static str),
|
|
#[error("io error: {0}")]
|
|
Io(#[from] std::io::Error),
|
|
#[error("timed out")]
|
|
Timeout,
|
|
#[error("session closed")]
|
|
Closed,
|
|
/// The host deliberately turned this connection away and said why (a typed QUIC application
|
|
/// close, [`crate::reject::RejectReason`]) — distinct from transport trouble ([`Self::Io`] /
|
|
/// [`Self::Timeout`]) and from a failed PIN proof ([`Self::Crypto`]) so UIs can render the
|
|
/// real cause instead of a generic "not accepted".
|
|
#[error("rejected by host: {0}")]
|
|
Rejected(crate::reject::RejectReason),
|
|
}
|
|
|
|
pub type Result<T> = core::result::Result<T, PunktfunkError>;
|
|
|
|
/// Stable C ABI status codes. `Ok` is 0; all errors are negative so callers can
|
|
/// test `rc < 0`. Do not renumber existing variants — only append.
|
|
#[repr(i32)]
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
pub enum PunktfunkStatus {
|
|
Ok = 0,
|
|
InvalidArg = -1,
|
|
Fec = -2,
|
|
Crypto = -3,
|
|
BadPacket = -4,
|
|
NoFrame = -5,
|
|
Unsupported = -6,
|
|
Io = -7,
|
|
NullPointer = -8,
|
|
Timeout = -9,
|
|
Closed = -10,
|
|
// -11..-19 reserved for future generic errors. The -20 block mirrors
|
|
// `crate::reject::RejectReason` one-to-one so FFI callers (Swift, JNI) can
|
|
// render the host's actual rejection reason.
|
|
RejectedNotArmed = -20,
|
|
RejectedBoundOther = -21,
|
|
RejectedRateLimited = -22,
|
|
RejectedIdentityRequired = -23,
|
|
RejectedDenied = -24,
|
|
RejectedApprovalTimeout = -25,
|
|
RejectedSuperseded = -26,
|
|
RejectedWireVersion = -27,
|
|
RejectedBusy = -28,
|
|
Panic = -99,
|
|
}
|
|
|
|
impl PunktfunkError {
|
|
/// Map to the C ABI status code.
|
|
pub fn status(&self) -> PunktfunkStatus {
|
|
match self {
|
|
PunktfunkError::InvalidArg(_) => PunktfunkStatus::InvalidArg,
|
|
PunktfunkError::Fec(_) => PunktfunkStatus::Fec,
|
|
PunktfunkError::Crypto => PunktfunkStatus::Crypto,
|
|
PunktfunkError::BadPacket => PunktfunkStatus::BadPacket,
|
|
PunktfunkError::NoFrame => PunktfunkStatus::NoFrame,
|
|
PunktfunkError::Unsupported(_) => PunktfunkStatus::Unsupported,
|
|
PunktfunkError::Io(_) => PunktfunkStatus::Io,
|
|
PunktfunkError::Timeout => PunktfunkStatus::Timeout,
|
|
PunktfunkError::Closed => PunktfunkStatus::Closed,
|
|
PunktfunkError::Rejected(r) => {
|
|
use crate::reject::RejectReason as R;
|
|
match r {
|
|
R::PairingNotArmed => PunktfunkStatus::RejectedNotArmed,
|
|
R::PairingBoundToOtherDevice => PunktfunkStatus::RejectedBoundOther,
|
|
R::PairingRateLimited => PunktfunkStatus::RejectedRateLimited,
|
|
R::IdentityRequired => PunktfunkStatus::RejectedIdentityRequired,
|
|
R::Denied => PunktfunkStatus::RejectedDenied,
|
|
R::ApprovalTimeout => PunktfunkStatus::RejectedApprovalTimeout,
|
|
R::Superseded => PunktfunkStatus::RejectedSuperseded,
|
|
R::WireVersionMismatch => PunktfunkStatus::RejectedWireVersion,
|
|
R::Busy => PunktfunkStatus::RejectedBusy,
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|