fix(lib): harden the distributed leaf against hostile and slow parents
Review of the leaf found the seams a stranger on the other end of a D connection could pull at, and a few that a slow one would. - A code-93 envelope is unwrapped once, as Nicotine+ does; a frame made of nothing but envelopes used to recurse once per byte. - A branch level is validated before it is used: negative levels leave the candidate unadoptable and the largest one no longer overflows. - The link reads through the crate's MessageReader, so a frame split across a read timeout is no longer torn or mistaken for a closed link. - Every event a link raises carries its link number, so a stale thread cannot adopt, close or move a newer link to the same user. - Later branch facts from the parent move our own place and are told to the server; a fresh login resets the leaf, whose stance is now the one place the four announcement messages come from. - Candidates from a later batch join the ones in flight instead of cancelling them, capped at ten; searches from the parent are budgeted at fifty a second; a reply queued for a searcher the server cannot place expires after a minute. - Truncated frames and half-read PossibleParents entries are dropped instead of yielding an empty root, a level of zero or a phantom candidate. The e2e now has a searcher of its own, so the answer must go to whoever asked and not to the parent that relayed it.
This commit is contained in:
@@ -821,3 +821,18 @@ fn watched_users_are_listed_in_a_stable_order() {
|
||||
context.add_watched_user("bob");
|
||||
assert_eq!(context.watched_users(), vec!["alice", "bob", "carol"]);
|
||||
}
|
||||
|
||||
// A reply queued for a searcher the server cannot place waits a minute,
|
||||
// not forever: every such search would otherwise pin a map entry and a
|
||||
// protected slot in the peer registry.
|
||||
#[test]
|
||||
fn a_peer_message_nobody_could_deliver_expires() {
|
||||
let mut ctx = ClientContext::for_user("me");
|
||||
ctx.queue_peer_message("ghost", crate::message::Message::new());
|
||||
ctx.expire_pending_peer_messages(Instant::now());
|
||||
assert_eq!(ctx.take_peer_messages("ghost").len(), 1, "still fresh");
|
||||
|
||||
ctx.queue_peer_message("ghost", crate::message::Message::new());
|
||||
ctx.expire_pending_peer_messages(Instant::now() + PENDING_PEER_TTL);
|
||||
assert!(ctx.take_peer_messages("ghost").is_empty(), "expired");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user