Drop a forward the peer refused twice

A cancel refusal leaves the forward standing, since the peer keeps a
listener it would not drop. There is no listener to keep when the setup
was refused too, so FwdRemoteSettle() unwinds a registration nothing
establishes and nothing is still owed an answer on, rather than leaving
it unconfirmed and unmatchable until the session ends.

- A cancel refusal unlinks an unconfirmed forward with no setup queued
- Test refuses a want-reply setup and the cancel behind it, and checks
  the registration is gone
pull/1214/head
John Safranek 2026-08-31 11:14:17 -07:00 committed by philljj
parent f6126898e0
commit a88404cb88
2 changed files with 36 additions and 1 deletions

View File

@ -4097,7 +4097,11 @@ static void FwdRemoteSettle(WOLFSSH* ssh, WOLFSSH_FWD_REMOTE* entry,
if (isCancel) {
if (!success) {
/* The peer kept the listener, so the forward stands and matching
* resumes unless a later cancel is outstanding. */
* resumes unless a later cancel is outstanding. An unconfirmed
* forward has no listener to keep, though: its setup was refused
* too, and with none still queued nothing will ever bind it. */
if (!entry->confirmed && !FwdReplyHasSetup(ssh, entry))
FwdRemoteUnlink(ssh, ssh->ctx->heap, entry);
return;
}

View File

@ -4051,6 +4051,36 @@ static void TestForwardedTcpipCancelBeforeSetupReply(void)
FreeChannelOpenHarness(&harness);
}
/* The same overlap with both requests refused. The setup bound no listener,
* so the cancel's refusal is only the peer saying it has none to drop, and
* nothing is left to hold the registration open. */
static void TestForwardedTcpipSetupAndCancelBothRefusedDrops(void)
{
ChannelOpenHarness harness;
InitFwdRemoteHarness(&harness);
AssertIntEQ(wolfSSH_FwdRemoteSetup(harness.ssh, "127.0.0.1", 8080, 1),
WS_SUCCESS);
AssertIntEQ(wolfSSH_FwdRemoteCancel(harness.ssh, "127.0.0.1", 8080, 1),
WS_SUCCESS);
/* The setup is refused, but the queued cancel still names the forward, so
* it is held for that answer. */
FeedRequestFailure(&harness);
AssertNotNull(harness.ssh->fwdRemoteList);
/* The cancel is refused too. Nothing establishes the forward and nothing
* is owed an answer on it, so it goes rather than sitting unmatchable
* until the session ends. */
FeedRequestFailure(&harness);
AssertNull(harness.ssh->fwdRemoteList);
AssertForwardedOpenRefused(&harness, "127.0.0.1", 8080);
FreeChannelOpenHarness(&harness);
}
/* The same overlap, but with a second forward outstanding behind it. The
* cancelled forward's reply must not be spent on the one still waiting. */
static void TestForwardedTcpipCancelBeforeSetupReplyKeepsOther(void)
@ -10800,6 +10830,7 @@ int main(int argc, char** argv)
TestForwardedTcpipUnmatchedCancelKeepsForward();
TestForwardedTcpipCancelBeforeSetupReply();
TestForwardedTcpipCancelBeforeSetupReplyKeepsOther();
TestForwardedTcpipSetupAndCancelBothRefusedDrops();
TestForwardedTcpipDuplicateSetupIsOneForward();
TestForwardedTcpipDuplicateSetupRefusalKeepsForward();
TestForwardedTcpipDuplicateSetupLaterSuccessBinds();