Exemple #1
0
static int sdp_extract_host(unsigned unused_ cmd, struct liner *liner, void *info_)
{
    struct sdp_proto_info *info = info_;

#define IN_IP "IN IP"
#define IN_IP_LEN strlen(IN_IP)

    if (liner_tok_length(liner) < IN_IP_LEN)
        return -1;

    if (strncasecmp(liner->start, IN_IP, IN_IP_LEN))
        return -1;

    char const *start = liner->start + IN_IP_LEN;
    int version = start[0] - '0';
    if (version != 4 && version != 6) {
        SLOG(LOG_DEBUG, "Bogus IP version (%d)", version);
        return -1;
    }

    struct liner space_liner;
    liner_init(&space_liner, &delim_spaces, (char const *)start, liner_tok_length(liner) - IN_IP_LEN);
    liner_next(&space_liner);   // skipping the IP version number

#undef IN_IP
#undef IN_IP_LEN

    if (0 != ip_addr_ctor_from_str(&info->host, space_liner.start, liner_tok_length(&space_liner), version))
        return -1;

    info->set_values |= SDP_HOST_SET;

    SLOG(LOG_DEBUG, "host found (%s)", ip_addr_2_str(&info->host));
    return 0;
}
Exemple #2
0
static void parse_observed_event(struct mgcp_proto_info *info, struct liner *liner)
{
    size_t len = strlen(info->dialed);
    struct liner tokenizer;
    for (
        liner_init(&tokenizer, &param_delims, liner->start, liner_tok_length(liner));
        ! liner_eof(&tokenizer);
        liner_next(&tokenizer)
    ) {
        unsigned events = parse_events(&tokenizer);
        if (events) {
            info->observed |= events;
            continue;
        }
        if (liner_tok_length(&tokenizer) < 3) continue;
        if (tokenizer.start[0] != 'D' && tokenizer.start[0] != 'd') continue;
        if (tokenizer.start[1] != '/') continue;
        // append dialed 'digit' into dialed info
        for (unsigned d = 2; d < liner_tok_length(&tokenizer); d++) {
            if (len >= sizeof(info->dialed)-1) break;
            char const c = tokenizer.start[d];
            if (c != 'T' && c != 't') info->dialed[len++] = c;
        }
    }
    assert(len < sizeof(info->dialed));
    info->dialed[len] = '\0';
}
Exemple #3
0
unsigned parse_events(struct liner *liner)
{
    unsigned flags = 0;
    for (unsigned e = 0; e < NB_ELEMS(events); e++) {
        if (
            (liner_tok_length(liner) >= 4 && 0 == strncasecmp(liner->start, events[e].code, 4)) ||
            (events[e].from_line && (liner_tok_length(liner) >= 2 && 0 == strncasecmp(liner->start, events[e].code+2, 2)))
        ) {
            flags |= events[e].flag;
        }
    }
    return flags;
}
Exemple #4
0
static int sip_extract_via(unsigned unused_ field, struct liner *liner, void *info_)
{
    struct sip_proto_info *info = info_;

    // We are interrested only in the first Via stanza
    if (info->set_values & SIP_VIA_SET) return 0;

    // We are parsing something like : SIP/2.0/UDP 123.456.789.123:12345;foo=bar etc
    struct liner spacer;
    liner_init(&spacer, &delim_blanks, liner->start, liner_tok_length(liner));

    // Extract IP protocol
#   define SIP_VER "SIP/2.0/"
    if (liner_tok_length(&spacer) < strlen(SIP_VER) + 3) {
        SLOG(LOG_DEBUG, "Via token too short (%.*s)", (int)liner_tok_length(&spacer), spacer.start);
        return 0;
    }
    char const *proto_str = spacer.start + strlen(SIP_VER);
    if (0 == strncasecmp(proto_str, "UDP", 3)) {
        info->via.protocol = IPPROTO_UDP;
    } else if (0 == strncasecmp(proto_str, "TCP", 3)) {
        info->via.protocol = IPPROTO_TCP;
    } else {
        SLOG(LOG_DEBUG, "Via protocol unknown (%.*s)", 3, proto_str);
        return 0;
    }

    // Extract IP
    liner_next(&spacer);
    struct liner semicoloner;   // first get IP:port or IP
    liner_init(&semicoloner, &delim_semicolons, spacer.start, liner_tok_length(&spacer));
    struct liner coloner;   // then only IP and then port
    liner_init(&coloner, &delim_colons, semicoloner.start, liner_tok_length(&semicoloner));
    if (0 != ip_addr_ctor_from_str(&info->via.addr, coloner.start, liner_tok_length(&coloner), 4)) {    // FIXME: ip_addr_ctor_from_str should detect IP version
        SLOG(LOG_DEBUG, "Cannot extract IP addr from Via string (%.*s)",
            (int)liner_tok_length(&coloner), coloner.start);
        return 0;
    }

    // Extract Port
    liner_next(&coloner);
    if (liner_eof(&coloner)) {   // no port specified
        SLOG(LOG_DEBUG, "No port specified in Via string, assuming "STRIZE(SIP_PORT));
        info->via.port = SIP_PORT;
    } else {    // port is present
        char const *end;
        info->via.port = liner_strtoull(&coloner, &end, 10);
        if (end == coloner.start) {
            SLOG(LOG_DEBUG, "Cannot extract IP port from Via string (%.*s)",
                (int)liner_tok_length(&coloner), coloner.start);
            return 0;
        }
    }

    info->set_values |= SIP_VIA_SET;
    return 0;
}
Exemple #5
0
int sdper_parse(struct sdper const *sdper, size_t *head_sz, uint8_t const *packet, size_t packet_len, void *user_data)
{
    // REVIEW: eols and spcs should be provided by liner.
    struct liner_delimiter
        eols[] = { { "\r\n", 2 }, { "\n", 1 } },
        cols[] = { { "= ", 2}, { "=", 1 } };
    struct liner_delimiter_set const
        lines =  { NB_ELEMS(eols), eols, false },
        eq = { NB_ELEMS(cols), cols, true };

    struct liner liner, tokenizer;

    liner_init(&liner, &lines, (char const *)packet, packet_len);

    // Parse header fields
    while (true) {
        // Next line
        if (liner_eof(&liner)) break;

        // Otherwise tokenize the header line
        liner_init(&tokenizer, &eq, liner.start, liner_tok_length(&liner));

        for (unsigned f = 0; f < sdper->nb_fields; f++) {
            struct sdper_field const *field = sdper->fields + f;

            size_t len = liner_tok_length(&tokenizer);
            if (len != field->length)
              continue;

            if (0 != strncasecmp(field->name, tokenizer.start, len)) continue;

            SLOG(LOG_DEBUG, "Found field %s", field->name);
            liner_next(&tokenizer);
            int ret = field->cb(f, &tokenizer, user_data);
            if (ret) return ret;
            break;
        }

        liner_next(&liner);
    }

    if (head_sz) *head_sz = liner_parsed(&liner);
    return 0;
}
Exemple #6
0
static void parse_signal_request(struct mgcp_proto_info *info, struct liner *liner)
{
    struct liner tokenizer;
    for (
        liner_init(&tokenizer, &param_delims, liner->start, liner_tok_length(liner));
        ! liner_eof(&tokenizer);
        liner_next(&tokenizer)
    ) {
        info->signaled |= parse_events(&tokenizer);
    }
}
Exemple #7
0
static int sdp_extract_port(unsigned unused_ cmd, struct liner *liner, void *info_)
{
    struct sdp_proto_info *info = info_;

    // In case several medias are advertised, we are interrested only in the first one.
    // FIXME: parse all m= stenzas with their respective attributes (a=).
    if (info->set_values & SDP_PORT_SET) return 0;

    // skip the media format ("audio", ...)
    struct liner space_liner;

    liner_init(&space_liner, &delim_spaces, (char const *)liner->start, liner_tok_length(liner));
    liner_next(&space_liner);

    char const *end;
    info->port = liner_strtoull(&space_liner, &end, 10);
    if (!info->port) // unable to extract an integer value
        return -1;

    info->set_values |= SDP_PORT_SET;
    SLOG(LOG_DEBUG, "port found (%"PRIu16")", info->port);
    return 0;
}
Exemple #8
0
enum proto_parse_status httper_parse(struct httper const *httper, size_t *head_sz, uint8_t const *packet, size_t packet_len, void *user_data)
{
    struct liner liner, tokenizer;
    bool found = false;

