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