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 tns_sbuf_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 tns_parser *tns_parser = DOWNCAST(parser, parser, tns_parser); // If this is the first time we are called, init c2s_way if (tns_parser->c2s_way == UNSET) { ASSIGN_INFO_OPT(tcp, parent); if (tcp) tns_parser->c2s_way = tcp->to_srv ? way : !way; else tns_parser->c2s_way = way; SLOG(LOG_DEBUG, "First packet, init c2s_way to %u", tns_parser->c2s_way); } if (!timeval_is_set(&tns_parser->first_ts)) { SLOG(LOG_DEBUG, "Setting first ts to %s", timeval_2_str(now)); tns_parser->first_ts = *now; } // Now build the proto_info struct sql_proto_info info; SLOG(LOG_DEBUG, "Constructing with %zu", wire_len); proto_info_ctor(&info.info, parser, parent, wire_len, 0); info.is_query = way == tns_parser->c2s_way; info.set_values = 0; info.msg_type = SQL_UNKNOWN; info.first_ts = tns_parser->first_ts; // and try to read a TNS PDN struct cursor cursor; cursor_ctor(&cursor, payload, cap_len); uint8_t const *const msg_start = cursor.head; size_t pdu_len = 0; enum tns_type pdu_type = 0; enum proto_parse_status status = cursor_read_tns_hdr(&cursor, &pdu_len, &pdu_type, wire_len); if (status == PROTO_PARSE_ERR) { SLOG(LOG_DEBUG, "Error while parsing tns header"); timeval_reset(&tns_parser->first_ts); return status; } bool has_gap = cap_len < wire_len; if (status == PROTO_TOO_SHORT && !has_gap) { proto_parse(NULL, parent, way, NULL, 0, 0, now, tot_cap_len, tot_packet); streambuf_set_restart(&tns_parser->sbuf, way, msg_start, pdu_len > 0 ? pdu_len : 8); SLOG(LOG_DEBUG, "Payload too short for parsing message, will restart @ %zu", tns_parser->sbuf.dir->restart_offset); return PROTO_OK; } switch (pdu_type) { case TNS_CONNECT: info.msg_type = SQL_STARTUP; status = tns_parse_connect(tns_parser, &info, &cursor); break; case TNS_ACCEPT: info.msg_type = SQL_STARTUP; status = tns_parse_accept(tns_parser, &info, &cursor); break; case TNS_DATA: status = tns_parse_data(tns_parser, &info, &cursor, way); break; case TNS_RESEND: SLOG(LOG_DEBUG, "Got a tns resend"); break; case TNS_REFUSE: case TNS_REDIRECT: case TNS_ABORT: case TNS_MARKER: case TNS_ATTENTION: case TNS_CONTROL: default: // A type we do not handle, skip the PDU break; } // We advertize the tns pdu even if we don't know how to parse it if (status != PROTO_OK) SLOG(LOG_DEBUG, "Error parsing tns packet"); timeval_reset(&tns_parser->first_ts); return proto_parse(NULL, &info.info, way, NULL, 0, 0, now, tot_cap_len, tot_packet); }
static enum proto_parse_status sdp_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 sdp_parser *sdp_parser = DOWNCAST(parser, parser, sdp_parser); static struct sdper_field const fields[] = { { 1, "c", sdp_extract_host }, { 1, "m", sdp_extract_port }, }; static struct sdper const sdper = { .nb_fields = NB_ELEMS(fields), .fields = fields }; SLOG(LOG_DEBUG, "Starting SDP analysis"); /* Parse */ struct sdp_proto_info info; sdp_proto_info_ctor(&info, parser, parent, wire_len, 0); if (0 != sdper_parse(&sdper, &cap_len, packet, cap_len, &info)) return PROTO_PARSE_ERR; // Start conntracking of RT(C)P streams if we have all required informations if ( (info.set_values & SDP_PORT_SET) && (info.set_values & SDP_HOST_SET) ) { SLOG(LOG_DEBUG, "SDP@%p, connect info is %s:%"PRIu16, sdp_parser, ip_addr_2_str(&info.host), info.port); /* FIXME: store both peers of the SDP tunnel and respawn the RT(C)Ps as soon as * one of the end changes. Problem is: we don't know which peer this is! */ if (! sdp_parser->host_set) { sdp_parser->host_set = true; sdp_parser->host = info.host; sdp_parser->port = info.port; ASSIGN_INFO_OPT(ip, parent); if (ip) { sdp_parser->sender = ip->key.addr[0]; sdp_parser->sender_set = true; } else { sdp_parser->sender_set = false; } } else if (0 != ip_addr_cmp(&sdp_parser->host, &info.host)) { /* Start conntracking between the advertized hosts * Notice that we request RT(C)P on behalf of our parent! */ spawn_rtp_subparsers(&sdp_parser->host, sdp_parser->port, &info.host, info.port, now, parent->parser->proto); ASSIGN_INFO_OPT(ip, parent); bool may_use_stun[2] = { 0 != ip_addr_cmp(&sdp_parser->sender, &sdp_parser->host), ip && 0 != ip_addr_cmp(&ip->key.addr[0], &info.host), }; // If the sender IP was different from the advertized host, start conntracking on this socket too if (may_use_stun[0]) { spawn_rtp_subparsers(&sdp_parser->sender, sdp_parser->port, &info.host, info.port, now, parent->parser->proto); } // If _this_ sender IP is different from this advertized host, start conntracking on this socket as well if (may_use_stun[1]) { spawn_rtp_subparsers(&sdp_parser->host, sdp_parser->port, &ip->key.addr[0], info.port, now, parent->parser->proto); } // If both senders IP were different from advertized ones then start conntracking between these two senders IP as well if (may_use_stun[0] && may_use_stun[1]) { spawn_rtp_subparsers(&sdp_parser->sender, sdp_parser->port, &ip->key.addr[0], info.port, now, parent->parser->proto); } // TODO: terminate this parser. meanwhile, reset its state : sdp_parser->host_set = false; sdp_parser->sender_set = false; } } return proto_parse(NULL, &info.info, way, NULL, 0, 0, now, tot_cap_len, tot_packet); }