static int rtsp_get_content_length(const guchar *line_begin, size_t line_len) { guchar buf[256]; guchar *tmp; long content_length; char *p; guchar *up; if (line_len > sizeof(buf) - 1) { /* * Don't overflow the buffer. */ line_len = sizeof(buf) - 1; } memcpy(buf, line_begin, line_len); buf[line_len] = '\0'; tmp = buf + STRLEN_CONST(rtsp_content_length); while (*tmp && isspace(*tmp)) tmp++; content_length = strtol(tmp, &p, 10); up = p; if (up == tmp || (*up != '\0' && !isspace(*up))) return -1; /* not a valid number */ return content_length; }
static char *purge_xml_comments(const char *str) { char *copy_dest; const char *copy_src; size_t len = strlen(str); char *new_str = (char*)malloc(len + 1); if (!new_str) return NULL; new_str[len] = '\0'; copy_dest = new_str; copy_src = str; for (;;) { ptrdiff_t copy_len; const char *comment_start = strstr(copy_src, "<!--"); const char *comment_end = strstr(copy_src, "-->"); if (!comment_start || !comment_end) break; copy_len = comment_start - copy_src; memcpy(copy_dest, copy_src, copy_len); copy_dest += copy_len; copy_src = comment_end + STRLEN_CONST("-->"); } /* Avoid strcpy() as OpenBSD is anal and hates you * for using it even when it's perfectly safe. */ len = strlen(copy_src); memcpy(copy_dest, copy_src, len); copy_dest[len] = '\0'; return new_str; }
static int dissect_rtspmessage(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree) { proto_tree *rtsp_tree = NULL; proto_tree *sub_tree = NULL; proto_item *ti = NULL; const guchar *line; gint next_offset; const guchar *linep, *lineend; int orig_offset; int first_linelen, linelen; int line_end_offset; int colon_offset; gboolean is_request_or_reply; gboolean body_requires_content_len; gboolean saw_req_resp_or_header; guchar c; rtsp_type_t rtsp_type; gboolean is_header; int datalen; int content_length; int reported_datalen; int value_offset; int value_len; e164_info_t e164_info; gint rdt_feature_level = 0; gchar *media_type_str_lower_case = NULL; int semi_colon_offset; int par_end_offset; /* * Is this a request or response? * * Note that "tvb_find_line_end()" will return a value that * is not longer than what's in the buffer, so the * "tvb_get_ptr()" call won't throw an exception. */ first_linelen = tvb_find_line_end(tvb, offset, tvb_ensure_length_remaining(tvb, offset), &next_offset, FALSE); /* * Is the first line a request or response? */ line = tvb_get_ptr(tvb, offset, first_linelen); is_request_or_reply = is_rtsp_request_or_reply(line, first_linelen, &rtsp_type); if (is_request_or_reply) { /* * Yes, it's a request or response. * Do header desegmentation if we've been told to, * and do body desegmentation if we've been told to and * we find a Content-Length header. */ if (!req_resp_hdrs_do_reassembly(tvb, offset, pinfo, rtsp_desegment_headers, rtsp_desegment_body)) { /* * More data needed for desegmentation. */ return -1; } } /* * RFC 2326 says that a content length must be specified * in requests that have a body, although section 4.4 speaks * of a server closing the connection indicating the end of * a reply body. * * We assume that an absent content length in a request means * that we don't have a body, and that an absent content length * in a reply means that the reply body runs to the end of * the connection. If the first line is neither, we assume * that whatever follows a blank line should be treated as a * body; there's not much else we can do, as we're jumping * into the message in the middle. * * XXX - if there was no Content-Length entity header, we should * accumulate all data until the end of the connection. * That'd require that the TCP dissector call subdissectors * for all frames with FIN, even if they contain no data, * which would require subdissectors to deal intelligently * with empty segments. */ if (rtsp_type == RTSP_REQUEST) body_requires_content_len = TRUE; else body_requires_content_len = FALSE; col_set_str(pinfo->cinfo, COL_PROTOCOL, "RTSP"); if (check_col(pinfo->cinfo, COL_INFO)) { /* * Put the first line from the buffer into the summary * if it's an RTSP request or reply (but leave out the * line terminator). * Otherwise, just call it a continuation. * * Note that "tvb_find_line_end()" will return a value that * is not longer than what's in the buffer, so the * "tvb_get_ptr()" call won't throw an exception. */ line = tvb_get_ptr(tvb, offset, first_linelen); if (is_request_or_reply) if ( rtsp_type == RTSP_REPLY ) { col_set_str(pinfo->cinfo, COL_INFO, "Reply: "); col_append_str(pinfo->cinfo, COL_INFO, format_text(line, first_linelen)); } else col_add_str(pinfo->cinfo, COL_INFO, format_text(line, first_linelen)); else col_set_str(pinfo->cinfo, COL_INFO, "Continuation"); } orig_offset = offset; if (tree) { ti = proto_tree_add_item(tree, proto_rtsp, tvb, offset, -1, FALSE); rtsp_tree = proto_item_add_subtree(ti, ett_rtsp); } /* * We haven't yet seen a Content-Length header. */ content_length = -1; /* * Process the packet data, a line at a time. */ saw_req_resp_or_header = FALSE; /* haven't seen anything yet */ while (tvb_reported_length_remaining(tvb, offset) != 0) { /* * We haven't yet concluded that this is a header. */ is_header = FALSE; /* * Find the end of the line. */ linelen = tvb_find_line_end(tvb, offset, tvb_ensure_length_remaining(tvb, offset), &next_offset, FALSE); if (linelen < 0) return -1; line_end_offset = offset + linelen; /* * colon_offset may be -1 */ colon_offset = tvb_find_guint8(tvb, offset, linelen, ':'); /* * Get a buffer that refers to the line. */ line = tvb_get_ptr(tvb, offset, linelen); lineend = line + linelen; /* * OK, does it look like an RTSP request or response? */ is_request_or_reply = is_rtsp_request_or_reply(line, linelen, &rtsp_type); if (is_request_or_reply) goto is_rtsp; /* * No. Does it look like a blank line (as would appear * at the end of an RTSP request)? */ if (linelen == 0) goto is_rtsp; /* Yes. */ /* * No. Does it look like a header? */ linep = line; while (linep < lineend) { c = *linep++; /* * This must be a CHAR to be part of a token; that * means it must be ASCII. */ if (!isascii(c)) break; /* not ASCII, thus not a CHAR */ /* * This mustn't be a CTL to be part of a token. * * XXX - what about leading LWS on continuation * lines of a header? */ if (iscntrl(c)) break; /* CTL, not part of a header */ switch (c) { case '(': case ')': case '<': case '>': case '@': case ',': case ';': case '\\': case '"': case '/': case '[': case ']': case '?': case '=': case '{': case '}': /* * It's a tspecial, so it's not * part of a token, so it's not * a field name for the beginning * of a header. */ goto not_rtsp; case ':': /* * This ends the token; we consider * this to be a header. */ is_header = TRUE; goto is_rtsp; case ' ': case '\t': /* * LWS (RFC-2616, 4.2); continue the previous * header. */ goto is_rtsp; } } /* * We haven't seen the colon, but everything else looks * OK for a header line. * * If we've already seen an RTSP request or response * line, or a header line, and we're at the end of * the tvbuff, we assume this is an incomplete header * line. (We quit this loop after seeing a blank line, * so if we've seen a request or response line, or a * header line, this is probably more of the request * or response we're presumably seeing. There is some * risk of false positives, but the same applies for * full request or response lines or header lines, * although that's less likely.) * * We throw an exception in that case, by checking for * the existence of the next byte after the last one * in the line. If it exists, "tvb_ensure_bytes_exist()" * throws no exception, and we fall through to the * "not RTSP" case. If it doesn't exist, * "tvb_ensure_bytes_exist()" will throw the appropriate * exception. */ if (saw_req_resp_or_header) tvb_ensure_bytes_exist(tvb, offset, linelen + 1); not_rtsp: /* * We don't consider this part of an RTSP request or * reply, so we don't display it. */ break; is_rtsp: /* * Process this line. */ if (linelen == 0) { /* * This is a blank line, which means that * whatever follows it isn't part of this * request or reply. */ proto_tree_add_text(rtsp_tree, tvb, offset, next_offset - offset, "%s", tvb_format_text(tvb, offset, next_offset - offset)); offset = next_offset; break; } /* * Not a blank line - either a request, a reply, or a header * line. */ saw_req_resp_or_header = TRUE; if (rtsp_tree) { switch (rtsp_type) { case RTSP_REQUEST: process_rtsp_request(tvb, offset, line, linelen, next_offset, rtsp_tree); break; case RTSP_REPLY: process_rtsp_reply(tvb, offset, line, linelen, next_offset, rtsp_tree); break; case RTSP_NOT_FIRST_LINE: /* Drop through, it may well be a header line */ break; } } if (is_header) { /* We know that colon_offset must be set */ /* Skip whitespace after the colon. */ value_offset = colon_offset + 1; while ((value_offset < line_end_offset) && ((c = tvb_get_guint8(tvb, value_offset)) == ' ' || c == '\t')) { value_offset++; } value_len = line_end_offset - value_offset; /* * Process some headers specially. */ #define HDR_MATCHES(header) \ ( (size_t)linelen > STRLEN_CONST(header) && \ g_ascii_strncasecmp(line, (header), STRLEN_CONST(header)) == 0) if (HDR_MATCHES(rtsp_transport)) { proto_tree_add_string(rtsp_tree, hf_rtsp_transport, tvb, offset, linelen, tvb_format_text(tvb, value_offset, value_len)); /* * Based on the port numbers specified * in the Transport: header, set up * a conversation that will be dissected * with the appropriate dissector. */ rtsp_create_conversation(pinfo, line, linelen, rdt_feature_level); } else if (HDR_MATCHES(rtsp_content_type)) { proto_tree_add_string(rtsp_tree, hf_rtsp_content_type, tvb, offset, linelen, tvb_format_text(tvb, value_offset, value_len)); offset = offset + STRLEN_CONST(rtsp_content_type); /* Skip wsp */ offset = tvb_skip_wsp(tvb, offset, value_len); semi_colon_offset = tvb_find_guint8(tvb, value_offset, value_len, ';'); if ( semi_colon_offset != -1) { /* m-parameter present */ par_end_offset = tvb_skip_wsp_return(tvb, semi_colon_offset-1); value_len = par_end_offset - offset; } media_type_str_lower_case = ascii_strdown_inplace( (gchar *)tvb_get_ephemeral_string(tvb, offset, value_len)); } else if (HDR_MATCHES(rtsp_content_length)) { proto_tree_add_uint(rtsp_tree, hf_rtsp_content_length, tvb, offset, linelen, atoi(tvb_format_text(tvb, value_offset, value_len))); /* * Only the amount specified by the * Content-Length: header should be treated * as payload. */ content_length = rtsp_get_content_length(line, linelen); } else if (HDR_MATCHES(rtsp_Session)) { /* Put the value into the protocol tree */ proto_tree_add_string(rtsp_tree, hf_rtsp_session, tvb, offset, linelen, tvb_format_text(tvb, value_offset, value_len)); } else if (HDR_MATCHES(rtsp_X_Vig_Msisdn)) { /* * Extract the X_Vig_Msisdn string */ if (colon_offset != -1) { /* Put the value into the protocol tree */ ti = proto_tree_add_string(rtsp_tree, hf_rtsp_X_Vig_Msisdn,tvb, offset, linelen , tvb_format_text(tvb, value_offset, value_len)); sub_tree = proto_item_add_subtree(ti, ett_rtsp_method); e164_info.e164_number_type = CALLING_PARTY_NUMBER; e164_info.nature_of_address = 0; e164_info.E164_number_str = tvb_get_ephemeral_string(tvb, value_offset, value_len); e164_info.E164_number_length = value_len; dissect_e164_number(tvb, sub_tree, value_offset, value_len, e164_info); } } else if (HDR_MATCHES(rtsp_rdt_feature_level)) { rdt_feature_level = atoi(tvb_format_text(tvb, value_offset, value_len)); proto_tree_add_uint(rtsp_tree, hf_rtsp_rdtfeaturelevel, tvb, offset, linelen, atoi(tvb_format_text(tvb, value_offset, value_len))); } else { /* Default case for headers. Show line as text */ proto_tree_add_text(rtsp_tree, tvb, offset, next_offset - offset, "%s", tvb_format_text(tvb, offset, next_offset - offset)); } } else if (rtsp_type == RTSP_NOT_FIRST_LINE) { /* Catch-all for all other lines... Show line as text. TODO: should these be shown as errors? */ proto_tree_add_text(rtsp_tree, tvb, offset, next_offset - offset, "%s", tvb_format_text(tvb, offset, next_offset - offset)); } offset = next_offset; } /* * Have now read all of the lines of this message. * * If a content length was supplied, the amount of data to be * processed as RTSP payload is the minimum of the content * length and the amount of data remaining in the frame. * * If no content length was supplied (or if a bad content length * was supplied), the amount of data to be processed is the amount * of data remaining in the frame. */ datalen = tvb_length_remaining(tvb, offset); reported_datalen = tvb_reported_length_remaining(tvb, offset); if (content_length != -1) { /* * Content length specified; display only that amount * as payload. */ if (datalen > content_length) datalen = content_length; /* * XXX - limit the reported length in the tvbuff we'll * hand to a subdissector to be no greater than the * content length. * * We really need both unreassembled and "how long it'd * be if it were reassembled" lengths for tvbuffs, so * that we throw the appropriate exceptions for * "not enough data captured" (running past the length), * "packet needed reassembly" (within the length but * running past the unreassembled length), and * "packet is malformed" (running past the reassembled * length). */ if (reported_datalen > content_length) reported_datalen = content_length; } else { /* * No content length specified; if this message doesn't * have a body if no content length is specified, process * nothing as payload. */ if (body_requires_content_len) datalen = 0; } if (datalen > 0) { /* * There's stuff left over; process it. */ tvbuff_t *new_tvb; /* * Now create a tvbuff for the Content-type stuff and * dissect it. * * The amount of data to be processed that's * available in the tvbuff is "datalen", which * is the minimum of the amount of data left in * the tvbuff and any specified content length. * * The amount of data to be processed that's in * this frame, regardless of whether it was * captured or not, is "reported_datalen", * which, if no content length was specified, * is -1, i.e. "to the end of the frame. */ new_tvb = tvb_new_subset(tvb, offset, datalen, reported_datalen); if (media_type_str_lower_case && dissector_try_string(media_type_dissector_table, media_type_str_lower_case, new_tvb, pinfo, rtsp_tree)){ }else { /* * Fix up the top-level item so that it doesn't * include the SDP stuff. */ if (ti != NULL) proto_item_set_len(ti, offset); if (tvb_get_guint8(tvb, offset) == RTSP_FRAMEHDR) { /* * This is interleaved stuff; don't * treat it as raw data - set "datalen" * to 0, so we won't skip the offset * past it, which will cause our * caller to process that stuff itself. */ datalen = 0; } else { proto_tree_add_text(rtsp_tree, tvb, offset, datalen, "Data (%d bytes)", reported_datalen); } } /* * We've processed "datalen" bytes worth of data * (which may be no data at all); advance the * offset past whatever data we've processed. */ offset += datalen; } return offset - orig_offset; }
static void rtsp_create_conversation(packet_info *pinfo, const guchar *line_begin, size_t line_len, gint rdt_feature_level) { conversation_t *conv; guchar buf[256]; guchar *tmp; gboolean rtp_transport = FALSE; gboolean rdt_transport = FALSE; guint c_data_port, c_mon_port; guint s_data_port, s_mon_port; gboolean is_video = FALSE; /* FIX ME - need to indicate video or not */ /* Copy line into buf */ if (line_len > sizeof(buf) - 1) { /* Don't overflow the buffer. */ line_len = sizeof(buf) - 1; } memcpy(buf, line_begin, line_len); buf[line_len] = '\0'; /* Get past "Transport:" and spaces */ tmp = buf + STRLEN_CONST(rtsp_transport); while (*tmp && isspace(*tmp)) tmp++; /* Work out which transport type is here */ if (g_ascii_strncasecmp(tmp, rtsp_rtp, strlen(rtsp_rtp)) == 0) rtp_transport = TRUE; else if (g_ascii_strncasecmp(tmp, rtsp_real_rdt, strlen(rtsp_real_rdt)) == 0 || g_ascii_strncasecmp(tmp, rtsp_real_tng, strlen(rtsp_real_tng)) == 0) rdt_transport = TRUE; else { /* Give up on unknown transport types */ return; } c_data_port = c_mon_port = 0; s_data_port = s_mon_port = 0; /* Look for server port */ if ((tmp = strstr(buf, rtsp_sps))) { tmp += strlen(rtsp_sps); if (sscanf(tmp, "%u-%u", &s_data_port, &s_mon_port) < 1) { g_warning("Frame %u: rtsp: bad server_port", pinfo->fd->num); return; } } /* Look for client port */ if ((tmp = strstr(buf, rtsp_cps))) { tmp += strlen(rtsp_cps); if (sscanf(tmp, "%u-%u", &c_data_port, &c_mon_port) < 1) { g_warning("Frame %u: rtsp: bad client_port", pinfo->fd->num); return; } } /* Deal with RTSP TCP-interleaved conversations. */ if (!c_data_port) { rtsp_conversation_data_t *data; guint s_data_chan, s_mon_chan; int i; /* Search tranport line for interleaved string */ if ((tmp = strstr(buf, rtsp_inter)) == NULL) { /* * No interleaved or server_port - probably a * SETUP request, rather than reply. */ return; } /* Move tmp to beyone interleaved string */ tmp += strlen(rtsp_inter); /* Look for channel number(s) */ i = sscanf(tmp, "%u-%u", &s_data_chan, &s_mon_chan); if (i < 1) { g_warning("Frame %u: rtsp: bad interleaved", pinfo->fd->num); return; } /* At least data channel present, look for conversation (presumably TCP) */ conv = find_conversation(pinfo->fd->num, &pinfo->src, &pinfo->dst, pinfo->ptype, pinfo->srcport, pinfo->destport, 0); /* Create new conversation if necessary */ if (!conv) { conv = conversation_new(pinfo->fd->num, &pinfo->src, &pinfo->dst, pinfo->ptype, pinfo->srcport, pinfo->destport, 0); } /* Look for previous data */ data = conversation_get_proto_data(conv, proto_rtsp); /* Create new data if necessary */ if (!data) { data = se_alloc(sizeof(rtsp_conversation_data_t)); conversation_add_proto_data(conv, proto_rtsp, data); } /* Now set the dissector handle of the interleaved channel according to the transport protocol used */ if (rtp_transport) { if (s_data_chan < RTSP_MAX_INTERLEAVED) { data->interleaved[s_data_chan].dissector = rtp_handle; } if (i > 1 && s_mon_chan < RTSP_MAX_INTERLEAVED) { data->interleaved[s_mon_chan].dissector = rtcp_handle; } } else if (rdt_transport) { if (s_data_chan < RTSP_MAX_INTERLEAVED) { data->interleaved[s_data_chan].dissector = rdt_handle; } } return; } /* * We only want to match on the destination address, not the * source address, because the server might send back a packet * from an address other than the address to which its client * sent the packet, so we construct a conversation with no * second address. */ if (rtp_transport) { /* There is always data for RTP */ rtp_add_address(pinfo, &pinfo->dst, c_data_port, s_data_port, "RTSP", pinfo->fd->num, is_video, NULL); /* RTCP only if indicated */ if (c_mon_port) { rtcp_add_address(pinfo, &pinfo->dst, c_mon_port, s_mon_port, "RTSP", pinfo->fd->num); } } else if (rdt_transport) { /* Real Data Transport */ rdt_add_address(pinfo, &pinfo->dst, c_data_port, s_data_port, "RTSP", rdt_feature_level); } }
static bool parse_line(config_file_t *conf, struct config_entry_list *list, char *line, config_file_cb_t *cb) { char *key = NULL; char *key_tmp = NULL; size_t cur_size = 8; size_t idx = 0; char *comment = strip_comment(line); /* Starting line with #include includes config files. */ if (comment == line) { comment++; if (strstr(comment, "include ") == comment) { char *line = comment + STRLEN_CONST("include "); char *path = extract_value(line, false); if (!path) return false; if (conf->include_depth >= MAX_INCLUDE_DEPTH) fprintf(stderr, "!!! #include depth exceeded for config. Might be a cycle.\n"); else add_sub_conf(conf, path, cb); free(path); } } /* Skips to first character. */ while (isspace((int)*line)) line++; key = (char*)malloc(9); while (isgraph((int)*line)) { if (idx == cur_size) { cur_size *= 2; key_tmp = (char*)realloc(key, cur_size + 1); if (!key_tmp) { free(key); return false; } key = key_tmp; } key[idx++] = *line++; } key[idx] = '\0'; list->key = key; list->value = extract_value(line, true); if (!list->value) { list->key = NULL; free(key); return false; } return true; }
static struct rxml_node *rxml_parse_node(const char **ptr_) { const char *ptr = NULL; const char *closing = NULL; char *str = NULL; bool is_closing = false; struct rxml_node *node = (struct rxml_node*)calloc(1, sizeof(*node)); if (!node) return NULL; rxml_skip_spaces(ptr_); ptr = *ptr_; if (*ptr != '<') goto error; closing = strchr(ptr, '>'); if (!closing) goto error; str = strdup_range(ptr + 1, closing); if (!str) goto error; if (!rxml_parse_tag(node, str)) goto error; /* Are spaces between / and > allowed? */ is_closing = strstr(ptr, "/>") + 1 == closing; /* Look for more data. Either child nodes or data. */ if (!is_closing) { size_t closing_tag_size = strlen(node->name) + 4; char *closing_tag = (char*)malloc(closing_tag_size); const char *cdata_start = NULL; const char *child_start = NULL; const char *closing_start = NULL; if (!closing_tag) goto error; snprintf(closing_tag, closing_tag_size, "</%s>", node->name); cdata_start = strstr(closing + 1, "<![CDATA["); child_start = strchr(closing + 1, '<'); closing_start = strstr(closing + 1, closing_tag); if (!closing_start) { free(closing_tag); goto error; } if (cdata_start && range_is_space(closing + 1, cdata_start)) { /* CDATA section */ const char *cdata_end = strstr(cdata_start, "]]>"); if (!cdata_end) { free(closing_tag); goto error; } node->data = strdup_range(cdata_start + STRLEN_CONST("<![CDATA["), cdata_end); } else if (closing_start && closing_start == child_start) /* Simple Data */ node->data = strdup_range(closing + 1, closing_start); else { /* Parse all child nodes. */ struct rxml_node *list = NULL; struct rxml_node *tail = NULL; const char *first_start = NULL; const char *first_closing = NULL; ptr = child_start; first_start = strchr(ptr, '<'); first_closing = strstr(ptr, "</"); while ( first_start && first_closing && (first_start < first_closing) ) { struct rxml_node *new_node = rxml_parse_node(&ptr); if (!new_node) { free(closing_tag); goto error; } if (tail) { tail->next = new_node; tail = new_node; } else list = tail = new_node; first_start = strchr(ptr, '<'); first_closing = strstr(ptr, "</"); } node->children = list; closing_start = strstr(ptr, closing_tag); if (!closing_start) { free(closing_tag); goto error; } } *ptr_ = closing_start + strlen(closing_tag); free(closing_tag); } else *ptr_ = closing + 1; if (str) free(str); return node; error: if (str) free(str); rxml_free_node(node); return NULL; }