| File: | epan/dissectors/packet-rtsp.c |
| Warning: | line 1568, column 5 Value stored to 'linelen' is never read |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | /* packet-rtsp.c |
| 2 | * Routines for RTSP packet disassembly (RFC 2326) |
| 3 | * |
| 4 | * Jason Lango <[email protected]> |
| 5 | * Liberally copied from packet-http.c, by Guy Harris <[email protected]> |
| 6 | * |
| 7 | * Wireshark - Network traffic analyzer |
| 8 | * By Gerald Combs <[email protected]> |
| 9 | * Copyright 1998 Gerald Combs |
| 10 | * |
| 11 | * SPDX-License-Identifier: GPL-2.0-or-later |
| 12 | * |
| 13 | * References: |
| 14 | * RTSP is defined in RFC 2326, https://tools.ietf.org/html/rfc2326 |
| 15 | * https://www.iana.org/assignments/rsvp-parameters |
| 16 | * RFC 7826 describes RTSP 2.0, and technically obsoletes RFC 2326. |
| 17 | * However, in practice due to lack of backwards compatibility, it has |
| 18 | * has seen limited adoption and this dissector does not attempt to |
| 19 | * dissect it. RFC 7826 does, however, have some useful comments about |
| 20 | * ambiguities and pitfalls in RFC 2326. |
| 21 | */ |
| 22 | |
| 23 | #include "config.h" |
| 24 | |
| 25 | #include <stdio.h> /* for sscanf() */ |
| 26 | |
| 27 | #include <epan/packet.h> |
| 28 | #include <epan/req_resp_hdrs.h> |
| 29 | #include <epan/prefs.h> |
| 30 | #include <epan/conversation.h> |
| 31 | #include <epan/expert.h> |
| 32 | #include <epan/strutil.h> |
| 33 | #include <epan/tap-voip.h> |
| 34 | #include <epan/stats_tree.h> |
| 35 | #include <epan/addr_resolv.h> |
| 36 | #include <wsutil/str_util.h> |
| 37 | #include <wsutil/strtoi.h> |
| 38 | #include <wsutil/array.h> |
| 39 | |
| 40 | #include "packet-rdt.h" |
| 41 | #include "packet-rtp.h" |
| 42 | #include "packet-rtcp.h" |
| 43 | #include "packet-e164.h" |
| 44 | #include "packet-rtsp.h" |
| 45 | #include "packet-media-type.h" |
| 46 | |
| 47 | void proto_register_rtsp(void); |
| 48 | |
| 49 | static int rtsp_tap; |
| 50 | |
| 51 | /* http://www.iana.org/assignments/rtsp-parameters/rtsp-parameters.xml */ |
| 52 | |
| 53 | static const value_string rtsp_status_code_vals[] = { |
| 54 | { 100, "Continue" }, |
| 55 | { 199, "Informational - Others" }, |
| 56 | |
| 57 | { 200, "OK"}, |
| 58 | { 201, "Created"}, |
| 59 | { 250, "Low on Storage Space"}, |
| 60 | { 299, "Success - Others"}, |
| 61 | |
| 62 | { 300, "Multiple Choices"}, |
| 63 | { 301, "Moved Permanently"}, |
| 64 | { 302, "Moved Temporarily"}, |
| 65 | { 303, "See Other"}, |
| 66 | { 305, "Use Proxy"}, |
| 67 | { 399, "Redirection - Others"}, |
| 68 | |
| 69 | { 400, "Bad Request"}, |
| 70 | { 401, "Unauthorized"}, |
| 71 | { 402, "Payment Required"}, |
| 72 | { 403, "Forbidden"}, |
| 73 | { 404, "Not Found"}, |
| 74 | { 405, "Method Not Allowed"}, |
| 75 | { 406, "Not Acceptable"}, |
| 76 | { 407, "Proxy Authentication Required"}, |
| 77 | { 408, "Request Timeout"}, |
| 78 | { 410, "Gone"}, |
| 79 | { 411, "Length Required"}, |
| 80 | { 412, "Precondition Failed"}, |
| 81 | { 413, "Request Entity Too Large"}, |
| 82 | { 414, "Request-URI Too Long"}, |
| 83 | { 415, "Unsupported Media Type"}, |
| 84 | { 451, "Invalid Parameter"}, |
| 85 | { 452, "Illegal Conference Identifier"}, |
| 86 | { 453, "Not Enough Bandwidth"}, |
| 87 | { 454, "Session Not Found"}, |
| 88 | { 455, "Method Not Valid In This State"}, |
| 89 | { 456, "Header Field Not Valid"}, |
| 90 | { 457, "Invalid Range"}, |
| 91 | { 458, "Parameter Is Read-Only"}, |
| 92 | { 459, "Aggregate Operation Not Allowed"}, |
| 93 | { 460, "Only Aggregate Operation Allowed"}, |
| 94 | { 461, "Unsupported Transport"}, |
| 95 | { 462, "Destination Unreachable"}, |
| 96 | { 499, "Client Error - Others"}, |
| 97 | |
| 98 | { 500, "Internal Server Error"}, |
| 99 | { 501, "Not Implemented"}, |
| 100 | { 502, "Bad Gateway"}, |
| 101 | { 503, "Service Unavailable"}, |
| 102 | { 504, "Gateway Timeout"}, |
| 103 | { 505, "RTSP Version not supported"}, |
| 104 | { 551, "Option Not Support"}, |
| 105 | { 599, "Server Error - Others"}, |
| 106 | |
| 107 | { 0, NULL((void*)0)} |
| 108 | }; |
| 109 | |
| 110 | static int proto_rtsp; |
| 111 | |
| 112 | static int ett_rtsp; |
| 113 | static int ett_rtspframe; |
| 114 | static int ett_rtsp_method; |
| 115 | |
| 116 | static int hf_rtsp_request; |
| 117 | static int hf_rtsp_response; |
| 118 | static int hf_rtsp_response_in; |
| 119 | static int hf_rtsp_response_to; |
| 120 | static int hf_rtsp_content_type; |
| 121 | static int hf_rtsp_content_length; |
| 122 | static int hf_rtsp_method; |
| 123 | static int hf_rtsp_url; |
| 124 | static int hf_rtsp_status; |
| 125 | static int hf_rtsp_session; |
| 126 | static int hf_rtsp_transport; |
| 127 | static int hf_rtsp_rdtfeaturelevel; |
| 128 | static int hf_rtsp_cseq; |
| 129 | static int hf_rtsp_content_base; |
| 130 | static int hf_rtsp_content_location; |
| 131 | static int hf_rtsp_X_Vig_Msisdn; |
| 132 | static int hf_rtsp_magic; |
| 133 | static int hf_rtsp_channel; |
| 134 | static int hf_rtsp_length; |
| 135 | static int hf_rtsp_data; |
| 136 | |
| 137 | static int voip_tap; |
| 138 | |
| 139 | static expert_field ei_rtsp_unknown_transport_type; |
| 140 | static expert_field ei_rtsp_bad_server_port; |
| 141 | static expert_field ei_rtsp_bad_client_port; |
| 142 | static expert_field ei_rtsp_bad_interleaved_channel; |
| 143 | static expert_field ei_rtsp_content_length_invalid; |
| 144 | static expert_field ei_rtsp_rdtfeaturelevel_invalid; |
| 145 | static expert_field ei_rtsp_cseq_invalid; |
| 146 | static expert_field ei_rtsp_bad_server_ip_address; |
| 147 | static expert_field ei_rtsp_bad_client_ip_address; |
| 148 | |
| 149 | static dissector_handle_t rtsp_handle; |
| 150 | static dissector_handle_t rtp_handle; |
| 151 | static dissector_handle_t rtp_rfc4571_handle; |
| 152 | static dissector_handle_t rtcp_handle; |
| 153 | static dissector_handle_t rdt_handle; |
| 154 | static dissector_table_t media_type_dissector_table; |
| 155 | static heur_dissector_list_t heur_subdissector_list; |
| 156 | |
| 157 | static const char *st_str_packets = "Total RTSP Packets"; |
| 158 | static const char *st_str_requests = "RTSP Request Packets"; |
| 159 | static const char *st_str_responses = "RTSP Response Packets"; |
| 160 | static const char *st_str_resp_broken = "???: broken"; |
| 161 | static const char *st_str_resp_100 = "1xx: Informational"; |
| 162 | static const char *st_str_resp_200 = "2xx: Success"; |
| 163 | static const char *st_str_resp_300 = "3xx: Redirection"; |
| 164 | static const char *st_str_resp_400 = "4xx: Client Error"; |
| 165 | static const char *st_str_resp_500 = "5xx: Server Error"; |
| 166 | static const char *st_str_other = "Other RTSP Packets"; |
| 167 | |
| 168 | static int st_node_packets = -1; |
| 169 | static int st_node_requests = -1; |
| 170 | static int st_node_responses = -1; |
| 171 | static int st_node_resp_broken = -1; |
| 172 | static int st_node_resp_100 = -1; |
| 173 | static int st_node_resp_200 = -1; |
| 174 | static int st_node_resp_300 = -1; |
| 175 | static int st_node_resp_400 = -1; |
| 176 | static int st_node_resp_500 = -1; |
| 177 | static int st_node_other = -1; |
| 178 | |
| 179 | static void |
| 180 | rtsp_stats_tree_init(stats_tree* st) |
| 181 | { |
| 182 | st_node_packets = stats_tree_create_node(st, st_str_packets, 0, STAT_DT_INT, true1); |
| 183 | st_node_requests = stats_tree_create_pivot(st, st_str_requests, st_node_packets); |
| 184 | st_node_responses = stats_tree_create_node(st, st_str_responses, st_node_packets, STAT_DT_INT, true1); |
| 185 | st_node_resp_broken = stats_tree_create_node(st, st_str_resp_broken, st_node_responses, STAT_DT_INT, true1); |
| 186 | st_node_resp_100 = stats_tree_create_node(st, st_str_resp_100, st_node_responses, STAT_DT_INT, true1); |
| 187 | st_node_resp_200 = stats_tree_create_node(st, st_str_resp_200, st_node_responses, STAT_DT_INT, true1); |
| 188 | st_node_resp_300 = stats_tree_create_node(st, st_str_resp_300, st_node_responses, STAT_DT_INT, true1); |
| 189 | st_node_resp_400 = stats_tree_create_node(st, st_str_resp_400, st_node_responses, STAT_DT_INT, true1); |
| 190 | st_node_resp_500 = stats_tree_create_node(st, st_str_resp_500, st_node_responses, STAT_DT_INT, true1); |
| 191 | st_node_other = stats_tree_create_node(st, st_str_other, st_node_packets, STAT_DT_INT, false0); |
| 192 | } |
| 193 | |
| 194 | /* RTSP/Packet Counter stats packet function */ |
| 195 | static tap_packet_status |
| 196 | rtsp_stats_tree_packet(stats_tree* st, packet_info* pinfo _U___attribute__((unused)), epan_dissect_t* edt _U___attribute__((unused)), const void* p, tap_flags_t flags _U___attribute__((unused))) |
| 197 | { |
| 198 | const rtsp_info_value_t *v = (const rtsp_info_value_t *)p; |
| 199 | unsigned i = v->response_code; |
| 200 | int resp_grp; |
| 201 | const char *resp_str; |
| 202 | char *str; |
| 203 | |
| 204 | tick_stat_node(st, st_str_packets, 0, false)(stats_tree_manip_node_int(MN_INCREASE,(st),(st_str_packets), (0),(0),1)); |
| 205 | |
| 206 | if (i) { |
| 207 | tick_stat_node(st, st_str_responses, st_node_packets, false)(stats_tree_manip_node_int(MN_INCREASE,(st),(st_str_responses ),(st_node_packets),(0),1)); |
| 208 | |
| 209 | if ( (i<100)||(i>=600) ) { |
| 210 | resp_grp = st_node_resp_broken; |
| 211 | resp_str = st_str_resp_broken; |
| 212 | } else if (i<200) { |
| 213 | resp_grp = st_node_resp_100; |
| 214 | resp_str = st_str_resp_100; |
| 215 | } else if (i<300) { |
| 216 | resp_grp = st_node_resp_200; |
| 217 | resp_str = st_str_resp_200; |
| 218 | } else if (i<400) { |
| 219 | resp_grp = st_node_resp_300; |
| 220 | resp_str = st_str_resp_300; |
| 221 | } else if (i<500) { |
| 222 | resp_grp = st_node_resp_400; |
| 223 | resp_str = st_str_resp_400; |
| 224 | } else { |
| 225 | resp_grp = st_node_resp_500; |
| 226 | resp_str = st_str_resp_500; |
| 227 | } |
| 228 | |
| 229 | tick_stat_node(st, resp_str, st_node_responses, false)(stats_tree_manip_node_int(MN_INCREASE,(st),(resp_str),(st_node_responses ),(0),1)); |
| 230 | |
| 231 | str = wmem_strdup_printf(pinfo->pool, "%u %s", i, val_to_str(pinfo->pool, i, rtsp_status_code_vals, "Unknown (%d)")); |
| 232 | tick_stat_node(st, str, resp_grp, false)(stats_tree_manip_node_int(MN_INCREASE,(st),(str),(resp_grp), (0),1)); |
| 233 | } else if (v->request_method) { |
| 234 | stats_tree_tick_pivot(st,st_node_requests,v->request_method); |
| 235 | } else { |
| 236 | tick_stat_node(st, st_str_other, st_node_packets, false)(stats_tree_manip_node_int(MN_INCREASE,(st),(st_str_other),(st_node_packets ),(0),1)); |
| 237 | } |
| 238 | |
| 239 | return TAP_PACKET_REDRAW; |
| 240 | } |
| 241 | void proto_reg_handoff_rtsp(void); |
| 242 | |
| 243 | /* |
| 244 | * desegmentation of RTSP headers |
| 245 | * (when we are over TCP or another protocol providing the desegmentation API) |
| 246 | */ |
| 247 | static bool_Bool rtsp_desegment_headers = true1; |
| 248 | |
| 249 | /* |
| 250 | * desegmentation of RTSP bodies |
| 251 | * (when we are over TCP or another protocol providing the desegmentation API) |
| 252 | * TODO let the user filter on content-type the bodies he wants desegmented |
| 253 | */ |
| 254 | static bool_Bool rtsp_desegment_body = true1; |
| 255 | |
| 256 | /* http://www.iana.org/assignments/port-numbers lists two rtsp ports. |
| 257 | * In Addition RTSP uses display port over Wi-Fi Display: 7236. |
| 258 | */ |
| 259 | #define RTSP_TCP_PORT_RANGE"554,8554,7236" "554,8554,7236" |
| 260 | |
| 261 | /* |
| 262 | * Takes an array of bytes, assumed to contain a null-terminated |
| 263 | * string, as an argument, and returns the length of the string - |
| 264 | * i.e., the size of the array, minus 1 for the null terminator. |
| 265 | */ |
| 266 | #define STRLEN_CONST(str)(sizeof (str) - 1) (sizeof (str) - 1) |
| 267 | |
| 268 | #define RTSP_FRAMEHDR('$') ('$') |
| 269 | |
| 270 | typedef struct { |
| 271 | char *request_uri; |
| 272 | uint32_t req_frame; |
| 273 | uint32_t resp_frame; |
| 274 | |
| 275 | } rtsp_req_resp_t; |
| 276 | |
| 277 | typedef struct { |
| 278 | dissector_handle_t dissector; |
| 279 | } rtsp_interleaved_t; |
| 280 | |
| 281 | #define RTSP_MAX_INTERLEAVED(256) (256) |
| 282 | |
| 283 | /* |
| 284 | * Careful about dynamically allocating memory in this structure (say |
| 285 | * for dynamically increasing the size of the 'interleaved' array) - |
| 286 | * the containing structure is garbage collected and contained |
| 287 | * pointers will not be freed. |
| 288 | * |
| 289 | * XXX - This is wmem allocated now. Rather than a array of fixed size, |
| 290 | * the array could be, e.g., a tree or map indexed by the channel. |
| 291 | */ |
| 292 | typedef struct { |
| 293 | rtsp_interleaved_t interleaved[RTSP_MAX_INTERLEAVED(256)]; |
| 294 | wmem_map_t *req_resp_map; |
| 295 | } rtsp_conversation_data_t; |
| 296 | |
| 297 | static rtsp_conversation_data_t* |
| 298 | get_rtsp_conversation_data(conversation_t *conv, packet_info *pinfo) |
| 299 | { |
| 300 | rtsp_conversation_data_t *data; |
| 301 | if (conv == NULL((void*)0)) { |
| 302 | conv = find_or_create_conversation_strat(pinfo); |
| 303 | } |
| 304 | |
| 305 | /* Look for previous data */ |
| 306 | data = (rtsp_conversation_data_t *)conversation_get_proto_data(conv, proto_rtsp); |
| 307 | |
| 308 | /* Create new data if necessary */ |
| 309 | if (!data) |
| 310 | { |
| 311 | data = wmem_new0(wmem_file_scope(), rtsp_conversation_data_t)((rtsp_conversation_data_t*)wmem_alloc0((wmem_file_scope()), sizeof (rtsp_conversation_data_t))); |
| 312 | data->req_resp_map = wmem_map_new(wmem_file_scope(), g_direct_hash, g_direct_equal); |
| 313 | conversation_add_proto_data(conv, proto_rtsp, data); |
| 314 | } |
| 315 | |
| 316 | return data; |
| 317 | } |
| 318 | |
| 319 | static int |
| 320 | dissect_rtspinterleaved(tvbuff_t *tvb, int offset, packet_info *pinfo, |
| 321 | proto_tree *tree) |
| 322 | { |
| 323 | unsigned length_remaining; |
| 324 | proto_item *ti; |
| 325 | proto_tree *rtspframe_tree = NULL((void*)0); |
| 326 | int orig_offset; |
| 327 | uint8_t rf_chan; /* interleaved channel id */ |
| 328 | uint16_t rf_len; /* packet length */ |
| 329 | tvbuff_t *next_tvb; |
| 330 | conversation_t *conv; |
| 331 | rtsp_conversation_data_t *data; |
| 332 | dissector_handle_t dissector; |
| 333 | |
| 334 | /* |
| 335 | * This will throw an exception if we don't have any data left. |
| 336 | * That's what we want. (See "tcp_dissect_pdus()", which is |
| 337 | * similar.) |
| 338 | */ |
| 339 | length_remaining = tvb_ensure_captured_length_remaining(tvb, offset); |
| 340 | |
| 341 | /* |
| 342 | * Can we do reassembly? |
| 343 | */ |
| 344 | if (rtsp_desegment_headers && pinfo->can_desegment) { |
| 345 | /* |
| 346 | * Yes - would an RTSP multiplexed header starting at |
| 347 | * this offset be split across segment boundaries? |
| 348 | */ |
| 349 | if (length_remaining < 4) { |
| 350 | /* |
| 351 | * Yes. Tell the TCP dissector where the data for |
| 352 | * this message starts in the data it handed us and |
| 353 | * that we need "some more data." Don't tell it |
| 354 | * exactly how many bytes we need because if/when we |
| 355 | * ask for even more (after the header) that will |
| 356 | * break reassembly. |
| 357 | */ |
| 358 | pinfo->desegment_offset = offset; |
| 359 | pinfo->desegment_len = DESEGMENT_ONE_MORE_SEGMENT0x0fffffff; |
| 360 | return -1; |
| 361 | } |
| 362 | } |
| 363 | |
| 364 | /* |
| 365 | * Get the "$", channel, and length from the header. |
| 366 | */ |
| 367 | orig_offset = offset; |
| 368 | rf_chan = tvb_get_uint8(tvb, offset+1); |
| 369 | rf_len = tvb_get_ntohs(tvb, offset+2); |
| 370 | |
| 371 | /* |
| 372 | * Can we do reassembly? |
| 373 | */ |
| 374 | if (rtsp_desegment_body && pinfo->can_desegment) { |
| 375 | /* |
| 376 | * Yes - is the header + encapsulated packet split |
| 377 | * across segment boundaries? |
| 378 | */ |
| 379 | if (length_remaining < 4U + rf_len) { |
| 380 | /* |
| 381 | * Yes. Tell the TCP dissector where the data |
| 382 | * for this message starts in the data it handed |
| 383 | * us, and how many more bytes we need, and return. |
| 384 | */ |
| 385 | pinfo->desegment_offset = offset; |
| 386 | pinfo->desegment_len = 4U + rf_len - length_remaining; |
| 387 | return -1; |
| 388 | } |
| 389 | } |
| 390 | |
| 391 | col_add_fstr(pinfo->cinfo, COL_INFO, |
| 392 | "Interleaved channel 0x%02x, %u bytes", |
| 393 | rf_chan, rf_len); |
| 394 | |
| 395 | ti = proto_tree_add_protocol_format(tree, proto_rtsp, tvb, |
| 396 | offset, 4, |
| 397 | "RTSP Interleaved Frame, Channel: 0x%02x, %u bytes", |
| 398 | rf_chan, rf_len); |
| 399 | rtspframe_tree = proto_item_add_subtree(ti, ett_rtspframe); |
| 400 | |
| 401 | proto_tree_add_item(rtspframe_tree, hf_rtsp_magic, tvb, offset, 1, ENC_BIG_ENDIAN0x00000000); |
| 402 | |
| 403 | offset += 1; |
| 404 | |
| 405 | proto_tree_add_item(rtspframe_tree, hf_rtsp_channel, tvb, offset, 1, ENC_BIG_ENDIAN0x00000000); |
| 406 | |
| 407 | offset += 1; |
| 408 | |
| 409 | proto_tree_add_item(rtspframe_tree, hf_rtsp_length, tvb, offset, 2, ENC_BIG_ENDIAN0x00000000); |
| 410 | offset += 2; |
| 411 | |
| 412 | next_tvb = tvb_new_subset_length(tvb, offset, rf_len); |
| 413 | |
| 414 | conv = find_conversation_pinfo_strat(pinfo, 0); |
| 415 | |
| 416 | if (conv && |
| 417 | (data = (rtsp_conversation_data_t *)conversation_get_proto_data(conv, proto_rtsp)) && |
| 418 | /* Add the following condition if it is not always true. |
| 419 | rf_chan < RTSP_MAX_INTERLEAVED && |
| 420 | */ |
| 421 | (dissector = data->interleaved[rf_chan].dissector)) { |
| 422 | call_dissector(dissector, next_tvb, pinfo, tree); |
| 423 | } else { |
| 424 | bool_Bool dissected = false0; |
| 425 | heur_dtbl_entry_t *hdtbl_entry = NULL((void*)0); |
| 426 | |
| 427 | dissected = dissector_try_heuristic(heur_subdissector_list, |
| 428 | next_tvb, pinfo, tree, &hdtbl_entry, NULL((void*)0)); |
| 429 | |
| 430 | if (!dissected) { |
| 431 | proto_tree_add_item(rtspframe_tree, hf_rtsp_data, tvb, offset, rf_len, ENC_NA0x00000000); |
| 432 | } |
| 433 | } |
| 434 | |
| 435 | offset += rf_len; |
| 436 | |
| 437 | return offset - orig_offset; |
| 438 | } |
| 439 | |
| 440 | static char* process_rtsp_request(tvbuff_t *tvb, |
| 441 | int linelen, packet_info *pinfo, proto_tree *tree); |
| 442 | |
| 443 | static void process_rtsp_reply(tvbuff_t *tvb, |
| 444 | int linelen, packet_info *pinfo, proto_tree *tree); |
| 445 | |
| 446 | typedef enum { |
| 447 | RTSP_REQUEST, |
| 448 | RTSP_REPLY, |
| 449 | RTSP_NOT_FIRST_LINE |
| 450 | } rtsp_type_t; |
| 451 | |
| 452 | static const char *rtsp_methods[] = { |
| 453 | "DESCRIBE", |
| 454 | "ANNOUNCE", |
| 455 | "GET_PARAMETER", |
| 456 | "OPTIONS", |
| 457 | "PAUSE", |
| 458 | "PLAY", |
| 459 | "RECORD", |
| 460 | "REDIRECT", |
| 461 | "SETUP", |
| 462 | "SET_PARAMETER", |
| 463 | "TEARDOWN" |
| 464 | }; |
| 465 | |
| 466 | #define RTSP_NMETHODS(sizeof (rtsp_methods) / sizeof (rtsp_methods)[0]) array_length(rtsp_methods)(sizeof (rtsp_methods) / sizeof (rtsp_methods)[0]) |
| 467 | |
| 468 | static bool_Bool |
| 469 | is_rtsp_request_or_reply(tvbuff_t *tvb, int linelen, rtsp_type_t *type, |
| 470 | rtsp_info_value_t *rtsp_stat_info, wmem_allocator_t *pool) |
| 471 | { |
| 472 | unsigned ii; |
| 473 | int offset = 0, next_offset; |
| 474 | int tokenlen; |
| 475 | char response_chars[4]; |
| 476 | |
| 477 | tokenlen = tvb_get_token_len(tvb, offset, linelen, &next_offset, false0); |
| 478 | |
| 479 | /* Is this an RTSP reply? */ |
| 480 | if (linelen >= 5 && tvb_strncaseeql(tvb, offset, "RTSP/", 5) == 0) { |
| 481 | /* |
| 482 | * Yes. |
| 483 | */ |
| 484 | *type = RTSP_REPLY; |
| 485 | /* The first token is the version. */ |
| 486 | if (tvb_reported_length_remaining(tvb, next_offset)) { |
| 487 | /* The next token is the status code. */ |
| 488 | offset = next_offset; |
| 489 | tokenlen = tvb_get_token_len(tvb, offset, tvb_reported_length_remaining(tvb, offset), NULL((void*)0), true1); |
| 490 | if (tokenlen >= 3) { |
| 491 | tvb_memcpy(tvb, response_chars, offset, 3); |
| 492 | response_chars[3] = '\0'; |
| 493 | ws_strtou32(response_chars, NULL((void*)0), &rtsp_stat_info->response_code); |
| 494 | } |
| 495 | } |
| 496 | return true1; |
| 497 | } |
| 498 | |
| 499 | /* |
| 500 | * Is this an RTSP request? |
| 501 | * Check whether the line begins with one of the RTSP request |
| 502 | * methods. |
| 503 | */ |
| 504 | for (ii = 0; ii < RTSP_NMETHODS(sizeof (rtsp_methods) / sizeof (rtsp_methods)[0]); ii++) { |
| 505 | size_t len = strlen(rtsp_methods[ii]); |
| 506 | if (len == (size_t)tokenlen && |
| 507 | tvb_strncaseeql(tvb, offset, rtsp_methods[ii], len) == 0) |
| 508 | { |
| 509 | *type = RTSP_REQUEST; |
| 510 | rtsp_stat_info->request_method = |
| 511 | wmem_strndup(pool, rtsp_methods[ii], len+1); |
| 512 | return true1; |
| 513 | } |
| 514 | } |
| 515 | |
| 516 | /* Wasn't a request or a response */ |
| 517 | *type = RTSP_NOT_FIRST_LINE; |
| 518 | return false0; |
| 519 | } |
| 520 | |
| 521 | static const char rtsp_content_type[] = "Content-Type:"; |
| 522 | static const char rtsp_transport[] = "Transport:"; |
| 523 | static const char rtsp_sps_server_port[] = "server_port="; |
| 524 | static const char rtsp_cps_server_port[] = "client_port="; |
| 525 | static const char rtsp_sps_dest_addr[] = "dest_addr="; |
| 526 | static const char rtsp_cps_src_addr[] = "src_addr="; |
| 527 | static const char rtsp_rtp_udp_default[] = "rtp/avp"; |
| 528 | static const char rtsp_rtp_udp[] = "rtp/avp/udp"; |
| 529 | static const char rtsp_rtp_tcp[] = "rtp/avp/tcp"; |
| 530 | static const char rtsp_rdt_feature_level[] = "RDTFeatureLevel"; |
| 531 | static const char rtsp_real_rdt[] = "x-real-rdt/"; |
| 532 | static const char rtsp_real_tng[] = "x-pn-tng/"; /* synonym for x-real-rdt */ |
| 533 | static const char rtsp_inter[] = "interleaved="; |
| 534 | static const char rtsp_cseq[] = "CSeq:"; |
| 535 | static const char rtsp_content_base[] = "Content-Base:"; |
| 536 | static const char rtsp_content_location[] = "Content-Location:"; |
| 537 | |
| 538 | static sdp_setup_info_t* |
| 539 | rtsp_create_setup_info(packet_info *pinfo, const char* session_id, const char *base_uri) |
| 540 | { |
| 541 | sdp_setup_info_t *setup_info = NULL((void*)0); |
| 542 | if (!PINFO_FD_VISITED(pinfo)((pinfo)->fd->visited)) { |
| 543 | // setup_info is only used on the first pass (by SDP or RTP) |
| 544 | setup_info = wmem_new0(pinfo->pool, sdp_setup_info_t)((sdp_setup_info_t*)wmem_alloc0((pinfo->pool), sizeof(sdp_setup_info_t ))); |
| 545 | setup_info->hf_id = hf_rtsp_session; |
| 546 | setup_info->hf_type = SDP_TRACE_ID_HF_TYPE_STR; |
| 547 | /* The session is a mandatory, opaque string for the session - but |
| 548 | * not necessarily available at the time of media initialization via SDP, |
| 549 | * whether DESCRIBE or via HTTP or some other protocol. It is known at |
| 550 | * the time of actual RTP setup by RTSP, though. |
| 551 | * |
| 552 | * wmem_strdup will return "<NULL>" when it is not available, which |
| 553 | * shouldn't ever be used by SDP (due to the "control" media attribute) |
| 554 | * but prevents a possible null dereference with, e.g., fuzzed captures. |
| 555 | * |
| 556 | * It's in file scope (unlike the setup_info struct itself) because the |
| 557 | * SDP and RTP dissectors don't copy the string but use it directly. |
| 558 | * We probably could store the session id copy in conversation data. |
| 559 | */ |
| 560 | setup_info->trace_id.str = wmem_strdup(wmem_file_scope(), session_id); |
| 561 | setup_info->base_uri = base_uri; |
| 562 | } |
| 563 | |
| 564 | return setup_info; |
| 565 | } |
| 566 | |
| 567 | static void |
| 568 | rtsp_create_conversation(packet_info *pinfo, proto_item *ti, |
| 569 | const char *line_begin, size_t line_len, |
| 570 | uint32_t rdt_feature_level, |
| 571 | rtsp_type_t rtsp_type_packet, |
| 572 | sdp_setup_info_t *setup_info) |
| 573 | { |
| 574 | char buf[256]; |
| 575 | char *tmp; |
| 576 | bool_Bool rtp_udp_transport = false0; |
| 577 | bool_Bool rtp_tcp_transport = false0; |
| 578 | bool_Bool rdt_transport = false0; |
| 579 | unsigned c_data_port, c_mon_port; |
| 580 | unsigned s_data_port, s_mon_port; |
| 581 | unsigned ipv4_1, ipv4_2, ipv4_3, ipv4_4; |
| 582 | bool_Bool is_video = false0; /* FIX ME - need to indicate video or not */ |
| 583 | address src_addr; |
| 584 | address dst_addr; |
| 585 | uint32_t ip4_addr; |
| 586 | rtp_dyn_payload_t *rtp_dyn_payload = NULL((void*)0); |
| 587 | |
| 588 | if (rtsp_type_packet != RTSP_REPLY) { |
| 589 | return; |
| 590 | } |
| 591 | |
| 592 | src_addr=pinfo->src; |
| 593 | dst_addr=pinfo->dst; |
| 594 | |
| 595 | /* Copy line into buf */ |
| 596 | if (line_len > sizeof(buf) - 1) |
| 597 | { |
| 598 | /* Don't overflow the buffer. */ |
| 599 | line_len = sizeof(buf) - 1; |
| 600 | } |
| 601 | memcpy(buf, line_begin, line_len); |
| 602 | buf[line_len] = '\0'; |
| 603 | |
| 604 | /* Get past "Transport:" and spaces */ |
| 605 | tmp = buf + STRLEN_CONST(rtsp_transport)(sizeof (rtsp_transport) - 1); |
| 606 | while (*tmp && g_ascii_isspace(*tmp)((g_ascii_table[(guchar) (*tmp)] & G_ASCII_SPACE) != 0)) |
| 607 | tmp++; |
| 608 | |
| 609 | /* Work out which transport type is here */ |
| 610 | if (g_ascii_strncasecmp(tmp, rtsp_rtp_udp, strlen(rtsp_rtp_udp)) == 0) |
| 611 | { |
| 612 | rtp_udp_transport = true1; |
| 613 | } |
| 614 | else if (g_ascii_strncasecmp(tmp, rtsp_rtp_tcp, strlen(rtsp_rtp_tcp)) == 0) |
| 615 | { |
| 616 | rtp_tcp_transport = true1; |
| 617 | } |
| 618 | else if (g_ascii_strncasecmp(tmp, rtsp_rtp_udp_default, strlen(rtsp_rtp_udp_default)) == 0) |
| 619 | { |
| 620 | rtp_udp_transport = true1; |
| 621 | } |
| 622 | else if (g_ascii_strncasecmp(tmp, rtsp_real_rdt, strlen(rtsp_real_rdt)) == 0 || |
| 623 | g_ascii_strncasecmp(tmp, rtsp_real_tng, strlen(rtsp_real_tng)) == 0) |
| 624 | { |
| 625 | rdt_transport = true1; |
| 626 | } |
| 627 | else |
| 628 | { |
| 629 | /* Give up on unknown transport types */ |
| 630 | expert_add_info(pinfo, ti, &ei_rtsp_unknown_transport_type); |
| 631 | return; |
| 632 | } |
| 633 | |
| 634 | c_data_port = c_mon_port = 0; |
| 635 | s_data_port = s_mon_port = 0; |
| 636 | |
| 637 | /* Look for server port */ |
| 638 | if ((tmp = strstr(buf, rtsp_sps_server_port))) { |
| 639 | tmp += strlen(rtsp_sps_server_port); |
| 640 | if (sscanf(tmp, "%u-%u", &s_data_port, &s_mon_port) < 1) { |
| 641 | expert_add_info(pinfo, ti, &ei_rtsp_bad_server_port); |
| 642 | return; |
| 643 | } |
| 644 | } |
| 645 | else if ((tmp = strstr(buf, rtsp_sps_dest_addr))) { |
| 646 | tmp += strlen(rtsp_sps_dest_addr); |
| 647 | if (sscanf(tmp, "\":%u\"", &s_data_port) == 1) { |
| 648 | /* :9 mean ignore */ |
| 649 | if (s_data_port == 9) { |
| 650 | s_data_port = 0; |
| 651 | } |
| 652 | } |
| 653 | else if (sscanf(tmp, "\"%u.%u.%u.%u:%u\"", &ipv4_1, &ipv4_2, &ipv4_3, &ipv4_4, &s_data_port) == 5) { |
| 654 | char *tmp2; |
| 655 | char *tmp3; |
| 656 | |
| 657 | /* Skip leading " */ |
| 658 | tmp++; |
| 659 | tmp2=strstr(tmp,":"); |
| 660 | tmp3=g_strndup(tmp,tmp2-tmp); |
| 661 | if (!str_to_ip(tmp3, &ip4_addr)) { |
| 662 | g_free(tmp3); |
| 663 | expert_add_info(pinfo, ti, &ei_rtsp_bad_server_ip_address); |
| 664 | return; |
| 665 | } |
| 666 | set_address(&dst_addr, AT_IPv4, 4, &ip4_addr); |
| 667 | g_free(tmp3); |
| 668 | } |
| 669 | else if (sscanf(tmp, "\"%u.%u.%u.%u\"", &ipv4_1, &ipv4_2, &ipv4_3, &ipv4_4) == 4) { |
| 670 | char *tmp2; |
| 671 | char *tmp3; |
| 672 | |
| 673 | /* Skip leading " */ |
| 674 | tmp++; |
| 675 | tmp2=strstr(tmp,"\""); |
| 676 | tmp3=g_strndup(tmp,tmp2-tmp); |
| 677 | if (!str_to_ip(tmp3, &ip4_addr)) { |
| 678 | g_free(tmp3); |
| 679 | expert_add_info(pinfo, ti, &ei_rtsp_bad_server_ip_address); |
| 680 | return; |
| 681 | } |
| 682 | set_address(&dst_addr, AT_IPv4, 4, &ip4_addr); |
| 683 | g_free(tmp3); |
| 684 | } |
| 685 | else |
| 686 | { |
| 687 | expert_add_info(pinfo, ti, &ei_rtsp_bad_server_port); |
| 688 | return; |
| 689 | } |
| 690 | } |
| 691 | |
| 692 | |
| 693 | /* Look for client port */ |
| 694 | if ((tmp = strstr(buf, rtsp_cps_server_port))) { |
| 695 | tmp += strlen(rtsp_cps_server_port); |
| 696 | if (sscanf(tmp, "%u-%u", &c_data_port, &c_mon_port) < 1) { |
| 697 | expert_add_info(pinfo, ti, &ei_rtsp_bad_client_port); |
| 698 | return; |
| 699 | } |
| 700 | } |
| 701 | else if ((tmp = strstr(buf, rtsp_cps_src_addr))) { |
| 702 | tmp += strlen(rtsp_cps_src_addr); |
| 703 | if (sscanf(tmp, "\"%u.%u.%u.%u:%u\"", &ipv4_1, &ipv4_2, &ipv4_3, &ipv4_4, &c_data_port) == 5) { |
| 704 | char *tmp2; |
| 705 | char *tmp3; |
| 706 | |
| 707 | /* Skip leading " */ |
| 708 | tmp++; |
| 709 | tmp2=strstr(tmp,":"); |
| 710 | tmp3=g_strndup(tmp,tmp2-tmp); |
| 711 | if (!str_to_ip(tmp3, &ip4_addr)) { |
| 712 | g_free(tmp3); |
| 713 | expert_add_info(pinfo, ti, &ei_rtsp_bad_client_ip_address); |
| 714 | return; |
| 715 | } |
| 716 | set_address(&src_addr, AT_IPv4, 4, &ip4_addr); |
| 717 | g_free(tmp3); |
| 718 | } |
| 719 | } |
| 720 | |
| 721 | if (setup_info && setup_info->base_uri) { |
| 722 | rtp_dyn_payload = sdp_get_rtsp_media_desc(setup_info->base_uri); |
| 723 | } |
| 724 | |
| 725 | /* Deal with RTSP TCP-interleaved conversations. */ |
| 726 | tmp = strstr(buf, rtsp_inter); |
| 727 | if (tmp != NULL((void*)0)) { |
| 728 | rtsp_conversation_data_t *data; |
| 729 | unsigned s_data_chan, s_mon_chan; |
| 730 | int i; |
| 731 | |
| 732 | /* Move tmp to beyond interleaved string */ |
| 733 | tmp += strlen(rtsp_inter); |
| 734 | /* Look for channel number(s) */ |
| 735 | i = sscanf(tmp, "%u-%u", &s_data_chan, &s_mon_chan); |
| 736 | if (i < 1) |
| 737 | { |
| 738 | expert_add_info(pinfo, ti, &ei_rtsp_bad_interleaved_channel); |
| 739 | return; |
| 740 | } |
| 741 | |
| 742 | /* At least data channel present, look for conversation (presumably TCP) */ |
| 743 | data = get_rtsp_conversation_data(NULL((void*)0), pinfo); |
| 744 | |
| 745 | /* XXX - This doesn't set up the rtp conversation data, including RTP |
| 746 | * dynamic payload types and setup info. Two possible approaches: |
| 747 | * 1) The RTP dissector have a function that attaches dynamic payload |
| 748 | * type and setup info to the TCP conversation but does *not* set |
| 749 | * the conversation dissector (TCP needs to call the RTSP dissector |
| 750 | * first for interleaved data). |
| 751 | * 2) Define a CONVERSATION_RTSP type and change the RTP dissector to |
| 752 | * do something other than only look for conversations that match |
| 753 | * conversation_pt_to_conversation_type(pinfo->ptype). |
| 754 | * |
| 755 | * The former needs to attach a "bundled" rtp_dyn_payload_t that |
| 756 | * includes mapping for the payload types of all possible channels; |
| 757 | * this usually happens when RTSP is used because the media descriptor |
| 758 | * port is usually 0, but we'd want to ensure it. (It also would not |
| 759 | * work if multiple sessions were SETUP simultaneously and media |
| 760 | * descriptors with different meanings for the same RTP dynamic payload |
| 761 | * type were PLAYed on different interleaved channels simulateously.) |
| 762 | */ |
| 763 | |
| 764 | /* Now set the dissector handle of the interleaved channel |
| 765 | according to the transport protocol used */ |
| 766 | if (rtp_tcp_transport) |
| 767 | { |
| 768 | if (s_data_chan < RTSP_MAX_INTERLEAVED(256)) { |
| 769 | data->interleaved[s_data_chan].dissector = |
| 770 | rtp_handle; |
| 771 | } |
| 772 | if (i > 1 && s_mon_chan < RTSP_MAX_INTERLEAVED(256)) { |
| 773 | data->interleaved[s_mon_chan].dissector = |
| 774 | rtcp_handle; |
| 775 | } |
| 776 | } |
| 777 | else if (rdt_transport) |
| 778 | { |
| 779 | if (s_data_chan < RTSP_MAX_INTERLEAVED(256)) { |
| 780 | data->interleaved[s_data_chan].dissector = |
| 781 | rdt_handle; |
| 782 | } |
| 783 | } |
| 784 | return; |
| 785 | } |
| 786 | |
| 787 | /* Noninterleaved options follow */ |
| 788 | /* |
| 789 | * We only want to match on the destination address, not the |
| 790 | * source address, because the server might send back a packet |
| 791 | * from an address other than the address to which its client |
| 792 | * sent the packet, so we construct a conversation with no |
| 793 | * second address. |
| 794 | */ |
| 795 | else if (rtp_udp_transport) |
| 796 | { |
| 797 | /* RTP only if indicated */ |
| 798 | if (c_data_port) |
| 799 | { |
| 800 | srtp_add_address(pinfo, PT_UDP, &dst_addr, c_data_port, s_data_port, |
| 801 | "RTSP", pinfo->num, is_video, rtp_dyn_payload, NULL((void*)0), setup_info); |
| 802 | } |
| 803 | else if (s_data_port) |
| 804 | { |
| 805 | srtp_add_address(pinfo, PT_UDP, &src_addr, s_data_port, 0, |
| 806 | "RTSP", pinfo->num, is_video, rtp_dyn_payload, NULL((void*)0), setup_info); |
| 807 | } |
| 808 | |
| 809 | /* RTCP only if indicated */ |
| 810 | if (c_mon_port) |
| 811 | { |
| 812 | rtcp_add_address(pinfo, &pinfo->dst, c_mon_port, s_mon_port, |
| 813 | "RTSP", pinfo->num); |
| 814 | } |
| 815 | } |
| 816 | else if (rtp_tcp_transport) |
| 817 | { |
| 818 | /* RTP only if indicated */ |
| 819 | srtp_add_address(pinfo, PT_TCP, &src_addr, c_data_port, s_data_port, |
| 820 | "RTSP", pinfo->num, is_video, rtp_dyn_payload, NULL((void*)0), setup_info); |
| 821 | } |
| 822 | else if (rdt_transport) |
| 823 | { |
| 824 | /* Real Data Transport */ |
| 825 | /* XXX - The RDT dissector considers this signed for some reason. */ |
| 826 | rdt_add_address(pinfo, &pinfo->dst, c_data_port, s_data_port, |
| 827 | "RTSP", (int)rdt_feature_level); |
| 828 | } |
| 829 | return; |
| 830 | } |
| 831 | |
| 832 | static const char rtsp_content_length[] = "Content-Length:"; |
| 833 | |
| 834 | static int |
| 835 | rtsp_get_content_length(const char *line_begin, size_t line_len _U___attribute__((unused))) |
| 836 | { |
| 837 | const char *tmp; |
| 838 | int32_t content_length; |
| 839 | const char *p; |
| 840 | const char *up; |
| 841 | |
| 842 | /* We only call this if HDR_MATCHES(rtsp_content_length)) is true, |
| 843 | * so line_len has already been checked to be long enough. The |
| 844 | * line has been extracted as a null terminated string from the |
| 845 | * packet data. */ |
| 846 | |
| 847 | tmp = line_begin + STRLEN_CONST(rtsp_content_length)(sizeof (rtsp_content_length) - 1); |
| 848 | while (*tmp && g_ascii_isspace(*tmp)((g_ascii_table[(guchar) (*tmp)] & G_ASCII_SPACE) != 0)) |
| 849 | tmp++; |
| 850 | ws_strtoi32(tmp, &p, &content_length); |
| 851 | up = p; |
| 852 | if (up == tmp || (*up != '\0' && !g_ascii_isspace(*up)((g_ascii_table[(guchar) (*up)] & G_ASCII_SPACE) != 0))) |
| 853 | return -1; /* not a valid number */ |
| 854 | return content_length; |
| 855 | } |
| 856 | |
| 857 | static const char rtsp_Session[] = "Session:"; |
| 858 | static const char rtsp_X_Vig_Msisdn[] = "X-Vig-Msisdn"; |
| 859 | |
| 860 | static int |
| 861 | dissect_rtspmessage(tvbuff_t *tvb, int offset, packet_info *pinfo, |
| 862 | proto_tree *tree) |
| 863 | { |
| 864 | proto_tree *rtsp_tree = NULL((void*)0); |
| 865 | proto_tree *req_tree = NULL((void*)0); |
| 866 | proto_tree *sub_tree = NULL((void*)0); |
| 867 | proto_item *ti_top = NULL((void*)0); |
| 868 | proto_item *ti = NULL((void*)0); |
| 869 | const char *line; |
| 870 | int next_offset; |
| 871 | int orig_offset; |
| 872 | int first_linelen, linelen; |
| 873 | int line_end_offset; |
| 874 | int colon_offset; |
| 875 | bool_Bool is_request_or_reply; |
| 876 | bool_Bool body_requires_content_len; |
| 877 | bool_Bool saw_req_resp_or_header; |
| 878 | unsigned char c; |
| 879 | rtsp_type_t rtsp_type_packet; |
| 880 | rtsp_type_t rtsp_type_line; |
| 881 | bool_Bool is_header; |
| 882 | int datalen; |
| 883 | int content_length; |
| 884 | int reported_datalen; |
| 885 | int value_offset; |
| 886 | int value_len; |
| 887 | e164_info_t e164_info; |
| 888 | uint32_t rdt_feature_level = 0; |
| 889 | char *media_type_str_lower_case = NULL((void*)0); |
| 890 | int semi_colon_offset; |
| 891 | int par_end_offset; |
| 892 | char *frame_label = NULL((void*)0); |
| 893 | char *session_id = NULL((void*)0); |
| 894 | voip_packet_info_t *stat_info = NULL((void*)0); |
| 895 | bool_Bool cseq_valid = false0; |
| 896 | uint32_t cseq = 0; |
| 897 | char *content_base = NULL((void*)0); |
| 898 | char *content_location = NULL((void*)0); |
| 899 | char *request_uri = NULL((void*)0); |
| 900 | char *base_uri = NULL((void*)0); |
| 901 | const char *transport_line = NULL((void*)0); |
| 902 | int transport_linelen; |
| 903 | sdp_setup_info_t *setup_info = NULL((void*)0); |
| 904 | rtsp_info_value_t *rtsp_stat_info; |
| 905 | |
| 906 | rtsp_stat_info = wmem_new(pinfo->pool, rtsp_info_value_t)((rtsp_info_value_t*)wmem_alloc((pinfo->pool), sizeof(rtsp_info_value_t ))); |
| 907 | rtsp_stat_info->framenum = pinfo->num; |
| 908 | rtsp_stat_info->response_code = 0; |
| 909 | rtsp_stat_info->request_method = NULL((void*)0); |
| 910 | rtsp_stat_info->request_uri = NULL((void*)0); |
| 911 | rtsp_stat_info->rtsp_host = NULL((void*)0); |
| 912 | |
| 913 | /* |
| 914 | * Is this a request or response? |
| 915 | * |
| 916 | * Note that "tvb_find_line_end()" will return a value that |
| 917 | * is not longer than what's in the buffer, so the |
| 918 | * "tvb_get_ptr()" call won't throw an exception. |
| 919 | */ |
| 920 | first_linelen = tvb_find_line_end(tvb, offset, -1, &next_offset, false0); |
| 921 | |
| 922 | /* |
| 923 | * Is the first line a request or response? |
| 924 | */ |
| 925 | is_request_or_reply = is_rtsp_request_or_reply(tvb_new_subset_length(tvb, offset, first_linelen), first_linelen, |
| 926 | &rtsp_type_packet, rtsp_stat_info, pinfo->pool); |
| 927 | if (is_request_or_reply) { |
| 928 | /* |
| 929 | * Yes, it's a request or response. |
| 930 | * Do header desegmentation if we've been told to, |
| 931 | * and do body desegmentation if we've been told to and |
| 932 | * we find a Content-Length header. |
| 933 | * |
| 934 | * RFC 7826, Section 18.17. requires Content-Length and |
| 935 | * assumes zero if missing. |
| 936 | */ |
| 937 | if (!req_resp_hdrs_do_reassembly(tvb, offset, pinfo, |
| 938 | rtsp_desegment_headers, rtsp_desegment_body, false0, NULL((void*)0), |
| 939 | NULL((void*)0), NULL((void*)0))) { |
| 940 | /* |
| 941 | * More data needed for desegmentation. |
| 942 | */ |
| 943 | return -1; |
| 944 | } |
| 945 | } |
| 946 | |
| 947 | /* |
| 948 | * RFC 2326 says that a content length must be specified |
| 949 | * in requests that have a body, although section 4.4 speaks |
| 950 | * of a server closing the connection indicating the end of |
| 951 | * a reply body. |
| 952 | * |
| 953 | * To support pipelining, we check if line behind blank line |
| 954 | * looks like RTSP header. If so, we process rest of packet with |
| 955 | * RTSP loop. |
| 956 | * |
| 957 | * If no, we assume that an absent content length in a request means |
| 958 | * that we don't have a body, and that an absent content length |
| 959 | * in a reply means that the reply body runs to the end of |
| 960 | * the connection. If the first line is neither, we assume |
| 961 | * that whatever follows a blank line should be treated as a |
| 962 | * body; there's not much else we can do, as we're jumping |
| 963 | * into the message in the middle. |
| 964 | * |
| 965 | * XXX - if there was no Content-Length entity header, we should |
| 966 | * accumulate all data until the end of the connection. |
| 967 | * That'd require that the TCP dissector call subdissectors |
| 968 | * for all frames with FIN, even if they contain no data, |
| 969 | * which would require subdissectors to deal intelligently |
| 970 | * with empty segments. |
| 971 | */ |
| 972 | if (rtsp_type_packet == RTSP_REQUEST) |
| 973 | body_requires_content_len = true1; |
| 974 | else |
| 975 | body_requires_content_len = false0; |
| 976 | |
| 977 | line = (char*)tvb_get_string_enc(pinfo->pool, tvb, offset, first_linelen, ENC_UTF_80x00000002); |
| 978 | if (is_request_or_reply) { |
| 979 | if ( rtsp_type_packet == RTSP_REPLY ) { |
| 980 | frame_label = wmem_strdup_printf(pinfo->pool, |
| 981 | "Reply: %s", format_text(pinfo->pool, line, first_linelen)); |
| 982 | } |
| 983 | else { |
| 984 | frame_label = format_text(pinfo->pool, line, first_linelen); |
| 985 | } |
| 986 | } |
| 987 | |
| 988 | col_set_str(pinfo->cinfo, COL_PROTOCOL, "RTSP"); |
| 989 | /* |
| 990 | * Put the first line from the buffer into the summary |
| 991 | * if it's an RTSP request or reply (but leave out the |
| 992 | * line terminator). |
| 993 | * Otherwise, just call it a continuation. |
| 994 | * |
| 995 | * Note that "tvb_find_line_end()" will return a value that |
| 996 | * is not longer than what's in the buffer, so the |
| 997 | * "tvb_get_ptr()" call won't throw an exception. |
| 998 | */ |
| 999 | if (is_request_or_reply) |
| 1000 | if ( rtsp_type_packet == RTSP_REPLY ) { |
| 1001 | col_set_str(pinfo->cinfo, COL_INFO, "Reply: "); |
| 1002 | col_append_str(pinfo->cinfo, COL_INFO, |
| 1003 | format_text(pinfo->pool, line, first_linelen)); |
| 1004 | } |
| 1005 | else { |
| 1006 | col_add_str(pinfo->cinfo, COL_INFO, |
| 1007 | format_text(pinfo->pool, line, first_linelen)); |
| 1008 | } |
| 1009 | |
| 1010 | else |
| 1011 | col_set_str(pinfo->cinfo, COL_INFO, "Continuation"); |
| 1012 | |
| 1013 | orig_offset = offset; |
| 1014 | if (tree) { |
| 1015 | ti_top = proto_tree_add_item(tree, proto_rtsp, tvb, offset, -1, |
| 1016 | ENC_NA0x00000000); |
| 1017 | rtsp_tree = proto_item_add_subtree(ti_top, ett_rtsp); |
| 1018 | } |
| 1019 | |
| 1020 | /* |
| 1021 | * We haven't yet seen a Content-Length header. |
| 1022 | */ |
| 1023 | content_length = -1; |
| 1024 | |
| 1025 | /* |
| 1026 | * Process the packet data, a line at a time. |
| 1027 | */ |
| 1028 | saw_req_resp_or_header = false0; /* haven't seen anything yet */ |
| 1029 | while (tvb_offset_exists(tvb, offset)) { |
| 1030 | /* |
| 1031 | * We haven't yet concluded that this is a header. |
| 1032 | */ |
| 1033 | is_header = false0; |
| 1034 | |
| 1035 | /* |
| 1036 | * Find the end of the line. |
| 1037 | */ |
| 1038 | linelen = tvb_find_line_end(tvb, offset, -1, &next_offset, false0); |
| 1039 | if (linelen < 0) |
| 1040 | return -1; |
| 1041 | line_end_offset = offset + linelen; |
| 1042 | /* |
| 1043 | * colon_offset may be -1 |
| 1044 | */ |
| 1045 | colon_offset = tvb_find_uint8(tvb, offset, linelen, ':'); |
| 1046 | |
| 1047 | |
| 1048 | /* |
| 1049 | * Get a buffer that refers to the line. |
| 1050 | */ |
| 1051 | line = (char*)tvb_get_string_enc(pinfo->pool, tvb, offset, linelen, ENC_UTF_80x00000002); |
| 1052 | |
| 1053 | /* |
| 1054 | * OK, does it look like an RTSP request or response? |
| 1055 | */ |
| 1056 | is_request_or_reply = is_rtsp_request_or_reply(tvb_new_subset_length(tvb, offset, linelen), linelen, &rtsp_type_line, |
| 1057 | rtsp_stat_info, pinfo->pool); |
| 1058 | if (is_request_or_reply) |
| 1059 | goto is_rtsp; |
| 1060 | |
| 1061 | /* |
| 1062 | * No. Does it look like a blank line (as would appear |
| 1063 | * at the end of an RTSP request)? |
| 1064 | */ |
| 1065 | if (linelen == 0) |
| 1066 | goto is_rtsp; /* Yes. */ |
| 1067 | |
| 1068 | /* |
| 1069 | * No. Does it look like a header? |
| 1070 | */ |
| 1071 | for (int current_offset = offset; current_offset < next_offset; ++current_offset) { |
| 1072 | c = tvb_get_uint8(tvb, current_offset); |
| 1073 | |
| 1074 | /* |
| 1075 | * This must be a CHAR, and must not be a CTL, to be part |
| 1076 | * of a token; that means it must be printable ASCII. |
| 1077 | * |
| 1078 | * XXX - what about leading LWS on continuation |
| 1079 | * lines of a header? |
| 1080 | */ |
| 1081 | if (!g_ascii_isprint(c)((g_ascii_table[(guchar) (c)] & G_ASCII_PRINT) != 0)) |
| 1082 | break; |
| 1083 | |
| 1084 | switch (c) { |
| 1085 | |
| 1086 | case '(': |
| 1087 | case ')': |
| 1088 | case '<': |
| 1089 | case '>': |
| 1090 | case '@': |
| 1091 | case ',': |
| 1092 | case ';': |
| 1093 | case '\\': |
| 1094 | case '"': |
| 1095 | case '/': |
| 1096 | case '[': |
| 1097 | case ']': |
| 1098 | case '?': |
| 1099 | case '=': |
| 1100 | case '{': |
| 1101 | case '}': |
| 1102 | /* |
| 1103 | * It's a tspecial, so it's not |
| 1104 | * part of a token, so it's not |
| 1105 | * a field name for the beginning |
| 1106 | * of a header. |
| 1107 | */ |
| 1108 | goto not_rtsp; |
| 1109 | |
| 1110 | case ':': |
| 1111 | /* |
| 1112 | * This ends the token; we consider |
| 1113 | * this to be a header. |
| 1114 | */ |
| 1115 | is_header = true1; |
| 1116 | goto is_rtsp; |
| 1117 | |
| 1118 | case ' ': |
| 1119 | case '\t': |
| 1120 | /* |
| 1121 | * LWS (RFC-2616, 4.2); continue the previous |
| 1122 | * header. |
| 1123 | */ |
| 1124 | goto is_rtsp; |
| 1125 | } |
| 1126 | } |
| 1127 | |
| 1128 | /* |
| 1129 | * We haven't seen the colon, but everything else looks |
| 1130 | * OK for a header line. |
| 1131 | * |
| 1132 | * If we've already seen an RTSP request or response |
| 1133 | * line, or a header line, and we're at the end of |
| 1134 | * the tvbuff, we assume this is an incomplete header |
| 1135 | * line. (We quit this loop after seeing a blank line, |
| 1136 | * so if we've seen a request or response line, or a |
| 1137 | * header line, this is probably more of the request |
| 1138 | * or response we're presumably seeing. There is some |
| 1139 | * risk of false positives, but the same applies for |
| 1140 | * full request or response lines or header lines, |
| 1141 | * although that's less likely.) |
| 1142 | * |
| 1143 | * We throw an exception in that case, by checking for |
| 1144 | * the existence of the next byte after the last one |
| 1145 | * in the line. If it exists, "tvb_ensure_bytes_exist()" |
| 1146 | * throws no exception, and we fall through to the |
| 1147 | * "not RTSP" case. If it doesn't exist, |
| 1148 | * "tvb_ensure_bytes_exist()" will throw the appropriate |
| 1149 | * exception. |
| 1150 | */ |
| 1151 | if (saw_req_resp_or_header) |
| 1152 | tvb_ensure_bytes_exist(tvb, offset, linelen + 1); |
| 1153 | |
| 1154 | not_rtsp: |
| 1155 | /* |
| 1156 | * We don't consider this part of an RTSP request or |
| 1157 | * reply, so we don't display it. |
| 1158 | */ |
| 1159 | break; |
| 1160 | |
| 1161 | is_rtsp: |
| 1162 | /* |
| 1163 | * Process this line. |
| 1164 | */ |
| 1165 | if (linelen == 0) { |
| 1166 | /* |
| 1167 | * This is a blank line, which means that |
| 1168 | * whatever follows it isn't part of this |
| 1169 | * request or reply. |
| 1170 | */ |
| 1171 | proto_tree_add_format_text(rtsp_tree, tvb, offset, next_offset - offset); |
| 1172 | offset = next_offset; |
| 1173 | break; |
| 1174 | } |
| 1175 | |
| 1176 | /* |
| 1177 | * Not a blank line - either a request, a reply, or a header |
| 1178 | * line. |
| 1179 | */ |
| 1180 | saw_req_resp_or_header = true1; |
| 1181 | |
| 1182 | switch (rtsp_type_line) |
| 1183 | { |
| 1184 | case RTSP_REQUEST: |
| 1185 | /* Add a tree for this request */ |
| 1186 | ti = proto_tree_add_string(rtsp_tree, hf_rtsp_request, tvb, offset, |
| 1187 | (int) (next_offset - offset), |
| 1188 | tvb_format_text(pinfo->pool, tvb, offset, (int) (next_offset - offset))); |
| 1189 | req_tree = proto_item_add_subtree(ti, ett_rtsp_method); |
| 1190 | request_uri = process_rtsp_request(tvb_new_subset_length(tvb, offset, linelen), linelen, pinfo, req_tree); |
| 1191 | break; |
| 1192 | |
| 1193 | case RTSP_REPLY: |
| 1194 | /* Add a tree for this response */ |
| 1195 | ti = proto_tree_add_string(rtsp_tree, hf_rtsp_response, tvb, offset, |
| 1196 | (int) (next_offset - offset), |
| 1197 | tvb_format_text(pinfo->pool, tvb, offset, (int) (next_offset - offset))); |
| 1198 | req_tree = proto_item_add_subtree(ti, ett_rtsp_method); |
| 1199 | process_rtsp_reply(tvb_new_subset_length(tvb, offset, linelen), linelen, pinfo, req_tree); |
| 1200 | break; |
| 1201 | |
| 1202 | case RTSP_NOT_FIRST_LINE: |
| 1203 | /* Drop through, it may well be a header line */ |
| 1204 | break; |
| 1205 | } |
| 1206 | |
| 1207 | if (is_header) |
| 1208 | { |
| 1209 | /* We know that colon_offset must be set */ |
| 1210 | |
| 1211 | /* Skip whitespace after the colon. */ |
| 1212 | value_offset = colon_offset + 1; |
| 1213 | while ((value_offset < line_end_offset) && |
| 1214 | ((c = tvb_get_uint8(tvb, value_offset)) == ' ' || c == '\t')) |
| 1215 | { |
| 1216 | value_offset++; |
| 1217 | } |
| 1218 | value_len = line_end_offset - value_offset; |
| 1219 | |
| 1220 | /* |
| 1221 | * Process some headers specially. |
| 1222 | */ |
| 1223 | #define HDR_MATCHES(header)( (size_t)linelen > (sizeof (header) - 1) && g_ascii_strncasecmp (line, (header), (sizeof (header) - 1)) == 0) \ |
| 1224 | ( (size_t)linelen > STRLEN_CONST(header)(sizeof (header) - 1) && \ |
| 1225 | g_ascii_strncasecmp(line, (header), STRLEN_CONST(header)(sizeof (header) - 1)) == 0) |
| 1226 | |
| 1227 | if (HDR_MATCHES(rtsp_transport)( (size_t)linelen > (sizeof (rtsp_transport) - 1) && g_ascii_strncasecmp(line, (rtsp_transport), (sizeof (rtsp_transport ) - 1)) == 0)) |
| 1228 | { |
| 1229 | ti = proto_tree_add_string(rtsp_tree, hf_rtsp_transport, tvb, |
| 1230 | offset, linelen, |
| 1231 | tvb_format_text(pinfo->pool, tvb, value_offset, |
| 1232 | value_len)); |
| 1233 | |
| 1234 | /* Setup the conversation after parsing all the headers. */ |
| 1235 | transport_line = line; |
| 1236 | transport_linelen = linelen; |
| 1237 | } else if (HDR_MATCHES(rtsp_content_type)( (size_t)linelen > (sizeof (rtsp_content_type) - 1) && g_ascii_strncasecmp(line, (rtsp_content_type), (sizeof (rtsp_content_type ) - 1)) == 0)) |
| 1238 | { |
| 1239 | proto_tree_add_string(rtsp_tree, hf_rtsp_content_type, |
| 1240 | tvb, offset, linelen, |
| 1241 | tvb_format_text(pinfo->pool, tvb, value_offset, |
| 1242 | value_len)); |
| 1243 | |
| 1244 | offset = offset + (int)STRLEN_CONST(rtsp_content_type)(sizeof (rtsp_content_type) - 1); |
| 1245 | /* Skip wsp */ |
| 1246 | offset = tvb_skip_wsp(tvb, offset, value_len); |
| 1247 | semi_colon_offset = tvb_find_uint8(tvb, value_offset, value_len, ';'); |
| 1248 | if ( semi_colon_offset != -1) { |
| 1249 | /* m-parameter present */ |
| 1250 | par_end_offset = tvb_skip_wsp_return(tvb, semi_colon_offset-1); |
| 1251 | value_len = par_end_offset - offset; |
| 1252 | } |
| 1253 | |
| 1254 | media_type_str_lower_case = ascii_strdown_inplace( |
| 1255 | (char *)tvb_get_string_enc(pinfo->pool, tvb, offset, value_len, ENC_ASCII0x00000000)); |
| 1256 | } else if (HDR_MATCHES(rtsp_content_length)( (size_t)linelen > (sizeof (rtsp_content_length) - 1) && g_ascii_strncasecmp(line, (rtsp_content_length), (sizeof (rtsp_content_length ) - 1)) == 0)) |
| 1257 | { |
| 1258 | uint32_t clength; |
| 1259 | bool_Bool clength_valid; |
| 1260 | clength_valid = ws_strtou32(tvb_format_text(pinfo->pool, tvb, value_offset, value_len), |
| 1261 | NULL((void*)0), &clength); |
| 1262 | ti = proto_tree_add_uint(rtsp_tree, hf_rtsp_content_length, |
| 1263 | tvb, offset, linelen, clength); |
| 1264 | if (!clength_valid) |
| 1265 | expert_add_info(pinfo, ti, &ei_rtsp_content_length_invalid); |
| 1266 | |
| 1267 | /* |
| 1268 | * Only the amount specified by the |
| 1269 | * Content-Length: header should be treated |
| 1270 | * as payload. |
| 1271 | */ |
| 1272 | content_length = rtsp_get_content_length(line, linelen); |
| 1273 | |
| 1274 | } else if (HDR_MATCHES(rtsp_Session)( (size_t)linelen > (sizeof (rtsp_Session) - 1) && g_ascii_strncasecmp(line, (rtsp_Session), (sizeof (rtsp_Session ) - 1)) == 0)) |
| 1275 | { |
| 1276 | session_id = tvb_format_text(pinfo->pool, tvb, value_offset, value_len); |
| 1277 | /* Put the value into the protocol tree */ |
| 1278 | proto_tree_add_string(rtsp_tree, hf_rtsp_session, tvb, |
| 1279 | offset, linelen, |
| 1280 | session_id); |
| 1281 | |
| 1282 | } else if (HDR_MATCHES(rtsp_X_Vig_Msisdn)( (size_t)linelen > (sizeof (rtsp_X_Vig_Msisdn) - 1) && g_ascii_strncasecmp(line, (rtsp_X_Vig_Msisdn), (sizeof (rtsp_X_Vig_Msisdn ) - 1)) == 0)) { |
| 1283 | /* |
| 1284 | * Extract the X_Vig_Msisdn string |
| 1285 | */ |
| 1286 | if (colon_offset != -1) |
| 1287 | { |
| 1288 | /* Put the value into the protocol tree */ |
| 1289 | ti = proto_tree_add_string(rtsp_tree, hf_rtsp_X_Vig_Msisdn,tvb, |
| 1290 | offset, linelen , |
| 1291 | tvb_format_text(pinfo->pool, tvb, value_offset, value_len)); |
| 1292 | sub_tree = proto_item_add_subtree(ti, ett_rtsp_method); |
| 1293 | |
| 1294 | e164_info.e164_number_type = CALLING_PARTY_NUMBER; |
| 1295 | e164_info.nature_of_address = 0; |
| 1296 | |
| 1297 | e164_info.E164_number_str = (char*)tvb_get_string_enc(pinfo->pool, tvb, value_offset, |
| 1298 | value_len, ENC_ASCII0x00000000); |
| 1299 | e164_info.E164_number_length = value_len; |
| 1300 | dissect_e164_number(tvb, sub_tree, value_offset, |
| 1301 | value_len, e164_info); |
| 1302 | } |
| 1303 | } else if (HDR_MATCHES(rtsp_rdt_feature_level)( (size_t)linelen > (sizeof (rtsp_rdt_feature_level) - 1) && g_ascii_strncasecmp(line, (rtsp_rdt_feature_level), (sizeof ( rtsp_rdt_feature_level) - 1)) == 0)) |
| 1304 | { |
| 1305 | bool_Bool rdt_feature_level_valid; |
| 1306 | rdt_feature_level_valid = ws_strtou32(tvb_format_text(pinfo->pool, tvb, value_offset, value_len), |
| 1307 | NULL((void*)0), &rdt_feature_level); |
| 1308 | ti = proto_tree_add_uint(rtsp_tree, hf_rtsp_rdtfeaturelevel, |
| 1309 | tvb, offset, linelen, rdt_feature_level); |
| 1310 | if (!rdt_feature_level_valid) |
| 1311 | expert_add_info(pinfo, ti, &ei_rtsp_rdtfeaturelevel_invalid); |
| 1312 | } else if (HDR_MATCHES(rtsp_cseq)( (size_t)linelen > (sizeof (rtsp_cseq) - 1) && g_ascii_strncasecmp (line, (rtsp_cseq), (sizeof (rtsp_cseq) - 1)) == 0)) |
| 1313 | { |
| 1314 | cseq_valid = ws_strtou32(tvb_format_text(pinfo->pool, tvb, value_offset, value_len), |
| 1315 | NULL((void*)0), &cseq); |
| 1316 | ti = proto_tree_add_uint(rtsp_tree, hf_rtsp_cseq, tvb, offset, linelen, cseq); |
| 1317 | if (!cseq_valid) { |
| 1318 | expert_add_info(pinfo, ti, &ei_rtsp_cseq_invalid); |
| 1319 | } |
| 1320 | } else if (HDR_MATCHES(rtsp_content_base)( (size_t)linelen > (sizeof (rtsp_content_base) - 1) && g_ascii_strncasecmp(line, (rtsp_content_base), (sizeof (rtsp_content_base ) - 1)) == 0)) |
| 1321 | { |
| 1322 | content_base = (char *)tvb_get_string_enc(pinfo->pool, tvb, value_offset, value_len, ENC_UTF_80x00000002); |
| 1323 | proto_tree_add_string(rtsp_tree, hf_rtsp_content_base, |
| 1324 | tvb, offset, linelen, content_base); |
| 1325 | } else if (HDR_MATCHES(rtsp_content_location)( (size_t)linelen > (sizeof (rtsp_content_location) - 1) && g_ascii_strncasecmp(line, (rtsp_content_location), (sizeof ( rtsp_content_location) - 1)) == 0)) |
| 1326 | { |
| 1327 | content_location = (char *)tvb_get_string_enc(pinfo->pool, tvb, value_offset, value_len, ENC_UTF_80x00000002); |
| 1328 | proto_tree_add_string(rtsp_tree, hf_rtsp_content_location, |
| 1329 | tvb, offset, linelen, content_location); |
| 1330 | } |
| 1331 | else |
| 1332 | { |
| 1333 | /* Default case for headers. Show line as text */ |
| 1334 | proto_tree_add_format_text(rtsp_tree, tvb, offset, next_offset - offset); |
| 1335 | } |
| 1336 | } |
| 1337 | else if (rtsp_type_line == RTSP_NOT_FIRST_LINE) |
| 1338 | { |
| 1339 | /* Catch-all for all other lines... Show line as text. |
| 1340 | TODO: should these be shown as errors? */ |
| 1341 | proto_tree_add_format_text(rtsp_tree, tvb, offset, next_offset - offset); |
| 1342 | } |
| 1343 | |
| 1344 | offset = next_offset; |
| 1345 | } |
| 1346 | |
| 1347 | if (cseq_valid) { |
| 1348 | rtsp_conversation_data_t *conv_data = get_rtsp_conversation_data(NULL((void*)0), pinfo); |
| 1349 | rtsp_req_resp_t *curr_req_resp = wmem_map_lookup(conv_data->req_resp_map, GUINT_TO_POINTER(cseq)((gpointer) (gulong) (cseq))); |
| 1350 | if (!curr_req_resp) { |
| 1351 | curr_req_resp = wmem_new0(wmem_file_scope(), rtsp_req_resp_t)((rtsp_req_resp_t*)wmem_alloc0((wmem_file_scope()), sizeof(rtsp_req_resp_t ))); |
| 1352 | wmem_map_insert(conv_data->req_resp_map, GUINT_TO_POINTER(cseq)((gpointer) (gulong) (cseq)), curr_req_resp); |
| 1353 | } |
| 1354 | if (rtsp_type_packet == RTSP_REQUEST) { |
| 1355 | if (curr_req_resp->req_frame == 0) { |
| 1356 | curr_req_resp->req_frame = pinfo->num; |
| 1357 | } |
| 1358 | if (curr_req_resp->resp_frame) { |
| 1359 | proto_tree_add_uint(req_tree, hf_rtsp_response_in, tvb, 0, 0, curr_req_resp->resp_frame); |
| 1360 | } |
| 1361 | if (request_uri && !curr_req_resp->request_uri) { |
| 1362 | curr_req_resp->request_uri = wmem_strdup(wmem_file_scope(), request_uri); |
| 1363 | } |
| 1364 | } else { |
| 1365 | if (curr_req_resp->resp_frame == 0) { |
| 1366 | curr_req_resp->resp_frame = pinfo->num; |
| 1367 | } |
| 1368 | if (curr_req_resp->request_uri) { |
| 1369 | ti = proto_tree_add_string(req_tree, hf_rtsp_url, tvb, 0, 0, curr_req_resp->request_uri); |
| 1370 | proto_item_set_generated(ti); |
| 1371 | } |
| 1372 | if (curr_req_resp->req_frame) { |
| 1373 | proto_tree_add_uint(req_tree, hf_rtsp_response_to, tvb, 0, 0, curr_req_resp->req_frame); |
| 1374 | } |
| 1375 | } |
| 1376 | if (curr_req_resp->request_uri) { |
| 1377 | base_uri = wmem_ascii_strdown(pinfo->pool, curr_req_resp->request_uri, -1); |
| 1378 | } |
| 1379 | } |
| 1380 | |
| 1381 | if (content_base) { |
| 1382 | base_uri = content_base; |
| 1383 | } else if (content_location) { |
| 1384 | /* XXX - Content-Location itself can be relative to the request_uri, at |
| 1385 | * least according to RFC 7826. (RTSP 2.0 is not widely implemented, but |
| 1386 | * it does have useful notes on gotchas and limitations of RTSP 1.0.) |
| 1387 | */ |
| 1388 | base_uri = content_location; |
| 1389 | } |
| 1390 | |
| 1391 | if (session_id) { |
| 1392 | stat_info = wmem_new0(pinfo->pool, voip_packet_info_t)((voip_packet_info_t*)wmem_alloc0((pinfo->pool), sizeof(voip_packet_info_t ))); |
| 1393 | stat_info->protocol_name = wmem_strdup(pinfo->pool, "RTSP"); |
| 1394 | stat_info->call_id = session_id; |
| 1395 | stat_info->frame_label = frame_label; |
| 1396 | stat_info->call_state = VOIP_CALL_SETUP; |
| 1397 | stat_info->call_active_state = VOIP_ACTIVE; |
| 1398 | stat_info->frame_comment = frame_label; |
| 1399 | tap_queue_packet(voip_tap, pinfo, stat_info); |
| 1400 | } |
| 1401 | |
| 1402 | if (transport_line) { |
| 1403 | /* |
| 1404 | * Based on the port numbers specified in the Transport: header, set up |
| 1405 | * a conversation that will be dissected with the appropriate dissector. |
| 1406 | */ |
| 1407 | setup_info = rtsp_create_setup_info(pinfo, session_id, base_uri); |
| 1408 | rtsp_create_conversation(pinfo, ti, transport_line, transport_linelen, rdt_feature_level, rtsp_type_packet, setup_info); |
| 1409 | } |
| 1410 | |
| 1411 | /* |
| 1412 | * Have now read all of the lines of this message. |
| 1413 | * |
| 1414 | * If a content length was supplied, the amount of data to be |
| 1415 | * processed as RTSP payload is the minimum of the content |
| 1416 | * length and the amount of data remaining in the frame. |
| 1417 | * |
| 1418 | * If no content length was supplied (or if a bad content length |
| 1419 | * was supplied), the amount of data to be processed is the amount |
| 1420 | * of data remaining in the frame. |
| 1421 | */ |
| 1422 | datalen = tvb_captured_length_remaining(tvb, offset); |
| 1423 | reported_datalen = tvb_reported_length_remaining(tvb, offset); |
| 1424 | if (content_length != -1) { |
| 1425 | /* |
| 1426 | * Content length specified; display only that amount |
| 1427 | * as payload. |
| 1428 | */ |
| 1429 | if (datalen > content_length) |
| 1430 | datalen = content_length; |
| 1431 | |
| 1432 | /* |
| 1433 | * XXX - limit the reported length in the tvbuff we'll |
| 1434 | * hand to a subdissector to be no greater than the |
| 1435 | * content length. |
| 1436 | * |
| 1437 | * We really need both unreassembled and "how long it'd |
| 1438 | * be if it were reassembled" lengths for tvbuffs, so |
| 1439 | * that we throw the appropriate exceptions for |
| 1440 | * "not enough data captured" (running past the length), |
| 1441 | * "packet needed reassembly" (within the length but |
| 1442 | * running past the unreassembled length), and |
| 1443 | * "packet is malformed" (running past the reassembled |
| 1444 | * length). |
| 1445 | */ |
| 1446 | if (reported_datalen > content_length) |
| 1447 | reported_datalen = content_length; |
| 1448 | } else { |
| 1449 | /* |
| 1450 | * No content length specified; if this message doesn't |
| 1451 | * have a body if no content length is specified, process |
| 1452 | * nothing as payload. |
| 1453 | */ |
| 1454 | if (body_requires_content_len) |
| 1455 | datalen = 0; |
| 1456 | } |
| 1457 | |
| 1458 | if (datalen > 0) { |
| 1459 | /* |
| 1460 | * There's stuff left over; process it. |
| 1461 | */ |
| 1462 | tvbuff_t *new_tvb; |
| 1463 | |
| 1464 | /* |
| 1465 | * Now create a tvbuff for the Content-type stuff and |
| 1466 | * dissect it. |
| 1467 | * |
| 1468 | * The amount of data to be processed that's |
| 1469 | * available in the tvbuff is "datalen", which |
| 1470 | * is the minimum of the amount of data left in |
| 1471 | * the tvbuff and any specified content length. |
| 1472 | * |
| 1473 | * The amount of data to be processed that's in |
| 1474 | * this frame, regardless of whether it was |
| 1475 | * captured or not, is "reported_datalen", |
| 1476 | * which, if no content length was specified, |
| 1477 | * is -1, i.e. "to the end of the frame. |
| 1478 | */ |
| 1479 | new_tvb = tvb_new_subset_length_caplen(tvb, offset, datalen, |
| 1480 | reported_datalen); |
| 1481 | |
| 1482 | /* |
| 1483 | * Check if next line is RTSP message - pipelining |
| 1484 | * If yes, stop processing and start next loop |
| 1485 | * If no, process rest of packet with dissectors |
| 1486 | */ |
| 1487 | first_linelen = tvb_find_line_end(new_tvb, 0, -1, &next_offset, false0); |
| 1488 | is_request_or_reply = is_rtsp_request_or_reply(tvb_new_subset_length(new_tvb, 0, first_linelen), first_linelen, |
| 1489 | &rtsp_type_packet, rtsp_stat_info, pinfo->pool); |
| 1490 | |
| 1491 | if (!is_request_or_reply){ |
| 1492 | setup_info = rtsp_create_setup_info(pinfo, session_id, base_uri); |
| 1493 | media_content_info_t content_info = { MEDIA_CONTAINER_SIP_DATA, media_type_str_lower_case, NULL((void*)0), setup_info }; |
| 1494 | if (media_type_str_lower_case && |
| 1495 | dissector_try_string_with_data(media_type_dissector_table, |
| 1496 | media_type_str_lower_case, |
| 1497 | new_tvb, pinfo, rtsp_tree, true1, &content_info)) { |
| 1498 | |
| 1499 | } else { |
| 1500 | /* |
| 1501 | * Fix up the top-level item so that it doesn't |
| 1502 | * include the SDP stuff. |
| 1503 | */ |
| 1504 | if (ti_top != NULL((void*)0)) |
| 1505 | proto_item_set_len(ti_top, offset); |
| 1506 | |
| 1507 | if (tvb_get_uint8(tvb, offset) == RTSP_FRAMEHDR('$')) { |
| 1508 | /* |
| 1509 | * This is interleaved stuff; don't |
| 1510 | * treat it as raw data - set "datalen" |
| 1511 | * to 0, so we won't skip the offset |
| 1512 | * past it, which will cause our |
| 1513 | * caller to process that stuff itself. |
| 1514 | */ |
| 1515 | datalen = 0; |
| 1516 | } else { |
| 1517 | proto_tree_add_bytes_format(rtsp_tree, hf_rtsp_data, tvb, offset, |
| 1518 | datalen, NULL((void*)0), "Data (%d bytes)", |
| 1519 | reported_datalen); |
| 1520 | } |
| 1521 | } |
| 1522 | |
| 1523 | /* |
| 1524 | * We've processed "datalen" bytes worth of data |
| 1525 | * (which may be no data at all); advance the |
| 1526 | * offset past whatever data we've processed. |
| 1527 | */ |
| 1528 | offset += datalen; |
| 1529 | } |
| 1530 | } |
| 1531 | |
| 1532 | tap_queue_packet(rtsp_tap, pinfo, rtsp_stat_info); |
| 1533 | |
| 1534 | return offset - orig_offset; |
| 1535 | } |
| 1536 | |
| 1537 | static char* |
| 1538 | process_rtsp_request(tvbuff_t *tvb, int linelen, packet_info *pinfo, proto_tree *tree) |
| 1539 | { |
| 1540 | unsigned ii; |
| 1541 | char *tmp_url; |
| 1542 | int token_len; |
| 1543 | int offset = 0, next_offset; |
| 1544 | |
| 1545 | token_len = tvb_get_token_len(tvb, offset, linelen, &next_offset, false0); |
| 1546 | |
| 1547 | /* Request Methods */ |
| 1548 | for (ii = 0; ii < RTSP_NMETHODS(sizeof (rtsp_methods) / sizeof (rtsp_methods)[0]); ii++) { |
| 1549 | size_t len = strlen(rtsp_methods[ii]); |
| 1550 | if (len == (size_t)token_len && |
| 1551 | tvb_strncaseeql(tvb, offset, rtsp_methods[ii], len) == 0) |
| 1552 | break; |
| 1553 | } |
| 1554 | if (ii == RTSP_NMETHODS(sizeof (rtsp_methods) / sizeof (rtsp_methods)[0])) { |
| 1555 | /* |
| 1556 | * We got here because "is_rtsp_request_or_reply()" returned |
| 1557 | * RTSP_REQUEST, so we know one of the request methods |
| 1558 | * matched, so we "can't get here". |
| 1559 | */ |
| 1560 | DISSECTOR_ASSERT_NOT_REACHED()(proto_report_dissector_bug("%s:%u: failed assertion \"DISSECTOR_ASSERT_NOT_REACHED\"" , "epan/dissectors/packet-rtsp.c", 1560)); |
| 1561 | } |
| 1562 | |
| 1563 | /* Add method name to tree */ |
| 1564 | proto_tree_add_string(tree, hf_rtsp_method, tvb, offset, |
| 1565 | token_len, rtsp_methods[ii]); |
| 1566 | |
| 1567 | /* token_len does not include the terminator. */ |
| 1568 | linelen -= token_len + 1; |
Value stored to 'linelen' is never read | |
| 1569 | |
| 1570 | /* URL */ |
| 1571 | /* next_offset is after the first space after the method name. |
| 1572 | * Skip any extra spaces (though there should only be a single |
| 1573 | * space according to RFC 2326s and 7230.) |
| 1574 | */ |
| 1575 | offset = tvb_skip_wsp(tvb, next_offset, tvb_reported_length_remaining(tvb, next_offset)); |
| 1576 | /* Scan to end of URL */ |
| 1577 | token_len = tvb_get_token_len(tvb, offset, tvb_reported_length_remaining(tvb, offset), NULL((void*)0), false0); |
| 1578 | /* Add URL to tree */ |
| 1579 | proto_tree_add_item_ret_string(tree, hf_rtsp_url, tvb, |
| 1580 | offset, token_len, ENC_UTF_80x00000002, pinfo->pool, (const uint8_t**)&tmp_url); |
| 1581 | return tmp_url; |
| 1582 | } |
| 1583 | |
| 1584 | /* Read first line of a reply message */ |
| 1585 | static void |
| 1586 | process_rtsp_reply(tvbuff_t *tvb, int linelen, packet_info *pinfo _U___attribute__((unused)), proto_tree *tree) |
| 1587 | { |
| 1588 | const char *status; |
| 1589 | unsigned status_i; |
| 1590 | int token_len; |
| 1591 | int offset = 0, next_offset; |
| 1592 | |
| 1593 | /* status code */ |
| 1594 | |
| 1595 | /* Skip protocol/version */ |
| 1596 | token_len = tvb_get_token_len(tvb, offset, linelen, &next_offset, false0); |
| 1597 | /* Skip spaces */ |
| 1598 | offset = tvb_skip_wsp(tvb, next_offset, tvb_reported_length_remaining(tvb, next_offset)); |
| 1599 | |
| 1600 | /* Actual code number now */ |
| 1601 | token_len = tvb_get_token_len(tvb, offset, linelen, &next_offset, false0); |
| 1602 | |
| 1603 | status = (char*)tvb_get_string_enc(pinfo->pool, tvb, offset, token_len, ENC_UTF_80x00000002); |
| 1604 | if (ws_strtou(status, NULL((void*)0), &status_i)) { |
| 1605 | /* Add field to tree */ |
| 1606 | proto_tree_add_uint(tree, hf_rtsp_status, tvb, |
| 1607 | offset, token_len, status_i); |
| 1608 | } |
| 1609 | // else error (There should be a space after the status code.) |
| 1610 | } |
| 1611 | |
| 1612 | static int |
| 1613 | dissect_rtsp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U___attribute__((unused))) |
| 1614 | { |
| 1615 | int offset = 0; |
| 1616 | int len; |
| 1617 | |
| 1618 | while (tvb_reported_length_remaining(tvb, offset) != 0) { |
| 1619 | /* |
| 1620 | * Add separator between multiple messages in column info text |
| 1621 | */ |
| 1622 | if (offset > 0) { |
| 1623 | col_set_str(pinfo->cinfo, COL_INFO, ", "); |
| 1624 | col_set_fence(pinfo->cinfo, COL_INFO); |
| 1625 | } |
| 1626 | len = (tvb_get_uint8(tvb, offset) == RTSP_FRAMEHDR('$')) |
| 1627 | ? dissect_rtspinterleaved(tvb, offset, pinfo, tree) |
| 1628 | : dissect_rtspmessage(tvb, offset, pinfo, tree); |
| 1629 | if (len == -1) |
| 1630 | break; |
| 1631 | offset += len; |
| 1632 | |
| 1633 | /* |
| 1634 | * OK, we've set the Protocol and Info columns for the |
| 1635 | * first RTSP message; set fence so changes are kept for |
| 1636 | * subsequent RTSP messages. |
| 1637 | */ |
| 1638 | col_set_fence(pinfo->cinfo, COL_INFO); |
| 1639 | } |
| 1640 | return tvb_captured_length(tvb); |
| 1641 | } |
| 1642 | |
| 1643 | void |
| 1644 | proto_register_rtsp(void) |
| 1645 | { |
| 1646 | static int *ett[] = { |
| 1647 | &ett_rtspframe, |
| 1648 | &ett_rtsp, |
| 1649 | &ett_rtsp_method, |
| 1650 | }; |
| 1651 | static hf_register_info hf[] = { |
| 1652 | { &hf_rtsp_request, |
| 1653 | { "Request", "rtsp.request", FT_STRING, BASE_NONE, NULL((void*)0), 0, |
| 1654 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }}, |
| 1655 | { &hf_rtsp_response, |
| 1656 | { "Response", "rtsp.response", FT_STRING, BASE_NONE, NULL((void*)0), 0, |
| 1657 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }}, |
| 1658 | { &hf_rtsp_response_in, |
| 1659 | { "Response in frame", "rtsp.response_in", FT_FRAMENUM, BASE_NONE, |
| 1660 | FRAMENUM_TYPE(FT_FRAMENUM_RESPONSE)((gpointer) (glong) (FT_FRAMENUM_RESPONSE)), 0, NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }}, |
| 1661 | { &hf_rtsp_response_to, |
| 1662 | { "Response to frame", "rtsp.response_to", FT_FRAMENUM, BASE_NONE, |
| 1663 | FRAMENUM_TYPE(FT_FRAMENUM_REQUEST)((gpointer) (glong) (FT_FRAMENUM_REQUEST)), 0, NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }}, |
| 1664 | { &hf_rtsp_method, |
| 1665 | { "Method", "rtsp.method", FT_STRING, BASE_NONE, NULL((void*)0), 0, |
| 1666 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }}, |
| 1667 | { &hf_rtsp_content_type, |
| 1668 | { "Content-type", "rtsp.content-type", FT_STRING, BASE_NONE, NULL((void*)0), 0, |
| 1669 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }}, |
| 1670 | { &hf_rtsp_content_length, |
| 1671 | { "Content-length", "rtsp.content-length", FT_UINT32, BASE_DEC, NULL((void*)0), 0, |
| 1672 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }}, |
| 1673 | { &hf_rtsp_url, |
| 1674 | { "URL", "rtsp.url", FT_STRING, BASE_NONE, NULL((void*)0), 0, |
| 1675 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }}, |
| 1676 | { &hf_rtsp_status, |
| 1677 | { "Status", "rtsp.status", FT_UINT32, BASE_DEC, NULL((void*)0), 0, |
| 1678 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }}, |
| 1679 | { &hf_rtsp_session, |
| 1680 | { "Session", "rtsp.session", FT_STRING, BASE_NONE, NULL((void*)0), 0, |
| 1681 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }}, |
| 1682 | { &hf_rtsp_transport, |
| 1683 | { "Transport", "rtsp.transport", FT_STRING, BASE_NONE, NULL((void*)0), 0, |
| 1684 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }}, |
| 1685 | { &hf_rtsp_rdtfeaturelevel, |
| 1686 | { "RDTFeatureLevel", "rtsp.rdt-feature-level", FT_UINT32, BASE_DEC, NULL((void*)0), 0, |
| 1687 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }}, |
| 1688 | { &hf_rtsp_cseq, |
| 1689 | { "CSeq", "rtsp.cseq", FT_UINT32, BASE_DEC, NULL((void*)0), 0, |
| 1690 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }}, |
| 1691 | { &hf_rtsp_content_base, |
| 1692 | { "Content-Base", "rtsp.content-base", FT_STRING, BASE_NONE, NULL((void*)0), 0, |
| 1693 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }}, |
| 1694 | { &hf_rtsp_content_location, |
| 1695 | { "Content-Location", "rtsp.content-location", FT_STRING, BASE_NONE, NULL((void*)0), 0, |
| 1696 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }}, |
| 1697 | { &hf_rtsp_X_Vig_Msisdn, |
| 1698 | { "X-Vig-Msisdn", "rtsp.X_Vig_Msisdn", FT_STRING, BASE_NONE, NULL((void*)0), 0, |
| 1699 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }}, |
| 1700 | { &hf_rtsp_magic, |
| 1701 | { "Magic", "rtsp.magic", FT_UINT8, BASE_HEX, NULL((void*)0), 0x0, |
| 1702 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }}, |
| 1703 | { &hf_rtsp_channel, |
| 1704 | { "Channel", "rtsp.channel", FT_UINT8, BASE_HEX, NULL((void*)0), 0x0, |
| 1705 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }}, |
| 1706 | { &hf_rtsp_length, |
| 1707 | { "Length", "rtsp.length", FT_UINT16, BASE_DEC, NULL((void*)0), 0x0, |
| 1708 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }}, |
| 1709 | { &hf_rtsp_data, |
| 1710 | { "Data", "rtsp.data", FT_BYTES, BASE_NONE, NULL((void*)0), 0x0, |
| 1711 | NULL((void*)0), HFILL-1, 0, HF_REF_TYPE_NONE, -1, ((void*)0) }}, |
| 1712 | }; |
| 1713 | |
| 1714 | static ei_register_info ei[] = { |
| 1715 | { &ei_rtsp_unknown_transport_type, |
| 1716 | { "rtsp.unknown_transport_type", PI_UNDECODED0x05000000, PI_WARN0x00600000, "Unknown transport type", EXPFILL0, ((void*)0), 0, ((void*)0), {0, {((void*)0), ((void*)0), FT_NONE , BASE_NONE, ((void*)0), 0, ((void*)0), -1, 0, HF_REF_TYPE_NONE , -1, ((void*)0)}} }}, |
| 1717 | { &ei_rtsp_bad_server_port, |
| 1718 | { "rtsp.bad_server_port", PI_UNDECODED0x05000000, PI_WARN0x00600000, "Bad server_port", EXPFILL0, ((void*)0), 0, ((void*)0), {0, {((void*)0), ((void*)0), FT_NONE , BASE_NONE, ((void*)0), 0, ((void*)0), -1, 0, HF_REF_TYPE_NONE , -1, ((void*)0)}} }}, |
| 1719 | { &ei_rtsp_bad_client_port, |
| 1720 | { "rtsp.bad_client_port", PI_UNDECODED0x05000000, PI_WARN0x00600000, "Bad client port", EXPFILL0, ((void*)0), 0, ((void*)0), {0, {((void*)0), ((void*)0), FT_NONE , BASE_NONE, ((void*)0), 0, ((void*)0), -1, 0, HF_REF_TYPE_NONE , -1, ((void*)0)}} }}, |
| 1721 | { &ei_rtsp_bad_interleaved_channel, |
| 1722 | { "rtsp.bad_interleaved_channel", PI_UNDECODED0x05000000, PI_WARN0x00600000, "Bad interleaved_channel", EXPFILL0, ((void*)0), 0, ((void*)0), {0, {((void*)0), ((void*)0), FT_NONE , BASE_NONE, ((void*)0), 0, ((void*)0), -1, 0, HF_REF_TYPE_NONE , -1, ((void*)0)}} }}, |
| 1723 | { &ei_rtsp_content_length_invalid, |
| 1724 | { "rtsp.content-length.invalid", PI_MALFORMED0x07000000, PI_ERROR0x00800000, "Invalid content length", EXPFILL0, ((void*)0), 0, ((void*)0), {0, {((void*)0), ((void*)0), FT_NONE , BASE_NONE, ((void*)0), 0, ((void*)0), -1, 0, HF_REF_TYPE_NONE , -1, ((void*)0)}} }}, |
| 1725 | { &ei_rtsp_rdtfeaturelevel_invalid, |
| 1726 | { "rtsp.rdt-feature-level.invalid", PI_MALFORMED0x07000000, PI_ERROR0x00800000, "Invalid RDTFeatureLevel", EXPFILL0, ((void*)0), 0, ((void*)0), {0, {((void*)0), ((void*)0), FT_NONE , BASE_NONE, ((void*)0), 0, ((void*)0), -1, 0, HF_REF_TYPE_NONE , -1, ((void*)0)}} }}, |
| 1727 | { &ei_rtsp_cseq_invalid, |
| 1728 | { "rtsp.cseq.invalid", PI_PROTOCOL0x09000000, PI_WARN0x00600000, "Invalid CSeq", EXPFILL0, ((void*)0), 0, ((void*)0), {0, {((void*)0), ((void*)0), FT_NONE , BASE_NONE, ((void*)0), 0, ((void*)0), -1, 0, HF_REF_TYPE_NONE , -1, ((void*)0)}} }}, |
| 1729 | { &ei_rtsp_bad_server_ip_address, |
| 1730 | { "rtsp.bad_server_ip_address", PI_MALFORMED0x07000000, PI_ERROR0x00800000, "Bad server IP address", EXPFILL0, ((void*)0), 0, ((void*)0), {0, {((void*)0), ((void*)0), FT_NONE , BASE_NONE, ((void*)0), 0, ((void*)0), -1, 0, HF_REF_TYPE_NONE , -1, ((void*)0)}} }}, |
| 1731 | { &ei_rtsp_bad_client_ip_address, |
| 1732 | { "rtsp.bad_client_ip_address", PI_MALFORMED0x07000000, PI_ERROR0x00800000, "Bad client IP address", EXPFILL0, ((void*)0), 0, ((void*)0), {0, {((void*)0), ((void*)0), FT_NONE , BASE_NONE, ((void*)0), 0, ((void*)0), -1, 0, HF_REF_TYPE_NONE , -1, ((void*)0)}} }} |
| 1733 | }; |
| 1734 | |
| 1735 | module_t *rtsp_module; |
| 1736 | expert_module_t *expert_rtsp; |
| 1737 | |
| 1738 | proto_rtsp = proto_register_protocol("Real Time Streaming Protocol", "RTSP", "rtsp"); |
| 1739 | |
| 1740 | proto_register_field_array(proto_rtsp, hf, array_length(hf)(sizeof (hf) / sizeof (hf)[0])); |
| 1741 | proto_register_subtree_array(ett, array_length(ett)(sizeof (ett) / sizeof (ett)[0])); |
| 1742 | |
| 1743 | expert_rtsp = expert_register_protocol(proto_rtsp); |
| 1744 | expert_register_field_array(expert_rtsp, ei, array_length(ei)(sizeof (ei) / sizeof (ei)[0])); |
| 1745 | |
| 1746 | /* Make this dissector findable by name */ |
| 1747 | rtsp_handle = register_dissector("rtsp", dissect_rtsp, proto_rtsp); |
| 1748 | |
| 1749 | /* Register our configuration options, particularly our ports */ |
| 1750 | |
| 1751 | rtsp_module = prefs_register_protocol(proto_rtsp, NULL((void*)0)); |
| 1752 | |
| 1753 | prefs_register_obsolete_preference(rtsp_module, "tcp.alternate_port"); |
| 1754 | |
| 1755 | prefs_register_bool_preference(rtsp_module, "desegment_headers", |
| 1756 | "Reassemble RTSP headers spanning multiple TCP segments", |
| 1757 | "Whether the RTSP dissector should reassemble headers " |
| 1758 | "of a request spanning multiple TCP segments. " |
| 1759 | " To use this option, you must also enable \"Allow subdissectors to reassemble TCP streams\" in the TCP protocol settings.", |
| 1760 | &rtsp_desegment_headers); |
| 1761 | prefs_register_bool_preference(rtsp_module, "desegment_body", |
| 1762 | "Trust the \"Content-length:\" header when desegmenting", |
| 1763 | "Whether the RTSP dissector should use the " |
| 1764 | "\"Content-length:\" value to desegment the body " |
| 1765 | "of a request spanning multiple TCP segments", |
| 1766 | &rtsp_desegment_body); |
| 1767 | |
| 1768 | /* |
| 1769 | * Heuristic dissectors SHOULD register themselves in |
| 1770 | * this table using the standard heur_dissector_add() |
| 1771 | * function. |
| 1772 | */ |
| 1773 | heur_subdissector_list = register_heur_dissector_list_with_description("rtsp", "RTSP data", proto_rtsp); |
| 1774 | |
| 1775 | /* |
| 1776 | * Register for tapping |
| 1777 | */ |
| 1778 | rtsp_tap = register_tap("rtsp"); /* RTSP statistics tap */ |
| 1779 | |
| 1780 | register_external_value_string("rtsp_status_code_vals", rtsp_status_code_vals); |
| 1781 | } |
| 1782 | |
| 1783 | void |
| 1784 | proto_reg_handoff_rtsp(void) |
| 1785 | { |
| 1786 | rtp_handle = find_dissector_add_dependency("rtp", proto_rtsp); |
| 1787 | rtp_rfc4571_handle = find_dissector_add_dependency("rtp.rfc4571", proto_rtsp); |
| 1788 | rtcp_handle = find_dissector_add_dependency("rtcp", proto_rtsp); |
| 1789 | rdt_handle = find_dissector_add_dependency("rdt", proto_rtsp); |
| 1790 | media_type_dissector_table = find_dissector_table("media_type"); |
| 1791 | voip_tap = find_tap_id("voip"); |
| 1792 | |
| 1793 | /* Set our port number for future use */ |
| 1794 | dissector_add_uint_range_with_preference("tcp.port", RTSP_TCP_PORT_RANGE"554,8554,7236", rtsp_handle); |
| 1795 | |
| 1796 | /* XXX: Do the following only once ?? */ |
| 1797 | stats_tree_register("rtsp","rtsp","RTSP" STATS_TREE_MENU_SEPARATOR"//" "Packet Counter", 0, rtsp_stats_tree_packet, rtsp_stats_tree_init, NULL((void*)0) ); |
| 1798 | |
| 1799 | } |
| 1800 | |
| 1801 | /* |
| 1802 | * Editor modelines - https://www.wireshark.org/tools/modelines.html |
| 1803 | * |
| 1804 | * Local variables: |
| 1805 | * c-basic-offset: 4 |
| 1806 | * tab-width: 8 |
| 1807 | * indent-tabs-mode: space |
| 1808 | * End: |
| 1809 | * |
| 1810 | * vi: set shiftwidth=4 tabstop=8 expandtab: |
| 1811 | * :indentSize=4:tabSize=8:noTabs=true: |
| 1812 | */ |