@@ -464,6 +464,30 @@ static const ProtoIdScriptVector protoIdScriptVectors[] = {
464464 WOLFSSH_ENDPOINT_CLIENT, WS_VERSION_E },
465465};
466466
467+ /* Capture-to-buffer send callback for the SendProtoId() vectors. The
468+ * proto ID is the first thing on the wire, so everything the callback
469+ * sees is the ID line itself. */
470+ typedef struct ProtoIdSendState {
471+ byte buf[WOLFSSH_PROTOID_LIMIT + 1];
472+ word32 len;
473+ } ProtoIdSendState;
474+
475+ static int ProtoIdCaptureSend(WOLFSSH* ssh, void* buf, word32 sz, void* ctx)
476+ {
477+ ProtoIdSendState* s = (ProtoIdSendState*)ctx;
478+
479+ WOLFSSH_UNUSED(ssh);
480+
481+ if (sz > sizeof(s->buf) - s->len)
482+ return WS_CBIO_ERR_GENERAL;
483+
484+ WMEMCPY(s->buf + s->len, buf, sz);
485+ s->len += sz;
486+
487+ return (int)sz;
488+ }
489+
490+
467491/* DoProtoId() Unit Test */
468492static int test_DoProtoId(void)
469493{
@@ -557,44 +581,54 @@ static int test_DoProtoId(void)
557581 }
558582 }
559583
584+ {
585+ static char tooLongProtoId[WOLFSSH_PROTOID_LIMIT + 2];
586+ static char justRightProtoId[WOLFSSH_PROTOID_LIMIT + 1];
560587 /* Ensure a malformed local protoId cannot be loaded. */
561588 {
562- static char tooLongProtoId[257];
563- static char justRightProtoId[256];
564589 static const struct {
565590 const char* name;
566591 const char* id;
567592 int expectSuccess;
568593 } protoIds[] = {
569- { "conforming custom ID", "SSH-2.0-this_is_my_app\r\n", 1 },
570- { "shortest valid Id", "SSH-2.0-t\r\n", 1 },
571- { "exact len custom ID", justRightProtoId, 1 },
572- { "wrong version prefix", "SSH-2-this_is_my_app\r\n", 0 },
573- { "bad casing prefix", "sSH-2.0-this_is_my_app\r\n", 0 },
574- { "LF terminator only", "SSH-2.0-this_is_my_app\n", 0 },
575- { "CR terminator only", "SSH-2.0-this_is_my_app\r", 0 },
576- { "empty string", "", 0 },
577- { "prefix with no body", "SSH-2.0-\r\n", 0 },
578- { "missing prefix", "hello-this-is\r\n", 0 },
579- { "non ascii char", "SSH-2.0-\x90s\r\n", 0 },
580- { "Body End in CR", "SSH-2.0-s\r\r\n", 0 },
581- { "Body End in TAB", "SSH-2.0-s\t\r\n", 0 },
582- { "Body Have bad char", "SSH-2.0-\x02-a\t\r\n", 0 },
583- { "too long id", tooLongProtoId, 0 },
594+ { "conforming custom ID", "SSH-2.0-this_is_my_app\r\n", 1 },
595+ { "shortest valid Id", "SSH-2.0-t\r\n", 1 },
596+ { "exact len custom ID", justRightProtoId, 1 },
597+ /* Pin the printable-ASCII range as inclusive at both ends: an
598+ * interior 0x20 (the RFC 4253 "SP comments" suffix) and a
599+ * 0x7e must both be accepted. */
600+ { "body w/ SP comments", "SSH-2.0-app comment\r\n", 1 },
601+ { "body w/ tilde", "SSH-2.0-app~1\r\n", 1 },
602+ /* Failing Tests */
603+ { "wrong version prefix", "SSH-2-this_is_my_app\r\n", 0 },
604+ { "bad casing prefix", "sSH-2.0-this_is_my_app\r\n", 0 },
605+ { "LF terminator only", "SSH-2.0-this_is_my_app\n", 0 },
606+ { "CR terminator only", "SSH-2.0-this_is_my_app\r", 0 },
607+ { "empty string", "", 0 },
608+ { "prefix with no body", "SSH-2.0-\r\n", 0 },
609+ { "missing prefix", "hello-this-is\r\n", 0 },
610+ { "non ascii char", "SSH-2.0-\x90s\r\n", 0 },
611+ { "Body End in CR", "SSH-2.0-s\r\r\n", 0 },
612+ { "Body End in TAB", "SSH-2.0-s\t\r\n", 0 },
613+ { "body starts w/ space", "SSH-2.0-\x20-a-b\r\n", 0 },
614+ { "body has embedded TAB", "SSH-2.0-\x7e-a\t\r\n", 0 },
615+ { "too long id", tooLongProtoId, 0 },
616+ { "null pointer", NULL, 0 },
584617 };
585618 int pc = (int)(sizeof(protoIds) / sizeof(protoIds[0]));
586619 WMEMSET(tooLongProtoId, 'a', sizeof(tooLongProtoId));
587620 WMEMCPY(tooLongProtoId, "SSH-2.0-", sizeof("SSH-2.0-") - 1);
588- tooLongProtoId[256 ] = '\0';
589- tooLongProtoId[255] = '\n';
590- tooLongProtoId[254 ] = '\r';
621+ tooLongProtoId[WOLFSSH_PROTOID_LIMIT + 1 ] = '\0';
622+ tooLongProtoId[WOLFSSH_PROTOID_LIMIT] = '\n';
623+ tooLongProtoId[WOLFSSH_PROTOID_LIMIT - 1 ] = '\r';
591624 WMEMSET(justRightProtoId, 'a', sizeof(justRightProtoId));
592625 WMEMCPY(justRightProtoId, "SSH-2.0-", sizeof("SSH-2.0-") - 1);
593- justRightProtoId[255] = '\0';
594- justRightProtoId[254 ] = '\n';
595- justRightProtoId[253 ] = '\r';
626+ justRightProtoId[WOLFSSH_PROTOID_LIMIT] = '\0';
627+ justRightProtoId[WOLFSSH_PROTOID_LIMIT - 1 ] = '\n';
628+ justRightProtoId[WOLFSSH_PROTOID_LIMIT - 2 ] = '\r';
596629
597630 for (i = 0; i < pc; i++) {
631+ const char* prevId = clientCtx->sshProtoIdStr;
598632 ret = wolfSSH_CTX_SetSshProtoIdStr(clientCtx, protoIds[i].id);
599633 if ((ret == WS_SUCCESS) != protoIds[i].expectSuccess) {
600634 fprintf(stderr,
@@ -604,17 +638,81 @@ static int test_DoProtoId(void)
604638 : "WS_BAD_ARGUMENT");
605639 failures++;
606640 }
607- if ((ret == WS_SUCCESS) && clientCtx->sshProtoIdStrSz !=
608- WSTRLEN(protoIds[i].id)) {
641+ if (!protoIds[i].expectSuccess &&
642+ clientCtx->sshProtoIdStr != prevId) {
643+ fprintf(stderr,
644+ "\t[protoId %d] \"%s\" FAIL: invalid proto id "
645+ "was stored\n",
646+ i, protoIds[i].name);
647+ failures++;
648+ }
649+ if (clientCtx->sshProtoIdStrSz !=
650+ (word32)WSTRLEN(clientCtx->sshProtoIdStr)) {
609651 fprintf(stderr,
610- "\t[protoId %d] \"%s\" FAIL: stored sshProtoIdSz "
652+ "\t[protoId %d] \"%s\" FAIL: stored sshProtoIdStrSz "
611653 "was not retained\n",
612654 i, protoIds[i].name);
613655 failures++;
614656 }
615657 }
616658 }
617659
660+ /* A configured proto ID must reach the wire byte for byte. */
661+ {
662+ static const char* const sendIds[] = {
663+ "SSH-2.0-this_is_my_app\r\n",
664+ "SSH-2.0-t\r\n",
665+ "SSH-2.0-app comment\r\n",
666+ justRightProtoId,
667+ };
668+ int sc = (int)(sizeof(sendIds) / sizeof(sendIds[0]));
669+
670+ wolfSSH_SetIOSend(clientCtx, ProtoIdCaptureSend);
671+
672+ for (i = 0; i < sc; i++) {
673+ ProtoIdSendState sendState;
674+ word32 expectSz = (word32)WSTRLEN(sendIds[i]);
675+
676+ ret = wolfSSH_CTX_SetSshProtoIdStr(clientCtx, sendIds[i]);
677+ if (ret != WS_SUCCESS) {
678+ fprintf(stderr,
679+ "\t[send %d] FAIL: set proto id returned %d\n",
680+ i, ret);
681+ failures++;
682+ continue;
683+ }
684+
685+ ssh = wolfSSH_new(clientCtx);
686+ if (ssh == NULL) {
687+ fprintf(stderr,
688+ "\t[send %d] FAIL: wolfSSH_new returned NULL\n", i);
689+ failures++;
690+ continue;
691+ }
692+
693+ WMEMSET(&sendState, 0, sizeof(sendState));
694+ wolfSSH_SetIOWriteCtx(ssh, &sendState);
695+
696+ ret = wolfSSH_TestSendProtoId(ssh);
697+ if (ret != WS_SUCCESS) {
698+ fprintf(stderr,
699+ "\t[send %d] FAIL: SendProtoId returned %d\n",
700+ i, ret);
701+ failures++;
702+ }
703+ else if (sendState.len != expectSz ||
704+ WMEMCMP(sendState.buf, sendIds[i], expectSz) != 0) {
705+ fprintf(stderr,
706+ "\t[send %d] FAIL: wrote %u bytes, expected the "
707+ "%u byte proto id back verbatim\n",
708+ i, sendState.len, expectSz);
709+ failures++;
710+ }
711+ wolfSSH_free(ssh);
712+ }
713+ }
714+ }
715+
618716 wolfSSH_CTX_free(serverCtx);
619717 wolfSSH_CTX_free(clientCtx);
620718
0 commit comments