### Bug Summary

File: | epan/dissectors/packet-rtsp.c
Warning: | line 1568, column 5 Value stored to 'linelen' is never read

### Annotated Source Code

Press '?' to see keyboard shortcuts

Show analyzer invocation
clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name packet-rtsp.c -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -fno-delete-null-pointer-checks -mframe-pointer=all -relaxed-aliasing -fmath-errno -ffp-contract=on -fno-rounding-math -ffloat16-excess-precision=fast -fbfloat16-excess-precision=fast -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/builds/wireshark/wireshark/build -fcoverage-compilation-dir=/builds/wireshark/wireshark/build -resource-dir /usr/lib/llvm-21/lib/clang/21 -isystem /usr/include/glib-2.0 -isystem /usr/lib/x86_64-linux-gnu/glib-2.0/include -isystem /builds/wireshark/wireshark/epan/dissectors -isystem /builds/wireshark/wireshark/build/epan/dissectors -isystem /usr/include/mit-krb5 -isystem /usr/include/libxml2 -isystem /builds/wireshark/wireshark/epan -D G_DISABLE_DEPRECATED -D G_DISABLE_SINGLE_INCLUDES -D WS_BUILD_DLL -D WS_DEBUG -D WS_DEBUG_UTF_8 -I /builds/wireshark/wireshark/build -I /builds/wireshark/wireshark -I /builds/wireshark/wireshark/include -D _GLIBCXX_ASSERTIONS -internal-isystem /usr/lib/llvm-21/lib/clang/21/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -fmacro-prefix-map=/builds/wireshark/wireshark/= -fmacro-prefix-map=/builds/wireshark/wireshark/build/= -fmacro-prefix-map=../= -Wno-format-nonliteral -std=gnu11 -ferror-limit 19 -fvisibility=hidden -fwrapv -fwrapv-pointer -fstrict-flex-arrays=3 -stack-protector 2 -fstack-clash-protection -fcf-protection=full -fgnuc-version=4.2.1 -fskip-odr-check-in-gmf -fexceptions -fcolor-diagnostics -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /builds/wireshark/wireshark/sbout/2025-12-06-100322-3573-1 -x c /builds/wireshark/wireshark/epan/dissectors/packet-rtsp.c

Keyboard shortcuts:

- Use 'j/k' keys for keyboard navigation
- Use 'Shift+S' to show/hide relevant lines
- Use '?' to toggle this window

Close
Show only relevant lines Show control flow arrows

1 | /* packet-rtsp.c
2 | * Routines for RTSP packet disassembly (RFC 2326)
3 | *
4 | * Jason Lango <jal@netapp.com>
5 | * Liberally copied from packet-http.c, by Guy Harris <guy@alum.mit.edu>
6 | *
7 | * Wireshark - Network traffic analyzer
8 | * By Gerald Combs <gerald@wireshark.org>
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 | */