Example #1
0
/*
 * After a query, server sends a list of column name with their types
 *
 * | 1 byte | 1 byte                | 0-8 bytes     | variable bytes           |
 * | Length | Size number of fields | Number fields | unknown flags and fields |
 */
static enum proto_parse_status tns_parse_row_description_prefix(struct tns_parser *tns_parser, struct sql_proto_info *info, struct cursor *cursor)
{
    enum proto_parse_status status;
    SLOG(LOG_DEBUG, "Parsing row description prefix");

    CHECK(1);
    unsigned length = cursor_read_u8(cursor);
    DROP_FIX(cursor, length);
    DROP_VAR(cursor);

    if (PROTO_OK != (status = read_field_count(tns_parser, info, cursor))) return status;

    DROP_FIX(cursor, 1);
    for (unsigned i = 0; i < info->u.query.nb_fields; i++) {
        DROP_FIX(cursor, 3);
        DROP_VARS(cursor, 4);
        DROP_DALC(cursor);
        DROP_VARS(cursor, 2);
        DROP_FIX(cursor, 1);
        DROP_VAR(cursor);
        DROP_FIX(cursor, 2);
        for (unsigned i = 0; i < 3; ++i) {
            DROP_DALC(cursor);
        }
        DROP_VAR(cursor);
    }
    DROP_DALC(cursor);
    DROP_VARS(cursor, 2);
    return PROTO_OK;
}
Example #2
0
static enum proto_parse_status tns_parse_sql_query_jdbc(struct sql_proto_info *info, struct cursor *cursor)
{
    SLOG(LOG_DEBUG, "Parsing a jdbc query");
    enum proto_parse_status status;

    DROP_FIX(cursor, 1);

    uint_least64_t sql_len;
    status = cursor_read_variable_int(cursor, &sql_len);
    if (status != PROTO_OK) return status;
    SLOG(LOG_DEBUG, "Size sql %zu", sql_len);

    DROP_FIX(cursor, 1);
    // We have a number of fields at the end of the query
    uint_least64_t end_len;
    status = cursor_read_variable_int(cursor, &end_len);
    if (status != PROTO_OK) return status;

    DROP_FIX(cursor, 2);
    DROP_VARS(cursor, 3);
    DROP_FIX(cursor, 1);
    DROP_VAR(cursor);
    DROP_FIX(cursor, 6);
    DROP_VAR(cursor);

    char *sql = "";
    if (sql_len > 0) {
        // Some unknown bytes
        while (cursor->cap_len > 1 && !isprint(cursor_peek_u8(cursor, 1))) {
            cursor_drop(cursor, 1);
        }

        CHECK(1);
        if (sql_len > 0xff && 0xff == cursor_peek_u8(cursor, 0)) {
            SLOG(LOG_DEBUG, "Looks like prefixed length chunks of size 0xff...");
            status = cursor_read_chunked_string(cursor, &sql, 0xff);
        } else if (sql_len > 0x40 && 0x40 == cursor_peek_u8(cursor, 1)) {
            SLOG(LOG_DEBUG, "Looks like prefixed length chunks of size 0x40...");
            cursor_drop(cursor, 1);
            status = cursor_read_chunked_string(cursor, &sql, 0x40);
        } else {
            if (!isprint(cursor_peek_u8(cursor, 0))) {
                cursor_drop(cursor, 1);
            }
            SLOG(LOG_DEBUG, "Looks like a fixed string of size %zu", sql_len);
            status = cursor_read_fixed_string(cursor, &sql, sql_len);
        }
        if (status != PROTO_OK) return status;
    }
    SLOG(LOG_DEBUG, "Sql parsed: %s", sql);
    sql_set_query(info, "%s", sql);

    SLOG(LOG_DEBUG, "Skipping %zu end variable fields", end_len);
    DROP_VARS(cursor, end_len);
    return PROTO_OK;
}
Example #3
0
/*
 * | 1 byte | (length + 1) * variable | 1 + 0-8 bytes | nb_ignore * variable | Variable until new ttc |
 * | length | ?                       | Nb ignore     | ?                    | ?                      |
 */
