/* Read a string prefixed by 1 byte size * Size String------------- * 0x04 0x41 0x42 0x42 0x40 */ static enum proto_parse_status cursor_read_variable_string(struct cursor *cursor, char **out_str, unsigned *out_str_len) { unsigned str_len; CHECK(1); str_len = cursor_read_u8(cursor); SLOG(LOG_DEBUG, "Reading variable str of length %d", str_len); if (out_str_len) *out_str_len = str_len; return cursor_read_fixed_string(cursor, out_str, str_len); }
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; }
/* Read a string prefixed by 1 byte size * Size String------------- * 0x04 0x41 0x42 0x42 0x40 */ static enum proto_parse_status cursor_read_variable_string(struct cursor *cursor, char *buf, size_t size_buf, unsigned *out_str_len) { unsigned str_len; CHECK(1); str_len = cursor_read_u8(cursor); SLOG(LOG_DEBUG, "Reading variable str of length %d", str_len); if (out_str_len) *out_str_len = str_len; int ret = cursor_read_fixed_string(cursor, buf, size_buf, str_len); if (ret == -1) return PROTO_TOO_SHORT; else return PROTO_OK; }
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; }