@@ -231,6 +231,21 @@ static word32 BuildDirectTcpipExtra(const char* host, word32 hostPort,
231231
232232 return idx ;
233233}
234+
235+ static word32 BuildGlobalRequestFwdPacket (const char * bindAddr , word32 bindPort ,
236+ int isCancel , byte wantReply , byte * out , word32 outSz )
237+ {
238+ byte payload [256 ];
239+ word32 idx = 0 ;
240+ const char * reqName = isCancel ? "cancel-tcpip-forward" : "tcpip-forward" ;
241+
242+ idx = AppendString (payload , sizeof (payload ), idx , reqName );
243+ idx = AppendByte (payload , sizeof (payload ), idx , wantReply );
244+ idx = AppendString (payload , sizeof (payload ), idx , bindAddr );
245+ idx = AppendUint32 (payload , sizeof (payload ), idx , bindPort );
246+
247+ return WrapPacket (MSGID_GLOBAL_REQUEST , payload , idx , out , outSz );
248+ }
234249#endif
235250
236251/* Simple in-memory transport harness */
@@ -957,6 +972,94 @@ static void AssertChannelOpenFailResponse(const ChannelOpenHarness* harness,
957972 AssertTrue (harness -> ssh -> channelList == NULL );
958973}
959974
975+ #ifdef WOLFSSH_FWD
976+ static word32 ParsePayloadLen (const byte * packet , word32 packetSz )
977+ {
978+ word32 packetLen ;
979+ byte padLen ;
980+
981+ AssertNotNull (packet );
982+ AssertTrue (packetSz >= 6 );
983+
984+ WMEMCPY (& packetLen , packet , sizeof (packetLen ));
985+ packetLen = ntohl (packetLen );
986+ padLen = packet [4 ];
987+
988+ AssertTrue (packetLen >= (word32 )padLen + 1 );
989+ AssertTrue (packetSz >= packetLen + 4 );
990+
991+ return packetLen - padLen - 1 ;
992+ }
993+
994+ static const byte * ParseGlobalRequestName (const byte * packet , word32 packetSz ,
995+ word32 * nameSz )
996+ {
997+ word32 packetLen ;
998+ word32 payloadLen ;
999+ word32 strSz ;
1000+ const byte * payload ;
1001+
1002+ AssertNotNull (packet );
1003+ AssertNotNull (nameSz );
1004+ AssertTrue (packetSz >= 10 );
1005+
1006+ WMEMCPY (& packetLen , packet , sizeof (packetLen ));
1007+ packetLen = ntohl (packetLen );
1008+ AssertTrue (packetSz >= packetLen + 4 );
1009+
1010+ payloadLen = ParsePayloadLen (packet , packetSz );
1011+ payload = packet + 5 ;
1012+
1013+ AssertTrue (payloadLen >= 1 + sizeof (word32 ));
1014+ AssertIntEQ (payload [0 ], MSGID_GLOBAL_REQUEST );
1015+
1016+ WMEMCPY (& strSz , payload + 1 , sizeof (strSz ));
1017+ strSz = ntohl (strSz );
1018+ AssertTrue (payloadLen >= 1 + sizeof (word32 ) + strSz );
1019+
1020+ * nameSz = strSz ;
1021+ return payload + 1 + sizeof (word32 );
1022+ }
1023+
1024+ static void AssertGlobalRequestReply (const ChannelOpenHarness * harness ,
1025+ byte expectedMsgId )
1026+ {
1027+ byte msgId ;
1028+ word32 payloadLen ;
1029+
1030+ AssertTrue (harness -> io .outSz > 0 );
1031+ msgId = ParseMsgId (harness -> io .out , harness -> io .outSz );
1032+ AssertIntEQ (msgId , expectedMsgId );
1033+
1034+ payloadLen = ParsePayloadLen (harness -> io .out , harness -> io .outSz );
1035+ if (expectedMsgId == MSGID_REQUEST_FAILURE ) {
1036+ AssertIntEQ (payloadLen , 1 );
1037+ }
1038+ else if (expectedMsgId == MSGID_REQUEST_SUCCESS ) {
1039+ const byte * reqName ;
1040+ word32 reqNameSz ;
1041+
1042+ reqName = ParseGlobalRequestName (harness -> io .in , harness -> io .inSz ,
1043+ & reqNameSz );
1044+
1045+ if (reqNameSz == sizeof ("tcpip-forward" ) - 1 &&
1046+ WMEMCMP (reqName , "tcpip-forward" ,
1047+ sizeof ("tcpip-forward" ) - 1 ) == 0 ) {
1048+ AssertIntEQ (payloadLen , 5 );
1049+ }
1050+ else if (reqNameSz == sizeof ("cancel-tcpip-forward" ) - 1 &&
1051+ WMEMCMP (reqName , "cancel-tcpip-forward" ,
1052+ sizeof ("cancel-tcpip-forward" ) - 1 ) == 0 ) {
1053+ AssertIntEQ (payloadLen , 1 );
1054+ }
1055+ else {
1056+ Fail (("unexpected global request name" ),
1057+ ("%.*s" , (int )reqNameSz , reqName ));
1058+ }
1059+ }
1060+ }
1061+ #endif
1062+
9601063static int RejectChannelOpenCb (WOLFSSH_CHANNEL * channel , void * ctx )
9611064{
9621065 (void )channel ;
@@ -978,6 +1081,17 @@ static int RejectDirectTcpipSetup(WS_FwdCbAction action, void* ctx,
9781081
9791082 return WS_SUCCESS ;
9801083}
1084+
1085+ static int AcceptFwdCb (WS_FwdCbAction action , void * ctx ,
1086+ const char * host , word32 port )
1087+ {
1088+ (void )action ;
1089+ (void )ctx ;
1090+ (void )host ;
1091+ (void )port ;
1092+
1093+ return WS_SUCCESS ;
1094+ }
9811095#endif
9821096
9831097
@@ -1242,6 +1356,101 @@ static void TestDirectTcpipNoFwdCbSendsOpenFail(void)
12421356
12431357 FreeChannelOpenHarness (& harness );
12441358}
1359+
1360+ static void TestGlobalRequestFwdNoCbSendsFailure (void )
1361+ {
1362+ ChannelOpenHarness harness ;
1363+ byte in [256 ];
1364+ word32 inSz ;
1365+ int ret ;
1366+
1367+ inSz = BuildGlobalRequestFwdPacket ("0.0.0.0" , 2222 , 0 , 1 , in , sizeof (in ));
1368+ InitChannelOpenHarness (& harness , in , inSz );
1369+ /* no fwdCb registered */
1370+
1371+ ret = DoReceive (harness .ssh );
1372+
1373+ AssertIntEQ (ret , WS_SUCCESS );
1374+ AssertGlobalRequestReply (& harness , MSGID_REQUEST_FAILURE );
1375+
1376+ FreeChannelOpenHarness (& harness );
1377+ }
1378+
1379+ static void TestGlobalRequestFwdNoCbNoReplyKeepsConnection (void )
1380+ {
1381+ ChannelOpenHarness harness ;
1382+ byte in [256 ];
1383+ word32 inSz ;
1384+ int ret ;
1385+
1386+ /* wantReply=0: no reply sent, connection must stay alive */
1387+ inSz = BuildGlobalRequestFwdPacket ("0.0.0.0" , 2222 , 0 , 0 , in , sizeof (in ));
1388+ InitChannelOpenHarness (& harness , in , inSz );
1389+ /* no fwdCb registered */
1390+
1391+ ret = DoReceive (harness .ssh );
1392+
1393+ AssertIntEQ (ret , WS_SUCCESS );
1394+ AssertIntEQ (harness .io .outSz , 0 ); /* no reply sent */
1395+
1396+ FreeChannelOpenHarness (& harness );
1397+ }
1398+
1399+ static void TestGlobalRequestFwdWithCbSendsSuccess (void )
1400+ {
1401+ ChannelOpenHarness harness ;
1402+ byte in [256 ];
1403+ word32 inSz ;
1404+ int ret ;
1405+
1406+ inSz = BuildGlobalRequestFwdPacket ("0.0.0.0" , 2222 , 0 , 1 , in , sizeof (in ));
1407+ InitChannelOpenHarness (& harness , in , inSz );
1408+ AssertIntEQ (wolfSSH_CTX_SetFwdCb (harness .ctx , AcceptFwdCb , NULL ), WS_SUCCESS );
1409+
1410+ ret = DoReceive (harness .ssh );
1411+
1412+ AssertIntEQ (ret , WS_SUCCESS );
1413+ AssertGlobalRequestReply (& harness , MSGID_REQUEST_SUCCESS );
1414+
1415+ FreeChannelOpenHarness (& harness );
1416+ }
1417+
1418+ static void TestGlobalRequestFwdCancelNoCbSendsFailure (void )
1419+ {
1420+ ChannelOpenHarness harness ;
1421+ byte in [256 ];
1422+ word32 inSz ;
1423+ int ret ;
1424+
1425+ inSz = BuildGlobalRequestFwdPacket ("0.0.0.0" , 2222 , 1 , 1 , in , sizeof (in ));
1426+ InitChannelOpenHarness (& harness , in , inSz );
1427+
1428+ ret = DoReceive (harness .ssh );
1429+
1430+ AssertIntEQ (ret , WS_SUCCESS );
1431+ AssertGlobalRequestReply (& harness , MSGID_REQUEST_FAILURE );
1432+
1433+ FreeChannelOpenHarness (& harness );
1434+ }
1435+
1436+ static void TestGlobalRequestFwdCancelWithCbSendsSuccess (void )
1437+ {
1438+ ChannelOpenHarness harness ;
1439+ byte in [256 ];
1440+ word32 inSz ;
1441+ int ret ;
1442+
1443+ inSz = BuildGlobalRequestFwdPacket ("0.0.0.0" , 2222 , 1 , 1 , in , sizeof (in ));
1444+ InitChannelOpenHarness (& harness , in , inSz );
1445+ AssertIntEQ (wolfSSH_CTX_SetFwdCb (harness .ctx , AcceptFwdCb , NULL ), WS_SUCCESS );
1446+
1447+ ret = DoReceive (harness .ssh );
1448+
1449+ AssertIntEQ (ret , WS_SUCCESS );
1450+ AssertGlobalRequestReply (& harness , MSGID_REQUEST_SUCCESS );
1451+
1452+ FreeChannelOpenHarness (& harness );
1453+ }
12451454#endif
12461455
12471456#ifdef WOLFSSH_AGENT
@@ -1707,6 +1916,11 @@ int main(int argc, char** argv)
17071916#ifdef WOLFSSH_FWD
17081917 TestDirectTcpipRejectSendsOpenFail ();
17091918 TestDirectTcpipNoFwdCbSendsOpenFail ();
1919+ TestGlobalRequestFwdNoCbSendsFailure ();
1920+ TestGlobalRequestFwdNoCbNoReplyKeepsConnection ();
1921+ TestGlobalRequestFwdWithCbSendsSuccess ();
1922+ TestGlobalRequestFwdCancelNoCbSendsFailure ();
1923+ TestGlobalRequestFwdCancelWithCbSendsSuccess ();
17101924#endif
17111925#ifdef WOLFSSH_AGENT
17121926 TestAgentChannelNullAgentSendsOpenFail ();
0 commit comments