static enum proto_parse_status pg_parse_startup(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) { info->msg_type = SQL_STARTUP; struct cursor cursor; cursor_ctor(&cursor, payload, cap_len); uint8_t type; size_t len; enum proto_parse_status status = cursor_read_msg(&cursor, &type, &len); if (status != PROTO_OK) return status; /* In this phase, we expect to see from the client the pwd message, * and from the server the authentication request. */ if (info->is_query) { // password message if (type != 'p') return PROTO_PARSE_ERR; char *passwd; status = cursor_read_string(&cursor, &passwd, len); if (status == PROTO_PARSE_ERR) return status; if (status == PROTO_TOO_SHORT) { // in case of GSSAPI or SSPI authentication then the "string" is in fact arbitrary bytes passwd = "GSSAPI/SSPI"; } info->set_values |= SQL_PASSWD; snprintf(info->u.startup.passwd, sizeof(info->u.startup.passwd), "%s", passwd); } else { // Authentication request SLOG(LOG_DEBUG, "Authentification response from server with type %c", type); if (len < 4) return PROTO_PARSE_ERR; if (type == 'E') { status = pg_parse_error(info, &cursor, len); if (status != PROTO_OK) return status; } else if (type == 'R' ) { // We don't care about the auth method, we just want to know when auth is complete uint32_t auth_type = cursor_read_u32n(&cursor); if (auth_type == 0) { // AuthenticationOK pg_parser->phase = QUERY; // we don't wait for the ReadyForQuery msg since we are not interrested in following messages info->set_values |= SQL_REQUEST_STATUS; info->request_status = SQL_REQUEST_COMPLETE; } } else { SLOG(LOG_DEBUG, "Unknown startup message with type %c", type); return PROTO_PARSE_ERR; } } // Discard the rest of the packet return proto_parse(NULL, &info->info, way, NULL, 0, 0, now, tot_cap_len, tot_packet); }
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 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); }
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 tds_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 tds_parser *tds_parser = DOWNCAST(parser, parser, tds_parser); SLOG(LOG_DEBUG, "Got tds packet, data_left %zu, way %d", tds_parser->data_left, way); bool has_gap = wire_len > cap_len; if (tds_parser->had_gap && tds_parser->data_left > 0) { tds_parser->data_left = wire_len > tds_parser->data_left ? 0 : tds_parser->data_left - wire_len; SLOG(LOG_DEBUG, "Discard tds with gap, data_left %zu...", tds_parser->data_left); return PROTO_OK; } tds_parser->had_gap = has_gap; struct tds_header tds_header; bool unknown_token = false; struct cursor cursor; cursor_ctor(&cursor, payload, cap_len); enum proto_parse_status status = tds_parse_header(&cursor, &tds_header, &unknown_token); if (status != PROTO_OK) { // We have an unknown token if the payload is encrypted after a ssl handshake // It is valid but we don't know how to parse it yet // TODO It would be better if we knew the values of the encryption options exchanged in prelogin messages if (unknown_token) return PROTO_OK; return status; } // Sanity check on pkt number if (tds_header.pkt_number > 1 && ((tds_parser->pkt_number + 1) != tds_header.pkt_number)) { SLOG(LOG_DEBUG, "Expected pkt number %"PRIu8", got %"PRIu8"", tds_parser->pkt_number + 1, tds_header.pkt_number); tds_parser->pkt_number = 1; return PROTO_OK; } else if (tds_header.pkt_number <= 1 || (tds_header.status & TDS_EOM)) { SLOG(LOG_DEBUG, "Reset pkt number from %"PRIu8"", tds_parser->pkt_number); tds_parser->pkt_number = 1; } // Sanity check on channels if (tds_parser->channels[way] && (tds_parser->channels[way] != tds_header.channel)) { SLOG(LOG_DEBUG, "Expected channel %"PRIu16", got channel %"PRIu16"", tds_parser->channels[way], tds_header.channel); return PROTO_OK; } if (wire_len > tds_header.len) { SLOG(LOG_DEBUG, "Wire len %zu unexpected (> %zu), considering a gap", wire_len, tds_header.len); has_gap = true; } tds_parser->data_left = wire_len >= tds_header.len ? 0 : tds_header.len - wire_len; SLOG(LOG_DEBUG, "Data left after wire %zu", tds_parser->data_left); if (tds_parser->data_left > 0 && !has_gap) { SLOG(LOG_DEBUG, "Incomplete tds packet, buffering it"); streambuf_set_restart(&tds_parser->sbuf, way, payload, true); return PROTO_OK; } struct tds_proto_info info; proto_info_ctor(&info.info, parser, parent, TDS_PKT_HDR_LEN, tds_header.len - TDS_PKT_HDR_LEN); info.type = tds_header.type; info.status = tds_header.status; info.length = tds_header.len; if (tds_header.channel > 0) { SLOG(LOG_DEBUG, "Saving channel %"PRIu16"", tds_header.channel); tds_parser->channels[way] = tds_header.channel; } SLOG(LOG_DEBUG, "Saving pkt number %"PRIu8"", tds_header.pkt_number); tds_parser->pkt_number = tds_header.pkt_number; SLOG(LOG_DEBUG, "Parsing %s", tds_header_2_str(&tds_header)); if (! tds_parser->msg_parser) { SLOG(LOG_DEBUG, "Building new tds_msg_parser"); tds_parser->msg_parser = proto_tds_msg->ops->parser_new(proto_tds_msg); } if (tds_parser->msg_parser) { proto_parse(tds_parser->msg_parser, &info.info, way, cursor.head, cursor.cap_len, wire_len - TDS_PKT_HDR_LEN, now, tot_cap_len, tot_packet); } // Advertise this packet if it was not done already return proto_parse(NULL, &info.info, way, payload, cap_len, wire_len, now, tot_cap_len, tot_packet); }
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) { tns_parser->c2s_way = way; SLOG(LOG_DEBUG, "First packet, init c2s_way to %u", tns_parser->c2s_way); } // Now build the proto_info struct sql_proto_info info; 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; // 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; enum tns_type pdu_type; enum proto_parse_status status = cursor_read_tns_hdr(&cursor, &pdu_len, &pdu_type); if (status == PROTO_PARSE_ERR) return status; if (status == PROTO_TOO_SHORT) { streambuf_set_restart(&tns_parser->sbuf, way, msg_start, true); SLOG(LOG_DEBUG, "Payload too short for parsing message, will restart @ %zu", tns_parser->sbuf.dir->restart_offset); return PROTO_OK; } assert(cursor.cap_len >= pdu_len); // We have the whole msg ready to be read struct cursor msg; cursor_ctor(&msg, cursor.head, pdu_len); switch (pdu_type) { case TNS_CONNECT: info.msg_type = SQL_STARTUP; status = tns_parse_connect(tns_parser, &info, &msg); break; case TNS_ACCEPT: info.msg_type = SQL_STARTUP; status = tns_parse_accept(tns_parser, &info, &msg); break; case TNS_DATA: status = tns_parse_data(tns_parser, &info, &msg, 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"); 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; }
static enum proto_parse_status tds_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) { if (cap_len == 0 && wire_len > 0) return PROTO_TOO_SHORT; // We do not know how to handle pure gaps struct tds_parser *tds_parser = DOWNCAST(parser, parser, tds_parser); SLOG(LOG_DEBUG, "Got tds packet, data_left %zu, way %d", tds_parser->data_left, way); bool has_gap = wire_len > cap_len; if (tds_parser->had_gap && tds_parser->data_left > 0) { tds_parser->data_left = wire_len > tds_parser->data_left ? 0 : tds_parser->data_left - wire_len; SLOG(LOG_DEBUG, "Discard tds with gap, data_left %zu...", tds_parser->data_left); timeval_reset(&tds_parser->first_ts); return PROTO_OK; } tds_parser->had_gap = has_gap; struct tds_header tds_header; bool unknown_token = false; struct cursor cursor; enum proto_parse_status status; cursor_ctor(&cursor, payload, cap_len); struct smp_header smp_header = {.length = 0}; if (PROTO_OK != (status = tds_parse_smp_header(&cursor, &smp_header))) return status; if (smp_header.length) { SLOG(LOG_DEBUG, "Smp header: %s", smp_header_2_str(&smp_header)); wire_len -= SMP_PKT_HDR_LEN; cap_len -= SMP_PKT_HDR_LEN; if (smp_header.flags & SMP_ACK) return PROTO_OK; } status = tds_parse_header(&cursor, &tds_header, &unknown_token); if (status != PROTO_OK) { // We have an unknown token if the payload is encrypted after a ssl handshake // It is valid but we don't know how to parse it yet // TODO It would be better if we knew the values of the encryption options exchanged in prelogin messages timeval_reset(&tds_parser->first_ts); if (unknown_token) return PROTO_OK; return status; } // Sanity check on pkt number if (tds_header.pkt_number > 1 && ((tds_parser->pkt_number + 1) != tds_header.pkt_number)) { SLOG(LOG_DEBUG, "Expected pkt number %"PRIu8", got %"PRIu8"", tds_parser->pkt_number + 1, tds_header.pkt_number); tds_parser->pkt_number = 1; timeval_reset(&tds_parser->first_ts); return PROTO_PARSE_ERR; } else if (tds_header.pkt_number <= 1) { SLOG(LOG_DEBUG, "Reset pkt number from %"PRIu8"", tds_parser->pkt_number); tds_parser->pkt_number = 1; } // Sanity check on channels if (tds_parser->channels[way] && (tds_parser->channels[way] != tds_header.channel)) { SLOG(LOG_DEBUG, "Expected channel %"PRIu16", got channel %"PRIu16"", tds_parser->channels[way], tds_header.channel); timeval_reset(&tds_parser->first_ts); return PROTO_PARSE_ERR; } if (wire_len > tds_header.len) { SLOG(LOG_DEBUG, "Wire len %zu unexpected (> %zu), considering a gap", wire_len, tds_header.len); has_gap = true; } tds_parser->data_left = wire_len >= tds_header.len ? 0 : tds_header.len - wire_len; SLOG(LOG_DEBUG, "Data left after wire %zu", tds_parser->data_left); if (tds_parser->data_left > 0 && !has_gap) { SLOG(LOG_DEBUG, "Incomplete tds packet, buffering it"); if (!timeval_is_set(&tds_parser->first_ts)) { SLOG(LOG_DEBUG, "Setting first ts to %s for way %d", timeval_2_str(now), way); tds_parser->first_ts = *now; } proto_parse(NULL, parent, way, NULL, 0, 0, now, tot_cap_len, tot_packet); streambuf_set_restart(&tds_parser->sbuf, way, payload, tds_header.len); return PROTO_OK; } // We have a buffered tds packet at this point if (!timeval_is_set(&tds_parser->first_ts)) { SLOG(LOG_DEBUG, "Setting first ts to %s for way %d since it is not setted", timeval_2_str(now), way); tds_parser->first_ts = *now; } struct tds_proto_info info; proto_info_ctor(&info.info, parser, parent, TDS_PKT_HDR_LEN, tds_header.len - TDS_PKT_HDR_LEN); info.type = tds_header.type; info.status = tds_header.status; info.length = tds_header.len; info.first_ts = tds_parser->first_ts; info.has_gap = has_gap; if (tds_header.channel > 0) { SLOG(LOG_DEBUG, "Saving channel %"PRIu16"", tds_header.channel); tds_parser->channels[way] = tds_header.channel; } SLOG(LOG_DEBUG, "Saving pkt number %"PRIu8"", tds_header.pkt_number); tds_parser->pkt_number = tds_header.pkt_number; SLOG(LOG_DEBUG, "Parsing %s", tds_header_2_str(&tds_header)); if (! tds_parser->msg_parser) { SLOG(LOG_DEBUG, "Building new tds_msg_parser"); tds_parser->msg_parser = proto_tds_msg->ops->parser_new(proto_tds_msg); if (!tds_parser->msg_parser) { SLOG(LOG_INFO, "Could not build tds msg parser"); return PROTO_PARSE_ERR; } } if (tds_header.status & TDS_EOM) { SLOG(LOG_DEBUG, "Reset pkt number from %"PRIu8" since we parsed an EOM", tds_parser->pkt_number); tds_parser->pkt_number = 1; } status = proto_parse(tds_parser->msg_parser, &info.info, way, cursor.head, cursor.cap_len, wire_len - TDS_PKT_HDR_LEN, now, tot_cap_len, tot_packet); if (status != PROTO_OK) { SLOG(LOG_INFO, "Tds msg parse failed, returning %s", proto_parse_status_2_str(status)); return status; } timeval_reset(&tds_parser->first_ts); // Advertise this packet if it was not done already return proto_parse(NULL, &info.info, way, payload, cap_len, wire_len, now, tot_cap_len, tot_packet); } static enum proto_parse_status tds_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 tds_parser *tds_parser = DOWNCAST(parser, parser, tds_parser); enum proto_parse_status const status = streambuf_add(&tds_parser->sbuf, parser, parent, way, payload, cap_len, wire_len, now, tot_cap_len, tot_packet); return status; } /* * Construction/Destruction */ static struct proto proto_tds_; struct proto *proto_tds = &proto_tds_; static struct port_muxer tds_tcp_muxer; void tds_init(void) { log_category_proto_tds_init(); static struct proto_ops const ops = { .parse = tds_parse, .parser_new = tds_parser_new, .parser_del = tds_parser_del, .info_2_str = tds_info_2_str, .info_addr = tds_info_addr }; proto_ctor(&proto_tds_, &ops, "TDS", PROTO_CODE_TDS); port_muxer_ctor(&tds_tcp_muxer, &tcp_port_muxers, 1433, 1433, proto_tds); }
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 pg_parse_init(struct pgsql_parser *pg_parser, struct sql_proto_info *info, 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) { info->msg_type = SQL_STARTUP; /* NONE phase is when we haven't seen the startup message yet. * In this phase, we expect to see from the client a startup message, * and from the server nothing but an answer to an SSL request. */ if (info->is_query) { struct cursor cursor; cursor_ctor(&cursor, payload, cap_len); // Startup message comes without a type tag size_t len; enum proto_parse_status status = cursor_read_msg(&cursor, NULL, &len); if (status != PROTO_OK) return status; SLOG(LOG_DEBUG, "Msg of length %zu", len); if (len < 4) return PROTO_PARSE_ERR; uint32_t msg = cursor_read_u32n(&cursor); if (msg == 80877103) { // magic value for SSL request SLOG(LOG_DEBUG, "Msg is an SSL request"); info->set_values |= SQL_SSL_REQUEST; info->u.startup.ssl_request = SQL_SSL_REQUESTED; } else if (msg == 196608) { // version number, here 00 03 00 00 (ie. 3.0), which is parsed here SLOG(LOG_DEBUG, "Msg is a startup message for v3.0"); info->version_maj = 3; info->version_min = 0; info->set_values |= SQL_VERSION; // fine, now parse all the strings that follow do { char *name, *value; status = cursor_read_string(&cursor, &name, len); if (status != PROTO_OK) return status; if (name[0] == '\0') break; status = cursor_read_string(&cursor, &value, len); if (status != PROTO_OK) return status; if (0 == strcmp(name, "user")) { info->set_values |= SQL_USER; snprintf(info->u.startup.user, sizeof(info->u.startup.user), "%s", value); } else if (0 == strcmp(name, "database")) { info->set_values |= SQL_DBNAME; snprintf(info->u.startup.dbname, sizeof(info->u.startup.dbname), "%s", value); } } while (1); // and enter "startup phase" untill the server is ready for query pg_parser->phase = STARTUP; } else { SLOG(LOG_DEBUG, "Unknown message"); return PROTO_PARSE_ERR; } } else { // reply (to an SSL request) if (wire_len != 1 || cap_len < 1) return PROTO_TOO_SHORT; info->set_values |= SQL_SSL_REQUEST; if (payload[0] == 'S') { info->u.startup.ssl_request = SQL_SSL_GRANTED; // We will get parse errors from now on :-< } else if (payload[0] == 'N') { info->u.startup.ssl_request = SQL_SSL_REFUSED; } else { return PROTO_PARSE_ERR; } } return proto_parse(NULL, &info->info, way, NULL, 0, 0, now, tot_cap_len, tot_packet); }