@@ -79,6 +79,94 @@ static int _DumpExtendedData(WOLFSSH* ssh)
7979}
8080
8181
82+ /* Sends sz bytes from data, completing any rekey that blocks the send.
83+ *
84+ * Drives wolfSSH_worker while the connection is keying or the channel window is
85+ * full, including a rekey triggered by a receive-side highwater check where
86+ * ssh->error holds a status other than WS_REKEYING (caught via ssh->isKeying).
87+ * In blocking mode the worker's DoReceive waits on the socket so progress
88+ * depends on the peer finishing the rekey or adjusting the window; in
89+ * non-blocking mode a WS_WANT_READ/WS_WANT_WRITE status is returned so a
90+ * stalled rekey cannot spin forever. Returns the byte count from
91+ * wolfSSH_stream_send (>= 0) or a negative error code, matching the wrapped
92+ * call so callers keep their existing return handling.
93+ */
94+ static int ScpStreamSend (WOLFSSH * ssh , byte * data , word32 sz )
95+ {
96+ int ret = WS_SUCCESS ;
97+ int err ;
98+
99+ if (ssh == NULL || data == NULL )
100+ return WS_BAD_ARGUMENT ;
101+
102+ /* Flush queued output before the drain loop. Otherwise a KEXINIT enqueued
103+ * by a rekey triggered on the prior send sits unsent while wolfSSH_worker
104+ * runs DoReceive before its own flush, blocking on the socket with the peer
105+ * waiting for our KEXINIT. */
106+ if (wolfSSH_OutputPending (ssh )) {
107+ ret = wolfSSH_SendPacket (ssh );
108+ if (ret < 0 )
109+ return ret ;
110+ }
111+
112+ err = wolfSSH_get_error (ssh );
113+ while (ssh -> isKeying || err == WS_WINDOW_FULL || err == WS_REKEYING ) {
114+ ret = wolfSSH_worker (ssh , NULL );
115+ if (ret < 0 && ret != WS_REKEYING && ret != WS_WINDOW_FULL
116+ && ret != WS_CHAN_RXD )
117+ return ret ;
118+ err = wolfSSH_get_error (ssh );
119+ }
120+
121+ return wolfSSH_stream_send (ssh , data , sz );
122+ }
123+
124+
125+ /* Reads up to sz bytes into data, completing any rekey that fires mid-read.
126+ *
127+ * Flushes queued output before reading so a KEXINIT enqueued by a receive-side
128+ * highwater rekey is actually sent, otherwise the peer can wait for our KEXINIT
129+ * while we block on the read. On a read that fails with WS_REKEYING the worker
130+ * is driven to finish the rekey and the read is retried. The helper is
131+ * error-code transparent: every other status (WS_EOF, WS_EXTDATA,
132+ * WS_CHANNEL_CLOSED, WS_SOCKET_ERROR_E, WS_WANT_READ/WS_WANT_WRITE, byte count)
133+ * is returned unchanged so each caller keeps its existing branch handling.
134+ */
135+ static int ScpStreamRead (WOLFSSH * ssh , byte * data , word32 sz )
136+ {
137+ int ret = WS_SUCCESS ;
138+ int done = 0 ;
139+
140+ if (ssh == NULL || data == NULL )
141+ return WS_BAD_ARGUMENT ;
142+
143+ do {
144+ if (wolfSSH_OutputPending (ssh )) {
145+ ret = wolfSSH_SendPacket (ssh );
146+ if (ret < 0 )
147+ return ret ;
148+ }
149+
150+ ret = wolfSSH_stream_read (ssh , data , sz );
151+ if (ret < 0 && wolfSSH_get_error (ssh ) == WS_REKEYING ) {
152+ /* Drive the rekey to completion, then retry the read. A worker
153+ * status that is not rekey or channel data means the rekey stalled
154+ * or a non-blocking want occurred, so return it rather than
155+ * spin. */
156+ ret = wolfSSH_worker (ssh , NULL );
157+ if (ret < 0 && ret != WS_CHAN_RXD
158+ && wolfSSH_get_error (ssh ) != WS_REKEYING )
159+ return ret ;
160+ }
161+ else {
162+ done = 1 ;
163+ }
164+ } while (!done );
165+
166+ return ret ;
167+ }
168+
169+
82170int DoScpSink (WOLFSSH * ssh )
83171{
84172 int ret = WS_SUCCESS ;
@@ -283,7 +371,7 @@ static int SendScpTimestamp(WOLFSSH* ssh)
283371#endif
284372 bufSz = (int )WSTRLEN (buf );
285373
286- ret = wolfSSH_stream_send (ssh , (byte * )buf , bufSz );
374+ ret = ScpStreamSend (ssh , (byte * )buf , bufSz );
287375 if (ret != bufSz ) {
288376 ret = WS_FATAL_ERROR ;
289377 } else {
@@ -324,7 +412,7 @@ static int SendScpFileHeader(WOLFSSH* ssh)
324412 return WS_BAD_ARGUMENT ;
325413#endif
326414 bufSz = (int )WSTRLEN (filehdr );
327- ret = wolfSSH_stream_send (ssh , (byte * )filehdr , bufSz );
415+ ret = ScpStreamSend (ssh , (byte * )filehdr , bufSz );
328416 if (ret != bufSz ) {
329417 ret = WS_FATAL_ERROR ;
330418 } else {
@@ -357,7 +445,7 @@ static int SendScpEnterDirectory(WOLFSSH* ssh)
357445
358446 bufSz = (int )WSTRLEN (buf );
359447
360- ret = wolfSSH_stream_send (ssh , (byte * )buf , bufSz );
448+ ret = ScpStreamSend (ssh , (byte * )buf , bufSz );
361449 if (ret != bufSz ) {
362450 ret = WS_FATAL_ERROR ;
363451 } else {
@@ -383,7 +471,7 @@ static int SendScpExitDirectory(WOLFSSH* ssh)
383471 buf [0 ] = 'E' ;
384472 buf [1 ] = '\n' ;
385473
386- ret = wolfSSH_stream_send (ssh , (byte * )buf , sizeof (buf ));
474+ ret = ScpStreamSend (ssh , (byte * )buf , sizeof (buf ));
387475 if (ret != sizeof (buf )) {
388476 ret = WS_FATAL_ERROR ;
389477 } else {
@@ -598,8 +686,8 @@ int DoScpSource(WOLFSSH* ssh)
598686 case SCP_SEND_FILE :
599687 WLOG (WS_LOG_DEBUG , scpState , "SCP_SEND_FILE" );
600688
601- ret = wolfSSH_stream_send (ssh , ssh -> scpFileBuffer ,
602- ssh -> scpBufferedSz );
689+ ret = ScpStreamSend (ssh , ssh -> scpFileBuffer ,
690+ ssh -> scpBufferedSz );
603691 if (ret == WS_WINDOW_FULL || ret == WS_REKEYING ) {
604692 ret = wolfSSH_worker (ssh , NULL );
605693 if (ret == WS_SUCCESS || ssh -> error == WS_WANT_READ )
@@ -736,7 +824,7 @@ int DoScpRequest(WOLFSSH* ssh)
736824
737825 /* Peer MUST send back a SSH_MSG_CHANNEL_CLOSE unless already
738826 sent*/
739- ret = wolfSSH_stream_read (ssh , buf , 1 );
827+ ret = ScpStreamRead (ssh , buf , 1 );
740828 if (ret == WS_SOCKET_ERROR_E || ret == WS_CHANNEL_CLOSED ) {
741829 WLOG (WS_LOG_DEBUG , scpState , "Peer hung up, but SCP is done" );
742830 ret = WS_SUCCESS ;
@@ -1395,6 +1483,31 @@ int ReceiveScpMessage(WOLFSSH* ssh)
13951483 return WS_BUFFER_E ;
13961484 }
13971485
1486+ /* Flush queued output before polling. A KEXINIT enqueued by a rekey
1487+ * would otherwise sit unsent while wolfSSH_worker runs DoReceive before
1488+ * its own flush, deadlocking against a peer that waits for our
1489+ * KEXINIT. */
1490+ if (wolfSSH_OutputPending (ssh )) {
1491+ ret = wolfSSH_SendPacket (ssh );
1492+ if (ret < 0 )
1493+ return ret ;
1494+ }
1495+
1496+ /* If channel data is already buffered, read it directly rather than
1497+ * polling the socket. A control message delivered into the channel
1498+ * buffer while a rekey was completing leaves wolfSSH_worker returning
1499+ * the rekey status (not WS_CHAN_RXD), so without this the buffered
1500+ * message is never read and the next worker blocks on the socket. */
1501+ if (wolfSSH_stream_peek (ssh , NULL , 1 ) > 0 ) {
1502+ sz = wolfSSH_stream_read (ssh , buf + ssh -> scpRecvMsgSz ,
1503+ DEFAULT_SCP_MSG_SZ - ssh -> scpRecvMsgSz );
1504+ if (sz < 0 )
1505+ return sz ;
1506+ ssh -> scpRecvMsgSz += sz ;
1507+ sz = ssh -> scpRecvMsgSz ;
1508+ continue ;
1509+ }
1510+
13981511 err = wolfSSH_worker (ssh , & lastChannel );
13991512 if (err < 0 ) {
14001513 int rc ;
@@ -1517,7 +1630,7 @@ int ReceiveScpFile(WOLFSSH* ssh)
15171630 }
15181631
15191632 if (ret == WS_SUCCESS ) {
1520- ret = wolfSSH_stream_read (ssh , ssh -> scpFileBuffer , partSz );
1633+ ret = ScpStreamRead (ssh , ssh -> scpFileBuffer , partSz );
15211634 if (ret > 0 ) {
15221635 ssh -> scpFileBufferSz = ret ;
15231636 }
@@ -1559,7 +1672,7 @@ int SendScpConfirmation(WOLFSSH* ssh)
15591672
15601673 /* skip first byte for accurate strlen, may be 0 */
15611674 msgSz = (int )XSTRLEN (msg + 1 ) + 1 ;
1562- ret = wolfSSH_stream_send (ssh , (byte * )msg , msgSz );
1675+ ret = ScpStreamSend (ssh , (byte * )msg , msgSz );
15631676 if (ret != msgSz || ssh -> scpConfirm == WS_SCP_ABORT ) {
15641677 ret = WS_FATAL_ERROR ;
15651678
@@ -1587,7 +1700,7 @@ int ReceiveScpConfirmation(WOLFSSH* ssh)
15871700 return WS_BAD_ARGUMENT ;
15881701
15891702 WMEMSET (msg , 0 , sizeof (msg ));
1590- msgSz = wolfSSH_stream_read (ssh , msg , DEFAULT_SCP_MSG_SZ );
1703+ msgSz = ScpStreamRead (ssh , msg , DEFAULT_SCP_MSG_SZ );
15911704
15921705 if (msgSz < 0 ) {
15931706 if (wolfSSH_get_error (ssh ) == WS_EXTDATA )
0 commit comments