    for (unsigned c = 0; c < httper->nb_commands; c++) {
        struct httper_command const *const cmd = httper->commands + c;

        // Start by looking for the command before tokenizing (tokenizing takes too much time on random traffic)
        if (0 != strncmp(cmd->name, (char const *)packet, MIN(packet_len, cmd->len))) continue;
        if (packet_len < cmd->len) return PROTO_TOO_SHORT;

        liner_init(&liner, &delim_lines, (char const *)packet, packet_len);
        liner_init(&tokenizer, &delim_blanks, liner.start, liner_tok_length(&liner));

        if (liner_tok_length(&tokenizer) != cmd->len) {
no_command:
            SLOG(LOG_DEBUG, "Cannot find command");
            return PROTO_PARSE_ERR;
        }
        SLOG(LOG_DEBUG, "Found command %s", cmd->name);
        liner_next(&tokenizer);
        int ret = cmd->cb(c, &tokenizer, user_data);
        if (ret) return PROTO_PARSE_ERR;

        found = true;
        break;
    }

    if (! found) goto no_command;

    // Parse header fields
    unsigned nb_hdr_lines = 0;

    int field_idx = -1;
    char const *field_end = NULL;

    while (true) {
        // Next line
        bool const has_newline = liner.delim_size > 0;
        liner_next(&liner);
        if (liner_eof(&liner)) {
            // As an accommodation to old HTTP implementations, we allow a single line command
            // FIXME: check line termination with "*/x.y" ?
            if (nb_hdr_lines == 0 && has_newline) break;
            return PROTO_TOO_SHORT;
        }

        // If empty line we reached the end of the headers
        if (liner_tok_length(&liner) == 0) break;

        // Check if we reached the end of a multiline field.
        // FIXME: Is isspace appropriate here?
        if (! isspace(liner.start[0])) {
            if (field_idx >= 0) {
                liner_grow(&tokenizer, field_end);
                // Absorb all remaining of line onto this token
                liner_expand(&tokenizer);
                int ret = httper->fields[field_idx].cb(field_idx, &tokenizer, user_data);
                if (ret) return PROTO_PARSE_ERR;
            }

            // Tokenize the header line
            liner_init(&tokenizer, &delim_colons, liner.start, liner_tok_length(&liner));

            field_idx = -1;
            for (unsigned f = 0; f < httper->nb_fields; f++) {
                struct httper_field const *field = httper->fields + f;

                if (liner_tok_length(&tokenizer) < field->len) continue;
                if (0 != strncasecmp(field->name, tokenizer.start, liner_tok_length(&tokenizer))) continue;

                SLOG(LOG_DEBUG, "Found field %s", field->name);
                liner_next(&tokenizer);
                field_idx = f;
                break;
            }
        }
        field_end = liner.start + liner.tok_size;   // save end of line position in field_end
        nb_hdr_lines ++;
    }

    if (field_idx >= 0) {
        liner_grow(&tokenizer, field_end);
        // Absorb all remaining of line onto this token
        liner_expand(&tokenizer);
        int ret = httper->fields[field_idx].cb(field_idx, &tokenizer, user_data);
        if (ret) return PROTO_PARSE_ERR;
    }

    if (head_sz) *head_sz = liner_parsed(&liner);
    return PROTO_OK;
}
Exemple #9
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;
}