@@ -714,6 +714,137 @@ static int test_DoUserAuthBanner(void)
714714 return result ;
715715}
716716
717+
718+ /* Verify DoChannelRequest sends CHANNEL_SUCCESS for known types and
719+ * CHANNEL_FAILURE for unrecognized ones (RFC 4254 Section 5.4).
720+ *
721+ * A custom IoSend callback captures the outgoing packet in plaintext
722+ * (no cipher negotiated on a fresh session). The SSH packet layout is:
723+ * [4-byte packet_length][1-byte padding_length][1-byte msg_id]...
724+ * so the message ID lives at byte offset 5. */
725+ static byte s_chanReqCapture [256 ];
726+ static word32 s_chanReqCaptureSz = 0 ;
727+
728+ static int CaptureIoSendChanReq (WOLFSSH * ssh , void * buf , word32 sz , void * ctx )
729+ {
730+ (void )ssh ; (void )ctx ;
731+ s_chanReqCaptureSz = (sz < (word32 )sizeof (s_chanReqCapture ))
732+ ? sz : (word32 )sizeof (s_chanReqCapture );
733+ WMEMCPY (s_chanReqCapture , buf , s_chanReqCaptureSz );
734+ return (int )sz ;
735+ }
736+
737+ static int test_DoChannelRequest (void )
738+ {
739+ WOLFSSH_CTX * ctx = NULL ;
740+ WOLFSSH * ssh = NULL ;
741+ WOLFSSH_CHANNEL * ch = NULL ;
742+ int result = 0 ;
743+ int i ;
744+
745+ /* Payloads: [uint32 channelId=0][string type][byte wantReply=1][extra] */
746+ static const byte payShell [] = {
747+ 0x00 ,0x00 ,0x00 ,0x00 , /* channelId = 0 */
748+ 0x00 ,0x00 ,0x00 ,0x05 , /* typeSz = 5 */
749+ 0x73 ,0x68 ,0x65 ,0x6C ,0x6C , /* "shell" */
750+ 0x01 /* wantReply = 1 */
751+ };
752+ static const byte payExec [] = {
753+ 0x00 ,0x00 ,0x00 ,0x00 , /* channelId = 0 */
754+ 0x00 ,0x00 ,0x00 ,0x04 , /* typeSz = 4 */
755+ 0x65 ,0x78 ,0x65 ,0x63 , /* "exec" */
756+ 0x01 , /* wantReply = 1 */
757+ 0x00 ,0x00 ,0x00 ,0x02 , /* cmdSz = 2 */
758+ 0x6C ,0x73 /* "ls" */
759+ };
760+ static const byte payUnknown [] = {
761+ 0x00 ,0x00 ,0x00 ,0x00 , /* channelId = 0 */
762+ 0x00 ,0x00 ,0x00 ,0x0C , /* typeSz = 12 */
763+ 0x75 ,0x6E ,0x6B ,0x6E ,0x6F ,0x77 ,
764+ 0x6E ,0x2D ,0x74 ,0x79 ,0x70 ,0x65 , /* "unknown-type" */
765+ 0x01 /* wantReply = 1 */
766+ };
767+
768+ struct {
769+ const char * label ;
770+ const byte * payload ;
771+ word32 payloadSz ;
772+ int expectRet ;
773+ byte expectMsgId ;
774+ } cases [] = {
775+ { "shell" ,
776+ payShell , (word32 )sizeof (payShell ),
777+ WS_SUCCESS , MSGID_CHANNEL_SUCCESS },
778+ { "exec" ,
779+ payExec , (word32 )sizeof (payExec ),
780+ WS_SUCCESS , MSGID_CHANNEL_SUCCESS },
781+ { "unknown-type" ,
782+ payUnknown , (word32 )sizeof (payUnknown ),
783+ WS_SUCCESS , MSGID_CHANNEL_FAILURE },
784+ };
785+
786+ ctx = wolfSSH_CTX_new (WOLFSSH_ENDPOINT_SERVER , NULL );
787+ if (ctx == NULL )
788+ return -400 ;
789+ wolfSSH_SetIOSend (ctx , CaptureIoSendChanReq );
790+
791+ ssh = wolfSSH_new (ctx );
792+ if (ssh == NULL ) {
793+ result = -401 ;
794+ goto done ;
795+ }
796+
797+ ch = ChannelNew (ssh , ID_CHANTYPE_SESSION ,
798+ DEFAULT_WINDOW_SZ , DEFAULT_MAX_PACKET_SZ );
799+ if (ch == NULL ) {
800+ result = -402 ;
801+ goto done ;
802+ }
803+ if (ChannelAppend (ssh , ch ) != WS_SUCCESS ) {
804+ ChannelDelete (ch , ssh -> ctx -> heap );
805+ result = -403 ;
806+ goto done ;
807+ }
808+
809+ for (i = 0 ; i < (int )(sizeof (cases ) / sizeof (cases [0 ])); i ++ ) {
810+ word32 idx = 0 ;
811+ int ret ;
812+
813+ s_chanReqCaptureSz = 0 ;
814+ WMEMSET (s_chanReqCapture , 0 , sizeof (s_chanReqCapture ));
815+
816+ ret = wolfSSH_TestDoChannelRequest (ssh ,
817+ (byte * )cases [i ].payload , cases [i ].payloadSz , & idx );
818+
819+ if (ret != cases [i ].expectRet ) {
820+ printf ("DoChannelRequest[%s]: ret=%d, expected=%d\n" ,
821+ cases [i ].label , ret , cases [i ].expectRet );
822+ result = -404 - i ;
823+ goto done ;
824+ }
825+
826+ if (s_chanReqCaptureSz <= 5 ) {
827+ printf ("DoChannelRequest[%s]: captured packet too short (%u)\n" ,
828+ cases [i ].label , s_chanReqCaptureSz );
829+ result = -410 - i ;
830+ goto done ;
831+ }
832+
833+ if (s_chanReqCapture [5 ] != cases [i ].expectMsgId ) {
834+ printf ("DoChannelRequest[%s]: msg_id=0x%02x, expected=0x%02x\n" ,
835+ cases [i ].label ,
836+ s_chanReqCapture [5 ], cases [i ].expectMsgId );
837+ result = -420 - i ;
838+ goto done ;
839+ }
840+ }
841+
842+ done :
843+ wolfSSH_free (ssh );
844+ wolfSSH_CTX_free (ctx );
845+ return result ;
846+ }
847+
717848#endif /* WOLFSSH_TEST_INTERNAL */
718849
719850
@@ -809,6 +940,10 @@ int wolfSSH_UnitTest(int argc, char** argv)
809940 unitResult = test_DoUserAuthBanner ();
810941 printf ("DoUserAuthBanner: %s\n" , (unitResult == 0 ? "SUCCESS" : "FAILED" ));
811942 testResult = testResult || unitResult ;
943+
944+ unitResult = test_DoChannelRequest ();
945+ printf ("DoChannelRequest: %s\n" , (unitResult == 0 ? "SUCCESS" : "FAILED" ));
946+ testResult = testResult || unitResult ;
812947#endif
813948
814949#ifdef WOLFSSH_KEYGEN
0 commit comments