static enum proto_parse_status tns_parse_row_description(struct cursor *cursor)
{
    enum proto_parse_status status;
    SLOG(LOG_DEBUG, "Parsing a row description");

    uint_least64_t length;
    status = cursor_read_variable_int(cursor, &length);
    if (status != PROTO_OK) return status;
    DROP_VARS(cursor, length);
    DROP_VAR(cursor);

    uint_least64_t nb_ignore;
    status = cursor_read_variable_int(cursor, &nb_ignore);
    if (status != PROTO_OK) return status;
    for (unsigned i = 0; i < nb_ignore; i++) {
        DROP_VAR(cursor);
        DROP_DALC(cursor);
        DROP_VAR(cursor);
    }
    CHECK(1);
    // Sometimes, we have some strange bytes...
    while (*cursor->head < 0x03 || *cursor->head > 0x15) {
        CHECK(2);
        DROP_FIX(cursor, 1);
    }
    return PROTO_OK;
}
Example #4
0
static enum proto_parse_status tns_parse_sql_query_oci(struct sql_proto_info *info, struct cursor *cursor)
{
    SLOG(LOG_DEBUG, "Parsing an oci query");
    enum proto_parse_status status;
    char pattern[] = {0xfe, MAX_OCI_CHUNK};
    uint8_t const *new_head = memmem(cursor->head, cursor->cap_len, pattern, sizeof(pattern));
    if (new_head != NULL) {
        size_t gap_size = new_head - cursor->head;
        SLOG(LOG_DEBUG, "%zu bytes before sql", gap_size);
        DROP_FIX(cursor, gap_size + 1);
    } else {
        SLOG(LOG_DEBUG, "{0xfe 0x40} not found, size might be < 0x40");
        if (!lookup_query(cursor)) return PROTO_PARSE_ERR;
    }

    char *sql;
    if (PROTO_OK != (status = cursor_read_chunked_string(cursor, &sql, MAX_OCI_CHUNK))) return status;
    SLOG(LOG_DEBUG, "Sql parsed: %s", sql);
    sql_set_query(info, "%s", sql);

    // Drop the rest
    if(cursor->cap_len > 0) cursor_drop(cursor, cursor->cap_len - 1);

    return PROTO_OK;
}
Example #5
0
static enum proto_parse_status tns_parse_close_statement(struct cursor *cursor)
{
    SLOG(LOG_DEBUG, "Parsing a close statement");
    // Subcode
    DROP_FIX(cursor, 1);
    // Sequence
    DROP_FIX(cursor, 1);
    // Pointer
    DROP_FIX(cursor, 1);

    // We seek the next query
    uint8_t marker[2] = {0x03, 0x5e};
    if (cursor_drop_until(cursor, marker, sizeof(marker), cursor->cap_len) < 0) return PROTO_PARSE_ERR;

    SLOG(LOG_DEBUG, "Found a possible query ttc, exiting close statement");
    return PROTO_OK;
}
Example #6
0
static enum proto_parse_status tns_parse_sql_query(struct sql_proto_info *info, struct cursor *cursor)
{
    DROP_FIX(cursor, 1);

    if (is_oci(cursor)) {
        // Option is not prefix based, seems like an oci query
        return tns_parse_sql_query_oci(info, cursor);
    } else {
        return tns_parse_sql_query_jdbc(info, cursor);
    }
}
Example #7
0
static enum proto_parse_status tns_parse_login_property(struct sql_proto_info *info, struct cursor *cursor)
{
    SLOG(LOG_DEBUG, "Parsing tns login property");
    // We are only interested in response
    if (info->is_query) return PROTO_OK;
    if (info->msg_type != SQL_UNKNOWN && info->msg_type != SQL_STARTUP) return PROTO_PARSE_ERR;

