diff --git a/src/ssh.c b/src/ssh.c index 5cc84b99c..30b66c918 100644 --- a/src/ssh.c +++ b/src/ssh.c @@ -5195,6 +5195,47 @@ static int _ChannelReadExt(WOLFSSH_CHANNEL* channel, byte* buf, word32 bufSz) } +int wolfSSH_ChannelIdPeek(WOLFSSH* ssh, word32 channelId, + byte* buf, word32 bufSz) +{ + WOLFSSH_CHANNEL* channel = NULL; + WOLFSSH_BUFFER* inputBuffer; + word32 avail; + + WLOG(WS_LOG_DEBUG, "Entering wolfSSH_ChannelIdPeek(), ID = %u", channelId); + + if (ssh == NULL) + return WS_BAD_ARGUMENT; + + channel = ChannelFind(ssh, channelId, WS_CHANNEL_ID_SELF); + if (channel == NULL) + return WS_INVALID_CHANID; + + inputBuffer = &channel->inputBuffer; + avail = inputBuffer->length - inputBuffer->idx; + + /* Report the EOF only once the buffered data is drained. */ + if (avail == 0 && channel->eofRxd) { + ssh->error = WS_EOF; + return WS_ERROR; + } + + /* The EOF above outranks this: it names the channel. */ + if (avail == 0 && ssh->disconnected) { + ssh->error = WS_DISCONNECT; + return WS_FATAL_ERROR; + } + + bufSz = min(bufSz, avail); + if (buf != NULL) { + WMEMCPY(buf, inputBuffer->buffer + inputBuffer->idx, bufSz); + } + + WLOG(WS_LOG_DEBUG, "Leaving wolfSSH_ChannelIdPeek(), rxd = %u", bufSz); + return (int)bufSz; +} + + int wolfSSH_ChannelIdRead(WOLFSSH* ssh, word32 channelId, byte* buf, word32 bufSz) { diff --git a/tests/sftp.c b/tests/sftp.c index 856753d82..681fc9c6f 100644 --- a/tests/sftp.c +++ b/tests/sftp.c @@ -469,7 +469,9 @@ int wolfSSH_SftpTest(int flag) int ret = 0; int argsCount; - const char* args[10]; + /* The server thread holds ser.argv, so the client cannot share it. */ + const char* argsServer[10]; + const char* argsClient[10]; #ifndef USE_WINDOWS_API char portNumber[8]; #endif @@ -496,16 +498,16 @@ int wolfSSH_SftpTest(int flag) #endif argsCount = 0; - args[argsCount++] = "."; - args[argsCount++] = "-1"; + argsServer[argsCount++] = "."; + argsServer[argsCount++] = "-1"; #ifndef USE_WINDOWS_API - args[argsCount++] = "-p"; - args[argsCount++] = "0"; + argsServer[argsCount++] = "-p"; + argsServer[argsCount++] = "0"; #endif if (flag) - args[argsCount++] = "-N"; + argsServer[argsCount++] = "-N"; - ser.argv = (char**)args; + ser.argv = (char**)argsServer; ser.argc = argsCount; ser.signal = &ready; InitTcpReady(ser.signal); @@ -513,23 +515,23 @@ int wolfSSH_SftpTest(int flag) WaitTcpReady(&ready); argsCount = 0; - args[argsCount++] = "."; - args[argsCount++] = "-u"; - args[argsCount++] = "jill"; - args[argsCount++] = "-P"; - args[argsCount++] = "upthehill"; + argsClient[argsCount++] = "."; + argsClient[argsCount++] = "-u"; + argsClient[argsCount++] = "jill"; + argsClient[argsCount++] = "-P"; + argsClient[argsCount++] = "upthehill"; #ifndef USE_WINDOWS_API /* use port that server has found */ - args[argsCount++] = "-p"; + argsClient[argsCount++] = "-p"; snprintf(portNumber, sizeof(portNumber), "%d", ready.port); - args[argsCount++] = portNumber; + argsClient[argsCount++] = portNumber; #endif if (flag) - args[argsCount++] = "-N"; + argsClient[argsCount++] = "-N"; - cli.argv = (char**)args; + cli.argv = (char**)argsClient; cli.argc = argsCount; cli.signal = &ready; cli.sftp_cb = commandCb; diff --git a/tests/unit.c b/tests/unit.c index a7d0c6bab..6aa588e84 100644 --- a/tests/unit.c +++ b/tests/unit.c @@ -10006,6 +10006,122 @@ static int test_stream_read_deferredWindowAdjust(void) return result; } +/* wolfSSH_ChannelIdPeek() reports a named channel without consuming it. */ +static int test_ChannelIdPeek(void) +{ + WOLFSSH_CTX* ctx = NULL; + WOLFSSH* ssh = NULL; + WOLFSSH_CHANNEL* ch = NULL; + int result = 0; + int ret; + byte in[32]; + byte out[32]; + word32 i; + + for (i = 0; i < (word32)sizeof(in); i++) { + in[i] = (byte)(i + 1); + } + + ctx = wolfSSH_CTX_new(WOLFSSH_ENDPOINT_SERVER, NULL); + if (ctx == NULL) + return -7050; + + ssh = wolfSSH_new(ctx); + if (ssh == NULL) { result = -7051; goto done; } + + /* Bad args, before any channel exists. */ + if (wolfSSH_ChannelIdPeek(NULL, 0, out, (word32)sizeof(out)) + != WS_BAD_ARGUMENT) { + result = -7052; goto done; + } + if (wolfSSH_ChannelIdPeek(ssh, 99, out, (word32)sizeof(out)) + != WS_INVALID_CHANID) { + result = -7053; goto done; + } + + ch = ChannelNew(ssh, ID_CHANTYPE_SESSION, + (word32)sizeof(in), DEFAULT_MAX_PACKET_SZ); + if (ch == NULL) { result = -7054; goto done; } + if (ChannelAppend(ssh, ch) != WS_SUCCESS) { + ChannelDelete(ch, ssh->ctx->heap); + result = -7055; + goto done; + } + ch->openConfirmed = 1; + + /* Open and empty: zero, not an error. */ + ret = wolfSSH_ChannelIdPeek(ssh, ch->channel, out, (word32)sizeof(out)); + if (ret != 0) { result = -7056; goto done; } + + if (wolfSSH_TestChannelPutData(ch, in, (word32)sizeof(in)) != WS_SUCCESS) { + result = -7057; goto done; + } + + /* Whole payload reported and copied out. */ + WMEMSET(out, 0, sizeof(out)); + ret = wolfSSH_ChannelIdPeek(ssh, ch->channel, out, (word32)sizeof(out)); + if (ret != (int)sizeof(in)) { result = -7058; goto done; } + if (WMEMCMP(out, in, sizeof(in)) != 0) { result = -7059; goto done; } + + /* Consumed nothing, credited nothing. */ + if (ch->inputBuffer.length - ch->inputBuffer.idx != (word32)sizeof(in)) { + result = -7060; goto done; + } + if (ch->windowSz != 0) { result = -7061; goto done; } + + /* bufSz caps the count and the bytes written. */ + WMEMSET(out, 0, sizeof(out)); + ret = wolfSSH_ChannelIdPeek(ssh, ch->channel, out, 8); + if (ret != 8) { result = -7062; goto done; } + if (WMEMCMP(out, in, 8) != 0) { result = -7063; goto done; } + if (out[8] != 0) { result = -7064; goto done; } + + /* A NULL buffer asks only for the count. */ + ret = wolfSSH_ChannelIdPeek(ssh, ch->channel, NULL, (word32)sizeof(out)); + if (ret != (int)sizeof(in)) { result = -7065; goto done; } + + /* Still reported mid-rekey, where stream_peek returns WS_REKEYING. */ + ssh->isKeying = WOLFSSH_SELF_IS_KEYING; + if (wolfSSH_stream_peek(ssh, out, (word32)sizeof(out)) != WS_REKEYING) { + result = -7066; goto done; + } + ret = wolfSSH_ChannelIdPeek(ssh, ch->channel, out, (word32)sizeof(out)); + if (ret != (int)sizeof(in)) { result = -7067; goto done; } + ssh->isKeying = 0; + + /* EOF stays hidden until the buffered data is drained. */ + ch->eofRxd = 1; + ret = wolfSSH_ChannelIdPeek(ssh, ch->channel, out, (word32)sizeof(out)); + if (ret != (int)sizeof(in)) { result = -7068; goto done; } + + ret = wolfSSH_ChannelIdRead(ssh, ch->channel, out, (word32)sizeof(out)); + if (ret != (int)sizeof(in)) { result = -7069; goto done; } + + ssh->error = WS_SUCCESS; + ret = wolfSSH_ChannelIdPeek(ssh, ch->channel, out, (word32)sizeof(out)); + if (ret != WS_ERROR) { result = -7070; goto done; } + if (ssh->error != WS_EOF) { result = -7071; goto done; } + + /* EOF outranks a disconnect. */ + ssh->disconnected = 1; + ssh->error = WS_SUCCESS; + ret = wolfSSH_ChannelIdPeek(ssh, ch->channel, out, (word32)sizeof(out)); + if (ret != WS_ERROR) { result = -7072; goto done; } + if (ssh->error != WS_EOF) { result = -7073; goto done; } + + /* Without the EOF, the dead session is reported instead. */ + ch->eofRxd = 0; + ssh->error = WS_SUCCESS; + ret = wolfSSH_ChannelIdPeek(ssh, ch->channel, out, (word32)sizeof(out)); + if (ret != WS_FATAL_ERROR) { result = -7074; goto done; } + if (ssh->error != WS_DISCONNECT) { result = -7075; goto done; } + +done: + wolfSSH_free(ssh); + wolfSSH_CTX_free(ctx); + return result; +} + /* wolfSSH_ChannelIdRead() counterpart of * test_stream_read_deferredWindowAdjust(): callers break out on a non-positive * read, and this entry point has to retire the owed-flush status itself. */ @@ -23094,6 +23210,11 @@ int wolfSSH_UnitTest(int argc, char** argv) printf("ChannelIdRead_deferredWindowAdjust: %s\n", (unitResult == 0 ? "SUCCESS" : "FAILED")); testResult = testResult || unitResult; + + unitResult = test_ChannelIdPeek(); + printf("ChannelIdPeek: %s\n", + (unitResult == 0 ? "SUCCESS" : "FAILED")); + testResult = testResult || unitResult; #endif /* NO_WOLFSSH_SERVER */ unitResult = test_BuildNameList_emptySrc(); diff --git a/wolfssh/ssh.h b/wolfssh/ssh.h index 938db9459..d1e2d10b0 100644 --- a/wolfssh/ssh.h +++ b/wolfssh/ssh.h @@ -979,6 +979,12 @@ WOLFSSH_API int wolfSSH_global_request(WOLFSSH* ssh, const unsigned char* data, * wolfSSH_ChannelRead() returns WS_REKEYING. */ WOLFSSH_API int wolfSSH_ChannelIdRead(WOLFSSH* ssh, word32 channelId, byte* buf, word32 bufSz); +/* Peeks the channel named by channelId, with wolfSSH_stream_peek()'s + * contract, except that it peeks during a rekey the way + * wolfSSH_ChannelIdRead() reads during one. A NULL buf asks only for the + * count, still capped at bufSz. */ +WOLFSSH_API int wolfSSH_ChannelIdPeek(WOLFSSH* ssh, word32 channelId, + byte* buf, word32 bufSz); WOLFSSH_API int wolfSSH_ChannelIdSend(WOLFSSH* ssh, word32 channelId, byte* buf, word32 bufSz); WOLFSSH_API int wolfSSH_ChannelIdReadExt(WOLFSSH* ssh, word32 channelId,