echoserver: keep the EOF reply owed across a rekey

wolfSSH_ChannelIdRead() has no rekey guard, so a drained channel still
reports zero mid-rekey and the drain loop calls the reply in. That send
returns WS_REKEYING before it prepares a packet, so nothing is queued.

- take the send's status instead of discarding it
- latch eofAnswered and ChildRunning on every status but WS_REKEYING, so
  the reply is retried on a later pass; the KEX traffic wakes it
- a short send is left latching: it bundled the EOF and set eofTxd, so a
  retry queues nothing and the loop would stall in an untimed select
  waiting on a peer that has already half-closed
- same change in the Espressif copy
pull/1218/head
John Safranek 2026-08-28 15:56:59 -07:00 committed by philljj
parent 7b17f65b67
commit 7325678553
2 changed files with 20 additions and 6 deletions

View File

@ -1067,9 +1067,16 @@ static int ssh_worker(thread_ctx_t* threadCtx)
/* Only an emptied channel earns the EOF; anything
* else is retried on a later pass. */
if (eofDrained) {
wolfSSH_ChannelSendEof(eofChannel);
eofAnswered = 1;
ChildRunning = 0;
int eofRet;
eofRet = wolfSSH_ChannelSendEof(eofChannel);
/* A rekey queues nothing, so the reply is still
* owed and the KEX traffic wakes the next pass.
* A short send already bundled it. */
if (eofRet != WS_REKEYING) {
eofAnswered = 1;
ChildRunning = 0;
}
}
}
}

View File

@ -1051,9 +1051,16 @@ static int ssh_worker(thread_ctx_t* threadCtx)
/* Only an emptied channel earns the EOF; anything
* else is retried on a later pass. */
if (eofDrained) {
wolfSSH_ChannelSendEof(eofChannel);
eofAnswered = 1;
ChildRunning = 0;
int eofRet;
eofRet = wolfSSH_ChannelSendEof(eofChannel);
/* A rekey queues nothing, so the reply is still
* owed and the KEX traffic wakes the next pass.
* A short send already bundled it. */
if (eofRet != WS_REKEYING) {
eofAnswered = 1;
ChildRunning = 0;
}
}
}
}