    // Drop Server version
    DROP_FIX(cursor, 3);
    // Drop Server version text
    uint8_t marker = 0x00;
    enum proto_parse_status status = cursor_drop_until(cursor, &marker, sizeof(marker));
    if (status != PROTO_OK) return status;
    // Drop Null byte
    DROP_FIX(cursor, 1);
    CHECK(2);
    uint16_t charset = cursor_read_u16le(cursor);
    SLOG(LOG_DEBUG, "Found a charset of 0x%02x", charset);
    switch (charset) {
        case 0x01:
        case 0x02:
        case 0x1f:
        case 0xb2:
            sql_set_encoding(info, SQL_ENCODING_LATIN1);
            break;
        case 0x366:
        case 0x367:
        case 0x369:
            sql_set_encoding(info, SQL_ENCODING_UTF8);
            break;
        default:
            SLOG(LOG_DEBUG, "Unknown charset");
            break;
    }
    // We don't care of the rest...
    cursor_drop(cursor, cursor->cap_len);
    return PROTO_OK;
}
Example #8
0
/*
 * | 1 byte | 1 + 0-8 bytes | up to 5 vars |
 * | Flag   | Number column | unknown      |
 */
static enum proto_parse_status tns_parse_row_prefix(struct tns_parser *tns_parser,
        struct sql_proto_info *info, struct cursor *cursor)
{
    enum proto_parse_status status;
    SLOG(LOG_DEBUG, "Parsing Row prefix");

    DROP_FIX(cursor, 1);
    if (PROTO_OK != (status = read_field_count(tns_parser, info, cursor))) return status;
    for (unsigned i = 0; i < 5; i++) {
        CHECK(1);
        char c = cursor_peek_u8(cursor, 0);
        if (c == TTC_ROW_DATA || c == TTC_END_MESSAGE) return PROTO_OK;
        DROP_VAR(cursor);
    }
    return PROTO_OK;
}
Example #9
0
static enum proto_parse_status tns_parse_row_recap(struct cursor *cursor)
{
    enum proto_parse_status status;
    SLOG(LOG_DEBUG, "Parsing Row recap");

    /* A row recap contains :
     * - 1 var number of column sent
     * - <number of fields> bytes to ignore
     */
    uint_least64_t num_fields;
    status = cursor_read_variable_int(cursor, &num_fields);
    if (status != PROTO_OK) return status;
    unsigned nb_ignore = (num_fields + 7) / 8;
    DROP_FIX(cursor, nb_ignore);
    return PROTO_OK;
}
Example #10
0
static enum proto_parse_status tns_parse_row_prefix(struct tns_parser *tns_parser,
        struct sql_proto_info *info, struct cursor *cursor)
{
    enum proto_parse_status status;
    SLOG(LOG_DEBUG, "Parsing Row prefix");

    /* A row prefix contains
     * - 1 byte flag
     * - Number column
     * - 5 var
     */
    DROP_FIX(cursor, 1);
    if (PROTO_OK != (status = read_field_count(tns_parser, info, cursor))) return status;
    DROP_VARS(cursor, 5);
    return PROTO_OK;
}
Example #11
0
static enum proto_parse_status tns_parse_sql_query(struct sql_proto_info *info, struct cursor *cursor)
{
    DROP_FIX(cursor, 1);
    if (is_oci(cursor)) {
        // Option is not prefix based, seems like an oci query
        return tns_parse_sql_query_oci(info, cursor);
    } else {
        struct cursor save_cursor = *cursor;
        if (tns_parse_sql_query_jdbc(info, cursor) != PROTO_OK) {
            // Fallback to query guessing
            SLOG(LOG_DEBUG, "jdbc query failed, fallback to oci");
            *cursor = save_cursor;
            return tns_parse_sql_query_oci(info, cursor);
        } else {
            return PROTO_OK;
        }
    }
}
Example #12
0
/*
 *  Sometimes, we have up to 10 {0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff} patterns.
 *  Skip them to avoid breaking on an eventual 0x07 (TTC_ROW_DATA)
 *  The query size seems to be at the end of the first pattern
 */
