1 /* packet-rtpproxy.c
2  * RTPproxy command protocol dissector
3  * Copyright 2013, Peter Lemenkov <lemenkov@gmail.com>
4  *
5  * This dissector tries to dissect rtpproxy control protocol. Please visit this
6  * link for brief details on the command format:
7  *
8  * http://www.rtpproxy.org/wiki/RTPproxy/Protocol
9  *
10  * Wireshark - Network traffic analyzer
11  * By Gerald Combs <gerald@wireshark.org>
12  * Copyright 1999 Gerald Combs
13  *
14  * SPDX-License-Identifier: GPL-2.0-or-later
15  */
16 
17 #include "config.h"
18 
19 #include <stdlib.h>
20 
21 #include <epan/packet.h>
22 #include <epan/prefs.h>
23 #include <epan/conversation.h>
24 #include <epan/expert.h>
25 #include <epan/rtp_pt.h>
26 #include <epan/addr_resolv.h>
27 
28 /* For setting up RTP/RTCP dissectors based on the RTPproxy's answers */
29 #include "packet-rtp.h"
30 #include "packet-rtcp.h"
31 
32 void proto_register_rtpproxy(void);
33 
34 static int proto_rtpproxy = -1;
35 
36 static int hf_rtpproxy_cookie = -1;
37 static int hf_rtpproxy_error = -1;
38 static int hf_rtpproxy_status = -1;
39 static int hf_rtpproxy_ok = -1;
40 static int hf_rtpproxy_ipv4 = -1;
41 static int hf_rtpproxy_ipv6 = -1;
42 static int hf_rtpproxy_port = -1;
43 static int hf_rtpproxy_lf = -1;
44 static int hf_rtpproxy_request = -1;
45 static int hf_rtpproxy_command = -1;
46 static int hf_rtpproxy_command_parameters = -1;
47 static int hf_rtpproxy_command_parameter = -1;
48 static int hf_rtpproxy_command_parameter_codec = -1;
49 static int hf_rtpproxy_command_parameter_local_ipv4 = -1;
50 static int hf_rtpproxy_command_parameter_remote_ipv4 = -1;
51 static int hf_rtpproxy_command_parameter_repacketize = -1;
52 static int hf_rtpproxy_command_parameter_dtmf = -1;
53 /* static int hf_rtpproxy_command_parameter_cmap = -1; TODO */
54 static int hf_rtpproxy_command_parameter_proto = -1;
55 static int hf_rtpproxy_command_parameter_transcode = -1;
56 static int hf_rtpproxy_command_parameter_acc = -1;
57 static int hf_rtpproxy_callid = -1;
58 static int hf_rtpproxy_copy_target = -1;
59 static int hf_rtpproxy_playback_filename = -1;
60 static int hf_rtpproxy_playback_codec = -1;
61 static int hf_rtpproxy_notify = -1;
62 static int hf_rtpproxy_notify_ipv4 = -1;
63 static int hf_rtpproxy_notify_ipv6 = -1;
64 static int hf_rtpproxy_notify_port = -1;
65 static int hf_rtpproxy_notify_tag = -1;
66 static int hf_rtpproxy_tag = -1;
67 static int hf_rtpproxy_mediaid = -1;
68 static int hf_rtpproxy_reply = -1;
69 static int hf_rtpproxy_version_request = -1;
70 static int hf_rtpproxy_version_supported = -1;
71 static int hf_rtpproxy_ng_bencode = -1;
72 
73 /* Expert fields */
74 static expert_field ei_rtpproxy_timeout = EI_INIT;
75 static expert_field ei_rtpproxy_notify_no_ip = EI_INIT;
76 static expert_field ei_rtpproxy_bad_ipv4 = EI_INIT;
77 static expert_field ei_rtpproxy_bad_ipv6 = EI_INIT;
78 
79 /* Request/response tracking */
80 static int hf_rtpproxy_request_in = -1;
81 static int hf_rtpproxy_response_in = -1;
82 static int hf_rtpproxy_response_time = -1;
83 
84 typedef struct _rtpproxy_info {
85     guint32 req_frame;
86     guint32 resp_frame;
87     nstime_t req_time;
88     gchar* callid;
89 } rtpproxy_info_t;
90 
91 static dissector_handle_t rtcp_handle;
92 static dissector_handle_t rtp_events_handle;
93 static dissector_handle_t rtp_handle;
94 static dissector_handle_t bencode_handle;
95 
96 typedef struct _rtpproxy_conv_info {
97     wmem_tree_t *trans;
98 } rtpproxy_conv_info_t;
99 
100 
101 static const string_string versiontypenames[] = {
102     { "20040107", "Basic RTP proxy functionality" },
103     { "20050322", "Support for multiple RTP streams and MOH" },
104     { "20060704", "Support for extra parameter in the V command" },
105     { "20071116", "Support for RTP re-packetization" },
106     { "20071218", "Support for forking (copying) RTP stream" },
107     { "20080403", "Support for RTP statistics querying" },
108     { "20081102", "Support for setting codecs in the update/lookup command" },
109     { "20081224", "Support for session timeout notifications" },
110     { "20090810", "Support for automatic bridging" },
111     { "20140323", "Support for tracking/reporting load" },
112     { "20140617", "Support for anchoring session connect time" },
113     { "20141004", "Support for extendable performance counters" },
114     { "20150330", "Support for allocating a new port (\"Un\"/\"Ln\" commands)" },
115     { 0, NULL }
116 };
117 
118 static const value_string commandtypenames[] = {
119     { 'V', "Handshake/Ping" },
120     { 'v', "Handshake/Ping" },
121     { 'U', "Offer/Update" },
122     { 'u', "Offer/Update" },
123     { 'L', "Answer/Lookup" },
124     { 'l', "Answer/Lookup" },
125     { 'I', "Information"},
126     { 'i', "Information"},
127     { 'X', "Close all active sessions"},
128     { 'x', "Close all active sessions"},
129     { 'D', "Delete an active session (Bye/Cancel/Error)"},
130     { 'd', "Delete an active session (Bye/Cancel/Error)"},
131     { 'P', "Start playback (music-on-hold)"},
132     { 'p', "Start playback (music-on-hold)"},
133     { 'S', "Stop playback (music-on-hold)"},
134     { 's', "Stop playback (music-on-hold)"},
135     { 'R', "Start recording"},
136     { 'r', "Start recording"},
137     { 'C', "Copy stream"},
138     { 'c', "Copy stream"},
139     { 'Q', "Query info about a session"},
140     { 'q', "Query info about a session"},
141     { 0, NULL }
142 };
143 
144 static const value_string paramtypenames[] = {
145     /* Official command parameters */
146     {'4', "Remote address is IPv4"},
147     {'6', "Remote address is IPv6"},
148     {'a', "Asymmetric stream"},
149     {'A', "Asymmetric stream"},
150     {'b', "Brief stats"},
151     {'B', "Brief stats"},
152     {'c', "Codecs"},
153     {'C', "Codecs"},
154     {'e', "External network (non RFC 1918)"},
155     {'E', "External network (non RFC 1918)"},
156     {'i', "Internal network (RFC 1918)"},
157     {'I', "Internal network (RFC 1918)"},
158     {'l', "Local address / Load average"},
159     {'L', "Local address / Load average"},
160     {'n', "request New port"},
161     {'N', "request New port"},
162     {'r', "Remote address"},
163     {'R', "Remote address"},
164     {'s', "Symmetric stream / Single file"},
165     {'S', "Symmetric stream / Single file"},
166     {'w', "Weak connection (allows roaming)"},
167     {'W', "Weak connection (allows roaming)"},
168     {'z', "repacketiZe"},
169     {'Z', "repacketiZe"},
170     /* Unofficial command parameters / expensions */
171     {'d', "DTMF payload ID (unofficial extension)"},
172     {'D', "DTMF payload ID (unofficial extension)"},
173     {'m', "codec Mapping (unofficial extension)"},
174     {'M', "codec Mapping (unofficial extension)"},
175     {'p', "Protocol type (unofficial extension)"},
176     {'P', "Protocol type (unofficial extension)"},
177     {'t', "Transcode to (unofficial extension)"},
178     {'T', "Transcode to (unofficial extension)"},
179     {'u', "accoUnting (unofficial extension)"},
180     {'U', "accoUnting (unofficial extension)"},
181     {0, NULL}
182 };
183 
184 static const value_string prototypenames[] = {
185     { '0', "UDP (default)"},
186     { '1', "TCP"},
187     { '2', "SCTP"},
188     { 0, NULL }
189 };
190 static const value_string acctypenames[] = {
191     { '0', "Start"},
192     { '1', "Interim update"},
193     { '2', "Stop"},
194     { 0, NULL }
195 };
196 
197 static const value_string oktypenames[] = {
198     { '0', "Ok"},
199     { '1', "Version Supported"},
200     { 0, NULL }
201 };
202 
203 static const string_string errortypenames[] = {
204     { "E0", "Syntax error: unknown command (CMDUNKN)" },
205     { "E1", "Syntax error: invalid number of arguments (PARSE_NARGS)" },
206     { "E2", "Syntax error: modifiers are not supported by the command (PARSE_MODS)" },
207     { "E3", "Syntax error: subcommand is not supported (PARSE_SUBC)" },
208     { "E5", "PARSE_1" },
209     { "E6", "PARSE_2" },
210     { "E7", "PARSE_3" },
211     { "E8", "PARSE_4" },
212     { "E9", "PARSE_5" },
213     { "E10", "PARSE_10" },
214     { "E11", "PARSE_11" },
215     { "E12", "PARSE_12" },
216     { "E13", "PARSE_13" },
217     { "E14", "PARSE_14" },
218     { "E15", "PARSE_15" },
219     { "E16", "PARSE_16" },
220     { "E17", "PARSE_6" },
221     { "E18", "PARSE_7" },
222     { "E19", "PARSE_8" },
223     { "E25", "Software error: output buffer overflow (RTOOBIG_1)" },
224     { "E26", "Software error: output buffer overflow (RTOOBIG_2)" },
225     { "E31", "Syntax error: invalid local address (INVLARG_1)" },
226     { "E32", "Syntax error: invalid remote address (INVLARG_2)" },
227     { "E33", "Syntax error: can't find local address for remote address (INVLARG_3)" },
228     { "E34", "Syntax error: invalid local address (INVLARG_4)" },
229     { "E35", "Syntax error: no codecs (INVLARG_5)" },
230     { "E36", "Syntax error: cannot match local address for the session (INVLARG_6)" },
231     { "E50", "Software error: session not found (SESUNKN)" },
232     { "E60", "PLRFAIL" },
233     { "E62", "Software error: unsupported/invalid counter name (QRYFAIL)" },
234     { "E65", "CPYFAIL" },
235     { "E68", "STSFAIL" },
236     { "E71", "Software error: can't create listener (LSTFAIL_1)" },
237     { "E72", "Software error: can't create listener (LSTFAIL_2)" },
238     { "E75", "Software error: must permit notification socket with -n (NSOFF)" },
239     { "E81", "Out of memory (NOMEM_1)" },
240     { "E82", "Out of memory (NOMEM_2)" },
241     { "E83", "Out of memory (NOMEM_3)" },
242     { "E84", "Out of memory (NOMEM_4)" },
243     { "E85", "Out of memory (NOMEM_5)" },
244     { "E86", "Out of memory (NOMEM_6)" },
245     { "E87", "Out of memory (NOMEM_7)" },
246     { "E88", "Out of memory (NOMEM_8)" },
247     { "E89", "Out of memory (NOMEM_9)" },
248     { "E98", "OVERLOAD" },
249     { "E99", "Software error: proxy is in the deorbiting-burn mode, new session rejected (SLOWSHTDN)" },
250     { 0, NULL }
251 };
252 
253 static gint ett_rtpproxy = -1;
254 
255 static gint ett_rtpproxy_request = -1;
256 static gint ett_rtpproxy_command = -1;
257 static gint ett_rtpproxy_command_parameters = -1;
258 static gint ett_rtpproxy_command_parameters_codecs = -1;
259 static gint ett_rtpproxy_command_parameters_local = -1;
260 static gint ett_rtpproxy_command_parameters_remote = -1;
261 static gint ett_rtpproxy_command_parameters_repacketize = -1;
262 static gint ett_rtpproxy_command_parameters_dtmf = -1;
263 static gint ett_rtpproxy_command_parameters_cmap = -1;
264 static gint ett_rtpproxy_command_parameters_proto = -1;
265 static gint ett_rtpproxy_command_parameters_transcode = -1;
266 static gint ett_rtpproxy_command_parameters_acc = -1;
267 static gint ett_rtpproxy_tag = -1;
268 static gint ett_rtpproxy_notify = -1;
269 
270 static gint ett_rtpproxy_reply = -1;
271 
272 static gint ett_rtpproxy_ng_bencode = -1;
273 
274 /* Default values */
275 #define RTPPROXY_PORT 22222  /* Not IANA registered */
276 static gboolean rtpproxy_establish_conversation = TRUE;
277 /* See - https://www.opensips.org/html/docs/modules/1.10.x/rtpproxy.html#id293555 */
278 /* See - http://www.kamailio.org/docs/modules/4.3.x/modules/rtpproxy.html#idp15794952 */
279 static guint rtpproxy_timeout = 1000;
280 static nstime_t rtpproxy_timeout_ns = NSTIME_INIT_ZERO;
281 
282 void proto_reg_handoff_rtpproxy(void);
283 
284 static gint
rtpproxy_add_tag(proto_tree * rtpproxy_tree,tvbuff_t * tvb,guint begin,guint realsize)285 rtpproxy_add_tag(proto_tree *rtpproxy_tree, tvbuff_t *tvb, guint begin, guint realsize)
286 {
287     proto_item *ti = NULL;
288     proto_tree *another_tree = NULL;
289     gint new_offset;
290     guint end;
291 
292     new_offset = tvb_find_guint8(tvb, begin, -1, ' ');
293     if(new_offset < 0)
294         end = realsize; /* No more parameters */
295     else
296         end = new_offset;
297 
298     /* SER/OpenSER/OpenSIPS/Kamailio adds Media-ID right after the Tag
299      * separated by a semicolon
300      */
301     new_offset = tvb_find_guint8(tvb, begin, end, ';');
302     if(new_offset == -1){
303         ti = proto_tree_add_item(rtpproxy_tree, hf_rtpproxy_tag, tvb, begin, end - begin, ENC_ASCII | ENC_NA);
304         another_tree = proto_item_add_subtree(ti, ett_rtpproxy_tag);
305         ti = proto_tree_add_item(another_tree, hf_rtpproxy_mediaid, tvb, new_offset+1, 0, ENC_ASCII | ENC_NA);
306         proto_item_append_text(ti, "<skipped>");
307         proto_item_set_generated(ti);
308     }
309     else{
310         ti = proto_tree_add_item(rtpproxy_tree, hf_rtpproxy_tag, tvb, begin, new_offset - begin, ENC_ASCII | ENC_NA);
311         if ((guint)new_offset == begin){
312             proto_item_append_text(ti, "<skipped>"); /* A very first Offer/Update command */
313             proto_item_set_generated(ti);
314         }
315         another_tree = proto_item_add_subtree(ti, ett_rtpproxy_tag);
316         proto_tree_add_item(another_tree, hf_rtpproxy_mediaid, tvb, new_offset+1, end - (new_offset+1), ENC_ASCII | ENC_NA);
317     }
318     return (end == realsize ? -1 : (gint)end);
319 }
320 
321 static void
rtpproxy_add_parameter(tvbuff_t * tvb,packet_info * pinfo,proto_tree * rtpproxy_tree,guint begin,guint realsize)322 rtpproxy_add_parameter(tvbuff_t *tvb, packet_info *pinfo, proto_tree *rtpproxy_tree, guint begin, guint realsize)
323 {
324     proto_item *ti;
325     proto_tree *another_tree = NULL;
326     guint offset = 0;
327     guint new_offset = 0;
328     gint i;
329     guint pt = 0;
330     gchar** codecs = NULL;
331     guint codec_len;
332     guint8* rawstr = NULL;
333     guint32 ipaddr[4]; /* Enough room for IPv4 or IPv6 */
334 
335     /* Extract the entire parameters line. */
336     /* Something like "t4p1iic8,0,2,4,18,96,97,98,100,101" */
337     rawstr = tvb_get_string_enc(pinfo->pool, tvb, begin, realsize, ENC_ASCII);
338 
339     while(offset < realsize){
340         ti = proto_tree_add_item(rtpproxy_tree, hf_rtpproxy_command_parameter, tvb, begin + offset, 1, ENC_ASCII | ENC_NA);
341         offset++; /* Skip 1-byte parameter's type */
342         switch (g_ascii_tolower(tvb_get_guint8(tvb, begin+offset-1)))
343         {
344             /* Official long parameters */
345             case 'c':
346                 new_offset = (gint)strspn(rawstr+offset, "0123456789,");
347                 another_tree = proto_item_add_subtree(ti, ett_rtpproxy_command_parameters_codecs);
348                 codecs = wmem_strsplit(pinfo->pool, tvb_get_string_enc(pinfo->pool, tvb, begin+offset, new_offset, ENC_ASCII), ",", 0);
349                 i = 0;
350                 while(codecs[i]){
351                     /* We assume strings < 2^32-1 bytes long. :-) */
352                     codec_len = (guint)strlen(codecs[i]);
353                     ti = proto_tree_add_uint(another_tree, hf_rtpproxy_command_parameter_codec, tvb, begin+offset, codec_len,
354                             (guint16) g_ascii_strtoull((gchar*)tvb_get_string_enc(pinfo->pool, tvb, begin+offset, codec_len, ENC_ASCII), NULL, 10));
355                     proto_item_append_text(ti, " (%s)", val_to_str_ext((guint)strtoul(tvb_format_text(pinfo->pool, tvb,begin+offset,codec_len),NULL,10), &rtp_payload_type_vals_ext, "Unknown"));
356                     offset += codec_len;
357                     if(codecs[i+1])
358                         offset++; /* skip comma */
359                     i++;
360                 };
361                 break;
362             case 'l':
363                 /* That's another one protocol shortcoming - the same parameter used twice. */
364                 /* https://github.com/sippy/rtpproxy/wiki/RTPP-%28RTPproxy-protocol%29-technical-specification#createupdatelookup-session */
365                 /* https://github.com/sippy/rtpproxy/wiki/RTPP-%28RTPproxy-protocol%29-technical-specification#get-information */
366                 new_offset = (gint)strspn(rawstr+offset, "0123456789.");
367                 if(new_offset){
368                     another_tree = proto_item_add_subtree(ti, ett_rtpproxy_command_parameters_local);
369                     if(str_to_ip((char*)tvb_get_string_enc(pinfo->pool, tvb, begin+offset, new_offset, ENC_ASCII), ipaddr))
370                         proto_tree_add_ipv4(another_tree, hf_rtpproxy_command_parameter_local_ipv4, tvb, begin+offset, new_offset, ipaddr[0]);
371                     else
372                         proto_tree_add_expert(another_tree, pinfo, &ei_rtpproxy_bad_ipv4, tvb, begin+offset, new_offset);
373                     offset += new_offset;
374                 }
375                 break;
376             case 'r':
377                 new_offset = (gint)strspn(rawstr+offset, "0123456789.");
378                 another_tree = proto_item_add_subtree(ti, ett_rtpproxy_command_parameters_remote);
379                 if(str_to_ip((char*)tvb_get_string_enc(pinfo->pool, tvb, begin+offset, new_offset, ENC_ASCII), ipaddr))
380                     proto_tree_add_ipv4(another_tree, hf_rtpproxy_command_parameter_remote_ipv4, tvb, begin+offset, new_offset, ipaddr[0]);
381                 else
382                     proto_tree_add_expert(another_tree, pinfo, &ei_rtpproxy_bad_ipv4, tvb, begin+offset, new_offset);
383                 offset += new_offset;
384                 break;
385             case 'z':
386                 new_offset = (gint)strspn(rawstr+offset, "0123456789");
387                 another_tree = proto_item_add_subtree(ti, ett_rtpproxy_command_parameters_repacketize);
388                 proto_tree_add_uint(another_tree, hf_rtpproxy_command_parameter_repacketize, tvb, begin+offset, new_offset,
389                         (guint16) g_ascii_strtoull((gchar*)tvb_get_string_enc(pinfo->pool, tvb, begin+offset, new_offset, ENC_ASCII), NULL, 10));
390                 offset += new_offset;
391                 break;
392             /* Unofficial long parameters */
393             case 'd':
394                 new_offset = (gint)strspn(rawstr+offset, "0123456789");
395                 another_tree = proto_item_add_subtree(ti, ett_rtpproxy_command_parameters_dtmf);
396                 proto_tree_add_uint(another_tree, hf_rtpproxy_command_parameter_dtmf, tvb, begin+offset, new_offset,
397                         (guint16) g_ascii_strtoull((gchar*)tvb_get_string_enc(pinfo->pool, tvb, begin+offset, new_offset, ENC_ASCII), NULL, 10));
398                 if(rtpproxy_establish_conversation){
399                     pt = (guint)strtoul(tvb_format_text(pinfo->pool, tvb,begin+offset,new_offset),NULL,10);
400                     dissector_add_uint("rtp.pt", pt, rtp_events_handle);
401                 }
402                 offset += new_offset;
403                 break;
404             case 'm':
405                 new_offset = (gint)strspn(rawstr+offset, "0123456789=,");
406                 /* TODO */
407                 offset += new_offset;
408                 break;
409             case 'p':
410                 another_tree = proto_item_add_subtree(ti, ett_rtpproxy_command_parameters_proto);
411                 proto_tree_add_item(another_tree, hf_rtpproxy_command_parameter_proto, tvb, begin+offset, 1, ENC_ASCII | ENC_NA);
412                 offset++;
413                 break;
414             case 't':
415                 new_offset = (gint)strspn(rawstr+offset, "0123456789");
416                 another_tree = proto_item_add_subtree(ti, ett_rtpproxy_command_parameters_transcode);
417                 ti = proto_tree_add_uint(another_tree, hf_rtpproxy_command_parameter_transcode, tvb, begin+offset, new_offset,
418                         (guint16) g_ascii_strtoull((gchar*)tvb_get_string_enc(pinfo->pool, tvb, begin+offset, new_offset, ENC_ASCII), NULL, 10));
419                 proto_item_append_text(ti, " (%s)", val_to_str_ext((guint)strtoul(tvb_format_text(pinfo->pool, tvb,begin+offset, new_offset),NULL,10), &rtp_payload_type_vals_ext, "Unknown"));
420                 offset += new_offset;
421                 break;
422             case 'u':
423                 another_tree = proto_item_add_subtree(ti, ett_rtpproxy_command_parameters_acc);
424                 proto_tree_add_item(another_tree, hf_rtpproxy_command_parameter_acc, tvb, begin+offset, 1, ENC_ASCII | ENC_NA);
425                 offset++;
426                 break;
427             default:
428                 break;
429         }
430     }
431 }
432 
433 static rtpproxy_info_t *
rtpproxy_add_tid(gboolean is_request,tvbuff_t * tvb,packet_info * pinfo,proto_tree * rtpproxy_tree,rtpproxy_conv_info_t * rtpproxy_conv,const guint8 * cookie)434 rtpproxy_add_tid(gboolean is_request, tvbuff_t *tvb, packet_info *pinfo, proto_tree *rtpproxy_tree, rtpproxy_conv_info_t *rtpproxy_conv, const guint8* cookie)
435 {
436     rtpproxy_info_t *rtpproxy_info;
437     proto_item *pi;
438 
439     if (!PINFO_FD_VISITED(pinfo)) {
440         if (is_request){
441             rtpproxy_info = wmem_new0(wmem_file_scope(), rtpproxy_info_t);
442             rtpproxy_info->req_frame = pinfo->num;
443             rtpproxy_info->req_time = pinfo->abs_ts;
444             wmem_tree_insert_string(rtpproxy_conv->trans, cookie, rtpproxy_info, 0);
445         } else {
446             rtpproxy_info = (rtpproxy_info_t *)wmem_tree_lookup_string(rtpproxy_conv->trans, cookie, 0);
447             if (rtpproxy_info) {
448                 rtpproxy_info->resp_frame = pinfo->num;
449             }
450         }
451     } else {
452         rtpproxy_info = (rtpproxy_info_t *)wmem_tree_lookup_string(rtpproxy_conv->trans, cookie, 0);
453         if (rtpproxy_info && (is_request ? rtpproxy_info->resp_frame : rtpproxy_info->req_frame)) {
454             nstime_t ns;
455 
456             pi = proto_tree_add_uint(rtpproxy_tree, is_request ? hf_rtpproxy_response_in : hf_rtpproxy_request_in, tvb, 0, 0, is_request ? rtpproxy_info->resp_frame : rtpproxy_info->req_frame);
457             proto_item_set_generated(pi);
458 
459             /* If not a request (so it's a reply) then calculate response time */
460             if (!is_request){
461                 nstime_delta(&ns, &pinfo->abs_ts, &rtpproxy_info->req_time);
462                 pi = proto_tree_add_time(rtpproxy_tree, hf_rtpproxy_response_time, tvb, 0, 0, &ns);
463                 proto_item_set_generated(pi);
464                 if (nstime_cmp(&rtpproxy_timeout_ns, &ns) < 0)
465                     expert_add_info_format(pinfo, rtpproxy_tree, &ei_rtpproxy_timeout, "Response timeout %.3f seconds", nstime_to_sec(&ns));
466             }
467         }
468     }
469     /* Could be NULL so we should check it before dereferencing */
470     return rtpproxy_info;
471 }
472 
473 static void
rtpproxy_add_notify_addr(tvbuff_t * tvb,packet_info * pinfo,proto_tree * rtpproxy_tree,guint begin,guint end)474 rtpproxy_add_notify_addr(tvbuff_t *tvb, packet_info *pinfo, proto_tree *rtpproxy_tree, guint begin, guint end)
475 {
476     gint offset = 0;
477     gint tmp = 0;
478     gboolean ipv6 = FALSE;
479     guint32 ipaddr[4]; /* Enough room for IPv4 or IPv6 */
480 
481     /* Check for at least one colon */
482     offset = tvb_find_guint8(tvb, begin, end, ':');
483     if(offset != -1){
484         /* Find if it's the latest colon (not in case of a IPv6) */
485         while((tmp = tvb_find_guint8(tvb, offset+1, end, ':')) != -1){
486             ipv6 = TRUE;
487             offset = tmp;
488         }
489         /* We have ip:port */
490         if(ipv6){
491             if(str_to_ip6((char*)tvb_get_string_enc(pinfo->pool, tvb, begin, offset - begin, ENC_ASCII), ipaddr))
492                 proto_tree_add_ipv6(rtpproxy_tree, hf_rtpproxy_notify_ipv6, tvb, begin, offset - begin, (const ws_in6_addr*)ipaddr);
493             else
494                 proto_tree_add_expert(rtpproxy_tree, pinfo, &ei_rtpproxy_bad_ipv6, tvb, begin, offset - begin);
495         }
496         else{
497             if(str_to_ip((char*)tvb_get_string_enc(pinfo->pool, tvb, begin, offset - begin, ENC_ASCII), ipaddr))
498                 proto_tree_add_ipv4(rtpproxy_tree, hf_rtpproxy_notify_ipv4, tvb, begin, offset - begin, ipaddr[0]);
499             else
500                 proto_tree_add_expert(rtpproxy_tree, pinfo, &ei_rtpproxy_bad_ipv4, tvb, begin, offset - begin);
501         }
502         proto_tree_add_uint(rtpproxy_tree, hf_rtpproxy_notify_port, tvb, offset+1, end - (offset+1),
503             (guint16) g_ascii_strtoull((gchar*)tvb_get_string_enc(pinfo->pool, tvb, offset+1, end - (offset+1), ENC_ASCII), NULL, 10));
504     }
505     else{
506         proto_item *ti = NULL;
507         /* Only port is supplied - take IPv4/IPv6 from  ip.src/ipv6.src respectively */
508         expert_add_info(pinfo, rtpproxy_tree, &ei_rtpproxy_notify_no_ip);
509         if (pinfo->src.type == AT_IPv4) {
510             ti = proto_tree_add_ipv4(rtpproxy_tree, hf_rtpproxy_notify_ipv4, tvb, begin, 0, *(const guint32*)(pinfo->src.data));
511         } else if (pinfo->src.type == AT_IPv6) {
512             ti = proto_tree_add_ipv6(rtpproxy_tree, hf_rtpproxy_notify_ipv6, tvb, begin, 0, (const ws_in6_addr *)(pinfo->src.data));
513         }
514         if (ti) {
515             proto_item_set_generated(ti);
516             proto_tree_add_uint(rtpproxy_tree, hf_rtpproxy_notify_port, tvb, begin, end - begin,
517                 (guint16) g_ascii_strtoull((gchar*)tvb_get_string_enc(pinfo->pool, tvb, begin, end - begin, ENC_ASCII), NULL, 10));
518         }
519     }
520 }
521 
522 static int
dissect_rtpproxy(tvbuff_t * tvb,packet_info * pinfo,proto_tree * tree,void * data _U_)523 dissect_rtpproxy(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
524 {
525     gboolean has_lf = FALSE;
526     gint offset = 0;
527     gint new_offset = 0;
528     guint tmp;
529     guint tmp2;
530     gint realsize = 0;
531     guint8* rawstr;
532     const guint8* tmpstr;
533     proto_item *ti;
534     proto_item *ti2;
535     proto_tree *rtpproxy_tree;
536     conversation_t *conversation;
537     rtpproxy_conv_info_t *rtpproxy_conv;
538     const guint8* cookie = NULL;
539     /* For RT(C)P setup */
540     address addr;
541     guint16 port;
542     guint32 ipaddr[4]; /* Enough room for IPv4 or IPv6 */
543     rtpproxy_info_t *rtpproxy_info = NULL;
544     tvbuff_t *subtvb;
545 
546     /* If it does not start with a printable character it's not RTPProxy */
547     if(!g_ascii_isprint(tvb_get_guint8(tvb, 0)))
548         return 0;
549 
550     /* Extract Cookie */
551     offset = tvb_find_guint8(tvb, offset, -1, ' ');
552     if(offset == -1)
553         return 0;
554 
555     /* We believe it's likely a RTPproxy / RTPproxy-ng protocol */
556     /* Note: we no longer distinct between packets with or w/o LF - it turned
557      * out to be useless */
558     col_set_str(pinfo->cinfo, COL_PROTOCOL, "RTPproxy");
559 
560     /* Clear out stuff in the info column - we'll set it later */
561     col_clear(pinfo->cinfo, COL_INFO);
562 
563     ti = proto_tree_add_item(tree, proto_rtpproxy, tvb, 0, -1, ENC_NA);
564     rtpproxy_tree = proto_item_add_subtree(ti, ett_rtpproxy);
565 
566     proto_tree_add_item_ret_string(rtpproxy_tree, hf_rtpproxy_cookie, tvb, 0, offset, ENC_ASCII | ENC_NA, pinfo->pool, &cookie);
567 
568     /* Skip whitespace */
569     offset = tvb_skip_wsp(tvb, offset+1, -1);
570 
571     /* Calculate size to prevent recalculation in the future */
572     realsize = tvb_reported_length(tvb);
573 
574     /* Don't count trailing zeroes (inserted by some SIP-servers sometimes) */
575     while (tvb_get_guint8(tvb, realsize - 1) == 0){
576         realsize -= 1;
577     }
578 
579     /* Check for LF (required for TCP connection, optional for UDP) */
580     if (tvb_get_guint8(tvb, realsize - 1) == '\n'){
581         /* Don't count trailing LF */
582         realsize -= 1;
583         has_lf = TRUE;
584     }
585 
586     /* Try to create conversation */
587     conversation = find_or_create_conversation(pinfo);
588     rtpproxy_conv = (rtpproxy_conv_info_t *)conversation_get_proto_data(conversation, proto_rtpproxy);
589     if (!rtpproxy_conv) {
590         rtpproxy_conv = wmem_new(wmem_file_scope(), rtpproxy_conv_info_t);
591         rtpproxy_conv->trans = wmem_tree_new(wmem_file_scope());
592         conversation_add_proto_data(conversation, proto_rtpproxy, rtpproxy_conv);
593     }
594 
595     /* Get payload string */
596     rawstr = tvb_format_text(pinfo->pool, tvb, offset, realsize - offset);
597 
598     /* Extract command */
599     tmp = g_ascii_tolower(tvb_get_guint8(tvb, offset));
600     switch (tmp)
601     {
602         case 's':
603             /* A specific case - long info answer */
604             /* %COOKIE% sessions created %NUM0% active sessions: %NUM1% */
605             /* FIXME https://github.com/sippy/rtpproxy/wiki/RTPP-%28RTPproxy-protocol%29-technical-specification#information */
606             rtpproxy_add_tid(FALSE, tvb, pinfo, rtpproxy_tree, rtpproxy_conv, cookie);
607             if ('e' == tvb_get_guint8(tvb, offset+1)){
608                 col_add_fstr(pinfo->cinfo, COL_INFO, "Reply: %s", rawstr);
609                 ti = proto_tree_add_item(rtpproxy_tree, hf_rtpproxy_reply, tvb, offset, -1, ENC_NA);
610 
611                 rtpproxy_tree = proto_item_add_subtree(ti, ett_rtpproxy_reply);
612                 proto_tree_add_item(rtpproxy_tree, hf_rtpproxy_status, tvb, offset, realsize - offset, ENC_ASCII | ENC_NA);
613                 break;
614             }
615         /* FALL THROUGH */
616         case 'i':
617         case 'x':
618         case 'u':
619         case 'l':
620         case 'd':
621             tmp2 = tvb_get_guint8(tvb, offset+1);
622             if(('1' <= tmp2) && (tmp2 <= '9') && (tvb_get_guint8(tvb, offset+2) == ':')){
623                 col_set_str(pinfo->cinfo, COL_PROTOCOL, "RTPproxy-ng");
624                 col_add_fstr(pinfo->cinfo, COL_INFO, "RTPproxy-ng: %s", rawstr);
625                 ti = proto_tree_add_item(rtpproxy_tree, hf_rtpproxy_ng_bencode, tvb, offset, -1, ENC_ASCII | ENC_NA);
626                 rtpproxy_tree = proto_item_add_subtree(ti, ett_rtpproxy_ng_bencode);
627                 subtvb = tvb_new_subset_remaining(tvb, offset);
628                 call_dissector(bencode_handle, subtvb, pinfo, rtpproxy_tree);
629                 break;
630             }
631         /* FALL THROUGH */
632         case 'p':
633         case 'v':
634         case 'r':
635         case 'c':
636         case 'q':
637             rtpproxy_info = rtpproxy_add_tid(TRUE, tvb, pinfo, rtpproxy_tree, rtpproxy_conv, cookie);
638             col_add_fstr(pinfo->cinfo, COL_INFO, "Request: %s", rawstr);
639             ti = proto_tree_add_item(rtpproxy_tree, hf_rtpproxy_request, tvb, offset, -1, ENC_NA);
640             rtpproxy_tree = proto_item_add_subtree(ti, ett_rtpproxy_request);
641 
642             /* A specific case - version request:
643              * https://github.com/sippy/rtpproxy/wiki/RTPP-%28RTPproxy-protocol%29-technical-specification#get-list-of-veatures
644              *
645              * In this case a command size must be bigger or equal to a "VF YYYYMMDD" string size.
646              * It's bigger if there is more than one space inserted between "VF" and "YYYYMMDD" tokens.
647              */
648             if ((tmp == 'v') && (offset + (gint)strlen("VF YYYYMMDD") <= realsize)){
649                 /* Skip whitespace between "VF" and "YYYYMMDD" tokens */
650                 new_offset = tvb_skip_wsp(tvb, offset + ((guint)strlen("VF") + 1), -1);
651                 ti = proto_tree_add_item_ret_string(rtpproxy_tree, hf_rtpproxy_version_request, tvb, new_offset, (gint)strlen("YYYYMMDD"), ENC_ASCII | ENC_NA, pinfo->pool, &tmpstr);
652                 proto_item_append_text(ti, " (%s)", str_to_str(tmpstr, versiontypenames, "Unknown"));
653                 break;
654             }
655 
656             /* All other commands */
657             ti = proto_tree_add_item(rtpproxy_tree, hf_rtpproxy_command, tvb, offset, 1, ENC_ASCII | ENC_NA);
658 
659             /* A specific case - handshake/ping */
660             if (tmp == 'v')
661                 break; /* No more parameters */
662 
663             /* A specific case - close all calls */
664             if (tmp == 'x')
665                 break; /* No more parameters */
666 
667             /* Extract parameters */
668             /* Parameters should be right after the command and before EOL (in case of Info command) or before whitespace */
669             new_offset = (tmp == 'i' ? (realsize - 1 > offset ? offset + (gint)strlen("Ib") : offset + (gint)strlen("I")) : tvb_find_guint8(tvb, offset, -1, ' '));
670 
671             if (new_offset != offset + 1){
672                 rtpproxy_tree = proto_item_add_subtree(ti, ett_rtpproxy_command);
673                 ti2 = proto_tree_add_item(rtpproxy_tree, hf_rtpproxy_command_parameters, tvb, offset+1, new_offset - (offset+1), ENC_ASCII | ENC_NA);
674                 rtpproxy_add_parameter(tvb, pinfo, proto_item_add_subtree(ti2, ett_rtpproxy_command_parameters), offset+1, new_offset - (offset+1));
675                 rtpproxy_tree = proto_item_get_parent(ti);
676             }
677 
678             /* A specific case - query information */
679             if (tmp == 'i')
680                 break; /* No more parameters */
681 
682             /* Skip whitespace */
683             offset = tvb_skip_wsp(tvb, new_offset+1, -1);
684 
685             /* Extract Call-ID */
686             new_offset = tvb_find_guint8(tvb, offset, -1, ' ');
687             proto_tree_add_item(rtpproxy_tree, hf_rtpproxy_callid, tvb, offset, new_offset - offset, ENC_ASCII | ENC_NA);
688             if(rtpproxy_info && !rtpproxy_info->callid)
689                 rtpproxy_info->callid = tvb_get_string_enc(wmem_file_scope(), tvb, offset, new_offset - offset, ENC_ASCII);
690             /* Skip whitespace */
691             offset = tvb_skip_wsp(tvb, new_offset+1, -1);
692 
693             /* Extract IP and Port in case of Offer/Answer */
694             if ((tmp == 'u') || (tmp == 'l')){
695                 /* Extract IP */
696                 new_offset = tvb_find_guint8(tvb, offset, -1, ' ');
697                 if (tvb_find_guint8(tvb, offset, new_offset - offset, ':') == -1){
698                     if(str_to_ip((char*)tvb_get_string_enc(pinfo->pool, tvb, offset, new_offset - offset, ENC_ASCII), ipaddr))
699                         proto_tree_add_ipv4(rtpproxy_tree, hf_rtpproxy_ipv4, tvb, offset, new_offset - offset, ipaddr[0]);
700                     else
701                         proto_tree_add_expert(rtpproxy_tree, pinfo, &ei_rtpproxy_bad_ipv4, tvb, offset, new_offset - offset);
702                 }
703                 else{
704                     if(str_to_ip6((char*)tvb_get_string_enc(pinfo->pool, tvb, offset, new_offset - offset, ENC_ASCII), ipaddr))
705                         proto_tree_add_ipv6(rtpproxy_tree, hf_rtpproxy_ipv6, tvb, offset, new_offset - offset, (const ws_in6_addr *)ipaddr);
706                     else
707                         proto_tree_add_expert(rtpproxy_tree, pinfo, &ei_rtpproxy_bad_ipv6, tvb, offset, new_offset - offset);
708                 }
709                 /* Skip whitespace */
710                 offset = tvb_skip_wsp(tvb, new_offset+1, -1);
711 
712                 /* Extract Port */
713                 new_offset = tvb_find_guint8(tvb, offset, -1, ' ');
714                 proto_tree_add_uint(rtpproxy_tree, hf_rtpproxy_port, tvb, offset, new_offset - offset,
715                         (guint16) g_ascii_strtoull((gchar*)tvb_get_string_enc(pinfo->pool, tvb, offset, new_offset - offset, ENC_ASCII), NULL, 10));
716                 /* Skip whitespace */
717                 offset = tvb_skip_wsp(tvb, new_offset+1, -1);
718             }
719 
720             /* Extract Copy target */
721             if (tmp == 'c'){
722                 new_offset = tvb_find_guint8(tvb, offset, -1, ' ');
723                 proto_tree_add_item(rtpproxy_tree, hf_rtpproxy_copy_target, tvb, offset, new_offset - offset, ENC_ASCII | ENC_NA);
724                 /* Skip whitespace */
725                 offset = tvb_skip_wsp(tvb, new_offset+1, -1);
726             }
727 
728             /* Extract Playback file and codecs */
729             if (tmp == 'p'){
730                 /* Extract filename */
731                 new_offset = tvb_find_guint8(tvb, offset, -1, ' ');
732                 proto_tree_add_item(rtpproxy_tree, hf_rtpproxy_playback_filename, tvb, offset, new_offset - offset, ENC_ASCII | ENC_NA);
733                 /* Skip whitespace */
734                 offset = tvb_skip_wsp(tvb, new_offset+1, -1);
735 
736                 /* Extract codec */
737                 new_offset = tvb_find_guint8(tvb, offset, -1, ' ');
738                 proto_tree_add_uint(rtpproxy_tree, hf_rtpproxy_playback_codec, tvb, offset, new_offset - offset,
739                         (guint16) g_ascii_strtoull((gchar*)tvb_get_string_enc(pinfo->pool, tvb, offset, new_offset - offset, ENC_ASCII), NULL, 10));
740                 /* Skip whitespace */
741                 offset = tvb_skip_wsp(tvb, new_offset+1, -1);
742             }
743 
744             /* Extract first tag */
745             new_offset = rtpproxy_add_tag(rtpproxy_tree, tvb, offset, realsize);
746             if(new_offset == -1)
747                 break; /* No more parameters */
748             /* Skip whitespace */
749             offset = tvb_skip_wsp(tvb, new_offset+1, -1);
750 
751             /* Extract second tag */
752             new_offset = rtpproxy_add_tag(rtpproxy_tree, tvb, offset, realsize);
753             if(new_offset == -1)
754                 break; /* No more parameters */
755             /* Skip whitespace */
756             offset = tvb_skip_wsp(tvb, new_offset+1, -1);
757 
758             /* Extract Notification address */
759             if (tmp == 'u'){
760                 ti = proto_tree_add_item(rtpproxy_tree, hf_rtpproxy_notify, tvb, offset, realsize - offset, ENC_ASCII | ENC_NA);
761                 proto_item_set_text(ti, "Notify");
762                 rtpproxy_tree = proto_item_add_subtree(ti, ett_rtpproxy_notify);
763 
764                 /* Check for NotifyTag parameter (separated by space) */
765                 new_offset = tvb_find_guint8(tvb, offset, -1, ' ');
766                 if(new_offset == -1){
767                     /* NotifyTag wasn't found (we should re-use Call-ID instead) */
768                     rtpproxy_add_notify_addr(tvb, pinfo, rtpproxy_tree, offset, realsize);
769                     break; /* No more parameters */
770                 }
771 
772                 /* NotifyTag was found */
773                 rtpproxy_add_notify_addr(tvb, pinfo, rtpproxy_tree, offset, new_offset);
774                 /* Skip whitespace */
775                 offset = tvb_skip_wsp(tvb, new_offset+1, -1);
776 
777                 proto_tree_add_item(rtpproxy_tree, hf_rtpproxy_notify_tag, tvb, offset, realsize - offset, ENC_ASCII | ENC_NA);
778             }
779             break;
780         case 'e':
781         case '0':
782         case '1':
783         case '2':
784         case '3':
785         case '4':
786         case '5':
787         case '6':
788         case '7':
789         case '8':
790         case '9':
791             rtpproxy_info = rtpproxy_add_tid(FALSE, tvb, pinfo, rtpproxy_tree, rtpproxy_conv, cookie);
792             if (tmp == 'e')
793                 col_add_fstr(pinfo->cinfo, COL_INFO, "Error reply: %s", rawstr);
794             else
795                 col_add_fstr(pinfo->cinfo, COL_INFO, "Reply: %s", rawstr);
796 
797             ti = proto_tree_add_item(rtpproxy_tree, hf_rtpproxy_reply, tvb, offset, -1, ENC_NA);
798             rtpproxy_tree = proto_item_add_subtree(ti, ett_rtpproxy_reply);
799 
800             if(rtpproxy_info && rtpproxy_info->callid){
801                 ti = proto_tree_add_string(rtpproxy_tree, hf_rtpproxy_callid, tvb, offset, 0, rtpproxy_info->callid);
802                 proto_item_set_generated(ti);
803             }
804 
805             if (tmp == 'e'){
806                 tmp = tvb_find_line_end(tvb, offset, -1, &new_offset, FALSE);
807                 tmpstr = tvb_get_string_enc(pinfo->pool, tvb, offset, tmp, ENC_ASCII);
808                 ti = proto_tree_add_item(rtpproxy_tree, hf_rtpproxy_error, tvb, offset, (gint)strlen(tmpstr), ENC_ASCII | ENC_NA);
809                 proto_item_append_text(ti, " (%s)", str_to_str(tmpstr, errortypenames, "Unknown"));
810                 break;
811             }
812 
813             /* Check for a single '0' or '1' character followed by the end-of-line.
814              * These both are positive replies - either a 'positive reply' or a 'version ack'.
815              *
816              * https://github.com/sippy/rtpproxy/wiki/RTPP-%28RTPproxy-protocol%29-technical-specification#positive-reply
817              * https://github.com/sippy/rtpproxy/wiki/RTPP-%28RTPproxy-protocol%29-technical-specification#version-reply
818              */
819             if (((tmp == '0') || (tmp == '1')) && (realsize == offset + (gint)strlen("X"))){
820                 proto_tree_add_item(rtpproxy_tree, hf_rtpproxy_ok, tvb, offset, 1, ENC_ASCII | ENC_NA);
821                 break;
822             }
823 
824             /* Check for the VERSION_NUMBER string reply:
825              * https://github.com/sippy/rtpproxy/wiki/RTPP-%28RTPproxy-protocol%29-technical-specification#version-reply
826              *
827              * If a total size equals to a current offset + size of "YYYYMMDD" string
828              * then it's a version reply.
829              */
830             if (realsize == offset + (gint)strlen("YYYYMMDD")){
831                 proto_tree_add_item(rtpproxy_tree, hf_rtpproxy_version_supported, tvb, offset, (guint32)strlen("YYYYMMDD"), ENC_ASCII | ENC_NA);
832                 break;
833             }
834 
835             /* Extract Port */
836             new_offset = tvb_find_guint8(tvb, offset, -1, ' ');
837             /* Convert port to unsigned 16-bit number */
838             port = (guint16) g_ascii_strtoull((gchar*)tvb_get_string_enc(pinfo->pool, tvb, offset, new_offset - offset, ENC_ASCII), NULL, 10);
839             proto_tree_add_uint(rtpproxy_tree, hf_rtpproxy_port, tvb, offset, new_offset - offset, port);
840             /* Skip whitespace */
841             offset = tvb_skip_wsp(tvb, new_offset+1, -1);
842 
843             /* Extract IP */
844             memset(&addr, 0, sizeof(address));
845 
846             /* Try rtpengine bogus extension first. It appends 4 or
847              * 6 depending on type of the IP. See
848              * https://github.com/sipwise/rtpengine/blob/eea3256/daemon/call_interfaces.c#L74
849              * for further details */
850             tmp = tvb_find_guint8(tvb, offset, -1, ' ');
851             if(tmp == (guint)(-1)){
852                 /* No extension - operate normally */
853                 tmp = tvb_find_line_end(tvb, offset, -1, &new_offset, FALSE);
854             }
855             else {
856                 tmp -= offset;
857             }
858 
859             if (tvb_find_guint8(tvb, offset, -1, ':') == -1){
860                 if (str_to_ip((char*)tvb_get_string_enc(pinfo->pool, tvb, offset, tmp, ENC_ASCII), ipaddr)){
861                     addr.type = AT_IPv4;
862                     addr.len  = 4;
863                     addr.data = wmem_memdup(pinfo->pool, ipaddr, 4);
864                     proto_tree_add_ipv4(rtpproxy_tree, hf_rtpproxy_ipv4, tvb, offset, tmp, ipaddr[0]);
865                 }
866                 else
867                     proto_tree_add_expert(rtpproxy_tree, pinfo, &ei_rtpproxy_bad_ipv4, tvb, offset, tmp);
868             }
869             else{
870                 if (str_to_ip6((char*)tvb_get_string_enc(pinfo->pool, tvb, offset, tmp, ENC_ASCII), ipaddr)){
871                     addr.type = AT_IPv6;
872                     addr.len  = 16;
873                     addr.data = wmem_memdup(pinfo->pool, ipaddr, 16);
874                     proto_tree_add_ipv6(rtpproxy_tree, hf_rtpproxy_ipv6, tvb, offset, tmp, (const ws_in6_addr *)ipaddr);
875                 }
876                 else
877                     proto_tree_add_expert(rtpproxy_tree, pinfo, &ei_rtpproxy_bad_ipv6, tvb, offset, tmp);
878             }
879 
880             if(rtpproxy_establish_conversation){
881                 if (rtp_handle) {
882                     /* FIXME tell if isn't a video stream, and setup codec mapping */
883                     if (addr.len)
884                         rtp_add_address(pinfo, PT_UDP, &addr, port, 0, "RTPproxy", pinfo->num, 0, NULL);
885                 }
886                 if (rtcp_handle) {
887                     if (addr.len)
888                         rtcp_add_address(pinfo, &addr, port+1, 0, "RTPproxy", pinfo->num);
889                 }
890             }
891             break;
892         default:
893             break;
894     }
895     /* TODO add an expert warning about packets w/o LF sent over TCP */
896     if (has_lf)
897         proto_tree_add_item(rtpproxy_tree, hf_rtpproxy_lf, tvb, realsize, 1, ENC_NA);
898 
899     return tvb_captured_length(tvb);
900 }
901 
902 void
proto_register_rtpproxy(void)903 proto_register_rtpproxy(void)
904 {
905     module_t *rtpproxy_module;
906     expert_module_t* expert_rtpproxy_module;
907 
908     static hf_register_info hf[] = {
909         {
910             &hf_rtpproxy_cookie,
911             {
912                 "Cookie",
913                 "rtpproxy.cookie",
914                 FT_STRING,
915                 BASE_NONE,
916                 NULL,
917                 0x0,
918                 NULL,
919                 HFILL
920             }
921         },
922         {
923             &hf_rtpproxy_version_request,
924             {
925                 "Version Request",
926                 "rtpproxy.version",
927                 FT_STRING,
928                 BASE_NONE,
929                 NULL,
930                 0x0,
931                 NULL,
932                 HFILL
933             }
934         },
935         {
936             &hf_rtpproxy_version_supported,
937             {
938                 "Version Supported",
939                 "rtpproxy.version_supported",
940                 FT_STRING,
941                 BASE_NONE,
942                 NULL,
943                 0x0,
944                 NULL,
945                 HFILL
946             }
947         },
948         {
949             &hf_rtpproxy_error,
950             {
951                 "Error",
952                 "rtpproxy.error",
953                 FT_STRING,
954                 BASE_NONE,
955                 NULL,
956                 0x0,
957                 NULL,
958                 HFILL
959             }
960         },
961         {
962             &hf_rtpproxy_ok,
963             {
964                 "Ok",
965                 "rtpproxy.ok",
966                 FT_CHAR,
967                 BASE_HEX,
968                 VALS(oktypenames),
969                 0x0,
970                 NULL,
971                 HFILL
972             }
973         },
974         {
975             &hf_rtpproxy_status,
976             {
977                 "Status",
978                 "rtpproxy.status",
979                 FT_STRING,
980                 BASE_NONE,
981                 NULL,
982                 0x0,
983                 NULL,
984                 HFILL
985             }
986         },
987         {
988             &hf_rtpproxy_ipv4,
989             {
990                 "IPv4",
991                 "rtpproxy.ipv4",
992                 FT_IPv4,
993                 BASE_NONE,
994                 NULL,
995                 0x0,
996                 NULL,
997                 HFILL
998             }
999         },
1000         {
1001             &hf_rtpproxy_ipv6,
1002             {
1003                 "IPv6",
1004                 "rtpproxy.ipv6",
1005                 FT_IPv6,
1006                 BASE_NONE,
1007                 NULL,
1008                 0x0,
1009                 NULL,
1010                 HFILL
1011             }
1012         },
1013         {
1014             &hf_rtpproxy_port,
1015             {
1016                 "Port",
1017                 "rtpproxy.port",
1018                 FT_UINT16, /* 0 - 65535 */
1019                 BASE_DEC,
1020                 NULL,
1021                 0x0,
1022                 NULL,
1023                 HFILL
1024             }
1025         },
1026         {
1027             &hf_rtpproxy_request,
1028             {
1029                 "Request",
1030                 "rtpproxy.request",
1031                 FT_NONE,
1032                 BASE_NONE,
1033                 NULL,
1034                 0x0,
1035                 NULL,
1036                 HFILL
1037             }
1038         },
1039         {
1040             &hf_rtpproxy_command,
1041             {
1042                 "Command",
1043                 "rtpproxy.command",
1044                 FT_CHAR,
1045                 BASE_HEX,
1046                 VALS(commandtypenames),
1047                 0x0,
1048                 NULL,
1049                 HFILL
1050             }
1051         },
1052         {
1053             &hf_rtpproxy_command_parameters,
1054             {
1055                 "Command parameters",
1056                 "rtpproxy.command_parameters",
1057                 FT_STRING,
1058                 BASE_NONE,
1059                 NULL,
1060                 0x0,
1061                 NULL,
1062                 HFILL
1063             }
1064         },
1065         {
1066             &hf_rtpproxy_command_parameter,
1067             {
1068                 "Parameter",
1069                 "rtpproxy.command_parameter",
1070                 FT_CHAR,
1071                 BASE_HEX,
1072                 VALS(paramtypenames),
1073                 0x0,
1074                 NULL,
1075                 HFILL
1076             }
1077         },
1078         {
1079             &hf_rtpproxy_command_parameter_codec,
1080             {
1081                 "Allowed codec",
1082                 "rtpproxy.command_parameter_codec",
1083                 FT_UINT8, /* 0 - 127 */
1084                 BASE_DEC,
1085                 NULL,
1086                 0x0,
1087                 NULL,
1088                 HFILL
1089             }
1090         },
1091         {
1092             &hf_rtpproxy_command_parameter_local_ipv4,
1093             {
1094                 "Local IPv4 address",
1095                 "rtpproxy.command_parameter_local_ipv4",
1096                 FT_IPv4, /* FIXME - is it ever possible to see IPv6 here? */
1097                 BASE_NONE,
1098                 NULL,
1099                 0x0,
1100                 NULL,
1101                 HFILL
1102             }
1103         },
1104         {
1105             &hf_rtpproxy_command_parameter_remote_ipv4,
1106             {
1107                 "Remote IPv4 address",
1108                 "rtpproxy.command_parameter_remote_ipv4",
1109                 FT_IPv4, /* FIXME - is it ever possible to see IPv6 here? */
1110                 BASE_NONE,
1111                 NULL,
1112                 0x0,
1113                 NULL,
1114                 HFILL
1115             }
1116         },
1117         {
1118             &hf_rtpproxy_command_parameter_repacketize,
1119             {
1120                 "Repacketize (ms)",
1121                 "rtpproxy.command_parameter_repacketize",
1122                 FT_UINT16, /* 0 - 1000 milliseconds */
1123                 BASE_DEC,
1124                 NULL,
1125                 0x0,
1126                 NULL,
1127                 HFILL
1128             }
1129         },
1130         {
1131             &hf_rtpproxy_command_parameter_dtmf,
1132             {
1133                 "DTMF payload ID",
1134                 "rtpproxy.command_parameter_dtmf",
1135                 FT_UINT8, /* 0 - 127 */
1136                 BASE_DEC,
1137                 NULL,
1138                 0x0,
1139                 NULL,
1140                 HFILL
1141             }
1142         },
1143         {
1144             &hf_rtpproxy_command_parameter_proto,
1145             {
1146                 "RTP transmission protocol",
1147                 "rtpproxy.command_parameter_proto",
1148                 FT_CHAR,
1149                 BASE_HEX,
1150                 VALS(prototypenames),
1151                 0x0,
1152                 NULL,
1153                 HFILL
1154             }
1155         },
1156         {
1157             &hf_rtpproxy_command_parameter_transcode,
1158             {
1159                 "Transcode to",
1160                 "rtpproxy.command_parameter_transcode",
1161                 FT_UINT8, /* 0 - 127 */
1162                 BASE_DEC,
1163                 NULL,
1164                 0x0,
1165                 NULL,
1166                 HFILL
1167             }
1168         },
1169         {
1170             &hf_rtpproxy_command_parameter_acc,
1171             {
1172                 "Accounting",
1173                 "rtpproxy.command_parameter_acc",
1174                 FT_CHAR,
1175                 BASE_HEX,
1176                 VALS(acctypenames),
1177                 0x0,
1178                 NULL,
1179                 HFILL
1180             }
1181         },
1182         {
1183             &hf_rtpproxy_copy_target,
1184             {
1185                 "Copy target",
1186                 "rtpproxy.copy_target",
1187                 FT_STRING, /* Filename or UDP address, e.g. /var/tmp/fileXXXX.yyy or IP:Port */
1188                 BASE_NONE,
1189                 NULL,
1190                 0x0,
1191                 NULL,
1192                 HFILL
1193             }
1194         },
1195         {
1196             &hf_rtpproxy_playback_filename,
1197             {
1198                 "Playback filename",
1199                 "rtpproxy.playback_filename",
1200                 FT_STRING,
1201                 BASE_NONE,
1202                 NULL,
1203                 0x0,
1204                 NULL,
1205                 HFILL
1206             }
1207         },
1208         {
1209             &hf_rtpproxy_playback_codec,
1210             {
1211                 "Playback codec",
1212                 "rtpproxy.playback_codec",
1213                 FT_UINT8, /* 0 - 127 */
1214                 BASE_DEC,
1215                 NULL,
1216                 0x0,
1217                 NULL,
1218                 HFILL
1219             }
1220         },
1221         {
1222             &hf_rtpproxy_callid,
1223             {
1224                 "Call-ID",
1225                 "rtpproxy.callid",
1226                 FT_STRING,
1227                 BASE_NONE,
1228                 NULL,
1229                 0x0,
1230                 NULL,
1231                 HFILL
1232             }
1233         },
1234         {
1235             &hf_rtpproxy_notify,
1236             {
1237                 "Notify",
1238                 "rtpproxy.notify",
1239                 FT_STRING,
1240                 BASE_NONE,
1241                 NULL,
1242                 0x0,
1243                 NULL,
1244                 HFILL
1245             }
1246         },
1247         {
1248             &hf_rtpproxy_tag,
1249             {
1250                 "Tag",
1251                 "rtpproxy.tag",
1252                 FT_STRING,
1253                 BASE_NONE,
1254                 NULL,
1255                 0x0,
1256                 NULL,
1257                 HFILL
1258             }
1259         },
1260         {
1261             &hf_rtpproxy_mediaid,
1262             {
1263                 "Media-ID",
1264                 "rtpproxy.mediaid",
1265                 FT_STRING,
1266                 BASE_NONE,
1267                 NULL,
1268                 0x0,
1269                 NULL,
1270                 HFILL
1271             }
1272         },
1273         {
1274             &hf_rtpproxy_notify_ipv4,
1275             {
1276                 "Notification IPv4",
1277                 "rtpproxy.notify_ipv4",
1278                 FT_IPv4,
1279                 BASE_NONE,
1280                 NULL,
1281                 0x0,
1282                 NULL,
1283                 HFILL
1284             }
1285         },
1286         {
1287             &hf_rtpproxy_notify_ipv6,
1288             {
1289                 "Notification IPv6",
1290                 "rtpproxy.notify_ipv6",
1291                 FT_IPv6,
1292                 BASE_NONE,
1293                 NULL,
1294                 0x0,
1295                 NULL,
1296                 HFILL
1297             }
1298         },
1299         {
1300             &hf_rtpproxy_notify_port,
1301             {
1302                 "Notification Port",
1303                 "rtpproxy.notify_port",
1304                 FT_UINT16,
1305                 BASE_DEC,
1306                 NULL,
1307                 0x0,
1308                 NULL,
1309                 HFILL
1310             }
1311         },
1312         {
1313             &hf_rtpproxy_notify_tag,
1314             {
1315                 "Notification Tag",
1316                 "rtpproxy.notify_tag",
1317                 FT_STRING,
1318                 BASE_NONE,
1319                 NULL,
1320                 0x0,
1321                 NULL,
1322                 HFILL
1323             }
1324         },
1325         {
1326             &hf_rtpproxy_reply,
1327             {
1328                 "Reply",
1329                 "rtpproxy.reply",
1330                 FT_NONE,
1331                 BASE_NONE,
1332                 NULL,
1333                 0x0,
1334                 NULL,
1335                 HFILL
1336             }
1337         },
1338         {
1339             &hf_rtpproxy_lf,
1340             {
1341                 "LF",
1342                 "rtpproxy.lf",
1343                 FT_NONE,
1344                 BASE_NONE,
1345                 NULL,
1346                 0x0,
1347                 NULL,
1348                 HFILL
1349             }
1350         },
1351         {
1352             &hf_rtpproxy_request_in,
1353             {
1354                 "Request In",
1355                 "rtpproxy.request_in",
1356                 FT_FRAMENUM,
1357                 BASE_NONE,
1358                 NULL,
1359                 0x0,
1360                 NULL,
1361                 HFILL
1362             }
1363 
1364         },
1365         {
1366             &hf_rtpproxy_response_in,
1367             {
1368                 "Response In",
1369                 "rtpproxy.response_in",
1370                 FT_FRAMENUM,
1371                 BASE_NONE,
1372                 NULL,
1373                 0x0,
1374                 NULL,
1375                 HFILL
1376             }
1377         },
1378         {
1379             &hf_rtpproxy_response_time,
1380             {
1381                 "Response Time",
1382                 "rtpproxy.response_time",
1383                 FT_RELATIVE_TIME,
1384                 BASE_NONE,
1385                 NULL,
1386                 0x0,
1387                 "The time between the Request and the Reply",
1388                 HFILL
1389              }
1390         },
1391         {
1392             &hf_rtpproxy_ng_bencode,
1393             {
1394                 "RTPproxy-ng bencode packet",
1395                 "rtpproxy.ng.bencode",
1396                 FT_STRING,
1397                 BASE_NONE,
1398                 NULL,
1399                 0x0,
1400                 "Serialized structure of integers, dictionaries, strings and lists.",
1401                 HFILL
1402             }
1403         }
1404     };
1405 
1406     static ei_register_info ei[] = {
1407         { &ei_rtpproxy_timeout,
1408           { "rtpproxy.response_timeout", PI_RESPONSE_CODE, PI_WARN,
1409             "TIMEOUT", EXPFILL }},
1410         { &ei_rtpproxy_notify_no_ip,
1411           { "rtpproxy.notify_no_ip", PI_RESPONSE_CODE, PI_COMMENT,
1412             "No notification IP address provided. Using ip.src or ipv6.src as a value.", EXPFILL }},
1413         { &ei_rtpproxy_bad_ipv4,
1414           { "rtpproxy.bad_ipv4", PI_MALFORMED, PI_ERROR,
1415             "Bad IPv4", EXPFILL }},
1416         { &ei_rtpproxy_bad_ipv6,
1417           { "rtpproxy.bad_ipv6", PI_MALFORMED, PI_ERROR,
1418             "Bad IPv6", EXPFILL }},
1419     };
1420 
1421     /* Setup protocol subtree array */
1422     static gint *ett[] = {
1423         &ett_rtpproxy,
1424         &ett_rtpproxy_request,
1425         &ett_rtpproxy_command,
1426         &ett_rtpproxy_command_parameters,
1427         &ett_rtpproxy_command_parameters_codecs,
1428         &ett_rtpproxy_command_parameters_local,
1429         &ett_rtpproxy_command_parameters_remote,
1430         &ett_rtpproxy_command_parameters_repacketize,
1431         &ett_rtpproxy_command_parameters_dtmf,
1432         &ett_rtpproxy_command_parameters_cmap,
1433         &ett_rtpproxy_command_parameters_proto,
1434         &ett_rtpproxy_command_parameters_transcode,
1435         &ett_rtpproxy_command_parameters_acc,
1436         &ett_rtpproxy_tag,
1437         &ett_rtpproxy_notify,
1438         &ett_rtpproxy_reply,
1439         &ett_rtpproxy_ng_bencode
1440     };
1441 
1442     proto_rtpproxy = proto_register_protocol ("Sippy RTPproxy Protocol", "RTPproxy", "rtpproxy");
1443 
1444     proto_register_field_array(proto_rtpproxy, hf, array_length(hf));
1445     proto_register_subtree_array(ett, array_length(ett));
1446 
1447     expert_rtpproxy_module = expert_register_protocol(proto_rtpproxy);
1448     expert_register_field_array(expert_rtpproxy_module, ei, array_length(ei));
1449 
1450     rtpproxy_module = prefs_register_protocol(proto_rtpproxy, proto_reg_handoff_rtpproxy);
1451 
1452     prefs_register_bool_preference(rtpproxy_module, "establish_conversation",
1453                                  "Establish Media Conversation",
1454                                  "Specifies that RTP/RTCP/T.38/MSRP/etc streams are decoded based "
1455                                  "upon port numbers found in RTPproxy answers",
1456                                  &rtpproxy_establish_conversation);
1457 
1458     prefs_register_uint_preference(rtpproxy_module, "reply.timeout",
1459                                  "RTPproxy reply timeout", /* Title */
1460                                  "Maximum timeout value in waiting for reply from RTPProxy (in milliseconds).", /* Descr */
1461                                  10,
1462                                  &rtpproxy_timeout);
1463 }
1464 
1465 void
proto_reg_handoff_rtpproxy(void)1466 proto_reg_handoff_rtpproxy(void)
1467 {
1468     static gboolean rtpproxy_initialized = FALSE;
1469 
1470     dissector_handle_t rtpproxy_tcp_handle, rtpproxy_udp_handle;
1471 
1472     if(!rtpproxy_initialized){
1473         rtpproxy_tcp_handle = create_dissector_handle(dissect_rtpproxy, proto_rtpproxy);
1474         rtpproxy_udp_handle = create_dissector_handle(dissect_rtpproxy, proto_rtpproxy);
1475 
1476         /* Register TCP port for dissection */
1477         dissector_add_uint_with_preference("tcp.port", RTPPROXY_PORT, rtpproxy_tcp_handle);
1478         dissector_add_uint_with_preference("udp.port", RTPPROXY_PORT, rtpproxy_udp_handle);
1479         rtpproxy_initialized = TRUE;
1480     }
1481 
1482     rtcp_handle   = find_dissector_add_dependency("rtcp", proto_rtpproxy);
1483     rtp_events_handle    = find_dissector_add_dependency("rtpevent", proto_rtpproxy);
1484     rtp_handle    = find_dissector_add_dependency("rtp", proto_rtpproxy);
1485     bencode_handle = find_dissector_add_dependency("bencode", proto_rtpproxy);
1486 
1487     /* Calculate nstime_t struct for the timeout from the rtpproxy_timeout value in milliseconds */
1488     rtpproxy_timeout_ns.secs = (rtpproxy_timeout - rtpproxy_timeout % 1000) / 1000;
1489     rtpproxy_timeout_ns.nsecs = (rtpproxy_timeout % 1000) * 1000;
1490 }
1491 
1492 /*
1493  * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
1494  *
1495  * Local variables:
1496  * c-basic-offset: 4
1497  * tab-width: 8
1498  * indent-tabs-mode: nil
1499  * End:
1500  *
1501  * vi: set shiftwidth=4 tabstop=8 expandtab:
1502  * :indentSize=4:tabSize=8:noTabs=true:
1503  */
1504