uint_least32_t cursor_read_u24n(struct cursor *cursor) { uint_least32_t a = cursor_read_u8(cursor); uint_least32_t b = cursor_read_u8(cursor); uint_least32_t c = cursor_read_u8(cursor); return (a << 16) | (b << 8) | c; }
uint_least32_t cursor_read_u24le(struct cursor *cursor) { uint_least32_t a = cursor_read_u8(cursor); uint_least32_t b = cursor_read_u8(cursor); uint_least32_t c = cursor_read_u8(cursor); return a | (b << 8) | (c << 16); }
static enum proto_parse_status read_string(char *dest, size_t max_size, struct cursor *curs) { // This is an error to not reach '\0' before the end of the cursor while (curs->cap_len > 0 && curs->head[0] != '\0') { if (max_size > 1) { max_size --; *dest ++ = cursor_read_u8(curs); } else { // we suppose, if this does not fit in max_size, that we are not parsing Skinny return PROTO_PARSE_ERR; } } if (! curs->cap_len) return PROTO_TOO_SHORT; *dest ++ = cursor_read_u8(curs); // the nul return PROTO_OK; }
/* TNS PDU have a header consisting of (in network byte order) : * * | 2 bytes | 2 bytes | 1 byte | 1 byte | 2 bytes | * | Length | Checksum | Type | a zero | Header checksum | */ static enum proto_parse_status cursor_read_tns_hdr(struct cursor *cursor, size_t *out_len, unsigned *out_type, size_t wire_len) { SLOG(LOG_DEBUG, "Reading a TNS PDU"); CHECK_LEN(cursor, 8, 0); size_t len = cursor_read_u16n(cursor); if (len < 8 || len < wire_len) return PROTO_PARSE_ERR; len -= 8; SLOG(LOG_DEBUG, "TNS PDU len == %zu", len); // Checksum should be 0 uint_least16_t checksum = cursor_read_u16n(cursor); if (checksum > 0) { SLOG(LOG_DEBUG, "Tns checksum should be 0, got %u", checksum); return PROTO_PARSE_ERR; } unsigned type = cursor_read_u8(cursor); if (type >= TNS_TYPE_MAX) { SLOG(LOG_DEBUG, "Tns type invalid, sould be < %u, got %u", TNS_TYPE_MAX, type); return PROTO_PARSE_ERR; } // reserved byte and header checksum should be 0 uint_least32_t head_checksum =cursor_read_u24(cursor); if (head_checksum > 0) { SLOG(LOG_DEBUG, "Reserved byte and checksum should be 0, got %u", head_checksum); return PROTO_PARSE_ERR; } if (out_len) *out_len = len; if (out_type) *out_type = type; // Check we have the msg payload CHECK(len); return PROTO_OK; }
/* Read a message header, return type and msg length, and advance the cursor to the msg payload. * if type is NULL that means no type are read from the cursor. * return PROTO_TOO_SHORT if the msg content is not available. */ static enum proto_parse_status cursor_read_msg(struct cursor *cursor, uint8_t *type, size_t *len_) { SLOG(LOG_DEBUG, "Reading new message"); unsigned rollback = 0; if (type) { // read type first CHECK_LEN(cursor, 1, rollback); *type = cursor_read_u8(cursor); rollback++; SLOG(LOG_DEBUG, "... of type %u ('%c')", (unsigned)*type, *type); } // read length CHECK_LEN(cursor, 4, rollback); size_t len = cursor_read_u32n(cursor); rollback += 4; if (len < 4) return PROTO_PARSE_ERR; // as length includes itself len -= 4; SLOG(LOG_DEBUG, "... of length %zu", len); if (len_) *len_ = len; // read payload CHECK_LEN(cursor, len, rollback); return PROTO_OK; }
/* * 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; }
/* Read an int prefixed by 1 byte size * | Size | Int------ | * | 0x02 | 0x01 0xdd | */ static enum proto_parse_status cursor_read_variable_int(struct cursor *cursor, uint_least64_t *res) { CHECK(1); unsigned len = cursor_read_u8(cursor); SLOG(LOG_DEBUG, "Variable len has size %d", len); return cursor_read_fixed_int_n(cursor, res, len); }
enum proto_parse_status cursor_read_fixed_int_le(struct cursor *cursor, uint_least64_t *out_res, unsigned len) { uint_least64_t res; if (cursor->cap_len < len) return PROTO_TOO_SHORT; switch (len) { case 0: res = 0; break; case 1: res = cursor_read_u8(cursor); break; case 2: res = cursor_read_u16le(cursor); break; case 3: res = cursor_read_u24le(cursor); break; case 4: res = cursor_read_u32le(cursor); break; case 8: res = cursor_read_u64le(cursor); break; default: SLOG(LOG_DEBUG, "Can't read a %d bytes long number", len); return PROTO_PARSE_ERR; } if (out_res) *out_res = res; return PROTO_OK; }
static enum proto_parse_status cursor_read_tns_hdr(struct cursor *cursor, size_t *out_len, unsigned *out_type) { /* TNS PDU have a header consisting of (in network byte order) : * - a 2 bytes length * - a 2 bytes checksum * - a one byte type * - a one byte 0 * - a 2 bytes header checksum (or 0) */ SLOG(LOG_DEBUG, "Reading a TNS PDU"); CHECK_LEN(cursor, 8, 0); size_t len = cursor_read_u16n(cursor); if (len < 8) return PROTO_PARSE_ERR; len -= 8; SLOG(LOG_DEBUG, "TNS PDU len == %zu", len); // skip packet checksum (2 bytes) cursor_drop(cursor, 2); unsigned type = cursor_read_u8(cursor); // Skip Reserved byte and header checksum (1 + 2 bytes) cursor_drop(cursor, 3); if (type > TNS_TYPE_MAX) return PROTO_PARSE_ERR; // Check we have the msg payload CHECK_LEN(cursor, len, 8); if (out_len) *out_len = len; if (out_type) *out_type = type; return PROTO_OK; }
static enum proto_parse_status pg_parse_error(struct sql_proto_info *info, struct cursor *cursor, size_t len) { enum proto_parse_status status; info->set_values |= SQL_REQUEST_STATUS; info->request_status = SQL_REQUEST_ERROR; size_t size = 0; char *str; while (size <= len) { size++; if (size > len) return PROTO_PARSE_ERR; char type = cursor_read_u8(cursor); if (type == 0x00) { break; } status = cursor_read_string(cursor, &str, len - size); if (status != PROTO_OK) return status; size += strlen(str) + 1; switch (type) { case 'C': info->set_values |= SQL_ERROR_SQL_STATUS; snprintf(info->error_sql_status, sizeof(info->error_sql_status), "%s", str); break; case 'M': info->set_values |= SQL_ERROR_MESSAGE; snprintf(info->error_message, sizeof(info->error_message), "%s", str); break; } } return PROTO_OK; }
static enum proto_parse_status tds_parse_header(struct cursor *cursor, struct tds_header *out_header, bool *unknown_token) { # define TDS_PKT_HDR_LEN 8 CHECK_LEN(cursor, TDS_PKT_HDR_LEN, 0); struct tds_header header; header.type = cursor_read_u8(cursor); header.status = cursor_read_u8(cursor); header.len = cursor_read_u16n(cursor); header.channel = cursor_read_u16n(cursor); header.pkt_number = cursor_read_u8(cursor); header.window = cursor_read_u8(cursor); SLOG(LOG_DEBUG, "Reading new TDS packet %s", tds_header_2_str(&header)); // sanity check if (header.len < TDS_PKT_HDR_LEN) return PROTO_PARSE_ERR; switch (header.type) { case TDS_PKT_TYPE_SQL_BATCH: case TDS_PKT_TYPE_LOGIN: case TDS_PKT_TYPE_RPC: case TDS_PKT_TYPE_RESULT: case TDS_PKT_TYPE_ATTENTION: case TDS_PKT_TYPE_BULK_LOAD: case TDS_PKT_TYPE_MANAGER_REQ: case TDS_PKT_TYPE_TDS7_LOGIN: case TDS_PKT_TYPE_SSPI: case TDS_PKT_TYPE_PRELOGIN: break; default: SLOG(LOG_DEBUG, "Unknown tds type %u", header.type); if (unknown_token) *unknown_token = true; return PROTO_PARSE_ERR; } if (header.window != 0) { SLOG(LOG_DEBUG, "Window is %"PRIu8" instead of 0", header.window); return PROTO_PARSE_ERR; } size_t data_left = header.len - TDS_PKT_HDR_LEN; if ((data_left > 0) != tds_packet_has_data(header.type)) { SLOG(LOG_DEBUG, "This TDS packet of type %s has %zu bytes of data, but should%s have data", tds_packet_type_2_str(header.type), data_left, tds_packet_has_data(header.type) ? "":" not"); return PROTO_PARSE_ERR; } if (out_header) *out_header = header; return PROTO_OK; }
/* * If oci, we will fallback on start query guesstimation * | 1 byte | * | Some flags (generally > 0x04) | * * If jdbc: * | 1 byte | 0-4 bytes | 1 byte | 0-4 bytes | * | Option size | Options | Var size | Var value | */ static bool is_oci(struct cursor *cursor) { CHECK(1); unsigned option_size = cursor_read_u8(cursor); CHECK(MAX(option_size, 2)); if (option_size > 0x04 || cursor_peek_u8(cursor, 1) == 0x00) return true; cursor_drop(cursor, option_size); // Should be a var here CHECK(1); unsigned var_size = cursor_read_u8(cursor); CHECK(MAX(var_size, 2)); if (var_size > 0x04 || cursor_peek_u8(cursor, 1) == 0x00) return true; cursor_drop(cursor, var_size); return false; }
/* 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); }
/* 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 void cursor_check(void) { struct cursor cursor; static uint8_t const data[] = { 1, 2 }; cursor_ctor(&cursor, data, sizeof(data)); assert(cursor_peek_u8(&cursor, 0) == 0x01U); assert(cursor_peek_u8(&cursor, 1) == 0x02U); assert(cursor_read_u8(&cursor) == 0x01U); assert(cursor_read_u8(&cursor) == 0x02U); static uint16_t const data16[] = { 0x0102, 0x0304 }; cursor_ctor(&cursor, (uint8_t *)data16, sizeof(data16)); assert(cursor_peek_u16le(&cursor, 0) == 0x0102U); assert(cursor_peek_u16le(&cursor, 2) == 0x0304U); assert(cursor_read_u16(&cursor) == 0x0102U); assert(cursor_read_u16(&cursor) == 0x0304U); static uint32_t const data32[] = { 0x01020304U, 0x05060708U }; cursor_ctor(&cursor, (uint8_t *)data32, sizeof(data32)); assert(cursor_peek_u32le(&cursor, 0) == 0x01020304U); assert(cursor_peek_u32le(&cursor, 4) == 0x05060708U); assert(cursor_read_u32(&cursor) == 0x01020304U); assert(cursor_read_u32(&cursor) == 0x05060708U); static uint64_t const data64[] = { 0x0102030405060708ULL }; cursor_ctor(&cursor, (uint8_t *)data64, sizeof(data64)); assert(cursor_peek_u64le(&cursor, 0) == 0x0102030405060708ULL); assert(cursor_read_u64(&cursor) == 0x0102030405060708ULL); static uint8_t const datan[] = { 1, 2, 3, 4 }; cursor_ctor(&cursor, datan, sizeof(datan)); assert(cursor_peek_u32n(&cursor, 0) == 0x01020304); assert(cursor_read_u32n(&cursor) == 0x01020304); cursor_ctor(&cursor, datan, sizeof(datan)); assert(cursor_peek_u16n(&cursor, 0) == 0x0102); assert(cursor_read_u16n(&cursor) == 0x0102); }
// | 1 byte | 1 byte | 2 bytes | 4 bytes | 4 bytes | 4 bytes | // | SMID (0x53) | Flag | SID | Length | Seq num | Window | static enum proto_parse_status tds_parse_smp_header(struct cursor *cursor, struct smp_header *out_header) { # define SMP_PKT_HDR_LEN 0x10 # define SMP_SMID 0x53 if (cursor_peek_u8(cursor, 0) == SMP_SMID) { CHECK_LEN(cursor, SMP_PKT_HDR_LEN, 0); cursor_drop(cursor, 1); out_header->flags = cursor_read_u8(cursor); out_header->sid = cursor_read_u16le(cursor); out_header->length = cursor_read_u32le(cursor); out_header->seq_num = cursor_read_u32le(cursor); out_header->window = cursor_read_u32le(cursor); } return PROTO_OK; }
/* * 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; }
/* Read a string splitted in chunk prefixed by 1 byte size * Chunk have a maximum size of 0x40 bytes * If there are more than one chunk, a 0x00 end it * * a multi chunked string * * Size String--------- Size String End * 0x40 0x41 0x42 0x.. 0x01 0x49 0x00 * * a single chunked string * * Size String--------- * 0x20 0x41 0x42 0x.. * * The global size might be unknown, so we try to guess it. We will have a parse problem * for string of size 0x40 */ static enum proto_parse_status cursor_read_chunked_string(struct cursor *cursor, char *buf, size_t size_buf, size_t max_chunk) { unsigned str_len = 0; struct string_buffer string_buf; if (buf) string_buffer_ctor(&string_buf, buf, size_buf); do { if (cursor->cap_len < 1) break; str_len = cursor_read_u8(cursor); SLOG(LOG_DEBUG, "Chunk of size of %u", str_len); size_t available_bytes = MIN(cursor->cap_len, str_len); if (buf) buffer_append_stringn(&string_buf, (char const *)cursor->head, available_bytes); cursor_drop(cursor, available_bytes); } while (str_len >= max_chunk); // There seems to be an null terminator when string length is > 0x40 // However, it can be a flag after the string. Ignore it for now. if (buf) buffer_get_string(&string_buf); return PROTO_OK; }
enum proto_parse_status cursor_read_string(struct cursor *cursor, char **out_str, size_t max_len) { char *str = tempstr(); unsigned len; if (max_len > TEMPSTR_SIZE-1) max_len = TEMPSTR_SIZE-1; for (len = 0; len < max_len; len ++) { CHECK_LEN(cursor, 1, len); uint8_t c = cursor_read_u8(cursor); if (c == '\0') break; str[len] = c; } if (len == max_len) { cursor_rollback(cursor, len); return PROTO_TOO_SHORT; } str[len] = '\0'; SLOG(LOG_DEBUG, "Reading string '%s' of size %u", str, len); if (out_str) *out_str = str; return PROTO_OK; }
/* Read a string splitted in chunk prefixed by 1 byte size * Chunk have a maximum size of 0x40 bytes * If there are more than one chunk, a 0x00 end it * * a multi chunked string * * Size String--------- Size String End * 0x40 0x41 0x42 0x.. 0x01 0x49 0x00 * * a single chunked string * * Size String--------- * 0x20 0x41 0x42 0x.. * * The global size might be unknown, so we try to guess it. We will have a parse problem * for string of size 0x40 */ static enum proto_parse_status cursor_read_chunked_string(struct cursor *cursor, char **out_str, size_t max_chunk) { char *str = tempstr(); unsigned pos = 0; while (pos < TEMPSTR_SIZE) { CHECK(1); unsigned str_len = cursor_read_u8(cursor); SLOG(LOG_DEBUG, "Chunk of size of %u", str_len); CHECK(str_len); size_t copied_len = MIN(TEMPSTR_SIZE - (pos + str_len + 1), str_len); cursor_copy(str + pos, cursor, copied_len); pos += str_len; if (str_len < max_chunk) break; } // There seems to be an null terminator when string length is > 0x40 // However, it can be a flag after the string. Ignore it for now. if (out_str) *out_str = str; str[MIN(pos, TEMPSTR_SIZE)] = 0; SLOG(LOG_DEBUG, "Chunk parsed of size %u", pos); return PROTO_OK; }
static enum proto_parse_status tns_parse_query(struct tns_parser *tns_parser, struct sql_proto_info *info, struct cursor *cursor) { enum proto_parse_status status = PROTO_OK; CHECK(1); enum query_subcode query_subcode = cursor_read_u8(cursor); SLOG(LOG_DEBUG, "Parsing tns query, subcode is %u", query_subcode); switch (query_subcode) { case TTC_QUERY_SQL: tns_parser->nb_fields = UNSET; status = tns_parse_sql_query(info, cursor); cursor_drop(cursor, cursor->cap_len); break; case TTC_QUERY_FETCH: cursor_drop(cursor, cursor->cap_len); break; default: // Probably initialization queries cursor_drop(cursor, cursor->cap_len); break; } return status; }
static enum proto_parse_status tns_parse_query(struct tns_parser *tns_parser, struct sql_proto_info *info, struct cursor *cursor) { enum proto_parse_status status = PROTO_OK; CHECK(1); enum query_subcode query_subcode = cursor_read_u8(cursor); SLOG(LOG_DEBUG, "Parsing tns query, subcode is %u", query_subcode); switch (query_subcode) { case TTC_QUERY_SQL: case TTC_QUERY_ALL_7: tns_parser->nb_fields = UNSET; status = tns_parse_sql_query(info, cursor); break; case TTC_QUERY_FETCH: break; default: // Probably initialization queries. Since we are unsure, don't // return PROTO_OK to avoid fix of c2s_way return PROTO_PARSE_ERR; } cursor_drop(cursor, cursor->cap_len); return status; }
static bool lookup_query(struct cursor *cursor) { #define MIN_QUERY_SIZE 10 #define QUERY_WITH_SIZE 12 while (cursor->cap_len > QUERY_WITH_SIZE) { uint8_t c; uint8_t potential_size = 0; do { c = cursor_peek_u8(cursor, 1); potential_size = cursor_read_u8(cursor); } while (cursor->cap_len > QUERY_WITH_SIZE && !isprint(c)); SLOG(LOG_DEBUG, "Found potential size 0x%02x and first printable %c", potential_size, c); // Check on found size if (potential_size < MIN_QUERY_SIZE || potential_size > cursor->cap_len) continue; // We check if last character is printable if (!isprint(cursor_peek_u8(cursor, potential_size - 1))) continue; // We check if first characters are printable if (PROTO_OK == is_range_print(cursor, MIN_QUERY_SIZE)) { cursor_rollback(cursor, 1); return true; } } return false; }
uint_least16_t cursor_read_u16le(struct cursor *cursor) { uint_least32_t a = cursor_read_u8(cursor); uint_least32_t b = cursor_read_u8(cursor); return a | (b << 8); }
uint_least16_t cursor_read_u16n(struct cursor *cursor) { uint_least32_t a = cursor_read_u8(cursor); uint_least32_t b = cursor_read_u8(cursor); return (a << 8) | b; }
static enum proto_parse_status dhcp_parse(struct parser *parser, struct proto_info *parent, unsigned way, uint8_t const *payload, size_t cap_len, size_t wire_len, struct timeval const *now, size_t tot_cap_len, uint8_t const *tot_packet) { struct dhcp const *dhcp = (struct dhcp *)payload; // Sanity Checks // Check that we have at least the size of an DHCP packet for IP protocol if (wire_len < sizeof(*dhcp)) return PROTO_PARSE_ERR; // And that we have enough data to parse it if (cap_len < sizeof(*dhcp)) return PROTO_TOO_SHORT; if (0 != memcmp(dhcp->cookie, &magic_cookie, sizeof(magic_cookie))) { SLOG(LOG_DEBUG, "Bad magic Cookie"); return PROTO_PARSE_ERR; } struct dhcp_proto_info info; proto_info_ctor(&info.info, parser, parent, wire_len, 0); info.opcode = READ_U8(&dhcp->op); if (info.opcode != BOOTP_REQUEST && info.opcode != BOOTP_REPLY) { SLOG(LOG_DEBUG, "Unknown DHCP opcode (%u)", info.opcode); return PROTO_PARSE_ERR; } uint8_t const hlen = READ_U8(&dhcp->hlen); if (hlen > sizeof(dhcp->chaddr)) { SLOG(LOG_DEBUG, "Bad hlen in DHCP (%u)", hlen); return PROTO_PARSE_ERR; } info.xid = READ_U32N(&dhcp->xid); info.set_values = 0; uint32_t const addr = READ_U32(&dhcp->yiaddr); if (addr) { info.set_values |= DHCP_CLIENT_SET; ip_addr_ctor_from_ip4(&info.client, addr); } uint8_t const htype = READ_U8(&dhcp->htype); info.hw_addr_is_eth = htype == 1; if (info.hw_addr_is_eth) { if (hlen != sizeof(info.client_mac)) { SLOG(LOG_DEBUG, "Bad hlen (%u) for Eth type", hlen); return PROTO_PARSE_ERR; } memcpy(info.client_mac, dhcp->chaddr, sizeof(info.client_mac)); } else { memset(info.client_mac, 0, sizeof(info.client_mac)); } memcpy(info.server_name, dhcp->sname, sizeof(info.server_name)); SLOG(LOG_DEBUG, "New DHCP %s", dhcp_opcode_2_str(info.opcode)); // parse options info.msg_type = 0; // mandatory struct cursor c; cursor_ctor(&c, dhcp->options, cap_len - offsetof(struct dhcp, options)); while (c.cap_len >= 2) { uint8_t const type = cursor_read_u8(&c); uint8_t const len = cursor_read_u8(&c); if (c.cap_len < len) { SLOG(LOG_DEBUG, "Cannot read options"); return PROTO_PARSE_ERR; } switch (type) { case 53: // msg type if (len != 1) { SLOG(LOG_DEBUG, "Bad length (%"PRIu8") for msg type DHCP option", len); return PROTO_PARSE_ERR; } info.msg_type = cursor_read_u8(&c); if (info.msg_type > DHCP_INFORM) { SLOG(LOG_DEBUG, "Bad DHCP msg type (%u)", info.msg_type); return PROTO_PARSE_ERR; } break; default: cursor_drop(&c, len); break; } } if (0 == info.msg_type) { // not found SLOG(LOG_DEBUG, "DHCP msg without msg type"); return PROTO_PARSE_ERR; } return proto_parse(NULL, &info.info, way, NULL, 0, 0, now, tot_cap_len, tot_packet); }
/* * | 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; }