static enum proto_parse_status netbios_parse(struct parser *parser, struct proto_layer *parent, unsigned way, uint8_t const *packet, size_t cap_len, size_t wire_len, struct timeval const *now, proto_okfn_t *okfn) { /* Sanity checks */ if (wire_len < NETBIOS_HEADER_SIZE) return PROTO_PARSE_ERR; if (cap_len < NETBIOS_HEADER_SIZE) return PROTO_TOO_SHORT; if (! packet_is_netbios(packet, cap_len)) return PROTO_PARSE_ERR; /* Parse */ struct netbios_proto_info info; netbios_proto_info_ctor(&info, NETBIOS_HEADER_SIZE, wire_len - NETBIOS_HEADER_SIZE); struct proto_layer layer; proto_layer_ctor(&layer, parent, parser, &info.info); uint8_t const *next_packet = packet + NETBIOS_HEADER_SIZE; struct parser *subparser = proto_cifs->ops->parser_new(proto_cifs, now); if (! subparser) goto fallback; /* List of protocols above NetBios: CIFS, SMB, ... */ int err = proto_parse(subparser, parent, way, next_packet, cap_len - NETBIOS_HEADER_SIZE, wire_len - NETBIOS_HEADER_SIZE, now, okfn); parser_unref(subparser); if (err) goto fallback; return PROTO_OK; fallback: (void)proto_parse(NULL, parent, way, next_packet, cap_len - NETBIOS_HEADER_SIZE, wire_len - NETBIOS_HEADER_SIZE, now, okfn); return PROTO_OK; }
static enum proto_parse_status icmpv6_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 icmp_hdr *icmphdr = (struct icmp_hdr *)packet; // Sanity checks if (wire_len < sizeof(*icmphdr)) return PROTO_PARSE_ERR; if (cap_len < sizeof(*icmphdr)) return PROTO_TOO_SHORT; struct icmp_proto_info info; uint8_t const type = READ_U8(&icmphdr->type); icmpv6_proto_info_ctor(&info, parser, parent, wire_len, type, READ_U8(&icmphdr->code)); if (icmpv6_has_id(type)) { info.set_values |= ICMP_ID_SET; info.id = READ_U16N(&icmphdr->id); } // Extract error values if (icmpv6_is_err(type)) { if (0 == icmpv6_extract_err_infos(&info, packet + sizeof(*icmphdr), cap_len - sizeof(*icmphdr))) { info.set_values |= ICMP_ERR_SET; } } return proto_parse(NULL, &info.info, way, NULL, 0, 0, now, tot_cap_len, tot_packet); }
static enum proto_parse_status fcoe_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) { /* We expect a packet size such that the data that contains the * ethernet type (0x8906) and the payload are a multiple of a * "word" (4 bytes). 18 bytes is the minimum size if the * encapsulated FC Frame is empty. */ if (wire_len < FCOE_WRAPPER_SIZE || (wire_len - 2) % 4 != 0) { return PROTO_PARSE_ERR; } /* We need the whole packet */ if (cap_len < wire_len) { return PROTO_TOO_SHORT; } /* packet[1]..packet[12] are reserved (always 0?) */ if ((packet[0] & 0x0f) != 0 || packet[1] != 0) { // Probably pre T11.3. Not supported.. yet. return PROTO_PARSE_ERR; } int version = (packet[0] & 0xf0) >> 4; int sof = packet[13]; // start of frame int eof = packet[wire_len - 4]; // end of frame switch (sof) { case 0x28: // SOFf case 0x2d: // SOFi2 case 0x35: // SOFn2 case 0x2e: // SOFi3 case 0x36: // SOFn3 // Valid SOF break; default: return PROTO_PARSE_ERR; } switch (eof) { case 0x41: // EOFn case 0x42: // EOFt case 0x49: // EOFni case 0x50: // EOFa // Valid EOF break; default: return PROTO_PARSE_ERR; } struct fcoe_proto_info info; fcoe_proto_info_ctor(&info, parser, parent, FCOE_WRAPPER_SIZE, wire_len-FCOE_WRAPPER_SIZE, version, sof, eof); return proto_parse(NULL, &info.info, way, NULL, 0, 0, now, tot_cap_len, tot_packet); }
static enum proto_parse_status cifs_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) { /* Sanity checks */ if (wire_len < CIFS_HEADER_SIZE) return PROTO_PARSE_ERR; if (cap_len < CIFS_HEADER_SIZE) return PROTO_TOO_SHORT; struct cifs_hdr const *cifshdr = (struct cifs_hdr const *) packet; if (! packet_is_cifs(cifshdr)) return PROTO_PARSE_ERR; struct cifs_proto_info info; cifs_proto_info_ctor(&info, parser, parent, CIFS_HEADER_SIZE, wire_len - CIFS_HEADER_SIZE, cifshdr); return proto_parse(NULL, &info.info, way, NULL, 0, 0, now, tot_cap_len, tot_packet); }
// cap_len is not the length of the actual packet but the size of the data we receive, ie struct frame + what we captured from the wire. static enum proto_parse_status cap_parse(struct parser *parser, struct proto_info *parent, unsigned way, uint8_t const *packet, size_t unused_ cap_len, size_t unused_ wire_len, struct timeval const *now, size_t tot_cap_len, uint8_t const *tot_packet) { struct mux_parser *mux_parser = DOWNCAST(parser, parser, mux_parser); struct frame const *frame = (struct frame *)packet; // Parse struct cap_proto_info info; cap_proto_info_ctor(&info, parser, parent, frame); // Get an eth parser for this dev_id, or create one struct mux_subparser *subparser = mux_subparser_lookup(mux_parser, proto_eth, NULL, collapse_ifaces ? &zero : &frame->pkt_source->dev_id, now); if (! subparser) goto fallback; enum proto_parse_status status = proto_parse(subparser->parser, &info.info, way, frame->data, frame->cap_len, frame->wire_len, now, tot_cap_len, tot_packet); mux_subparser_unref(&subparser); if (status == PROTO_OK) return PROTO_OK; fallback: (void)proto_parse(NULL, &info.info, way, frame->data, frame->cap_len, frame->wire_len, now, tot_cap_len, tot_packet); return PROTO_OK; }
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 icmp_parse(struct parser *parser, struct proto_layer *parent, unsigned way, uint8_t const *packet, size_t cap_len, size_t wire_len, struct timeval const *now, proto_okfn_t *okfn) { struct icmp_hdr *icmphdr = (struct icmp_hdr *)packet; // Sanity checks if (wire_len < sizeof(*icmphdr)) return PROTO_PARSE_ERR; if (cap_len < sizeof(*icmphdr)) return PROTO_TOO_SHORT; struct icmp_proto_info info; icmp_proto_info_ctor(&info, wire_len, icmphdr->type, icmphdr->code); struct proto_layer layer; proto_layer_ctor(&layer, parent, parser, &info.info); // Extract error values if (icmp_is_err(icmphdr->type)) { if (0 == icmp_extract_err_infos(&info, packet + sizeof(*icmphdr), cap_len - sizeof(*icmphdr))) { info.set_values |= ICMP_ERR_SET; } } return proto_parse(NULL, &layer, way, NULL, 0, 0, now, okfn); }
static enum proto_parse_status rtp_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) { SLOG(LOG_DEBUG, "Starting RTP analysis"); /* Parse */ struct rtp_hdr *rtph = (struct rtp_hdr *)packet; if (wire_len < sizeof(*rtph)) return PROTO_PARSE_ERR; if (cap_len < sizeof(*rtph)) return PROTO_TOO_SHORT; unsigned const version = READ_U8(&rtph->flags0) >> 6U; unsigned const csrc_count = READ_U8(&rtph->flags0) & F0_CSRC_COUNT_MASK; unsigned const payload_type = READ_U8(&rtph->flags1) & F1_PLD_TYPE_MASK; SLOG(LOG_DEBUG, "RTP header, version=%u, CSRC_count=%u, payload_type=%u", version, csrc_count, payload_type); size_t head_len = sizeof(*rtph) + csrc_count * 4; if (wire_len < head_len) return PROTO_PARSE_ERR; if (cap_len < head_len) return PROTO_TOO_SHORT; struct rtp_proto_info info; rtp_proto_info_ctor(&info, parser, parent, rtph, head_len, wire_len - head_len); return proto_parse(NULL, &info.info, way, NULL, 0, 0, now, tot_cap_len, tot_packet); }
static enum proto_parse_status rtp_parse(struct parser *parser, struct proto_layer *parent, unsigned way, uint8_t const *packet, size_t cap_len, size_t wire_len, struct timeval const *now, proto_okfn_t *okfn) { SLOG(LOG_DEBUG, "Starting RTP analysis"); /* Parse */ struct rtp_header *rtph = (struct rtp_header *)packet; if (wire_len < sizeof(*rtph)) return PROTO_PARSE_ERR; if (cap_len < sizeof(*rtph)) return PROTO_TOO_SHORT; SLOG(LOG_DEBUG, "RTP header, version=%u, CSRC_count=%u, payload_type=%u", rtph->version, rtph->csrc_count, rtph->payload_type); size_t head_len = sizeof(*rtph) + rtph->csrc_count * 4; if (wire_len < head_len) return PROTO_PARSE_ERR; if (cap_len < head_len) return PROTO_TOO_SHORT; struct rtp_proto_info info; rtp_proto_info_ctor(&info, rtph, head_len, wire_len - head_len); struct proto_layer layer; proto_layer_ctor(&layer, parent, parser, &info.info); return proto_parse(NULL, &layer, way, NULL, 0, 0, now, okfn); }
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 dns_tcp_parse(struct parser unused_ *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) { size_t const hlen = 2; size_t offset = 0; while (offset + hlen < cap_len) { size_t len = READ_U16N(packet); offset += hlen; // Sanity check if (offset + len > wire_len) return PROTO_PARSE_ERR; struct parser *dns = proto_dns->ops->parser_new(proto_dns); if (! dns) break; enum proto_parse_status status = proto_parse(dns, parent, way, packet+offset, cap_len-offset, wire_len-offset, now, tot_cap_len, tot_packet); parser_unref(&dns); if (status != PROTO_OK) break; offset += len; } return PROTO_OK; }
static enum proto_parse_status dns_tcp_parse(struct parser unused_ *parser, struct proto_layer *parent, unsigned way, uint8_t const *packet, size_t cap_len, size_t wire_len, struct timeval const *now, proto_okfn_t *okfn) { size_t const hlen = 2; size_t offset = 0; while (offset + hlen < cap_len) { size_t len = ntohs(*(uint16_t*)packet); offset += hlen; // Sanity check if (offset + len > wire_len) return PROTO_PARSE_ERR; struct parser *subparser = proto_dns->ops->parser_new(proto_dns, now); if (! subparser) break; int err = proto_parse(subparser, parent, way, packet+offset, cap_len-offset, wire_len-offset, now, okfn); parser_unref(subparser); if (err) break; offset += len; } return PROTO_OK; }
int main(int argc, char *argv[]) { const struct parsed_proto *pp; FILE *fout, *fhdr; char basename[256] = { 0, }; char line[256]; char fmt[256]; char word[256]; int noname = 0; const char *p2; char *p; int arg; int ret, ord; int l; for (arg = 1; arg < argc; arg++) { if (IS(argv[arg], "-n")) noname = 1; else if (IS(argv[arg], "-b") && arg < argc - 1) snprintf(basename, sizeof(basename), "%s", argv[++arg]); else break; } if (argc != arg + 2) { printf("usage:\n%s [-n] [-b <basename>] <.h> <.def>\n", argv[0]); return 1; } hdrfn = argv[arg++]; fhdr = fopen(hdrfn, "r"); my_assert_not(fhdr, NULL); fout = fopen(argv[arg++], "w"); my_assert_not(fout, NULL); if (basename[0] == 0) { p = strrchr(hdrfn, '.'); my_assert_not(p, NULL); p2 = strrchr(hdrfn, '/'); if (p2++ == NULL) p2 = hdrfn; l = p - p2; my_assert((unsigned int)l < 256, 1); memcpy(basename, p2, l); basename[l] = 0; } snprintf(fmt, sizeof(fmt), "%s_%%d", basename); fprintf(fout, "LIBRARY %s\n", basename); fprintf(fout, "EXPORTS\n"); while (fgets(line, sizeof(line), fhdr)) { p = sskip(line); if (*p == 0) continue; if (IS_START(p, "//")) continue; ret = 0; while (p != NULL && *p != 0) { p = next_word(word, sizeof(word), p); ret = sscanf(word, fmt, &ord); if (ret == 1) break; } if (ret != 1) { printf("scan for '%s' failed for '%s'\n", fmt, line); return 1; } snprintf(word, sizeof(word), fmt, ord); pp = proto_parse(fhdr, word, 0); if (pp == NULL) return 1; fputc(' ', fout); fputc(pp->is_fastcall ? '@' : ' ', fout); fprintf(fout, "%s", word); if (pp->is_stdcall) fprintf(fout, "@%-2d", pp->argc * 4); else fprintf(fout, " "); fprintf(fout, " @%d", ord); if (noname) fprintf(fout, " NONAME"); fprintf(fout, "\n"); } fclose(fhdr); fclose(fout); return 0; }
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 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 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); }
static enum proto_parse_status eth_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 mux_parser *mux_parser = DOWNCAST(parser, parser, mux_parser); struct eth_hdr const *ethhdr = (struct eth_hdr *)packet; uint16_t h_proto = READ_U16N(ðhdr->proto); int vlan_id = VLAN_UNSET; size_t ethhdr_len = sizeof(*ethhdr); // Sanity checks if (wire_len < ethhdr_len) { SLOG(LOG_DEBUG, "Bogus Eth packet: too short (%zu < %zu)", wire_len, ethhdr_len); return PROTO_PARSE_ERR; } if (cap_len < ethhdr_len) return PROTO_TOO_SHORT; if (h_proto == 0) { // Take into account Linux Cooked Capture if (cap_len < ethhdr_len + 2) return PROTO_TOO_SHORT; struct eth_lcc { uint16_t h_proto; } packed_ *eth_lcc = (struct eth_lcc *)((char *)ethhdr + ethhdr_len); h_proto = READ_U16N(ð_lcc->h_proto); ethhdr_len += 2; // We dont care about the source MAC being funny } while (h_proto == ETH_PROTO_8021Q || h_proto == ETH_PROTO_8021QinQ || h_proto == ETH_PROTO_8021QinQ_alt) { // Take into account 802.1q vlan tag (with possible QinQ) if (cap_len < ethhdr_len + 4) return PROTO_TOO_SHORT; struct eth_vlan { uint16_t vlan_id, h_proto; } packed_ *eth_vlan = (struct eth_vlan *)((char *)ethhdr + ethhdr_len); h_proto = READ_U16N(ð_vlan->h_proto); vlan_id = READ_U16N(ð_vlan->vlan_id) & 0xfff; ethhdr_len += 4; } size_t frame_wire_len = wire_len - ethhdr_len; if (h_proto <= 1500) { // h_proto is then the length of payload /* According to IEEE Std. 802.3: * "This two-octet field takes one of two meanings, depending on its numeric value. For numerical evaluation, * the first octet is the most significant octet of this field. * a) If the value of this field is less than or equal to 1500 decimal (05DC hexadecimal), then the Length/ * Type field indicates the number of MAC client data octets contained in the subsequent MAC Client * Data field of the basic frame (Length interpretation). * b) If the value of this field is greater than or equal to 1536 decimal (0600 hexadecimal), then the * Length/Type field indicates the nature of the MAC client protocol (Type interpretation). * The Length and Type interpretations of this field are mutually exclusive." */ if (h_proto > frame_wire_len) { SLOG(LOG_DEBUG, "Bogus Eth packet: specified length too bug (%"PRIu16" > %zu)", h_proto, frame_wire_len); return PROTO_PARSE_ERR; } frame_wire_len = h_proto; h_proto = 0; // no indication of a protocol, then } size_t const frame_cap_len = MIN(cap_len - ethhdr_len, frame_wire_len); // Parse struct eth_proto_info info; eth_proto_info_ctor(&info, parser, parent, ethhdr_len, frame_wire_len, h_proto, vlan_id, ethhdr); if (! h_proto) goto fallback; // no indication of what's the payload struct proto *sub_proto = eth_subproto_lookup(h_proto); struct mux_subparser *subparser = mux_subparser_lookup(mux_parser, sub_proto, NULL, collapse_vlans ? &vlan_unset : &vlan_id, now); if (! subparser) goto fallback; assert(ethhdr_len <= cap_len); enum proto_parse_status status = proto_parse(subparser->parser, &info.info, way, packet + ethhdr_len, frame_cap_len, frame_wire_len, now, tot_cap_len, tot_packet); mux_subparser_unref(&subparser); if (status == PROTO_OK) return PROTO_OK; fallback: (void)proto_parse(NULL, &info.info, way, packet + ethhdr_len, frame_cap_len, frame_wire_len, now, tot_cap_len, tot_packet); return PROTO_OK; }
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 tcp_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 mux_parser *mux_parser = DOWNCAST(parser, parser, mux_parser); struct tcp_hdr const *tcphdr = (struct tcp_hdr *)packet; // Sanity checks if (wire_len < sizeof(*tcphdr)) { SLOG(LOG_DEBUG, "Bogus TCP packet: too short (%zu < %zu)", wire_len, sizeof(*tcphdr)); return PROTO_PARSE_ERR; } if (cap_len < sizeof(*tcphdr)) return PROTO_TOO_SHORT; size_t tcphdr_len = TCP_HDR_LENGTH(tcphdr); if (tcphdr_len < sizeof(*tcphdr)) { SLOG(LOG_DEBUG, "Bogus TCP packet: header size too smal (%zu < %zu)", tcphdr_len, sizeof(*tcphdr)); return PROTO_PARSE_ERR; } if (tcphdr_len > wire_len) { SLOG(LOG_DEBUG, "Bogus TCP packet: wrong length %zu > %zu", tcphdr_len, wire_len); return PROTO_PARSE_ERR; } if (tcphdr_len > cap_len) return PROTO_TOO_SHORT; // TODO: move this below call to tcp_proto_info_ctor() and use info instead of reading tcphdr directly uint16_t const sport = READ_U16N(&tcphdr->src); uint16_t const dport = READ_U16N(&tcphdr->dst); bool const syn = !!(READ_U8(&tcphdr->flags) & TCP_SYN_MASK); bool const fin = !!(READ_U8(&tcphdr->flags) & TCP_FIN_MASK); bool const ack = !!(READ_U8(&tcphdr->flags) & TCP_ACK_MASK); bool const rst = !!(READ_U8(&tcphdr->flags) & TCP_RST_MASK); bool const urg = !!(READ_U8(&tcphdr->flags) & TCP_URG_MASK); bool const psh = !!(READ_U8(&tcphdr->flags) & TCP_PSH_MASK); SLOG(LOG_DEBUG, "New TCP packet of %zu bytes (%zu captured), %zu payload, ports %"PRIu16" -> %"PRIu16" Flags: %s%s%s%s%s%s, Seq:%"PRIu32", Ack:%"PRIu32, wire_len, cap_len, wire_len - tcphdr_len, sport, dport, syn ? "Syn":"", fin ? "Fin":"", ack ? "Ack":"", rst ? "Rst":"", urg ? "Urg":"", psh ? "Psh":"", READ_U32N(&tcphdr->seq_num), READ_U32N(&tcphdr->ack_seq)); // Parse struct tcp_proto_info info; tcp_proto_info_ctor(&info, parser, parent, tcphdr_len, wire_len - tcphdr_len, sport, dport, tcphdr); // Parse TCP options uint8_t const *options = (uint8_t *)(tcphdr+1); assert(tcphdr_len >= sizeof(*tcphdr)); for (size_t rem_len = tcphdr_len - sizeof(*tcphdr); rem_len > 0; ) { ssize_t const len = parse_next_option(&info, options, rem_len); if (len < 0) return PROTO_PARSE_ERR; rem_len -= len; options += len; } // Search an already spawned subparser struct port_key key; port_key_init(&key, sport, dport, way); struct mux_subparser *subparser = mux_subparser_lookup(mux_parser, NULL, NULL, &key, now); if (subparser) SLOG(LOG_DEBUG, "Found subparser@%p for this cnx, for proto %s", subparser->parser, subparser->parser->proto->name); if (! subparser) { struct proto *requestor = NULL; struct proto *sub_proto = NULL; // Use connection tracking first ASSIGN_INFO_OPT2(ip, ip6, parent); if (! ip) ip = ip6; if (ip) sub_proto = cnxtrack_ip_lookup(IPPROTO_TCP, ip->key.addr+0, sport, ip->key.addr+1, dport, now, &requestor); if (! sub_proto) { // Then try predefined ports sub_proto = port_muxer_find(&tcp_port_muxers, info.key.port[0], info.key.port[1]); } if (sub_proto) { subparser = mux_subparser_and_parser_new(mux_parser, sub_proto, requestor, &key, now); } else { // Even if we have no child parser to send payload to, we want to submit payload in stream order to our plugins subparser = tcp_subparser_new(mux_parser, NULL, NULL, &key, now); } } if (! subparser) goto fallback; // Keep track of TCP flags & ISN struct tcp_subparser *tcp_sub = DOWNCAST(subparser, mux_subparser, tcp_subparser); mutex_lock(tcp_sub->mutex); if ( info.ack && (!IS_SET_FOR_WAY(way, tcp_sub->ack) || seqnum_gt(info.ack_num, tcp_sub->max_acknum[way])) ) { SET_FOR_WAY(way, tcp_sub->ack); tcp_sub->max_acknum[way] = info.ack_num; } if (info.fin) { SET_FOR_WAY(way, tcp_sub->fin); tcp_sub->fin_seqnum[way] = info.seq_num + info.info.payload; // The FIN is acked after the payload } if (info.syn && !IS_SET_FOR_WAY(way, tcp_sub->syn)) { SET_FOR_WAY(way, tcp_sub->syn); tcp_sub->isn[way] = info.seq_num; } if (!IS_SET_FOR_WAY(way, tcp_sub->origin)) { SET_FOR_WAY(way, tcp_sub->origin); tcp_sub->wl_origin[way] = info.seq_num; if (! IS_SET_FOR_WAY(way, tcp_sub->syn)) SLOG(LOG_DEBUG, "Starting a WL while SYN is yet to be received!"); } // Set relative sequence number if we know it if (IS_SET_FOR_WAY(way, tcp_sub->syn)) info.rel_seq_num = info.seq_num - tcp_sub->isn[way]; // Set srv_way assert(tcp_sub->srv_set < 3); if (tcp_sub->srv_set == 0 || (tcp_sub->srv_set == 1 && info.syn)) { if (comes_from_client(info.key.port, info.syn, info.ack)) { // this packet comes from the client tcp_sub->srv_way = !way; } else { tcp_sub->srv_way = way; } tcp_sub->srv_set = info.syn ? 2:1; } // Now patch it into tcp info info.to_srv = tcp_sub->srv_way != way; SLOG(LOG_DEBUG, "Subparser@%p state: >ISN:%"PRIu32"%s Fin:%"PRIu32" Ack:%"PRIu32" <ISN:%"PRIu32"%s Fin:%"PRIu32" Ack:%"PRIu32", SrvWay=%u%s", subparser->parser, IS_SET_FOR_WAY(0, tcp_sub->syn) ? tcp_sub->isn[0] : IS_SET_FOR_WAY(0, tcp_sub->origin) ? tcp_sub->wl_origin[0] : 0, IS_SET_FOR_WAY(0, tcp_sub->syn) ? "" : " (approx)", IS_SET_FOR_WAY(0, tcp_sub->fin) ? tcp_sub->fin_seqnum[0] : 0, IS_SET_FOR_WAY(0, tcp_sub->ack) ? tcp_sub->max_acknum[0] : 0, IS_SET_FOR_WAY(1, tcp_sub->syn) ? tcp_sub->isn[1] : IS_SET_FOR_WAY(1, tcp_sub->origin) ? tcp_sub->wl_origin[1] : 0, IS_SET_FOR_WAY(1, tcp_sub->syn) ? "" : " (approx)", IS_SET_FOR_WAY(1, tcp_sub->fin) ? tcp_sub->fin_seqnum[1] : 0, IS_SET_FOR_WAY(1, tcp_sub->ack) ? tcp_sub->max_acknum[1] : 0, tcp_sub->srv_way, tcp_sub->srv_set == 0 ? " (unset)": tcp_sub->srv_set == 1 ? " (unsure)":"(certain)"); enum proto_parse_status err; /* Use the wait_list to parse this packet. Notice that we do queue empty packets because subparser (or subscriber) want to receive all packets in order, including empty ones. */ size_t const packet_len = wire_len - tcphdr_len; assert(IS_SET_FOR_WAY(way, tcp_sub->origin)); unsigned const offset = info.seq_num - tcp_sub->wl_origin[way]; unsigned const next_offset = offset + packet_len + info.syn + info.fin; unsigned const sync_offset = info.ack_num - tcp_sub->wl_origin[!way]; // we must not parse this one before we parsed (or timeouted) this one from wl[!way] // FIXME: Here the parser is chosen before we actually parse anything. If later the parser fails we cannot try another one. // Choice of parser should be delayed until we start actual parse. bool const do_sync = info.ack && IS_SET_FOR_WAY(!way, tcp_sub->origin); err = pkt_wait_list_add(tcp_sub->wl+way, offset, next_offset, do_sync, sync_offset, true, &info.info, way, packet + tcphdr_len, cap_len - tcphdr_len, packet_len, now, tot_cap_len, tot_packet); SLOG(LOG_DEBUG, "Waiting list returned %s", proto_parse_status_2_str(err)); if (err == PROTO_OK) { // Try advancing each WL until we are stuck or met an error pkt_wait_list_try_both(tcp_sub->wl+!way, &err, now, false); } bool const term = tcp_subparser_term(tcp_sub); mutex_unlock(tcp_sub->mutex); if (term || err == PROTO_PARSE_ERR) { if (term) { SLOG(LOG_DEBUG, "TCP cnx terminated (was %s)", parser_name(subparser->parser)); } else { SLOG(LOG_DEBUG, "No suitable subparser for this payload"); } mux_subparser_deindex(subparser); } mux_subparser_unref(&subparser); if (err == PROTO_OK) return PROTO_OK; fallback: (void)proto_parse(NULL, &info.info, way, packet + tcphdr_len, cap_len - tcphdr_len, wire_len - tcphdr_len, now, tot_cap_len, tot_packet); return PROTO_OK; }
// Lua: mcu.read( pin ) static int mcu_in_read( lua_State* L ) { static uint8_t adc_inc = 0, len; uint8_t txdata[32], txbuf[16], rxdata[32], rxbuf[16], ret = 0, i; uint16_t phVal, vrefVal; float oph = 4.8387, vref, pHvalue, pHvolt; txdata[0] = CMD_READ_ANALOG; txdata[1] = lua_tointeger( L, 1 ); txdata[2] = adc_inc ++; len = proto_create(txdata, txbuf, 3); transfer(txbuf, NULL, 16); os_delay_us(2000); txbuf[0] = 0x7E; txbuf[1] = 0xFF; txbuf[2] = 0x7F; transfer(txbuf, rxbuf, 16); /*begin transfer*/ for(i=0; i<16; i++) proto_parse(rxbuf[i], rxdata); //lua_pushinteger( L, rxdata[10]); switch(rxdata[1]) { case IN_WATER: lua_pushinteger( L, *(uint16_t *) &rxdata[2]); lua_pushinteger( L, *(uint16_t *) &rxdata[4]); ret += 2; break; case IN_PH: phVal = *(uint16_t *) &rxdata[6]; vrefVal = *(uint16_t *) &rxdata[10]; vref = (((float)4096*1.2)/vrefVal); pHvolt = ((float)phVal*vref)/4096; // pH 7.0 -> pHvolt = 1.75 if(pHvolt >= 1.75) { pHvalue = 7.0 - ((pHvolt-1.75)*oph); } else { pHvalue = 7.0 + ((1.75-pHvolt)*oph); } //lua_pushinteger( L, phVal); //lua_pushinteger( L, vrefVal); lua_pushnumber( L, pHvalue); ret += 1; break; case IN_EC: lua_pushinteger( L, *(uint16_t *) &rxdata[8]); ret += 1; break; case IN_TEMP: lua_pushinteger( L, *(uint8_t *) &rxdata[2]); ret = 1; break; case IN_HUM: lua_pushinteger( L, *(uint8_t *) &rxdata[3]); ret = 1; break; case IN_ENV_TEMP: lua_pushinteger( L, *(uint8_t *) &rxdata[4]); ret = 1; break; default: lua_pushinteger( L, 0); ret = 1; } //while(platform_gpio_read( PIN_GPIO2 ) == 0); return ret; }
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); }
// FIXME: give wire_len to liner ?? static enum proto_parse_status mgcp_parse(struct parser *parser, struct proto_info *parent, unsigned way, uint8_t const *packet, size_t cap_len, size_t unused_ wire_len, struct timeval const *now, size_t tot_cap_len, uint8_t const *tot_packet) { struct mgcp_parser *mgcp_parser = DOWNCAST(parser, parser, mgcp_parser); size_t payload = 0; // Parse one message (in case of piggybacking, will call ourself recursively so that subscribers are called once for each msg) struct mgcp_proto_info info; struct liner liner; liner_init(&liner, &delim_lines, (char const *)packet, cap_len); // Parse command, which is either verb + seq + endpoint + version or code + seq + blabla struct liner tokenizer; liner_init(&tokenizer, &delim_blanks, liner.start, liner_tok_length(&liner)); if (liner_tok_length(&tokenizer) == 3 && isdigit(tokenizer.start[0])) { // Response info.response = true; info.u.resp.code = liner_strtoull(&tokenizer, NULL, 10); liner_next(&tokenizer); if (liner_eof(&tokenizer)) { SLOG(LOG_DEBUG, "Cannot parse MGCP response: missing TXID"); return PROTO_PARSE_ERR; } info.u.resp.txid = liner_strtoull(&tokenizer, NULL, 10); } else { // Request info.response = false; info.u.query.command = mgcp_code_2_command(tokenizer.start, liner_tok_length(&tokenizer)); if ((int)info.u.query.command == -1) return PROTO_PARSE_ERR; liner_next(&tokenizer); if (liner_eof(&tokenizer)) { SLOG(LOG_DEBUG, "Cannot parse MGCP query: missing TXID"); return PROTO_PARSE_ERR; } info.u.resp.txid = liner_strtoull(&tokenizer, NULL, 10); liner_next(&tokenizer); if (liner_eof(&tokenizer)) { SLOG(LOG_DEBUG, "Cannot parse MGCP query: missing endpoint"); return PROTO_PARSE_ERR; } copy_token(info.u.query.endpoint, sizeof(info.u.query.endpoint), &tokenizer); } liner_next(&liner); // Parse parameters up to end of msg or single dot struct parser *child = NULL; info.dialed[0] = '\0'; info.cnx_id[0] = '\0'; info.call_id[0] = '\0'; info.observed = info.signaled = 0; while (! liner_eof(&liner)) { liner_init(&tokenizer, &delim_blanks, liner.start, liner_tok_length(&liner)); liner_next(&liner); if (liner_tok_length(&tokenizer) == 0) { // we met an empty line, assume following msg is SDP if (! mgcp_parser->sdp_parser) { mgcp_parser->sdp_parser = proto_sdp->ops->parser_new(proto_sdp); } child = mgcp_parser->sdp_parser; break; } else if (liner_tok_length(&tokenizer) == 1 && tokenizer.start[0] == '.') { break; } else if (liner_tok_length(&tokenizer) == 2 && tokenizer.start[1] == ':') { char p = tokenizer.start[0]; liner_next(&tokenizer); if (liner_eof(&tokenizer)) { SLOG(LOG_DEBUG, "Cannot parse MGCP parameter '%c'", p); return PROTO_PARSE_ERR; } liner_expand(&tokenizer); SLOG(LOG_DEBUG, "parameter '%c'", p); switch (p) { case 'O': // ObservedEvents: we are looking for dialed numbers or other interresting events parse_observed_event(&info, &tokenizer); break; case 'S': parse_signal_request(&info, &tokenizer); break; case 'I': parse_connection_id(&info, &tokenizer); break; case 'C': parse_call_id(&info, &tokenizer); break; } } } // End of message const size_t tot_len = liner.start - (char const *)packet; mgcp_proto_info_ctor(&info, parser, parent, tot_len - payload, payload); if (child) { // TODO: We suppose a call is unique in the socket pair, ie. that this parser will handle only one call, so we can keep our SDP with us. // SO, create a mgcp_parser with an embedded sdp parser, created as soon as mgcp_parser is constructed. size_t const rem_len = liner_rem_length(&liner); int err = proto_parse(child, &info.info, way, (uint8_t *)liner.start, rem_len, rem_len, now, tot_cap_len, tot_packet); if (err) proto_parse(NULL, &info.info, way, NULL, 0, 0, now, tot_cap_len, tot_packet); return PROTO_OK; } (void)proto_parse(NULL, &info.info, way, NULL, 0, 0, now, tot_cap_len, tot_packet); // In case of piggybacking, we may have further messages down there if (! liner_eof(&liner)) { size_t const rem_len = liner_rem_length(&liner); return mgcp_parse(parser, parent, way, (uint8_t *)liner.start, rem_len, rem_len, now, tot_cap_len, tot_packet); } return PROTO_OK; }
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_layer *parent, unsigned way, uint8_t const *packet, size_t cap_len, size_t wire_len, struct timeval const *now, proto_okfn_t *okfn) { 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, wire_len, 0); struct proto_layer layer; proto_layer_ctor(&layer, parent, parser, &info.info); if (0 != sdper_parse(&sdper, &cap_len, packet, cap_len, &info)) return PROTO_PARSE_ERR; 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); if (! sdp_parser->host_set) { sdp_parser->host_set = true; sdp_parser->host = info.host; sdp_parser->port = info.port; ASSIGN_LAYER_AND_INFO_OPT(ip, parent); if (layer_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 spawn_subparsers(&sdp_parser->host, sdp_parser->port, &info.host, info.port, parent, now); ASSIGN_LAYER_AND_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 also if (may_use_stun[0]) { spawn_subparsers(&sdp_parser->sender, sdp_parser->port, &info.host, info.port, parent, now); } // If _this_ sender IP is different from this advertized host, start conntracking on this socket as well if (may_use_stun[1]) { spawn_subparsers(&sdp_parser->host, sdp_parser->port, &ip->key.addr[0], info.port, parent, now); } // 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_subparsers(&sdp_parser->sender, sdp_parser->port, &ip->key.addr[0], info.port, parent, now); } // TODO: terminate this parser. meanwhile, reset its state : sdp_parser->host_set = false; sdp_parser->sender_set = false; } } return proto_parse(NULL, &layer, way, NULL, 0, 0, now, okfn); }
static enum proto_parse_status udp_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 mux_parser *mux_parser = DOWNCAST(parser, parser, mux_parser); struct udp_hdr const *udphdr = (struct udp_hdr *)packet; // Sanity checks if (wire_len < sizeof(*udphdr)) { SLOG(LOG_DEBUG, "Bogus UDP packet: too short (%zu < %zu)", wire_len, sizeof(*udphdr)); return PROTO_PARSE_ERR; } if (cap_len < sizeof(*udphdr)) return PROTO_TOO_SHORT; size_t tot_len = READ_U16N(&udphdr->len); if (tot_len < sizeof(*udphdr)) { SLOG(LOG_DEBUG, "Bogus UDP packet: UDP tot len shorter than UDP header (%zu < %zu)", tot_len, sizeof(*udphdr)); return PROTO_PARSE_ERR; } size_t payload = tot_len - sizeof(*udphdr); if (payload > wire_len) { SLOG(LOG_DEBUG, "Bogus UDP packet: wrong length %zu > %zu", payload, wire_len); return PROTO_PARSE_ERR; } uint16_t const sport = READ_U16N(&udphdr->src); uint16_t const dport = READ_U16N(&udphdr->dst); SLOG(LOG_DEBUG, "New UDP packet of %zu bytes (%zu captured), ports %"PRIu16" -> %"PRIu16, wire_len, cap_len, sport, dport); // Parse struct udp_proto_info info; udp_proto_info_ctor(&info, parser, parent, sizeof(*udphdr), payload, sport, dport); // Search an already spawned subparser struct port_key key; port_key_init(&key, sport, dport, way); struct mux_subparser *subparser = mux_subparser_lookup(mux_parser, NULL, NULL, &key, now); if (subparser) SLOG(LOG_DEBUG, "Found subparser for this cnx, for proto %s", subparser->parser->proto->name); if (! subparser) { struct proto *requestor = NULL; struct proto *sub_proto = NULL; // Use connection tracking first ASSIGN_INFO_OPT2(ip, ip6, parent); if (! ip) ip = ip6; if (ip) sub_proto = cnxtrack_ip_lookup(IPPROTO_UDP, ip->key.addr+0, sport, ip->key.addr+1, dport, now, &requestor); if (! sub_proto) { // Then try predefined ports first sub_proto = port_muxer_find(&udp_port_muxers, info.key.port[0], info.key.port[1]); } if (sub_proto) subparser = mux_subparser_and_parser_new(mux_parser, sub_proto, requestor, &key, now); } if (! subparser) goto fallback; enum proto_parse_status status = proto_parse(subparser->parser, &info.info, way, packet + sizeof(*udphdr), cap_len - sizeof(*udphdr), wire_len - sizeof(*udphdr), now, tot_cap_len, tot_packet); if (status == PROTO_PARSE_ERR) { SLOG(LOG_DEBUG, "No suitable subparser for this payload"); mux_subparser_deindex(subparser); } mux_subparser_unref(&subparser); if (status == PROTO_OK) return PROTO_OK; fallback: (void)proto_parse(NULL, &info.info, way, packet + sizeof(*udphdr), cap_len - sizeof(*udphdr), wire_len - sizeof(*udphdr), now, tot_cap_len, tot_packet); return PROTO_OK; }
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 const struct parsed_proto *check_var(FILE *fhdr, const char *sym, const char *varname) { const struct parsed_proto *pp, *pp_sym; char fp_sym[256], fp_var[256], *p; int i; pp = proto_parse(fhdr, varname, 1); if (pp == NULL) { if (IS_START(varname, "sub_")) awarn("sub_ sym missing proto: '%s'\n", varname); return NULL; } if (!pp->is_func && !pp->is_fptr) return NULL; pp_print(fp_var, sizeof(fp_var), pp); if (pp->argc_reg == 0) goto check_sym; if (pp->argc_reg == 1 && pp->argc_stack == 0 && IS(pp->arg[0].reg, "ecx")) { goto check_sym; } if (!g_cconv_novalidate && (pp->argc_reg != 2 || !IS(pp->arg[0].reg, "ecx") || !IS(pp->arg[1].reg, "edx"))) { awarn("unhandled reg call: %s\n", fp_var); } check_sym: // fptrs must use 32bit args, callsite might have no information and // lack a cast to smaller types, which results in incorrectly masked // args passed (callee may assume masked args, it does on ARM) for (i = 0; i < pp->argc; i++) { if (pp->arg[i].type.is_ptr) continue; p = pp->arg[i].type.name; if (strstr(p, "int8") || strstr(p, "int16") || strstr(p, "char") || strstr(p, "short")) { awarn("reference to %s with arg%d '%s'\n", pp->name, i + 1, p); } } sprint_pp_short(pp, g_comment, sizeof(g_comment)); if (sym != NULL) { g_func_sym_pp = NULL; pp_sym = proto_parse(fhdr, sym, 1); if (pp_sym == NULL) return pp; if (!pp_sym->is_fptr) aerr("func ptr data, but label '%s' !is_fptr\n", pp_sym->name); g_func_sym_pp = pp_sym; } else { pp_sym = g_func_sym_pp; if (pp_sym == NULL) return pp; } if (pp_cmp_func(pp, pp_sym)) { pp_print(fp_sym, sizeof(fp_sym), pp_sym); anote("var: %s\n", fp_var); anote("sym: %s\n", fp_sym); awarn("^ mismatch\n"); } return pp; }
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 netbios_parse_frame(struct netbios_parser *netbios_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, size_t *pos) { if (cap_len == 0 && netbios_parser->sbuf.dir[way].cap_len == 0) { // Ignore pure gap start timeval_reset(netbios_parser->first_packet_tv + way); return PROTO_PARSE_ERR; } if (cap_len > 0 && !timeval_is_set(&netbios_parser->first_packet_tv[way])) { SLOG(LOG_DEBUG, "Set first packet ts for way %d to %s", way, timeval_2_str(now)); netbios_parser->first_packet_tv[way] = *now; } if (wire_len < NETBIOS_HEADER_SIZE + SMB_FLAG_SIZE) { streambuf_set_restart(&netbios_parser->sbuf, way, packet, NETBIOS_HEADER_SIZE + SMB_FLAG_SIZE); return PROTO_OK; } if (cap_len < NETBIOS_HEADER_SIZE + SMB_FLAG_SIZE) { SLOG(LOG_DEBUG, "Got a gap on neccessary bytes"); timeval_reset(netbios_parser->first_packet_tv + way); return PROTO_PARSE_ERR; } if (packet[0] != 0) { SLOG(LOG_DEBUG, "Expected Session message type 0x00, got 0x%"PRIx8, packet[0]); timeval_reset(netbios_parser->first_packet_tv + way); return PROTO_PARSE_ERR; } uint32_t smb_version = READ_U32N(packet + NETBIOS_HEADER_SIZE); if (smb_version != CIFS_SMB_HEADER && smb_version != CIFS_SMB2_HEADER) { static unsigned char smb_header[SMB_FLAG_SIZE] = {0xff, 0x53, 0x4d, 0x42}; static unsigned char smb2_header[SMB_FLAG_SIZE] = {0xfe, 0x53, 0x4d, 0x42}; void *res = memmem(packet + NETBIOS_HEADER_SIZE, cap_len - NETBIOS_HEADER_SIZE, &smb_header, SMB_FLAG_SIZE); if (!res) { res = memmem(packet + NETBIOS_HEADER_SIZE, cap_len - NETBIOS_HEADER_SIZE, &smb2_header, SMB_FLAG_SIZE); } if (!res) { SLOG(LOG_DEBUG, "Netbios payload does not expected header (expected %"PRIx32" or %"PRIx32")," " got %"PRIx32, CIFS_SMB_HEADER, CIFS_SMB2_HEADER, smb_version); return PROTO_PARSE_ERR; } SLOG(LOG_DEBUG, "Found a SMB header in payload, restarting there"); timeval_reset(netbios_parser->first_packet_tv + way); streambuf_set_restart(&netbios_parser->sbuf, way, res - NETBIOS_HEADER_SIZE, NETBIOS_HEADER_SIZE + SMB_FLAG_SIZE); return PROTO_OK; } uint32_t len = READ_U32N((uint32_t*) packet) & 0x00ffffff; *pos = len; size_t current_payload = wire_len - NETBIOS_HEADER_SIZE; SLOG(LOG_DEBUG, "Found netbios payload of %"PRIu32", current payload %zu", len, current_payload); if (len > current_payload) { streambuf_set_restart(&netbios_parser->sbuf, way, packet, len + NETBIOS_HEADER_SIZE); return PROTO_OK; } /* Parse */ struct netbios_proto_info info; netbios_proto_info_ctor(&info, &netbios_parser->parser, parent, NETBIOS_HEADER_SIZE, wire_len - NETBIOS_HEADER_SIZE, len, netbios_parser->first_packet_tv + way); timeval_reset(netbios_parser->first_packet_tv + way); SLOG(LOG_DEBUG, "Parsing netbios content"); uint8_t const *next_packet = packet + NETBIOS_HEADER_SIZE; if (!netbios_parser->msg_parser) { netbios_parser->msg_parser = proto_cifs->ops->parser_new(proto_cifs); } enum proto_parse_status status = PROTO_OK; if (netbios_parser->msg_parser) { status = proto_parse(netbios_parser->msg_parser, &info.info, way, next_packet, cap_len - NETBIOS_HEADER_SIZE, wire_len - NETBIOS_HEADER_SIZE, now, tot_cap_len, tot_packet); if (status == PROTO_OK) return PROTO_OK; } (void)proto_parse(NULL, &info.info, way, next_packet, cap_len - NETBIOS_HEADER_SIZE, wire_len - NETBIOS_HEADER_SIZE, now, tot_cap_len, tot_packet); return status; }
int main(int argc, char *argv[]) { FILE *fout, *fasm, *fhdr = NULL, *frlist; const struct parsed_proto *pp; int no_decorations = 0; char comment_char = '#'; char words[20][256]; char word[256]; char line[256]; char last_sym[32]; unsigned long val; unsigned long cnt; const char *sym; enum dx_type type; char **pub_syms; int pub_sym_cnt = 0; int pub_sym_alloc; char **rlist; int rlist_cnt = 0; int rlist_alloc; int header_mode = 0; int is_ro = 0; int is_label; int is_bss; int wordc; int first; int arg_out; int arg = 1; int len; int w, i; char *p; char *p2; if (argc < 4) { // -nd: no symbol decorations printf("usage:\n%s [-nd] [-i] [-a] <.s> <.asm> <hdrf> [rlist]*\n" "%s -hdr <.h> <.asm>\n", argv[0], argv[0]); return 1; } for (arg = 1; arg < argc; arg++) { if (IS(argv[arg], "-nd")) no_decorations = 1; else if (IS(argv[arg], "-i")) g_cconv_novalidate = 1; else if (IS(argv[arg], "-a")) { comment_char = '@'; g_arm_mode = 1; } else if (IS(argv[arg], "-hdr")) header_mode = 1; else break; } arg_out = arg++; asmfn = argv[arg++]; fasm = fopen(asmfn, "r"); my_assert_not(fasm, NULL); if (!header_mode) { hdrfn = argv[arg++]; fhdr = fopen(hdrfn, "r"); my_assert_not(fhdr, NULL); } fout = fopen(argv[arg_out], "w"); my_assert_not(fout, NULL); pub_sym_alloc = 64; pub_syms = malloc(pub_sym_alloc * sizeof(pub_syms[0])); my_assert_not(pub_syms, NULL); rlist_alloc = 64; rlist = malloc(rlist_alloc * sizeof(rlist[0])); my_assert_not(rlist, NULL); for (; arg < argc; arg++) { frlist = fopen(argv[arg], "r"); my_assert_not(frlist, NULL); while (my_fgets(line, sizeof(line), frlist)) { p = sskip(line); if (*p == 0 || *p == ';') continue; p = next_word(words[0], sizeof(words[0]), p); if (words[0][0] == 0) continue; if (rlist_cnt >= rlist_alloc) { rlist_alloc = rlist_alloc * 2 + 64; rlist = realloc(rlist, rlist_alloc * sizeof(rlist[0])); my_assert_not(rlist, NULL); } rlist[rlist_cnt++] = strdup(words[0]); } fclose(frlist); frlist = NULL; } if (rlist_cnt > 0) qsort(rlist, rlist_cnt, sizeof(rlist[0]), cmpstringp); qsort(unwanted_syms, ARRAY_SIZE(unwanted_syms), sizeof(unwanted_syms[0]), cmpstringp); last_sym[0] = 0; while (1) { next_section(fasm, line); if (feof(fasm)) break; if (IS(line + 1, "text")) continue; if (IS(line + 1, "rdata")) { is_ro = 1; if (!header_mode) fprintf(fout, "\n.section .rodata\n"); } else if (IS(line + 1, "data")) { is_ro = 0; if (!header_mode) fprintf(fout, "\n.data\n"); } else aerr("unhandled section: '%s'\n", line); if (!header_mode) fprintf(fout, ".align %d\n", align_value(4)); while (my_fgets(line, sizeof(line), fasm)) { sym = NULL; asmln++; p = sskip(line); if (*p == 0) continue; if (*p == ';') { if (IS_START(p, ";org") && sscanf(p + 5, "%Xh", &i) == 1) { // ;org is only seen at section start, so assume . addr 0 i &= 0xfff; if (i != 0 && !header_mode) fprintf(fout, "\t\t .skip 0x%x\n", i); } continue; } for (wordc = 0; wordc < ARRAY_SIZE(words); wordc++) { p = sskip(next_word_s(words[wordc], sizeof(words[0]), p)); if (*p == 0 || *p == ';') { wordc++; break; } if (*p == ',') { p = sskip(p + 1); } } if (*p == ';') { p = sskip(p + 1); if (IS_START(p, "sctclrtype")) g_func_sym_pp = NULL; } if (wordc == 2 && IS(words[1], "ends")) break; if (wordc <= 2 && IS(words[0], "end")) break; if (wordc < 2) aerr("unhandled: '%s'\n", words[0]); // don't cares if (IS(words[0], "assume")) continue; if (IS(words[0], "align")) { if (header_mode) continue; val = parse_number(words[1]); fprintf(fout, "\t\t .align %d", align_value(val)); goto fin; } w = 1; type = parse_dx_directive(words[0]); if (type == DXT_UNSPEC) { type = parse_dx_directive(words[1]); sym = words[0]; w = 2; } if (type == DXT_UNSPEC) aerr("unhandled decl: '%s %s'\n", words[0], words[1]); if (sym != NULL) { if (header_mode) { int is_str = 0; fprintf(fout, "extern "); if (is_ro) fprintf(fout, "const "); switch (type) { case DXT_BYTE: for (i = w; i < wordc; i++) if (words[i][0] == '\'') is_str = 1; if (is_str) fprintf(fout, "char %s[];\n", sym); else fprintf(fout, "uint8_t %s;\n", sym); break; case DXT_WORD: fprintf(fout, "uint16_t %s;\n", sym); break; case DXT_DWORD: fprintf(fout, "uint32_t %s;\n", sym); break; default: fprintf(fout, "_UNKNOWN %s;\n", sym); break; } continue; } snprintf(last_sym, sizeof(last_sym), "%s", sym); pp = proto_parse(fhdr, sym, 1); if (pp != NULL) { g_func_sym_pp = NULL; // public/global name if (pub_sym_cnt >= pub_sym_alloc) { pub_sym_alloc *= 2; pub_syms = realloc(pub_syms, pub_sym_alloc * sizeof(pub_syms[0])); my_assert_not(pub_syms, NULL); } pub_syms[pub_sym_cnt++] = strdup(sym); } len = strlen(sym); fprintf(fout, "%s%s:", no_decorations ? "" : "_", sym); len += 2; if (len < 8) fprintf(fout, "\t"); if (len < 16) fprintf(fout, "\t"); if (len <= 16) fprintf(fout, " "); else fprintf(fout, " "); } else { if (header_mode) continue; fprintf(fout, "\t\t "); } // fill out some unwanted strings with zeroes.. if (type == DXT_BYTE && words[w][0] == '\'' && is_unwanted_sym(last_sym)) { len = 0; for (; w < wordc; w++) { if (words[w][0] == '\'') { p = words[w] + 1; for (; *p && *p != '\''; p++) len++; } else { // assume encoded byte len++; } } fprintf(fout, ".skip %d", len); goto fin; } else if (type == DXT_BYTE && (words[w][0] == '\'' || (w + 1 < wordc && words[w + 1][0] == '\''))) { // string; use asciz for most common case if (w == wordc - 2 && IS(words[w + 1], "0")) { fprintf(fout, ".asciz \""); wordc--; } else fprintf(fout, ".ascii \""); for (; w < wordc; w++) { if (words[w][0] == '\'') { p = words[w] + 1; p2 = strchr(p, '\''); if (p2 == NULL) aerr("unterminated string? '%s'\n", p); memcpy(word, p, p2 - p); word[p2 - p] = 0; fprintf(fout, "%s", escape_string(word)); } else { val = parse_number(words[w]); if (val & ~0xff) aerr("bad string trailing byte?\n"); // unfortunately \xHH is unusable - gas interprets // things like \x27b as 0x7b, so have to use octal here fprintf(fout, "\\%03lo", val); } } fprintf(fout, "\""); goto fin; } if (w == wordc - 2) { if (IS_START(words[w + 1], "dup(")) { cnt = parse_number(words[w]); p = words[w + 1] + 4; p2 = strchr(p, ')'); if (p2 == NULL) aerr("bad dup?\n"); memmove(word, p, p2 - p); word[p2 - p] = 0; val = 0; if (!IS(word, "?")) val = parse_number(word); fprintf(fout, ".fill 0x%02lx,%d,0x%02lx", cnt, type_size(type), val); goto fin; } } if (type == DXT_DWORD && words[w][0] == '\'' && words[w][5] == '\'' && strlen(words[w]) == 6) { if (w != wordc - 1) aerr("TODO\n"); p = words[w]; val = (p[1] << 24) | (p[2] << 16) | (p[3] << 8) | p[4]; fprintf(fout, ".long 0x%lx", val); snprintf(g_comment, sizeof(g_comment), "%s", words[w]); goto fin; } if (type >= DXT_DWORD && strchr(words[w], '.')) { if (w != wordc - 1) aerr("TODO\n"); if (g_arm_mode && type == DXT_TEN) { fprintf(fout, ".fill 10"); snprintf(g_comment, sizeof(g_comment), "%s %s", type_name_float(type), words[w]); } else fprintf(fout, "%s %s", type_name_float(type), words[w]); goto fin; } first = 1; fprintf(fout, "%s ", type_name(type)); for (; w < wordc; w++) { if (!first) fprintf(fout, ", "); is_label = is_bss = 0; if (w <= wordc - 2 && IS(words[w], "offset")) { is_label = 1; w++; } else if (IS(words[w], "?")) { is_bss = 1; } else if (type == DXT_DWORD && !('0' <= words[w][0] && words[w][0] <= '9')) { // assume label is_label = 1; } if (is_bss) { fprintf(fout, "0"); } else if (is_label) { p = words[w]; if (IS_START(p, "loc_") || IS_START(p, "__imp") || strchr(p, '?') || strchr(p, '@') || bsearch(&p, rlist, rlist_cnt, sizeof(rlist[0]), cmpstringp)) { fprintf(fout, "0"); snprintf(g_comment, sizeof(g_comment), "%s", p); } else { pp = check_var(fhdr, sym, p); if (pp == NULL) { fprintf(fout, "%s%s", (no_decorations || p[0] == '_') ? "" : "_", p); } else { if (no_decorations) fprintf(fout, "%s", pp->name); else output_decorated_pp(fout, pp); } } } else { val = parse_number(words[w]); if (val < 10) fprintf(fout, "%ld", val); else fprintf(fout, "0x%lx", val); } first = 0; } fin: if (g_comment[0] != 0) { fprintf(fout, "\t\t%c %s", comment_char, g_comment); g_comment[0] = 0; } fprintf(fout, "\n"); } } fprintf(fout, "\n"); // dump public syms for (i = 0; i < pub_sym_cnt; i++) fprintf(fout, ".global %s%s\n", no_decorations ? "" : "_", pub_syms[i]); fclose(fout); fclose(fasm); if (fhdr != NULL) fclose(fhdr); return 0; }