static uint8_t parse_query_header(struct cursor *cursor)
{
    uint8_t const *new_head = cursor->head;
    uint8_t query_size = 0;
    for (unsigned i = 0; i < 10 && new_head; i++) {
        char pattern[8] = {0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
        new_head = memmem(cursor->head, cursor->cap_len, pattern, sizeof(pattern));
        if (new_head) {
            size_t gap_size = new_head - cursor->head;
            DROP_FIX(cursor, gap_size + sizeof(pattern));
            if (i == 0) {
                CHECK(1);
                query_size = cursor_read_u8(cursor);
                SLOG(LOG_DEBUG, "Found potential query size: %d", query_size);
            }
        }
    };
    return query_size;
}
Example #13
0
static enum proto_parse_status tns_parse_sql_query_oci(struct sql_proto_info *info, struct cursor *cursor)
{
    SLOG(LOG_DEBUG, "Parsing an oci query");
    uint8_t query_size = parse_query_header(cursor);
    struct query_candidate candidate = {.num_candidate_size = 0};
    if (query_size > 0) {
        candidate.candidate_sizes[candidate.num_candidate_size++] = query_size;
    }

    bool has_query = lookup_query(cursor, &candidate);
    info->u.query.sql[0] = '\0';
    info->u.query.truncated = 0;
    if (has_query) {
        SLOG(LOG_DEBUG, "Found a query, parsing it");
        if (candidate.is_chunked) {
            cursor_read_chunked_string(cursor, info->u.query.sql, sizeof(info->u.query.sql), MAX_OCI_CHUNK);
        } else {
            cursor_read_fixed_string(cursor, info->u.query.sql, sizeof(info->u.query.sql), candidate.query_size);
        }
    }
    SLOG(LOG_DEBUG, "Sql parsed: %s", info->u.query.sql);
    info->set_values |= SQL_SQL;
    // Drop the rest
    if(cursor->cap_len > 0) cursor_drop(cursor, cursor->cap_len - 1);
    return PROTO_OK;
}

/*
 * | 1 byte | 1 + 0-8 bytes | 1 byte | 1 + 0-4 bytes  | Lots of unknown bytes | variable  |
 * | Unk    | Sql len       | Unk    | Num end fields | Unk                   | sql query |
 */
static enum proto_parse_status tns_parse_sql_query_jdbc(struct sql_proto_info *info, struct cursor *cursor)
{
    SLOG(LOG_DEBUG, "Parsing a jdbc query");
    enum proto_parse_status status = PROTO_OK;

    DROP_FIX(cursor, 1);

    uint_least64_t sql_len;
    if (PROTO_OK != (status = cursor_read_variable_int(cursor, &sql_len))) return status;
    SLOG(LOG_DEBUG, "Size sql %zu", sql_len);

    DROP_FIX(cursor, 1);
    DROP_VAR(cursor);
    DROP_FIX(cursor, 2);

    info->u.query.sql[0] = '\0';
    info->u.query.truncated = 0; // TODO Handle truncated
    if (sql_len > 0) {
        // Some unknown bytes
        while (cursor->cap_len > 1 && PROTO_OK != is_range_print(cursor, MIN(MIN_QUERY_SIZE, sql_len), 1)) {
            // TODO drop to the first non printable
            cursor_drop(cursor, 1);
        }
        CHECK(1);
        if (sql_len > 0xff && 0xff == cursor_peek_u8(cursor, 0)) {
            SLOG(LOG_DEBUG, "Looks like prefixed length chunks of size 0xff...");
            status = cursor_read_chunked_string(cursor, info->u.query.sql, sizeof(info->u.query.sql), 0xff);
        } else if (sql_len > MAX_OCI_CHUNK && MAX_OCI_CHUNK == cursor_peek_u8(cursor, 0)) {
            SLOG(LOG_DEBUG, "Looks like prefixed length chunks of size 0x40...");
            status = cursor_read_chunked_string(cursor, info->u.query.sql, sizeof(info->u.query.sql),
                    MAX_OCI_CHUNK);
        } else {
            // We don't care about the first non printable character
            cursor_drop(cursor, 1);
            CHECK(1);
            // In rare occurrence where sql_len == first character, we check the byte after the expected query,
            // if it's printable, the first character is probably the prefixed size.
            if (cursor_peek_u8(cursor, 0) == sql_len && sql_len < cursor->cap_len && is_print(cursor_peek_u8(cursor, sql_len)))
                cursor_drop(cursor, 1);
            SLOG(LOG_DEBUG, "Looks like a fixed string of size %zu", sql_len);
            int written_bytes = cursor_read_fixed_string(cursor, info->u.query.sql,
                    sizeof(info->u.query.sql), sql_len);
            if (written_bytes < 0) return PROTO_TOO_SHORT;
        }
        if (status != PROTO_OK) return status;
    }
    SLOG(LOG_DEBUG, "Sql parsed: %s", info->u.query.sql);
    info->set_values |= SQL_SQL;

    return PROTO_OK;
}
Example #14
0
static enum proto_parse_status tns_parse_end(struct sql_proto_info *info, struct cursor *cursor)
{
    SLOG(LOG_DEBUG, "Parsing tns end packet");
    enum proto_parse_status status;

    uint_least64_t var[6];
    for (unsigned i = 0; i < 6; i++) {
        if (PROTO_OK != (status = cursor_read_variable_int(cursor, var + i))) return status;
    }

    uint_least64_t nb_rows;
    uint_least64_t error_code;

    // let's use the double 0x00 to guess the position of row number and error code
    if (var[0] > 0 && var[4] == 0 && var[5] == 0) {
        // var[0] is unknown?
        // var[1] == sequence
        // var[2] == rows
        // var[3] == error code
        SLOG(LOG_DEBUG, "Unknown bits after ttc code");
        nb_rows = var[2];
        error_code = var[3];
        DROP_VAR(cursor);
    } else if (var[3] == 0 && var[4] == 0) {
        // var[0] == sequence
        // var[1] == rows
        // var[2] == error code
        nb_rows = var[1];
        error_code = var[2];
    } else {
        // var[0] == rows
        // var[1] == error code
        nb_rows = var[0];
        error_code = var[1];
    }

    if (info->msg_type == SQL_QUERY) {
        sql_set_row_count(info, nb_rows);
        SLOG(LOG_DEBUG, "Nb rows %d", info->u.query.nb_rows);
    }
    SLOG(LOG_DEBUG, "Error code is %zu", error_code);

    DROP_VARS(cursor, 1);
    DROP_FIX(cursor, 2);
    DROP_VARS(cursor, 2);
    DROP_FIX(cursor, 2);
    DROP_VARS(cursor, 2);
    DROP_FIX(cursor, 1);
    DROP_VARS(cursor, 3);

    if (error_code != 0) {
        SLOG(LOG_DEBUG, "Parsing error message");
        char *error_msg = tempstr();
        unsigned error_len;

        // Drop an unknown number of bytes here
        while(cursor->cap_len > 2 && (cursor_peek_u8(cursor, 0) == 0 || !is_print(cursor_peek_u8(cursor, 1)))){
            DROP_FIX(cursor, 1);
        }
        SLOG(LOG_DEBUG, "First printable char is %c", cursor_peek_u8(cursor, 1));
        status = cursor_read_variable_string(cursor, error_msg, TEMPSTR_SIZE, &error_len);
        if (status != PROTO_OK) return status;
        if (error_len < 12) return PROTO_PARSE_ERR;
        // Split "ORA-XXXX: msg"
        // Part before : is the error code
        // Part after is the localized message
        char *colon_pos = memchr(error_msg, ':', error_len);
        sql_set_request_status(info, SQL_REQUEST_ERROR);
        if (colon_pos) {
            // We extract the error code
            unsigned error_code_size = colon_pos - error_msg;
            int size_err = MIN(error_code_size, sizeof(info->error_code));
            memcpy(info->error_code, error_msg, size_err);
            info->error_code[size_err] = '\0';
            info->set_values |= SQL_ERROR_CODE;
            if (0 == strcmp("ORA-01403", info->error_code))
                info->request_status = SQL_REQUEST_COMPLETE;

            // We skip ':' in errror message
            char const *start_message = colon_pos + 1;
            // We skip spaces before errror message
            while (start_message < error_len + error_msg && *start_message == ' ')
                start_message++;

            copy_string(info->error_message, start_message, sizeof(info->error_message));
            info->set_values |= SQL_ERROR_MESSAGE;
        } else {
            copy_string(info->error_message, error_msg, sizeof(info->error_message));
            info->set_values |= SQL_ERROR_MESSAGE;
        }
    }

    return PROTO_OK;
}
Example #15
0
static enum proto_parse_status tns_parse_end(struct sql_proto_info *info, struct cursor *cursor)
{
    SLOG(LOG_DEBUG, "Parsing tns end packet");
    enum proto_parse_status status;

    uint_least64_t var0;
    uint_least64_t var1;
    uint_least64_t var2;
    uint_least64_t var3;
    uint_least64_t var4;
    uint_least64_t var5;
    if (PROTO_OK != (status = cursor_read_variable_int(cursor, &var0))) return status;
    if (PROTO_OK != (status = cursor_read_variable_int(cursor, &var1))) return status;
    if (PROTO_OK != (status = cursor_read_variable_int(cursor, &var2))) return status;
    if (PROTO_OK != (status = cursor_read_variable_int(cursor, &var3))) return status;
    if (PROTO_OK != (status = cursor_read_variable_int(cursor, &var4))) return status;
    if (PROTO_OK != (status = cursor_read_variable_int(cursor, &var5))) return status;

    uint_least64_t nb_rows;
    uint_least64_t error_code;

    if (var0 != 0 && var4 == 0 && var5 == 0) {
        // First var is unknown?
        SLOG(LOG_DEBUG, "Unknown bits after ttc code");
        nb_rows = var2;
        error_code = var3;
        DROP_VAR(cursor);
    } else {
        // var0 == sequence
        // var1 == rows
        // var2 == error code
        nb_rows = var1;
        error_code = var2;
    }

    if (info->msg_type == SQL_QUERY) {
        sql_set_row_count(info, nb_rows);
        SLOG(LOG_DEBUG, "Nb rows %d", info->u.query.nb_rows);
    }
    SLOG(LOG_DEBUG, "Error code is %zu", error_code);

    DROP_VARS(cursor, 1);
    DROP_FIX(cursor, 2);
    DROP_VARS(cursor, 2);
    DROP_FIX(cursor, 2);
    DROP_VARS(cursor, 2);
    DROP_FIX(cursor, 1);
    DROP_VARS(cursor, 3);
    DROP_FIX(cursor, 2);
    DROP_VARS(cursor, 2);

    if (error_code != 0) {
        SLOG(LOG_DEBUG, "Parsing error message");
        char *error_msg;
        unsigned error_len;

        // Drop an unknown number of column here
        while(cursor->cap_len > 1 && !isprint(*(cursor->head + 1))){
            DROP_FIX(cursor, 1);
        }
        status = cursor_read_variable_string(cursor, &error_msg, &error_len);
        if (status != PROTO_OK) return status;
        // Split "ORA-XXXX: msg"
        // Part before : is the error code
        // Part after is the localized message
        char *colon_pos = memchr(error_msg, ':', error_len);
        sql_set_request_status(info, SQL_REQUEST_ERROR);
        if (colon_pos) {
            // We extract the error code
            unsigned error_code_size = colon_pos - error_msg;
            int size_err = MIN(error_code_size, sizeof(info->error_code));
            memcpy(info->error_code, error_msg, size_err);
            info->error_code[size_err] = '\0';
            info->set_values |= SQL_ERROR_CODE;
            if (0 == strcmp("ORA-01403", info->error_code))
                info->request_status = SQL_REQUEST_COMPLETE;

            // We skip ':' in errror message
            char const *start_message = colon_pos + 1;
            // We skip spaces before errror message
            while (start_message < error_len + error_msg && *start_message == ' ')
                start_message++;

            copy_string(info->error_message, start_message, sizeof(info->error_message));
            info->set_values |= SQL_ERROR_MESSAGE;
        } else {
            copy_string(info->error_message, error_msg, sizeof(info->error_message));
            info->set_values |= SQL_ERROR_MESSAGE;
        }
    }

    return PROTO_OK;
}