static int sip_extract_content_length(unsigned unused_ field, struct liner *liner, void *info_) { struct sip_proto_info *info = info_; info->set_values |= SIP_LENGTH_SET; info->content_length = liner_strtoull(liner, NULL, 10); return 0; }
static int sip_extract_cseq(unsigned unused_ cmd, struct liner *liner, void *info_) { struct sip_proto_info *info = info_; info->cseq = liner_strtoull(liner, NULL, 10); info->set_values |= SIP_CSEQ_SET; return 0; }
static int sip_set_response(unsigned unused_ cmd, struct liner *liner, void *info_) { struct sip_proto_info *info = info_; info->set_values |= SIP_CODE_SET; info->code = liner_strtoull(liner, NULL, 10); return 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; }
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; }
// 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; }