static enum proto_parse_status tns_parse_accept(struct tns_parser unused_ *tns_parser, struct sql_proto_info *info, struct cursor *cursor) { if (info->is_query) return PROTO_PARSE_ERR; SLOG(LOG_DEBUG, "Parsing TNS accept PDU of size %zu", cursor->cap_len); /* An accept message is constitued of : * - 2 bytes version * - 2 bytes service options * - 2 bytes session data unit size * - 2 bytes max transm. data unit size * - 2 bytes value of one * - 2 bytes data length * - 2 bytes data offset * - 1 byte connect flag 0 * - 1 byte connect flag 1 */ CHECK(10); unsigned version = cursor_read_u16n(cursor); info->version_maj = version/100; info->version_min = version%100; info->set_values |= SQL_VERSION; sql_set_request_status(info, SQL_REQUEST_COMPLETE); return PROTO_OK; }
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; }
/* * | 2 bytes | 1 byte | Variable | * | Flags | TTC code | TTC body | */ static enum proto_parse_status tns_parse_data(struct tns_parser *tns_parser, struct sql_proto_info *info, struct cursor *cursor, unsigned way) { SLOG(LOG_DEBUG, "Parsing TNS data PDU of size %zu", cursor->cap_len); enum proto_parse_status status = PROTO_OK; // First, read the data flags CHECK(2); unsigned flags = cursor_read_u16n(cursor); SLOG(LOG_DEBUG, "Data flags = 0x%x", flags); if (flags & 0x40) { // End Of File if (cursor->cap_len != 0) return PROTO_PARSE_ERR; // This may be wrong, maybe a command is allowed anyway info->msg_type = SQL_EXIT; sql_set_request_status(info, SQL_REQUEST_COMPLETE); return PROTO_OK; } info->msg_type = tns_parser->msg_type; while (status == PROTO_OK && cursor->cap_len) { CHECK(1); enum ttc_code ttc_code = cursor_read_u8(cursor); SLOG(LOG_DEBUG, "Ttc code = 0x%02x, msg type %s", ttc_code, sql_msg_type_2_str(tns_parser->msg_type)); switch (ttc_code) { case TTC_ROW_PREFIX: status = tns_parse_row_prefix(tns_parser, info, cursor); break; case TTC_ROW_DATA: status = tns_parse_row_data(tns_parser, info, cursor); break; case TTC_ROW_DESCRIPTION_PREFIX: status = tns_parse_row_description_prefix(tns_parser, info, cursor); break; case TTC_ROW_RECAP: status = tns_parse_row_recap(cursor); break; case TTC_ROW_DESCRIPTION: status = tns_parse_row_description(cursor); break; case TTC_LOGIN_PROPERTY: status = tns_parse_login_property(info, cursor); break; case TTC_QUERY: status = tns_parse_query(tns_parser, info, cursor); break; case TTC_END_MESSAGE: status = tns_parse_end(info, cursor); break; case TTC_CLOSE: status = tns_parse_close_statement(cursor); break; default: SLOG(LOG_DEBUG, "Unknown ttc_code = %u", ttc_code); return PROTO_OK; } if (status == PROTO_OK) { enum sql_msg_type ttc_msg_type = ttc_to_msg_type(tns_parser, ttc_code); if (ttc_msg_type != SQL_UNKNOWN) { info->msg_type = ttc_msg_type; tns_parser->msg_type = ttc_msg_type; } // Fix c2s_way bool old_c2s_way = tns_parser->c2s_way; switch (ttc_code) { case TTC_ROW_DESCRIPTION_PREFIX: case TTC_ROW_RECAP: case TTC_ROW_DESCRIPTION: case TTC_END_MESSAGE: tns_parser->c2s_way = !way; break; case TTC_QUERY: case TTC_CLOSE: tns_parser->c2s_way = way; break; default: break; } if (old_c2s_way != tns_parser->c2s_way) { SLOG(LOG_DEBUG, "Fix c2s way from %d to %d", old_c2s_way, tns_parser->c2s_way); } info->is_query = way == tns_parser->c2s_way; } } return status; }
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; }