Exemple #1
0
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;
}
Exemple #2
0
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);
}
Exemple #3
0
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);
}
Exemple #4
0
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);
}
Exemple #5
0
// 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;
}
Exemple #6
0
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);
}
Exemple #7
0
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);
}
Exemple #8
0
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);
}
Exemple #9
0
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);
}
Exemple #10
0
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;
}
Exemple #11
0
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;
}
Exemple #12
0
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;
}
Exemple #13
0
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;
}
Exemple #14
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);
}
Exemple #15
0
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);
}
Exemple #16
0
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);
}
Exemple #17
0
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(&ethhdr->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(&eth_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(&eth_vlan->h_proto);
        vlan_id = READ_U16N(&eth_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;
}
Exemple #18
0
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);
}
Exemple #19
0
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;
}
Exemple #20
0
// 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;
}
Exemple #21
0
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);
}
Exemple #22
0
// 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;
}
Exemple #23
0
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);
}
Exemple #24
0
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);
}
Exemple #25
0
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;
}
Exemple #26
0
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;
}
Exemple #27
0
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;
}
Exemple #28
0
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);
}
Exemple #29
0
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;
}
Exemple #30
0
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;
}