static enum proto_parse_status read_channel(struct skinny_parser *parser, unsigned from, struct skinny_proto_info *info, struct cursor *curs, struct timeval const *now) { assert(from == FROM_MGR || from == FROM_STATION); if (curs->cap_len < 4+16+4) return PROTO_TOO_SHORT; uint32_t ip_version = cursor_read_u32le(curs); if (ip_version == 0) { // v4 uint32_t ip = cursor_read_u32(curs); ip_addr_ctor_from_ip4(&parser->peer[from], ip); cursor_drop(curs, 12); // this field is 16 bytes in length } else if (ip_version == 1) { // v16 ip_addr_ctor_from_ip6(&parser->peer[from], (struct in6_addr const *)curs->head); cursor_drop(curs, 16); } else { SLOG(LOG_DEBUG, "Invalid IP version (%d)", ip_version); return PROTO_PARSE_ERR; } parser->port[from] = cursor_read_u32le(curs); parser->media_set[from] = true; try_cnxtrack(parser, now); // Copy these into the info block SLOG(LOG_DEBUG, "Got media info"); info->set_values |= SKINNY_MEDIA_CNX; info->media_ip = parser->peer[from]; info->media_port = parser->port[from]; 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 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; }
static enum proto_parse_status tns_parse_connect(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 connect PDU of size %zu", cursor->cap_len); /* A connect is (in network byte order) : * - 2 bytes version * - 2 bytes back compatibility * - 2 bytes service options * - 2 bytes session data unit size * - 2 bytes max transm. data unit size * - 2 bytes proto characteristics * - 2 bytes line turnaround * - 2 bytes value of one * - 2 bytes connect data length * - 2 bytes connect data offset * - 4 bytes connect data max * - 1 byte connect flags 0 * - 1 byte connect flags 1 * - optionaly, 16 bytes for trace things * - padding until data offset * - then connect data */ size_t const pdu_len = cursor->cap_len; if (pdu_len < 26) return PROTO_PARSE_ERR; unsigned version = cursor_read_u16n(cursor); info->version_maj = version/100; info->version_min = version%100; info->set_values |= SQL_VERSION; cursor_drop(cursor, 14); // jump to connect data length unsigned data_length = cursor_read_u16n(cursor); unsigned data_offset = cursor_read_u16n(cursor); SLOG(LOG_DEBUG, "Connect, data length=%u, data offset=%u", data_length, data_offset); if (data_offset > pdu_len || data_offset < 26 + 8) return PROTO_PARSE_ERR; if (data_length + data_offset > pdu_len + 8) return PROTO_PARSE_ERR; cursor_drop(cursor, data_offset - 20 - 8); // jump to data // Now look for user and dbname (ie. service_name) # define USER_TOKEN "(USER="******"(SERVICE_NAME=" char const *data_end = (char const *)(cursor->head + data_length); char const *str; if (NULL != (str = strnstr((char const *)cursor->head, USER_TOKEN, data_length))) { str += strlen(USER_TOKEN); info->set_values |= SQL_USER; copy_token(info->u.startup.user, sizeof(info->u.startup.user), str, data_end-str); } if (NULL != (str = strnstr((char const *)cursor->head, DBNAME_TOKEN, data_length))) { str += strlen(DBNAME_TOKEN); info->set_values |= SQL_DBNAME; copy_token(info->u.startup.dbname, sizeof(info->u.startup.dbname), str, data_end-str); } return PROTO_OK; }
/* * Check if query is valid. * @param cursor to read * @param candidate Filled with potential sql size candidate * @return True if a correct query has been found and cursor is positioned at the begin of the query */ static bool lookup_query(struct cursor *cursor, struct query_candidate *candidate) { SLOG(LOG_DEBUG, "Start looking for query"); uint8_t current; uint8_t next; while (cursor->cap_len > QUERY_WITH_SIZE) { current = cursor_peek_u8(cursor, 0); next = cursor_peek_u8(cursor, 1); if (current == TTC_ROW_DATA && next > 0 && next <= 4) { SLOG(LOG_DEBUG, " Looks like start of a data query row"); return false; } if (current > 0 && current < 3 && next > MIN_QUERY_SIZE && candidate->num_candidate_size < NB_ELEMS(candidate->candidate_sizes)) { uint64_t buf = 0; // Copy cursor since we might have pattern like 0x01 Size Query struct cursor cursor_copy = *cursor; if (PROTO_OK == cursor_read_variable_int(&cursor_copy, &buf)) { SLOG(LOG_DEBUG, " Found a candidate size %"PRIu64, buf); insert_array_sorted(candidate, buf); } } if (candidate->num_candidate_size == 0 || current >= candidate->candidate_sizes[0]) { if (check_chuncked_query(cursor, current, next, candidate)) return true; if (check_fixed_query(cursor, current, next, candidate)) return true; } else { if (check_fixed_query(cursor, current, next, candidate)) return true; if (check_chuncked_query(cursor, current, next, candidate)) return true; } cursor_drop(cursor, 1); } return false; }
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; }
static enum proto_parse_status cursor_drop_until(struct cursor *cursor, const void *marker, size_t marker_len) { uint8_t *new_head = memmem(cursor->head, cursor->cap_len, marker, marker_len); if (!new_head) return PROTO_PARSE_ERR; size_t gap_size = new_head - cursor->head; cursor_drop(cursor, gap_size); 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; }
enum proto_parse_status cursor_read_fixed_string(struct cursor *cursor, char **out_str, size_t str_len) { SLOG(LOG_DEBUG, "Reading string of size %zu", str_len); if (cursor->cap_len < str_len) return PROTO_PARSE_ERR; if (!out_str) { cursor_drop(cursor, str_len); return PROTO_OK; } char *str = tempstr(); unsigned copied_len = MIN(str_len, TEMPSTR_SIZE - 1); cursor_copy(str, cursor, copied_len); str[copied_len] = '\0'; if (copied_len < str_len) { cursor_drop(cursor, str_len - copied_len); } if(out_str) *out_str = str; 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; }
// | 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; }
static enum proto_parse_status rpc_parse(struct parser *parser, struct proto_info *parent, unsigned unused_ way, uint8_t const *packet, size_t cap_len, size_t wire_len, struct timeval const unused_ *now, size_t unused_ tot_cap_len, uint8_t const unused_ *tot_packet) { struct cursor cursor; cursor_ctor(&cursor, packet, cap_len); if (wire_len < 12) return PROTO_PARSE_ERR; if (cap_len < 12) return PROTO_TOO_SHORT; ASSIGN_INFO_OPT(tcp, parent); struct rpc_proto_info info; proto_info_ctor(&info.info, parser, parent, wire_len, 0); if (tcp) cursor_drop(&cursor, 4); // Drop fragment header cursor_drop(&cursor, 4); info.msg_type = cursor_read_u32n(&cursor); enum proto_parse_status status = PROTO_OK; switch (info.msg_type) { case RPC_CALL: if (wire_len < 40) return PROTO_PARSE_ERR; status = parse_rpc_call(&cursor, &info); break; case RPC_REPLY: status = parse_rpc_reply(&cursor, &info); break; default: return PROTO_PARSE_ERR; } SLOG(LOG_DEBUG, "Parsed rpc status %s, %s", proto_parse_status_2_str(status), rpc_info_2_str(&info.info)); if (status == PROTO_OK) { // TODO We can have a nfs payload (void)proto_parse(NULL, &info.info, way, NULL, 0, 0, now, tot_cap_len, tot_packet); } return status; }
static enum proto_parse_status read_channel(struct skinny_parser *parser, unsigned from, struct skinny_proto_info *info, struct cursor *curs, struct timeval const *now) { assert(from == FROM_MGR || from == FROM_STATION); if (curs->cap_len < 4+16+4) return PROTO_TOO_SHORT; uint32_t ip_version = 0; // The ip field has a 16 byte lenght on CM7 headers. We // need to drop some bytes before parsing remote port short offset_ip_port = 0; switch (info->header_ver) { case SKINNY_BASIC: break; case SKINNY_CM7_TYPE_A: case SKINNY_CM7_TYPE_B: case SKINNY_CM7_TYPE_C: ip_version = cursor_read_u32le(curs); // We drop (16 - 4) for ipv4 and (16 - 8) for ipv6 offset_ip_port = ip_version ? 8 : 12; break; } if (ip_version == 0) { // v4 uint32_t ip = cursor_read_u32(curs); ip_addr_ctor_from_ip4(&parser->peer[from], ip); } else if (ip_version == 1) { // v6 ip_addr_ctor_from_ip6(&parser->peer[from], (struct in6_addr const *)curs->head); } else { SLOG(LOG_DEBUG, "Invalid IP version (%d)", ip_version); return PROTO_PARSE_ERR; } cursor_drop(curs, offset_ip_port); parser->port[from] = cursor_read_u32le(curs); parser->media_set[from] = true; try_cnxtrack(parser, now); // Copy these into the info block SLOG(LOG_DEBUG, "Got media info"); info->set_values |= SKINNY_MEDIA_CNX; info->media_ip = parser->peer[from]; info->media_port = parser->port[from]; 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 *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; }
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; }
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 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; }
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); }
static enum proto_parse_status pg_parse_query(struct pgsql_parser *pg_parser, struct sql_proto_info *info, unsigned way, uint8_t const *payload, size_t cap_len, size_t unused_ wire_len, struct timeval const *now, size_t tot_cap_len, uint8_t const *tot_packet) { enum proto_parse_status status; info->msg_type = SQL_QUERY; struct cursor cursor; cursor_ctor(&cursor, payload, cap_len); uint8_t type; size_t len; /* In this phase, we are looking for SimpleQuery from the client and Data from the server. * This is very simplistic, to be completed later with more interresting query types. * Also, the client can send a termination request. */ if (info->is_query) { status = cursor_read_msg(&cursor, &type, &len); if (status != PROTO_OK) return status; if (type == 'Q') { // simple query char *sql; status = cursor_read_string(&cursor, &sql, len); if (status != PROTO_OK) return status; info->set_values |= SQL_SQL; snprintf(info->u.query.sql, sizeof(info->u.query.sql), "%s", sql); } else if (type == 'X') { info->msg_type = SQL_EXIT; info->set_values |= SQL_REQUEST_STATUS; info->request_status = SQL_REQUEST_COMPLETE; pg_parser->phase = EXIT; } else return PROTO_PARSE_ERR; } else { while (! cursor_is_empty(&cursor)) { uint8_t const *const msg_start = cursor.head; status = cursor_read_msg(&cursor, &type, &len); if (status == PROTO_PARSE_ERR) return status; else if (status == PROTO_TOO_SHORT) { SLOG(LOG_DEBUG, "Payload too short for parsing message, will restart"); status = proto_parse(NULL, &info->info, way, NULL, 0, 0, now, tot_cap_len, tot_packet); // ack what we had so far streambuf_set_restart(&pg_parser->sbuf, way, msg_start, true); return PROTO_OK; } uint8_t const *const msg_end = cursor.head + len; if (type == 'T') { // row description (fetch nb_fields) if (len < 2) return PROTO_PARSE_ERR; info->u.query.nb_fields = cursor_read_u16n(&cursor); info->set_values |= SQL_NB_FIELDS; SLOG(LOG_DEBUG, "Setting nb_fields to %u", info->u.query.nb_fields); } else if (type == 'D') { // data row if (len < 2) return PROTO_PARSE_ERR; if (! (info->set_values & SQL_NB_ROWS)) { info->set_values |= SQL_NB_ROWS; info->u.query.nb_rows = 0; } info->u.query.nb_rows ++; SLOG(LOG_DEBUG, "Incrementing nb_rows (now %u)", info->u.query.nb_rows); } else if (type == 'C') { // command complete (fetch nb rows) char *result; info->set_values |= SQL_REQUEST_STATUS; info->request_status = SQL_REQUEST_COMPLETE; status = cursor_read_string(&cursor, &result, len); if (status != PROTO_OK) return status; status = fetch_nb_rows(result, &info->u.query.nb_rows); if (status == PROTO_OK) { info->set_values |= SQL_NB_ROWS; } else { //return status; // Do not use this as the actual protocol does not seam to implement the doc :-< } } else if (type == 'E') { // error status = pg_parse_error(info, &cursor, len); if (status != PROTO_OK) return status; } // Skip what's left of this message and go for the next assert(msg_end >= cursor.head); cursor_drop(&cursor, msg_end - cursor.head); } } return proto_parse(NULL, &info->info, way, NULL, 0, 0, now, tot_cap_len, tot_packet); }
static enum proto_parse_status skinny_sbuf_parse(struct parser *parser, struct proto_info *parent, unsigned way, uint8_t const *packet, size_t cap_len, size_t wire_len, struct timeval const *now, size_t tot_cap_len, uint8_t const *tot_packet) { struct skinny_parser *skinny_parser = DOWNCAST(parser, parser, skinny_parser); # define SKINNY_HDR_SIZE 8 # define SKINNY_MIN_MSG_SIZE 12 if (wire_len < SKINNY_MIN_MSG_SIZE) { streambuf_set_restart(&skinny_parser->sbuf, way, packet, true); // wait for more return PROTO_OK; } if (cap_len < SKINNY_MIN_MSG_SIZE) return PROTO_TOO_SHORT; struct cursor curs; cursor_ctor(&curs, packet, cap_len); uint32_t msg_len = cursor_read_u32le(&curs); enum skinny_header_version header_ver = cursor_read_u32le(&curs); enum skinny_msgid msg_id = cursor_read_u32le(&curs); SLOG(LOG_DEBUG, "New SKINNY msg of size %"PRIu32", msgid=0x%"PRIx32, msg_len, msg_id); if (header_ver != SKINNY_BASIC && header_ver != SKINNY_CM7_TYPE_A && header_ver != SKINNY_CM7_TYPE_B && header_ver != SKINNY_CM7_TYPE_C) return PROTO_PARSE_ERR; if (msg_len < 4 || msg_len > SKINNY_MAX_HDR_SIZE /* guestimated */) return PROTO_PARSE_ERR; if (wire_len < msg_len + SKINNY_HDR_SIZE) return PROTO_TOO_SHORT; // wait for the message to be complete // Ok we have what looks like a skinny message in there struct skinny_proto_info info; skinny_proto_info_ctor(&info, parser, parent, SKINNY_HDR_SIZE, msg_len, msg_id, header_ver); switch (msg_id) { case SKINNY_STATION_KEY_PAD_BUTTON: if (curs.cap_len < 12) return PROTO_TOO_SHORT; info.set_values |= SKINNY_NEW_KEY_PAD | SKINNY_LINE_INSTANCE | SKINNY_CALL_ID; info.new_key_pad = cursor_read_u32le(&curs); info.line_instance = cursor_read_u32le(&curs); info.call_id = cursor_read_u32le(&curs); break; case SKINNY_MGR_CALL_STATE: if (curs.cap_len < 12) return PROTO_TOO_SHORT; info.set_values |= SKINNY_CALL_STATE | SKINNY_LINE_INSTANCE | SKINNY_CALL_ID; info.call_state = cursor_read_u32le(&curs); info.line_instance = cursor_read_u32le(&curs); info.call_id = cursor_read_u32le(&curs); SLOG(LOG_DEBUG, "New call state: %s", skinny_call_state_2_str(info.call_state)); break; case SKINNY_MGR_CLOSE_RECV_CHANNEL: case SKINNY_MGR_STOP_MEDIA_TRANSMIT: if (curs.cap_len < 8) return PROTO_TOO_SHORT; info.set_values |= SKINNY_CONFERENCE_ID | SKINNY_PASS_THRU_ID; info.conf_id = cursor_read_u32le(&curs); info.pass_thru_id = cursor_read_u32le(&curs); break; case SKINNY_MGR_START_MEDIA_TRANSMIT: if (curs.cap_len < 8) return PROTO_TOO_SHORT; info.set_values |= SKINNY_CONFERENCE_ID | SKINNY_PASS_THRU_ID; info.conf_id = cursor_read_u32le(&curs); info.pass_thru_id = cursor_read_u32le(&curs); enum proto_parse_status status = read_channel(skinny_parser, FROM_MGR, &info, &curs, now); if (PROTO_OK != status) return status; break; case SKINNY_STATION_OPEN_RECV_CHANNEL_ACK: if (curs.cap_len < 4) return PROTO_TOO_SHORT; uint32_t open_status = cursor_read_u32le(&curs); if (open_status == 0 /* Ok */) { enum proto_parse_status status = read_channel(skinny_parser, FROM_STATION, &info, &curs, now); if (PROTO_OK != status) return status; info.set_values |= SKINNY_PASS_THRU_ID; if (curs.cap_len < 4) return PROTO_TOO_SHORT; info.pass_thru_id = cursor_read_u32le(&curs); } break; case SKINNY_MGR_OPEN_RECV_CHANNEL: if (curs.cap_len < 8) return PROTO_TOO_SHORT; info.set_values |= SKINNY_CONFERENCE_ID | SKINNY_PASS_THRU_ID; info.conf_id = cursor_read_u32le(&curs); info.pass_thru_id = cursor_read_u32le(&curs); break; case SKINNY_MGR_DIALED_NUMBER: # define DIALED_NUMBER_SIZE 24 if (curs.cap_len < DIALED_NUMBER_SIZE+8) return PROTO_TOO_SHORT; info.set_values |= SKINNY_CALLED_PARTY | SKINNY_LINE_INSTANCE | SKINNY_CALL_ID; // 24 chars, terminated with 0 (if fits) snprintf(info.called_party, sizeof(info.called_party), "%.*s", (int)DIALED_NUMBER_SIZE, curs.head); cursor_drop(&curs, DIALED_NUMBER_SIZE); info.line_instance = cursor_read_u32le(&curs); info.call_id = cursor_read_u32le(&curs); break; case SKINNY_MGR_CALL_INFO: if (curs.cap_len < 8 + 4 + 5*4) return PROTO_TOO_SHORT; info.set_values |= SKINNY_CALLING_PARTY | SKINNY_CALLED_PARTY | SKINNY_LINE_INSTANCE | SKINNY_CALL_ID; info.line_instance = cursor_read_u32le(&curs); info.call_id = cursor_read_u32le(&curs); cursor_drop(&curs, 4 + 5*4); // drop Call Type and 5 unknown fields // From now on, informations are nul terminated strings if (PROTO_OK != (status = read_string(info.calling_party, sizeof(info.calling_party), &curs))) return status; // Calling party if (header_ver == SKINNY_CM7_TYPE_A || header_ver == SKINNY_CM7_TYPE_B || header_ver == SKINNY_CM7_TYPE_C) { cursor_read_string(&curs, NULL, 24); // Drop calling party voice mailbox } if (PROTO_OK != (status = read_string(info.called_party, sizeof(info.called_party), &curs))) return status; // Called party // discard the rest of informations break; default: break; } (void)proto_parse(NULL, &info.info, way, NULL, 0, 0, now, tot_cap_len, tot_packet); streambuf_set_restart(&skinny_parser->sbuf, way, packet + SKINNY_HDR_SIZE + msg_len, false); // go to next msg return PROTO_OK; }