Example #1
0
int BusyWithClassicConnection(EvalContext *ctx, ServerConnectionState *conn)
{
    time_t tloc, trem = 0;
    char recvbuffer[CF_BUFSIZE + CF_BUFEXT], check[CF_BUFSIZE];
    char sendbuffer[CF_BUFSIZE] = { 0 };
    char filename[CF_BUFSIZE], buffer[CF_BUFSIZE], args[CF_BUFSIZE], out[CF_BUFSIZE];
    long time_no_see = 0;
    unsigned int len = 0;
    int drift, plainlen, received, encrypted = 0;
    ServerFileGetState get_args;
    Item *classes;

    memset(recvbuffer, 0, CF_BUFSIZE + CF_BUFEXT);
    memset(&get_args, 0, sizeof(get_args));

    received = ReceiveTransaction(conn->conn_info, recvbuffer, NULL);
    if (received == -1 || received == 0)
    {
        return false;
    }

    if (strlen(recvbuffer) == 0)
    {
        Log(LOG_LEVEL_WARNING, "Got NULL transmission, skipping!");
        return true;
    }

    /* Don't process request if we're signalled to exit. */
    if (IsPendingTermination())
    {
        return false;
    }

    ProtocolCommandClassic command = GetCommandClassic(recvbuffer);

    switch (command)
    {
    /* Plain text authentication; this MUST be the first command client
       using classic protocol is sending. */
    case PROTOCOL_COMMAND_AUTH_PLAIN:
        SetConnectionData(conn, (char *) (recvbuffer + strlen("CAUTH ")));

        if (conn->username == NULL || IsUserNameValid(conn->username) == false)
        {
            Log(LOG_LEVEL_INFO, "Client is sending wrong username: '******'", conn->username);
            RefuseAccess(conn, recvbuffer);
            return false;
        }

        /* This is used only for forcing correct state of state machine while
           connecting and authenticating user using classic protocol. */
        conn->user_data_set = true;

        return true;

    /* This MUST be exactly second command client using classic protocol is sending.
       This is where key agreement takes place. */
    case PROTOCOL_COMMAND_AUTH_SECURE:
        /* First command was ommited by client; this is protocol violation. */
        if (!conn->user_data_set)
        {
            Log(LOG_LEVEL_INFO, "Client is not verified; rejecting connection");
            RefuseAccess(conn, recvbuffer);
            return false;
        }

        conn->rsa_auth = AuthenticationDialogue(conn, recvbuffer, received);
        if (!conn->rsa_auth)
        {
            Log(LOG_LEVEL_INFO, "Auth dialogue error");
            RefuseAccess(conn, recvbuffer);
            return false;
        }

        return true;
    default:
        break;
    }

    /* At this point we should have both user_data_set and rsa_auth set to perform any operation.
       We can check only for second one as without first it won't be set up. */
    if (!conn->rsa_auth)
    {
        Log(LOG_LEVEL_INFO, "Server refusal due to no RSA authentication [command: %d]", command);
        RefuseAccess(conn, recvbuffer);
        return false;
    }

    /* We have to have key at this point. */
    assert(conn->session_key);

    /* At this point we can safely do next switch and make sure user is authenticated. */
    switch (command)
    {
    case PROTOCOL_COMMAND_EXEC:
        memset(args, 0, CF_BUFSIZE);
        sscanf(recvbuffer, "EXEC %255[^\n]", args);

        if (!AllowedUser(conn->username))
        {
            Log(LOG_LEVEL_INFO, "Server refusal due to non-allowed user");
            RefuseAccess(conn, recvbuffer);
            return false;
        }

        if (!AccessControl(ctx, CommandArg0(CFRUNCOMMAND), conn, false))
        {
            Log(LOG_LEVEL_INFO, "Server refusal due to denied access to requested object");
            RefuseAccess(conn, recvbuffer);
            return false;
        }

        if (!MatchClasses(ctx, conn))
        {
            Log(LOG_LEVEL_INFO, "Server refusal due to failed class/context match");
            Terminate(conn->conn_info);
            return false;
        }

        DoExec(ctx, conn, args);
        Terminate(conn->conn_info);
        return false;

    case PROTOCOL_COMMAND_VERSION:
        snprintf(sendbuffer, sizeof(sendbuffer), "OK: %s", Version());
        SendTransaction(conn->conn_info, sendbuffer, 0, CF_DONE);
        return conn->user_data_set;

    case PROTOCOL_COMMAND_GET:
        memset(filename, 0, CF_BUFSIZE);
        sscanf(recvbuffer, "GET %d %[^\n]", &(get_args.buf_size), filename);

        if ((get_args.buf_size < 0) || (get_args.buf_size > CF_BUFSIZE))
        {
            Log(LOG_LEVEL_INFO, "GET buffer out of bounds");
            RefuseAccess(conn, recvbuffer);
            return false;
        }

        if (!AccessControl(ctx, filename, conn, false))
        {
            Log(LOG_LEVEL_INFO, "Access denied to get object");
            RefuseAccess(conn, recvbuffer);
            return true;
        }

        memset(sendbuffer, 0, sizeof(sendbuffer));

        if (get_args.buf_size >= CF_BUFSIZE)
        {
            get_args.buf_size = 2048;
        }

        get_args.connect = conn;
        get_args.encrypt = false;
        get_args.replybuff = sendbuffer;
        get_args.replyfile = filename;

        CfGetFile(&get_args);

        return true;

    case PROTOCOL_COMMAND_GET_SECURE:
        memset(buffer, 0, CF_BUFSIZE);
        sscanf(recvbuffer, "SGET %u %d", &len, &(get_args.buf_size));

        if (received != len + CF_PROTO_OFFSET)
        {
            Log(LOG_LEVEL_VERBOSE, "Protocol error SGET");
            RefuseAccess(conn, recvbuffer);
            return false;
        }

        plainlen = DecryptString(conn->encryption_type, recvbuffer + CF_PROTO_OFFSET, buffer, conn->session_key, len);

        cfscanf(buffer, strlen("GET"), strlen("dummykey"), check, sendbuffer, filename);

        if (strcmp(check, "GET") != 0)
        {
            Log(LOG_LEVEL_INFO, "SGET/GET problem");
            RefuseAccess(conn, recvbuffer);
            return true;
        }

        if ((get_args.buf_size < 0) || (get_args.buf_size > 8192))
        {
            Log(LOG_LEVEL_INFO, "SGET bounding error");
            RefuseAccess(conn, recvbuffer);
            return false;
        }

        if (get_args.buf_size >= CF_BUFSIZE)
        {
            get_args.buf_size = 2048;
        }

        Log(LOG_LEVEL_DEBUG, "Confirm decryption, and thus validity of caller");
        Log(LOG_LEVEL_DEBUG, "SGET '%s' with blocksize %d", filename, get_args.buf_size);

        if (!AccessControl(ctx, filename, conn, true))
        {
            Log(LOG_LEVEL_INFO, "Access control error");
            RefuseAccess(conn, recvbuffer);
            return false;
        }

        memset(sendbuffer, 0, sizeof(sendbuffer));

        get_args.connect = conn;
        get_args.encrypt = true;
        get_args.replybuff = sendbuffer;
        get_args.replyfile = filename;

        CfEncryptGetFile(&get_args);
        return true;

    case PROTOCOL_COMMAND_OPENDIR_SECURE:
        memset(buffer, 0, CF_BUFSIZE);
        sscanf(recvbuffer, "SOPENDIR %u", &len);

        if ((len >= sizeof(out)) || (received != (len + CF_PROTO_OFFSET)))
        {
            Log(LOG_LEVEL_VERBOSE, "Protocol error OPENDIR: %d", len);
            RefuseAccess(conn, recvbuffer);
            return false;
        }

        memcpy(out, recvbuffer + CF_PROTO_OFFSET, len);

        plainlen = DecryptString(conn->encryption_type, out, recvbuffer, conn->session_key, len);

        if (strncmp(recvbuffer, "OPENDIR", 7) != 0)
        {
            Log(LOG_LEVEL_INFO, "Opendir failed to decrypt");
            RefuseAccess(conn, recvbuffer);
            return true;
        }

        memset(filename, 0, CF_BUFSIZE);
        sscanf(recvbuffer, "OPENDIR %[^\n]", filename);

        if (!AccessControl(ctx, filename, conn, true))        /* opendir don't care about privacy */
        {
            Log(LOG_LEVEL_INFO, "Access error");
            RefuseAccess(conn, recvbuffer);
            return false;
        }

        CfSecOpenDirectory(conn, sendbuffer, filename);
        return true;

    case PROTOCOL_COMMAND_OPENDIR:
        memset(filename, 0, CF_BUFSIZE);
        sscanf(recvbuffer, "OPENDIR %[^\n]", filename);

        if (!AccessControl(ctx, filename, conn, true))        /* opendir don't care about privacy */
        {
            Log(LOG_LEVEL_INFO, "DIR access error");
            RefuseAccess(conn, recvbuffer);
            return false;
        }

        CfOpenDirectory(conn, sendbuffer, filename);
        return true;

    case PROTOCOL_COMMAND_SYNC_SECURE:
        memset(buffer, 0, CF_BUFSIZE);
        sscanf(recvbuffer, "SSYNCH %u", &len);

        if ((len >= sizeof(out)) || (received != (len + CF_PROTO_OFFSET)))
        {
            Log(LOG_LEVEL_VERBOSE, "Protocol error SSYNCH: %d", len);
            RefuseAccess(conn, recvbuffer);
            return false;
        }

        memcpy(out, recvbuffer + CF_PROTO_OFFSET, len);

        plainlen = DecryptString(conn->encryption_type, out, recvbuffer, conn->session_key, len);

        if (plainlen < 0)
        {
            DebugBinOut((char *) conn->session_key, 32, "Session key");
            Log(LOG_LEVEL_ERR, "Bad decrypt (%d)", len);
        }

        if (strncmp(recvbuffer, "SYNCH", 5) != 0)
        {
            Log(LOG_LEVEL_INFO, "No synch");
            RefuseAccess(conn, recvbuffer);
            return true;
        }

        /* roll through, no break */

    case PROTOCOL_COMMAND_SYNC:
        memset(filename, 0, CF_BUFSIZE);
        sscanf(recvbuffer, "SYNCH %ld STAT %[^\n]", &time_no_see, filename);

        trem = (time_t) time_no_see;

        if ((time_no_see == 0) || (filename[0] == '\0'))
        {
            break;
        }

        if ((tloc = time((time_t *) NULL)) == -1)
        {
            Log(LOG_LEVEL_INFO, "Couldn't read system clock. (time: %s)", GetErrorStr());
            SendTransaction(conn->conn_info, "BAD: clocks out of synch", 0, CF_DONE);
            return true;
        }

        drift = (int) (tloc - trem);

        if (!AccessControl(ctx, filename, conn, true))
        {
            Log(LOG_LEVEL_INFO, "Access control in sync");
            RefuseAccess(conn, recvbuffer);
            return true;
        }

        if (DENYBADCLOCKS && (drift * drift > CLOCK_DRIFT * CLOCK_DRIFT))
        {
            snprintf(sendbuffer, sizeof(sendbuffer),
                     "BAD: Clocks are too far unsynchronized %ld/%ld",
                     (long) tloc, (long) trem);
            SendTransaction(conn->conn_info, sendbuffer, 0, CF_DONE);
            return true;
        }
        else
        {
            Log(LOG_LEVEL_DEBUG, "Clocks were off by %ld", (long) tloc - (long) trem);
            StatFile(conn, sendbuffer, filename);
        }

        return true;

    case PROTOCOL_COMMAND_MD5_SECURE:
        sscanf(recvbuffer, "SMD5 %u", &len);

        if ((len >= sizeof(out)) || (received != (len + CF_PROTO_OFFSET)))
        {
            Log(LOG_LEVEL_INFO, "Decryption error");
            RefuseAccess(conn, recvbuffer);
            return true;
        }

        memcpy(out, recvbuffer + CF_PROTO_OFFSET, len);
        plainlen = DecryptString(conn->encryption_type, out, recvbuffer, conn->session_key, len);

        if (strncmp(recvbuffer, "MD5", 3) != 0)
        {
            Log(LOG_LEVEL_INFO, "MD5 protocol error");
            RefuseAccess(conn, recvbuffer);
            return false;
        }

        /* roll through, no break */

    case PROTOCOL_COMMAND_MD5:
        CompareLocalHash(conn, sendbuffer, recvbuffer);
        return true;

    case PROTOCOL_COMMAND_VAR_SECURE:
        sscanf(recvbuffer, "SVAR %u", &len);

        if ((len >= sizeof(out)) || (received != (len + CF_PROTO_OFFSET)))
        {
            Log(LOG_LEVEL_INFO, "Decrypt error SVAR");
            RefuseAccess(conn, "decrypt error SVAR");
            return true;
        }

        memcpy(out, recvbuffer + CF_PROTO_OFFSET, len);
        plainlen = DecryptString(conn->encryption_type, out, recvbuffer, conn->session_key, len);
        encrypted = true;

        if (strncmp(recvbuffer, "VAR", 3) != 0)
        {
            Log(LOG_LEVEL_INFO, "VAR protocol defect");
            RefuseAccess(conn, "decryption failure");
            return false;
        }

        /* roll through, no break */

    case PROTOCOL_COMMAND_VAR:
        if (!LiteralAccessControl(ctx, recvbuffer, conn, encrypted))
        {
            Log(LOG_LEVEL_INFO, "Literal access failure");
            RefuseAccess(conn, recvbuffer);
            return false;
        }

        GetServerLiteral(ctx, conn, sendbuffer, recvbuffer, encrypted);
        return true;

    case PROTOCOL_COMMAND_CONTEXT_SECURE:
        sscanf(recvbuffer, "SCONTEXT %u", &len);

        if ((len >= sizeof(out)) || (received != (len + CF_PROTO_OFFSET)))
        {
            Log(LOG_LEVEL_INFO, "Decrypt error SCONTEXT, len,received = %d,%d", len, received);
            RefuseAccess(conn, "decrypt error SCONTEXT");
            return true;
        }

        memcpy(out, recvbuffer + CF_PROTO_OFFSET, len);
        plainlen = DecryptString(conn->encryption_type, out, recvbuffer, conn->session_key, len);
        encrypted = true;

        if (strncmp(recvbuffer, "CONTEXT", 7) != 0)
        {
            Log(LOG_LEVEL_INFO, "CONTEXT protocol defect...");
            RefuseAccess(conn, "Decryption failed?");
            return false;
        }

        /* roll through, no break */

    case PROTOCOL_COMMAND_CONTEXT:
        if ((classes = ContextAccessControl(ctx, recvbuffer, conn, encrypted)) == NULL)
        {
            Log(LOG_LEVEL_INFO, "Context access failure on %s", recvbuffer);
            RefuseAccess(conn, recvbuffer);
            return false;
        }

        ReplyServerContext(conn, encrypted, classes);
        return true;

    case PROTOCOL_COMMAND_QUERY_SECURE:
        sscanf(recvbuffer, "SQUERY %u", &len);

        if ((len >= sizeof(out)) || (received != (len + CF_PROTO_OFFSET)))
        {
            Log(LOG_LEVEL_INFO, "Decrypt error SQUERY");
            RefuseAccess(conn, "decrypt error SQUERY");
            return true;
        }

        memcpy(out, recvbuffer + CF_PROTO_OFFSET, len);
        plainlen = DecryptString(conn->encryption_type, out, recvbuffer, conn->session_key, len);

        if (strncmp(recvbuffer, "QUERY", 5) != 0)
        {
            Log(LOG_LEVEL_INFO, "QUERY protocol defect");
            RefuseAccess(conn, "decryption failure");
            return false;
        }

        if (!LiteralAccessControl(ctx, recvbuffer, conn, true))
        {
            Log(LOG_LEVEL_INFO, "Query access failure");
            RefuseAccess(conn, recvbuffer);
            return false;
        }

        if (GetServerQuery(conn, recvbuffer, true))       /* always encrypt */
        {
            return true;
        }

        break;

    case PROTOCOL_COMMAND_CALL_ME_BACK:
        sscanf(recvbuffer, "SCALLBACK %u", &len);

        if ((len >= sizeof(out)) || (received != (len + CF_PROTO_OFFSET)))
        {
            Log(LOG_LEVEL_INFO, "Decrypt error CALL_ME_BACK");
            RefuseAccess(conn, "decrypt error CALL_ME_BACK");
            return true;
        }

        memcpy(out, recvbuffer + CF_PROTO_OFFSET, len);
        plainlen = DecryptString(conn->encryption_type, out, recvbuffer, conn->session_key, len);

        if (strncmp(recvbuffer, "CALL_ME_BACK collect_calls", strlen("CALL_ME_BACK collect_calls")) != 0)
        {
            Log(LOG_LEVEL_INFO, "CALL_ME_BACK protocol defect");
            RefuseAccess(conn, "decryption failure");
            return false;
        }

        if (!LiteralAccessControl(ctx, recvbuffer, conn, true))
        {
            Log(LOG_LEVEL_INFO, "Query access failure");
            RefuseAccess(conn, recvbuffer);
            return false;
        }

        if (ReceiveCollectCall(conn))
        {
            return true;
        }

    case PROTOCOL_COMMAND_AUTH_PLAIN:
    case PROTOCOL_COMMAND_AUTH_SECURE:
    case PROTOCOL_COMMAND_AUTH:
    case PROTOCOL_COMMAND_CONTEXTS:
    case PROTOCOL_COMMAND_BAD:
        Log(LOG_LEVEL_WARNING, "Unexpected protocol command");
    }

    strcpy(sendbuffer, "BAD: Request denied");
    SendTransaction(conn->conn_info, sendbuffer, 0, CF_DONE);
    Log(LOG_LEVEL_INFO, "Closing connection, due to request: '%s'", recvbuffer);
    return false;
}
Example #2
0
static void test_command_parser(void)
{
    ProtocolCommandClassic parsed = PROTOCOL_COMMAND_BAD;
    ProtocolCommandClassic expected = PROTOCOL_COMMAND_BAD;
    /*
     * Test all the commands, one by one.
     */
    // EXEC
    expected = PROTOCOL_COMMAND_EXEC;
    parsed = GetCommandClassic("EXEC");
    assert_int_equal(expected, parsed);
    // AUTH
    expected = PROTOCOL_COMMAND_AUTH;
    parsed = GetCommandClassic("AUTH");
    assert_int_equal(expected, parsed);
    // GET
    expected = PROTOCOL_COMMAND_GET;
    parsed = GetCommandClassic("GET");
    assert_int_equal(expected, parsed);
    // OPENDIR
    expected = PROTOCOL_COMMAND_OPENDIR;
    parsed = GetCommandClassic("OPENDIR");
    assert_int_equal(expected, parsed);
    // SYNCH
    expected = PROTOCOL_COMMAND_SYNC;
    parsed = GetCommandClassic("SYNCH");
    assert_int_equal(expected, parsed);
    // CLASSES
    expected = PROTOCOL_COMMAND_CONTEXTS;
    parsed = GetCommandClassic("CLASSES");
    assert_int_equal(expected, parsed);
    // MD5
    expected = PROTOCOL_COMMAND_MD5;
    parsed = GetCommandClassic("MD5");
    assert_int_equal(expected, parsed);
    // SMD5
    expected = PROTOCOL_COMMAND_MD5_SECURE;
    parsed = GetCommandClassic("SMD5");
    assert_int_equal(expected, parsed);
    // CAUTH
    expected = PROTOCOL_COMMAND_AUTH_CLEAR;
    parsed = GetCommandClassic("CAUTH");
    assert_int_equal(expected, parsed);
    // SAUTH
    expected = PROTOCOL_COMMAND_AUTH_SECURE;
    parsed = GetCommandClassic("SAUTH");
    assert_int_equal(expected, parsed);
    // SSYNCH
    expected = PROTOCOL_COMMAND_SYNC_SECURE;
    parsed = GetCommandClassic("SSYNCH");
    assert_int_equal(expected, parsed);
    // SGET
    expected = PROTOCOL_COMMAND_GET_SECURE;
    parsed = GetCommandClassic("SGET");
    assert_int_equal(expected, parsed);
    // VERSION
    expected = PROTOCOL_COMMAND_VERSION;
    parsed = GetCommandClassic("VERSION");
    assert_int_equal(expected, parsed);
    // SOPENDIR
    expected = PROTOCOL_COMMAND_OPENDIR_SECURE;
    parsed = GetCommandClassic("SOPENDIR");
    assert_int_equal(expected, parsed);
    // VAR
    expected = PROTOCOL_COMMAND_VAR;
    parsed = GetCommandClassic("VAR");
    assert_int_equal(expected, parsed);
    // SVAR
    expected = PROTOCOL_COMMAND_VAR_SECURE;
    parsed = GetCommandClassic("SVAR");
    assert_int_equal(expected, parsed);
    // CONTEXT
    expected = PROTOCOL_COMMAND_CONTEXT;
    parsed = GetCommandClassic("CONTEXT");
    assert_int_equal(expected, parsed);
    // SCONTEXT
    expected = PROTOCOL_COMMAND_CONTEXT_SECURE;
    parsed = GetCommandClassic("SCONTEXT");
    assert_int_equal(expected, parsed);
    // SQUERY
    expected = PROTOCOL_COMMAND_QUERY_SECURE;
    parsed = GetCommandClassic("SQUERY");
    assert_int_equal(expected, parsed);
    // SCALLBACK
    expected = PROTOCOL_COMMAND_CALL_ME_BACK;
    parsed = GetCommandClassic("SCALLBACK");
    assert_int_equal(expected, parsed);
    /*
     * Try using lowercase
     */
    // EXEC
    expected = PROTOCOL_COMMAND_BAD;
    parsed = GetCommandClassic("exec");
    assert_int_equal(expected, parsed);
    // AUTH
    parsed = GetCommandClassic("auth");
    assert_int_equal(expected, parsed);
    // GET
    parsed = GetCommandClassic("get");
    assert_int_equal(expected, parsed);
    // OPENDIR
    parsed = GetCommandClassic("opendir");
    assert_int_equal(expected, parsed);
    // SYNCH
    parsed = GetCommandClassic("synch");
    assert_int_equal(expected, parsed);
    // CLASSES
    parsed = GetCommandClassic("classes");
    assert_int_equal(expected, parsed);
    // MD5
    parsed = GetCommandClassic("md5");
    assert_int_equal(expected, parsed);
    // SMD5
    parsed = GetCommandClassic("smd5");
    assert_int_equal(expected, parsed);
    // CAUTH
    parsed = GetCommandClassic("cauth");
    assert_int_equal(expected, parsed);
    // SAUTH
    parsed = GetCommandClassic("sauth");
    assert_int_equal(expected, parsed);
    // SSYNCH
    parsed = GetCommandClassic("synch");
    assert_int_equal(expected, parsed);
    // SGET
    parsed = GetCommandClassic("sget");
    assert_int_equal(expected, parsed);
    // VERSION
    parsed = GetCommandClassic("version");
    assert_int_equal(expected, parsed);
    // SOPENDIR
    parsed = GetCommandClassic("sopendir");
    assert_int_equal(expected, parsed);
    // VAR
    parsed = GetCommandClassic("var");
    assert_int_equal(expected, parsed);
    // SVAR
    parsed = GetCommandClassic("svar");
    assert_int_equal(expected, parsed);
    // CONTEXT
    parsed = GetCommandClassic("context");
    assert_int_equal(expected, parsed);
    // SCONTEXT
    parsed = GetCommandClassic("scontext");
    assert_int_equal(expected, parsed);
    // SQUERY
    parsed = GetCommandClassic("squery");
    assert_int_equal(expected, parsed);
    // SCALLBACK
    parsed = GetCommandClassic("scallback");
    assert_int_equal(expected, parsed);
    /*
     * Try the commands with something in front
     */
    // EXEC
    expected = PROTOCOL_COMMAND_BAD;
    parsed = GetCommandClassic("eEXEC");
    assert_int_equal(expected, parsed);
    // AUTH
    parsed = GetCommandClassic("aAUTH");
    assert_int_equal(expected, parsed);
    // GET
    parsed = GetCommandClassic("gGET");
    assert_int_equal(expected, parsed);
    // OPENDIR
    parsed = GetCommandClassic("oOPENDIR");
    assert_int_equal(expected, parsed);
    // SYNCH
    parsed = GetCommandClassic("sSYNCH");
    assert_int_equal(expected, parsed);
    // CLASSES
    parsed = GetCommandClassic("cCLASSES");
    assert_int_equal(expected, parsed);
    // MD5
    parsed = GetCommandClassic("mMD5");
    assert_int_equal(expected, parsed);
    // SMD5
    parsed = GetCommandClassic("sMD5");
    assert_int_equal(expected, parsed);
    // CAUTH
    parsed = GetCommandClassic("cCAUTH");
    assert_int_equal(expected, parsed);
    // SAUTH
    parsed = GetCommandClassic("sSAUTH");
    assert_int_equal(expected, parsed);
    // SSYNCH
    parsed = GetCommandClassic("sSSYNCH");
    assert_int_equal(expected, parsed);
    // SGET
    parsed = GetCommandClassic("sSGET");
    assert_int_equal(expected, parsed);
    // VERSION
    parsed = GetCommandClassic("vVERSION");
    assert_int_equal(expected, parsed);
    // SOPENDIR
    parsed = GetCommandClassic("sSOPENDIR");
    assert_int_equal(expected, parsed);
    // VAR
    parsed = GetCommandClassic("vVAR");
    assert_int_equal(expected, parsed);
    // SVAR
    parsed = GetCommandClassic("sSVAR");
    assert_int_equal(expected, parsed);
    // CONTEXT
    parsed = GetCommandClassic("cCONTEXT");
    assert_int_equal(expected, parsed);
    // SCONTEXT
    parsed = GetCommandClassic("sSCONTEXT");
    assert_int_equal(expected, parsed);
    // SQUERY
    parsed = GetCommandClassic("sSQUERY");
    assert_int_equal(expected, parsed);
    // SCALLBACK
    parsed = GetCommandClassic("sSCALLBACK");
    assert_int_equal(expected, parsed);
    /*
     * Try the commands with something after them
     */
    // EXEC
    expected = PROTOCOL_COMMAND_BAD;
    parsed = GetCommandClassic("EXECx");
    assert_int_equal(expected, parsed);
    // AUTH
    parsed = GetCommandClassic("AUTHx");
    assert_int_equal(expected, parsed);
    // GET
    parsed = GetCommandClassic("GETx");
    assert_int_equal(expected, parsed);
    // OPENDIR
    parsed = GetCommandClassic("OPENDIRx");
    assert_int_equal(expected, parsed);
    // SYNCH
    parsed = GetCommandClassic("SYNCHx");
    assert_int_equal(expected, parsed);
    // CLASSES
    parsed = GetCommandClassic("CLASSESx");
    assert_int_equal(expected, parsed);
    // MD5
    parsed = GetCommandClassic("MD5x");
    assert_int_equal(expected, parsed);
    // SMD5
    parsed = GetCommandClassic("SMD5x");
    assert_int_equal(expected, parsed);
    // CAUTH
    parsed = GetCommandClassic("CAUTHx");
    assert_int_equal(expected, parsed);
    // SAUTH
    parsed = GetCommandClassic("SAUTHx");
    assert_int_equal(expected, parsed);
    // SSYNCH
    parsed = GetCommandClassic("SSYNCHx");
    assert_int_equal(expected, parsed);
    // SGET
    parsed = GetCommandClassic("SGETx");
    assert_int_equal(expected, parsed);
    // VERSION
    parsed = GetCommandClassic("VERSIONx");
    assert_int_equal(expected, parsed);
    // SOPENDIR
    parsed = GetCommandClassic("SOPENDIRx");
    assert_int_equal(expected, parsed);
    // VAR
    parsed = GetCommandClassic("VARx");
    assert_int_equal(expected, parsed);
    // SVAR
    parsed = GetCommandClassic("SVARx");
    assert_int_equal(expected, parsed);
    // CONTEXT
    parsed = GetCommandClassic("CONTEXTx");
    assert_int_equal(expected, parsed);
    // SCONTEXT
    parsed = GetCommandClassic("SCONTEXTx");
    assert_int_equal(expected, parsed);
    // SQUERY
    parsed = GetCommandClassic("SQUERYx");
    assert_int_equal(expected, parsed);
    // SCALLBACK
    parsed = GetCommandClassic("SCALLBACKx");
    assert_int_equal(expected, parsed);
    /*
     * Try some common mispellings.
     */
    // EXEC
    expected = PROTOCOL_COMMAND_BAD;
    parsed = GetCommandClassic("EXE");
    assert_int_equal(expected, parsed);
    parsed = GetCommandClassic("EXECUTE");
    assert_int_equal(expected, parsed);
    // AUTH
    parsed = GetCommandClassic("AUTHORIZATION");
    assert_int_equal(expected, parsed);
    // SYNCH
    parsed = GetCommandClassic("SYNC");
    assert_int_equal(expected, parsed);
    parsed = GetCommandClassic("SYNCHRONIZE");
    assert_int_equal(expected, parsed);
    // CLASSES
    parsed = GetCommandClassic("CLASS");
    assert_int_equal(expected, parsed);
    // CAUTH
    parsed = GetCommandClassic("CAUTHORIZATION");
    assert_int_equal(expected, parsed);
    // SAUTH
    parsed = GetCommandClassic("SAUTHORIZATION");
    assert_int_equal(expected, parsed);
    // SSYNCH
    parsed = GetCommandClassic("SSYNCHRONIZE");
    assert_int_equal(expected, parsed);
    parsed = GetCommandClassic("SSYNC");
    assert_int_equal(expected, parsed);
    // VERSION
    parsed = GetCommandClassic("V");
    assert_int_equal(expected, parsed);
    // VAR
    parsed = GetCommandClassic("VARIABLE");
    assert_int_equal(expected, parsed);
    // SVAR
    parsed = GetCommandClassic("SVARIABLE");
    assert_int_equal(expected, parsed);
    /*
     * Finally, try the commands with a space and something else, they should be recognized"
     */
    // EXEC
    expected = PROTOCOL_COMMAND_EXEC;
    parsed = GetCommandClassic("EXEC 123");
    assert_int_equal(expected, parsed);
    // AUTH
    expected = PROTOCOL_COMMAND_AUTH;
    parsed = GetCommandClassic("AUTH 123");
    assert_int_equal(expected, parsed);
    // GET
    expected = PROTOCOL_COMMAND_GET;
    parsed = GetCommandClassic("GET 123");
    assert_int_equal(expected, parsed);
    // OPENDIR
    expected = PROTOCOL_COMMAND_OPENDIR;
    parsed = GetCommandClassic("OPENDIR 123");
    assert_int_equal(expected, parsed);
    // SYNCH
    expected = PROTOCOL_COMMAND_SYNC;
    parsed = GetCommandClassic("SYNCH 123");
    assert_int_equal(expected, parsed);
    // CLASSES
    expected = PROTOCOL_COMMAND_CONTEXTS;
    parsed = GetCommandClassic("CLASSES 123");
    assert_int_equal(expected, parsed);
    // MD5
    expected = PROTOCOL_COMMAND_MD5;
    parsed = GetCommandClassic("MD5 123");
    assert_int_equal(expected, parsed);
    // SMD5
    expected = PROTOCOL_COMMAND_MD5_SECURE;
    parsed = GetCommandClassic("SMD5 123");
    assert_int_equal(expected, parsed);
    // CAUTH
    expected = PROTOCOL_COMMAND_AUTH_CLEAR;
    parsed = GetCommandClassic("CAUTH 123");
    assert_int_equal(expected, parsed);
    // SAUTH
    expected = PROTOCOL_COMMAND_AUTH_SECURE;
    parsed = GetCommandClassic("SAUTH 123");
    assert_int_equal(expected, parsed);
    // SSYNCH
    expected = PROTOCOL_COMMAND_SYNC_SECURE;
    parsed = GetCommandClassic("SSYNCH 123");
    assert_int_equal(expected, parsed);
    // SGET
    expected = PROTOCOL_COMMAND_GET_SECURE;
    parsed = GetCommandClassic("SGET 123");
    assert_int_equal(expected, parsed);
    // VERSION
    expected = PROTOCOL_COMMAND_VERSION;
    parsed = GetCommandClassic("VERSION 123");
    assert_int_equal(expected, parsed);
    // SOPENDIR
    expected = PROTOCOL_COMMAND_OPENDIR_SECURE;
    parsed = GetCommandClassic("SOPENDIR 123");
    assert_int_equal(expected, parsed);
    // VAR
    expected = PROTOCOL_COMMAND_VAR;
    parsed = GetCommandClassic("VAR 123");
    assert_int_equal(expected, parsed);
    // SVAR
    expected = PROTOCOL_COMMAND_VAR_SECURE;
    parsed = GetCommandClassic("SVAR 123");
    assert_int_equal(expected, parsed);
    // CONTEXT
    expected = PROTOCOL_COMMAND_CONTEXT;
    parsed = GetCommandClassic("CONTEXT 123");
    assert_int_equal(expected, parsed);
    // SCONTEXT
    expected = PROTOCOL_COMMAND_CONTEXT_SECURE;
    parsed = GetCommandClassic("SCONTEXT 123");
    assert_int_equal(expected, parsed);
    // SQUERY
    expected = PROTOCOL_COMMAND_QUERY_SECURE;
    parsed = GetCommandClassic("SQUERY 123");
    assert_int_equal(expected, parsed);
    // SCALLBACK
    expected = PROTOCOL_COMMAND_CALL_ME_BACK;
    parsed = GetCommandClassic("SCALLBACK 123");
    assert_int_equal(expected, parsed);
}