name change
[obnox/wireshark/wip.git] / plugins / mgcp / packet-mgcp.c
1 /* packet-mgcp.c
2  * Routines for mgcp packet disassembly
3  * RFC 2705
4  * RFC 3435 (obsoletes 2705): Media Gateway Control Protocol (MGCP) Version 1.0
5  * RFC 3660: Basic MGCP Packages
6  * RFC 3661: MGCP Return Code Usage
7  * NCS 1.0: PacketCable Network-Based Call Signaling Protocol Specification,
8  *          PKT-SP-EC-MGCP-I09-040113, January 13, 2004, Cable Television
9  *          Laboratories, Inc., http://www.PacketCable.com/
10  * www.iana.org/assignments/mgcp-localconnectionoptions
11  *
12  * $Id$
13  *
14  * Copyright (c) 2000 by Ed Warnicke <hagbard@physics.rutgers.edu>
15  * Copyright (c) 2004 by Thomas Anders <thomas.anders [AT] blue-cable.de>
16  *
17  * Wireshark - Network traffic analyzer
18  * By Gerald Combs <gerald@wireshark.org>
19  * Copyright 1999 Gerald Combs
20  *
21  * This program is free software; you can redistribute it and/or
22  * modify it under the terms of the GNU General Public License
23  * as published by the Free Software Foundation; either version 2
24  * of the License, or (at your option) any later version.
25  *
26  * This program is distributed in the hope that it will be useful,
27  * but WITHOUT ANY WARRANTY; without even the implied warranty of
28  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
29  * GNU General Public License for more details.
30  *
31  * You should have received a copy of the GNU General Public License
32  * along with this program; if not, write to the Free Software
33  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
34  */
35
36 #ifdef HAVE_CONFIG_H
37 #include "config.h"
38 #endif
39
40 #include <ctype.h>
41 #include <string.h>
42 #include <epan/packet.h>
43 #include <epan/emem.h>
44 #include <epan/proto.h>
45 #include <epan/prefs.h>
46 #include <epan/conversation.h>
47 #include <epan/tap.h>
48 #include "packet-mgcp.h"
49
50 #define TCP_PORT_MGCP_GATEWAY 2427
51 #define UDP_PORT_MGCP_GATEWAY 2427
52 #define TCP_PORT_MGCP_CALLAGENT 2727
53 #define UDP_PORT_MGCP_CALLAGENT 2727
54
55
56 /* Define the mgcp proto */
57 static int proto_mgcp = -1;
58
59 /* Define many many headers for mgcp */
60 static int hf_mgcp_req = -1;
61 static int hf_mgcp_req_verb = -1;
62 static int hf_mgcp_req_endpoint = -1;
63 static int hf_mgcp_req_frame = -1;
64 static int hf_mgcp_rsp = -1;
65 static int hf_mgcp_rsp_frame = -1;
66 static int hf_mgcp_time = -1;
67 static int hf_mgcp_transid = -1;
68 static int hf_mgcp_version = -1;
69 static int hf_mgcp_rsp_rspcode = -1;
70 static int hf_mgcp_rsp_rspstring = -1;
71 static int hf_mgcp_params = -1;
72 static int hf_mgcp_param_rspack = -1;
73 static int hf_mgcp_param_bearerinfo = -1;
74 static int hf_mgcp_param_callid = -1;
75 static int hf_mgcp_param_connectionid = -1;
76 static int hf_mgcp_param_secondconnectionid = -1;
77 static int hf_mgcp_param_notifiedentity = -1;
78 static int hf_mgcp_param_requestid = -1;
79 static int hf_mgcp_param_localconnoptions = -1;
80 static int hf_mgcp_param_localconnoptions_p = -1;
81 static int hf_mgcp_param_localconnoptions_a = -1;
82 static int hf_mgcp_param_localconnoptions_s = -1;
83 static int hf_mgcp_param_localconnoptions_e = -1;
84 static int hf_mgcp_param_localconnoptions_scrtp = -1;
85 static int hf_mgcp_param_localconnoptions_scrtcp = -1;
86 static int hf_mgcp_param_localconnoptions_b = -1;
87 static int hf_mgcp_param_localconnoptions_esccd = -1;
88 static int hf_mgcp_param_localconnoptions_escci = -1;
89 static int hf_mgcp_param_localconnoptions_dqgi = -1;
90 static int hf_mgcp_param_localconnoptions_dqrd = -1;
91 static int hf_mgcp_param_localconnoptions_dqri = -1;
92 static int hf_mgcp_param_localconnoptions_dqrr = -1;
93 static int hf_mgcp_param_localconnoptions_k = -1;
94 static int hf_mgcp_param_localconnoptions_gc = -1;
95 static int hf_mgcp_param_localconnoptions_fmtp = -1;
96 static int hf_mgcp_param_localconnoptions_nt = -1;
97 static int hf_mgcp_param_localconnoptions_ofmtp = -1;
98 static int hf_mgcp_param_localconnoptions_r = -1;
99 static int hf_mgcp_param_localconnoptions_t = -1;
100 static int hf_mgcp_param_localconnoptions_rcnf = -1;
101 static int hf_mgcp_param_localconnoptions_rdir = -1;
102 static int hf_mgcp_param_localconnoptions_rsh = -1;
103 static int hf_mgcp_param_connectionmode = -1;
104 static int hf_mgcp_param_reqevents = -1;
105 static int hf_mgcp_param_restartmethod = -1;
106 static int hf_mgcp_param_restartdelay = -1;
107 static int hf_mgcp_param_signalreq  = -1;
108 static int hf_mgcp_param_digitmap = -1;
109 static int hf_mgcp_param_observedevent = -1;
110 static int hf_mgcp_param_connectionparam = -1;
111 static int hf_mgcp_param_connectionparam_ps = -1;
112 static int hf_mgcp_param_connectionparam_os = -1;
113 static int hf_mgcp_param_connectionparam_pr = -1;
114 static int hf_mgcp_param_connectionparam_or = -1;
115 static int hf_mgcp_param_connectionparam_pl = -1;
116 static int hf_mgcp_param_connectionparam_ji = -1;
117 static int hf_mgcp_param_connectionparam_la = -1;
118 static int hf_mgcp_param_connectionparam_pcrps = -1;
119 static int hf_mgcp_param_connectionparam_pcros = -1;
120 static int hf_mgcp_param_connectionparam_pcrpl = -1;
121 static int hf_mgcp_param_connectionparam_pcrji = -1;
122 static int hf_mgcp_param_connectionparam_x = -1;
123 static int hf_mgcp_param_reasoncode = -1;
124 static int hf_mgcp_param_eventstates = -1;
125 static int hf_mgcp_param_specificendpoint = -1;
126 static int hf_mgcp_param_secondendpointid = -1;
127 static int hf_mgcp_param_reqinfo = -1;
128 static int hf_mgcp_param_quarantinehandling = -1;
129 static int hf_mgcp_param_detectedevents = -1;
130 static int hf_mgcp_param_capabilities = -1;
131 static int hf_mgcp_param_maxmgcpdatagram = -1;
132 static int hf_mgcp_param_packagelist = -1;
133 static int hf_mgcp_param_extension = -1;
134 static int hf_mgcp_param_extension_critical = -1;
135 static int hf_mgcp_param_invalid = -1;
136 static int hf_mgcp_messagecount = -1;
137 static int hf_mgcp_dup = -1;
138 static int hf_mgcp_req_dup = -1;
139 static int hf_mgcp_rsp_dup = -1;
140
141 static const value_string mgcp_return_code_vals[] = {
142         {000, "Response Acknowledgement"},
143         {100, "The transaction is currently being executed.  An actual completion message will follow on later."},
144         {101, "The transaction has been queued for execution.  An actual completion message will follow later."},
145         {200, "The requested transaction was executed normally."},
146         {250, "The connection was deleted."},
147         {400, "The transaction could not be executed, due to a transient error."},
148         {401, "The phone is already off hook"},
149         {402, "The phone is already on hook"},
150         {403, "The transaction could not be executed, because the endpoint does not have sufficient resources at this time"},
151         {404, "Insufficient bandwidth at this time"},
152         {405, "The transaction could not be executed, because the endpoint is \"restarting\"."},
153         {406, "Transaction time-out.  The transaction did not complete in a reasonable period of time and has been aborted."},
154         {407, "Transaction aborted.  The transaction was aborted by some external action, e.g., a ModifyConnection command aborted by a DeleteConnection command."},
155         {409, "The transaction could not be executed because of internal overload."},
156         {410, "No endpoint available.  A valid \"any of\" wildcard was used, however there was no endpoint available to satisfy the request."},
157         {500, "The transaction could not be executed, because the endpoint is unknown."},
158         {501, "The transaction could not be executed, because the endpoint is not ready."},
159         {502, "The transaction could not be executed, because the endpoint does not have sufficient resources"},
160         {503, "\"All of\" wildcard too complicated."},
161         {504, "Unknown or unsupported command."},
162         {505, "Unsupported RemoteConnectionDescriptor."},
163         {506, "Unable to satisfy both LocalConnectionOptions and RemoteConnectionDescriptor."},
164         {507, "Unsupported functionality."},
165         {508, "Unknown or unsupported quarantine handling."},
166         {509, "Error in RemoteConnectionDescriptor."},
167         {510, "The transaction could not be executed, because a protocol error was detected."},
168         {511, "The transaction could not be executed, because the command contained an unrecognized extension."},
169         {512, "The transaction could not be executed, because the gateway is not equipped to detect one of the requested events."},
170         {513, "The transaction could not be executed, because the gateway is not equipped to generate one of the requested signals."},
171         {514, "The transaction could not be executed, because the gateway cannot send the specified announcement."},
172         {515, "The transaction refers to an incorrect connection-id (may have been already deleted)"},
173         {516, "The transaction refers to an unknown call-id."},
174         {517, "Unsupported or invalid mode."},
175         {518, "Unsupported or unknown package."},
176         {519, "Endpoint does not have a digit map."},
177         {520, "The transaction could not be executed, because the endpoint is 'restarting'."},
178         {521, "Endpoint redirected to another Call Agent."},
179         {522, "No such event or signal."},
180         {523, "Unknown action or illegal combination of actions"},
181         {524, "Internal inconsistency in LocalConnectionOptions"},
182         {525, "Unknown extension in LocalConnectionOptions"},
183         {526, "Insufficient bandwidth"},
184         {527, "Missing RemoteConnectionDescriptor"},
185         {528, "Incompatible protocol version"},
186         {529, "Internal hardware failure"},
187         {530, "CAS signaling protocol error."},
188         {531, "failure of a grouping of trunks (e.g. facility failure)."},
189         {532, "Unsupported value(s) in LocalConnectionOptions."},
190         {533, "Response too large."},
191         {534, "Codec negotiation failure."},
192         {535, "Packetization period not supported"},
193         {536, "Unknown or unsupported RestartMethod"},
194         {537, "Unknown or unsupported digit map extension"},
195         {538, "Event/signal parameter error (e.g., missing, erroneous, unsupported, unknown, etc.)"},
196         {539, "Invalid or unsupported command parameter."},
197         {540, "Per endpoint connection limit exceeded."},
198         {541, "Invalid or unsupported LocalConnectionOptions"},
199         {0,   NULL }
200 };
201
202 /* TODO: add/use when tested/have capture to test with */
203 /*
204 static const value_string mgcp_reason_code_vals[] = {
205         {0,   "Endpoint state is normal"},
206         {900, "Endpoint malfunctioning."},
207         {901, "Endpoint taken out-of-service."},
208         {902, "Loss of lower layer connectivity (e.g., downstream sync)."},
209         {903, "QoS resource reservation was lost."},
210         {904, "Manual intervention."},
211         {905, "Facility failure (e.g., DS-0 failure)."},
212         {0,   NULL }
213 };
214 */
215
216
217 /*
218  * Define the trees for mgcp
219  * We need one for MGCP itself, one for the MGCP paramters and one
220  * for each of the dissected parameters
221  */
222 static int ett_mgcp = -1;
223 static int ett_mgcp_param = -1;
224 static int ett_mgcp_param_connectionparam = -1;
225 static int ett_mgcp_param_localconnectionoptions = -1;
226
227 /*
228  * Define the tap for mgcp
229  */
230 static int mgcp_tap = -1;
231
232 /*
233  * Here are the global variables associated with
234  * the various user definable characteristics of the dissection
235  *
236  * MGCP has two kinds of "agents", gateways and callagents.  Callagents
237  * control gateways in a master/slave sort of arrangement.  Since gateways
238  * and callagents have different well known ports and could both
239  * operate under either udp or tcp we have rather a lot of port info to
240  * specify.
241  *
242  * global_mgcp_raw_text determines whether we are going to display
243  * the raw text of the mgcp message, much like the HTTP dissector does.
244  *
245  */
246 static int global_mgcp_gateway_tcp_port = TCP_PORT_MGCP_GATEWAY;
247 static int global_mgcp_gateway_udp_port = UDP_PORT_MGCP_GATEWAY;
248 static int global_mgcp_callagent_tcp_port = TCP_PORT_MGCP_CALLAGENT;
249 static int global_mgcp_callagent_udp_port = UDP_PORT_MGCP_CALLAGENT;
250 static gboolean global_mgcp_raw_text = FALSE;
251 static gboolean global_mgcp_message_count = FALSE;
252
253 /*
254  * Variables to allow for proper deletion of dissector registration when
255  * the user changes port from the gui.
256  */
257 static int gateway_tcp_port = 0;
258 static int gateway_udp_port = 0;
259 static int callagent_tcp_port = 0;
260 static int callagent_udp_port = 0;
261
262 /* Some basic utility functions that are specific to this dissector */
263 static gboolean is_mgcp_verb(tvbuff_t *tvb, gint offset, gint maxlength, const gchar **verb_name);
264 static gboolean is_mgcp_rspcode(tvbuff_t *tvb, gint offset, gint maxlength);
265 static gint tvb_parse_param(tvbuff_t *tvb, gint offset, gint maxlength, int** hf);
266
267 /*
268  * The various functions that either dissect some
269  * subpart of MGCP.  These aren't really proto dissectors but they
270  * are written in the same style.
271  */
272 static void dissect_mgcp_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
273                                  proto_tree *mgcp_tree, proto_tree *ti);
274 static void dissect_mgcp_firstline(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree);
275 static void dissect_mgcp_params(tvbuff_t *tvb, proto_tree *tree);
276 static void dissect_mgcp_connectionparams(proto_tree *parent_tree, tvbuff_t *tvb,
277                                           gint offset, gint param_type_len,
278                                           gint param_val_len);
279 static void dissect_mgcp_localconnectionoptions(proto_tree *parent_tree, tvbuff_t *tvb,
280                                                 gint offset, gint param_type_len,
281                                                 gint param_val_len);
282                                           
283 static void mgcp_raw_text_add(tvbuff_t *tvb, proto_tree *tree);
284
285 /*
286  * Some functions which should be moved to a library
287  * as I think that people may find them of general usefulness.
288  */
289 static gint tvb_skip_wsp(tvbuff_t* tvb, gint offset, gint maxlength);
290 static gint tvb_find_null_line(tvbuff_t* tvb, gint offset, gint len, gint* next_offset);
291 static gint tvb_find_dot_line(tvbuff_t* tvb, gint offset, gint len, gint* next_offset);
292 static gboolean is_rfc2234_alpha(guint8 c);
293
294 static dissector_handle_t sdp_handle;
295
296
297 /*
298  * Init Hash table stuff
299  */
300
301 typedef struct _mgcp_call_info_key
302 {
303         guint32 transid;
304         conversation_t *conversation;
305 } mgcp_call_info_key;
306
307 static GMemChunk *mgcp_call_info_key_chunk;
308 static GMemChunk *mgcp_call_info_value_chunk;
309 static GHashTable *mgcp_calls;
310
311 /* Compare 2 keys */
312 static gint mgcp_call_equal(gconstpointer k1, gconstpointer k2)
313 {
314         const mgcp_call_info_key* key1 = (const mgcp_call_info_key*) k1;
315         const mgcp_call_info_key* key2 = (const mgcp_call_info_key*) k2;
316
317         return (key1->transid == key2->transid &&
318                 key1->conversation == key2->conversation);
319 }
320
321 /* Calculate a hash key */
322 static guint mgcp_call_hash(gconstpointer k)
323 {
324         const mgcp_call_info_key* key = (const mgcp_call_info_key*) k;
325
326         return key->transid  + key->conversation->index;
327 }
328
329
330
331 /************************************************************************
332  * dissect_mgcp - The dissector for the Media Gateway Control Protocol
333  ************************************************************************/
334 static void dissect_mgcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
335 {
336         gint sectionlen;
337         guint32 num_messages;
338         gint tvb_sectionend,tvb_sectionbegin, tvb_len, tvb_current_len;
339         proto_tree *mgcp_tree, *ti;
340         const gchar *verb_name = "";
341
342         /* Initialize variables */
343         tvb_sectionend = 0;
344         tvb_sectionbegin = tvb_sectionend;
345         sectionlen = 0;
346         tvb_len = tvb_length(tvb);
347         tvb_current_len  = tvb_len;
348         num_messages = 0;
349         mgcp_tree = NULL;
350         ti = NULL;
351
352         /*
353          * Set the columns now, so that they'll be set correctly if we throw
354          * an exception.  We can set them later as well....
355          */
356         if (check_col(pinfo->cinfo, COL_PROTOCOL))
357                 col_add_str(pinfo->cinfo, COL_PROTOCOL, "MGCP");
358         if (check_col(pinfo->cinfo, COL_INFO))
359                 col_clear(pinfo->cinfo, COL_INFO);
360
361         /*
362          * Check to see whether we're really dealing with MGCP by looking
363          * for a valid MGCP verb or response code.  This isn't infallible,
364          * but its cheap and its better than nothing.
365          */
366         if (is_mgcp_verb(tvb,0,tvb_len, &verb_name) || is_mgcp_rspcode(tvb,0,tvb_len))
367         {
368                 /*
369                  * Loop through however many mgcp messages may be stuck in
370                  * this packet using piggybacking
371                  */
372                 do
373                 {
374                         num_messages++;
375                         if (tree)
376                         {
377                                 /* Create our mgcp subtree */
378                                 ti = proto_tree_add_item(tree,proto_mgcp,tvb,0,0, FALSE);
379                                 mgcp_tree = proto_item_add_subtree(ti, ett_mgcp);
380                         }
381
382                         sectionlen = tvb_find_dot_line(tvb, tvb_sectionbegin, -1, &tvb_sectionend);
383                         if (sectionlen != -1)
384                         {
385                                 dissect_mgcp_message(tvb_new_subset(tvb, tvb_sectionbegin,
386                                                                     sectionlen, -1),
387                                                                     pinfo, tree, mgcp_tree,ti);
388                                 tvb_sectionbegin = tvb_sectionend;
389                         }
390                         else
391                         {
392                                 break;
393                         }
394                 } while (tvb_sectionend < tvb_len);
395
396                 if (mgcp_tree)
397                 {
398                         proto_tree_add_uint_hidden(mgcp_tree, hf_mgcp_messagecount, tvb,
399                                                    0 ,0 , num_messages);
400                 }
401
402                 /*
403                  * Add our column information after dissecting SDP
404                  * in order to prevent the column info changing to reflect the SDP
405                  * (when showing message count)
406                  */
407                 tvb_sectionbegin = 0;
408                 if (check_col(pinfo->cinfo, COL_PROTOCOL))
409                 {
410                         if (global_mgcp_message_count == TRUE )
411                         {
412                                 if (num_messages > 1)
413                                 {
414                                         col_add_fstr(pinfo->cinfo, COL_PROTOCOL, "MGCP (%i messages)",num_messages);
415                                 }
416                                 else
417                                 {
418                                         col_add_fstr(pinfo->cinfo, COL_PROTOCOL, "MGCP (%i message)",num_messages);
419                                 }
420                         }
421                 }
422
423                 if (check_col(pinfo->cinfo, COL_INFO))
424                 {
425                         sectionlen = tvb_find_line_end(tvb, tvb_sectionbegin,-1,
426                                                        &tvb_sectionend,FALSE);
427                         col_prepend_fstr(pinfo->cinfo, COL_INFO, "%s",
428                                          tvb_format_text(tvb, tvb_sectionbegin, sectionlen));
429                 }
430         }
431 }
432
433 #define MAX_MGCP_MESSAGES_IN_PACKET 5
434 static mgcp_info_t pi_arr[MAX_MGCP_MESSAGES_IN_PACKET];
435 static int pi_current = 0;
436 static mgcp_info_t *mi;
437
438 /* Dissect an individual MGCP message */
439 static void dissect_mgcp_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
440                                  proto_tree *mgcp_tree, proto_tree *ti)
441 {
442         /* Declare variables */
443         gint sectionlen;
444         gint tvb_sectionend,tvb_sectionbegin, tvb_len, tvb_current_len;
445         tvbuff_t *next_tvb;
446         const gchar *verb_name = "";
447
448         /* Initialise stat info for passing to tap */
449         pi_current++;
450         if (pi_current == MAX_MGCP_MESSAGES_IN_PACKET)
451         {
452                 /* Overwrite info in first struct if run out of space... */
453                 pi_current = 0;
454         }
455         mi = &pi_arr[pi_current];
456
457
458         mi->mgcp_type = MGCP_OTHERS;
459         mi->code[0] = '\0';
460         mi->transid = 0;
461         mi->req_time.secs = 0;
462         mi->req_time.nsecs = 0;
463         mi->is_duplicate = FALSE;
464         mi->request_available = FALSE;
465         mi->req_num = 0;
466         mi->endpointId = NULL;
467         mi->observedEvents = NULL;
468         mi->rspcode = 0;
469         mi->signalReq = NULL;
470         mi->hasDigitMap = FALSE;
471
472         /* Initialize variables */
473         tvb_sectionend = 0;
474         tvb_sectionbegin = tvb_sectionend;
475         sectionlen = 0;
476         tvb_len = tvb_length(tvb);
477         tvb_current_len  = tvb_len;
478
479         /*
480          * Check to see whether we're really dealing with MGCP by looking
481          * for a valid MGCP verb or response code.  This isn't infallible,
482          * but its cheap and its better than nothing.
483          */
484         if (is_mgcp_verb(tvb,0,tvb_len,&verb_name) || is_mgcp_rspcode(tvb,0,tvb_len))
485         {
486                 /* dissect first line */
487                 tvb_sectionbegin = 0;
488                 tvb_current_len = tvb_len;
489                 tvb_sectionend = tvb_sectionbegin;
490                 sectionlen = tvb_find_line_end(tvb,0,-1,&tvb_sectionend,FALSE);
491                 if (sectionlen > 0)
492                 {
493                         dissect_mgcp_firstline(tvb_new_subset(tvb, tvb_sectionbegin,
494                                                sectionlen,-1), pinfo,
495                                                mgcp_tree);
496                 }
497                 tvb_sectionbegin = tvb_sectionend;
498
499                 /* Dissect params */
500                 if (tvb_sectionbegin < tvb_len)
501                 {
502                         sectionlen = tvb_find_null_line(tvb, tvb_sectionbegin, -1,
503                                                         &tvb_sectionend);
504                         if (sectionlen > 0)
505                         {
506                                 dissect_mgcp_params(tvb_new_subset(tvb, tvb_sectionbegin, sectionlen, -1),
507                                                                    mgcp_tree);
508                                 tvb_sectionbegin = tvb_sectionend;
509                         }
510                 }
511
512                 /* Set the mgcp payload length correctly so we don't include any
513                    encapsulated SDP */
514                 sectionlen = tvb_sectionend;
515                 proto_item_set_len(ti,sectionlen);
516
517                 /* Display the raw text of the mgcp message if desired */
518
519                 /* Do we want to display the raw text of our MGCP packet? */
520                 if (global_mgcp_raw_text)
521                 {
522                         if (tree)
523                                 mgcp_raw_text_add(tvb, mgcp_tree);
524                 }
525
526                 /* Dissect sdp payload */
527                 if (tvb_sectionend < tvb_len)
528                 {
529                         next_tvb = tvb_new_subset(tvb, tvb_sectionend, -1, -1);
530                         call_dissector(sdp_handle, next_tvb, pinfo, tree);
531                 }
532         }
533 }
534
535
536 /*
537  * Add the raw text of the message to the dissect tree if appropriate
538  * preferences are specified.
539  */
540 static void mgcp_raw_text_add(tvbuff_t *tvb, proto_tree *tree)
541 {
542         gint tvb_linebegin,tvb_lineend,tvb_len,linelen;
543
544         tvb_linebegin = 0;
545         tvb_len = tvb_length(tvb);
546
547         do
548         {
549                 tvb_find_line_end(tvb,tvb_linebegin,-1,&tvb_lineend,FALSE);
550                 linelen = tvb_lineend - tvb_linebegin;
551                 proto_tree_add_text(tree, tvb, tvb_linebegin, linelen, "%s",
552                                     tvb_format_text(tvb, tvb_linebegin, linelen));
553                 tvb_linebegin = tvb_lineend;
554         } while (tvb_lineend < tvb_len);
555 }
556
557 /* Discard and init any state we've saved */
558 static void mgcp_init_protocol(void)
559 {
560         if (mgcp_calls != NULL)
561         {
562                 g_hash_table_destroy(mgcp_calls);
563                 mgcp_calls = NULL;
564         }
565         if (mgcp_call_info_key_chunk != NULL)
566         {
567                 g_mem_chunk_destroy(mgcp_call_info_key_chunk);
568                 mgcp_call_info_key_chunk = NULL;
569         }
570         if (mgcp_call_info_value_chunk != NULL)
571         {
572                 g_mem_chunk_destroy(mgcp_call_info_value_chunk);
573                 mgcp_call_info_value_chunk = NULL;
574         }
575
576         mgcp_calls = g_hash_table_new(mgcp_call_hash, mgcp_call_equal);
577         mgcp_call_info_key_chunk = g_mem_chunk_new("call_info_key_chunk",
578                                                    sizeof(mgcp_call_info_key),
579                                                    200 * sizeof(mgcp_call_info_key),
580                                                    G_ALLOC_ONLY);
581         mgcp_call_info_value_chunk = g_mem_chunk_new("call_info_value_chunk",
582                                                      sizeof(mgcp_call_t),
583                                                      200 * sizeof(mgcp_call_t),
584                                                      G_ALLOC_ONLY);
585 }
586
587 /* Register all the bits needed with the filtering engine */
588 void proto_register_mgcp(void)
589 {
590     static hf_register_info hf[] =
591     {
592         { &hf_mgcp_req,
593           { "Request", "mgcp.req", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
594             "True if MGCP request", HFILL }},
595         { &hf_mgcp_rsp,
596           { "Response", "mgcp.rsp", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
597             "TRUE if MGCP response", HFILL }},
598         { &hf_mgcp_req_frame,
599           { "Request Frame", "mgcp.reqframe", FT_FRAMENUM, BASE_NONE, NULL, 0,
600             "Request Frame", HFILL }},
601         { &hf_mgcp_rsp_frame,
602           { "Response Frame", "mgcp.rspframe", FT_FRAMENUM, BASE_NONE, NULL, 0,
603             "Response Frame", HFILL }},
604         { &hf_mgcp_time,
605           { "Time from request", "mgcp.time", FT_RELATIVE_TIME, BASE_NONE, NULL, 0,
606             "Timedelta between Request and Response", HFILL }},
607         { &hf_mgcp_req_verb,
608           { "Verb", "mgcp.req.verb", FT_STRING, BASE_DEC, NULL, 0x0,
609             "Name of the verb", HFILL }},
610         { &hf_mgcp_req_endpoint,
611           { "Endpoint", "mgcp.req.endpoint", FT_STRING, BASE_DEC, NULL, 0x0,
612             "Endpoint referenced by the message", HFILL }},
613         { &hf_mgcp_transid,
614           { "Transaction ID", "mgcp.transid", FT_STRING, BASE_DEC, NULL, 0x0,
615             "Transaction ID of this message", HFILL }},
616         { &hf_mgcp_version,
617           { "Version", "mgcp.version", FT_STRING, BASE_DEC, NULL, 0x0,
618             "MGCP Version", HFILL }},
619         { &hf_mgcp_rsp_rspcode,
620           { "Response Code", "mgcp.rsp.rspcode", FT_UINT32, BASE_DEC, VALS(mgcp_return_code_vals), 0x0,
621             "Response Code", HFILL }},
622         { &hf_mgcp_rsp_rspstring,
623           { "Response String", "mgcp.rsp.rspstring", FT_STRING, BASE_DEC, NULL, 0x0,
624             "Response String", HFILL }},
625         { &hf_mgcp_params,
626           { "Parameters", "mgcp.params", FT_NONE, 0, NULL, 0x0,
627             "MGCP parameters", HFILL }},
628         { &hf_mgcp_param_rspack,
629           { "ResponseAck (K)", "mgcp.param.rspack", FT_STRING, BASE_DEC, NULL, 0x0,
630             "Response Ack", HFILL }},
631         { &hf_mgcp_param_bearerinfo,
632           { "BearerInformation (B)", "mgcp.param.bearerinfo", FT_STRING, BASE_DEC, NULL, 0x0,
633             "Bearer Information", HFILL }},
634         { &hf_mgcp_param_callid,
635           { "CallId (C)", "mgcp.param.callid", FT_STRING, BASE_DEC, NULL, 0x0,
636             "Call Id", HFILL }},
637         { &hf_mgcp_param_connectionid,
638           {"ConnectionIdentifier (I)", "mgcp.param.connectionid", FT_STRING, BASE_DEC, NULL, 0x0,
639             "Connection Identifier", HFILL }},
640         { &hf_mgcp_param_secondconnectionid,
641           { "SecondConnectionID (I2)", "mgcp.param.secondconnectionid", FT_STRING, BASE_DEC, NULL, 0x0,
642             "Second Connection Identifier", HFILL }},
643         { &hf_mgcp_param_notifiedentity,
644           { "NotifiedEntity (N)", "mgcp.param.notifiedentity", FT_STRING, BASE_DEC, NULL, 0x0,
645             "Notified Entity", HFILL }},
646         { &hf_mgcp_param_requestid,
647           { "RequestIdentifier (X)", "mgcp.param.requestid", FT_STRING, BASE_DEC, NULL, 0x0,
648             "Request Identifier", HFILL }},
649         { &hf_mgcp_param_localconnoptions,
650           { "LocalConnectionOptions (L)", "mgcp.param.localconnectionoptions", FT_STRING, BASE_DEC, NULL, 0x0,
651             "Local Connection Options", HFILL }},
652         { &hf_mgcp_param_localconnoptions_p,
653           { "Packetization period (p)", "mgcp.param.localconnectionoptions.p", FT_UINT32, BASE_DEC, NULL, 0x0,
654             "Packetization period", HFILL }},
655         { &hf_mgcp_param_localconnoptions_a,
656           { "Codecs (a)", "mgcp.param.localconnectionoptions.a", FT_STRING, BASE_DEC, NULL, 0x0,
657             "Codecs", HFILL }},
658         { &hf_mgcp_param_localconnoptions_s,
659           { "Silence Suppression (s)", "mgcp.param.localconnectionoptions.s", FT_STRING, BASE_DEC, NULL, 0x0,
660             "Silence Suppression", HFILL }},
661         { &hf_mgcp_param_localconnoptions_e,
662           { "Echo Cancellation (e)", "mgcp.param.localconnectionoptions.e", FT_STRING, BASE_DEC, NULL, 0x0,
663             "Echo Cancellation", HFILL }},
664         { &hf_mgcp_param_localconnoptions_scrtp,
665           { "RTP ciphersuite (sc-rtp)", "mgcp.param.localconnectionoptions.scrtp", FT_STRING, BASE_DEC, NULL, 0x0,
666             "RTP ciphersuite", HFILL }},
667         { &hf_mgcp_param_localconnoptions_scrtcp,
668           { "RTCP ciphersuite (sc-rtcp)", "mgcp.param.localconnectionoptions.scrtcp", FT_STRING, BASE_DEC, NULL, 0x0,
669             "RTCP ciphersuite", HFILL }},
670         { &hf_mgcp_param_localconnoptions_b,
671           { "Bandwidth (b)", "mgcp.param.localconnectionoptions.b", FT_STRING, BASE_DEC, NULL, 0x0,
672             "Bandwidth", HFILL }},
673         { &hf_mgcp_param_localconnoptions_esccd,
674           { "Content Destination (es-ccd)", "mgcp.param.localconnectionoptions.esccd", FT_STRING, BASE_DEC, NULL, 0x0,
675             "Content Destination", HFILL }},
676         { &hf_mgcp_param_localconnoptions_escci,
677           { "Content Identifier (es-cci)", "mgcp.param.localconnectionoptions.escci", FT_STRING, BASE_DEC, NULL, 0x0,
678             "Content Identifier", HFILL }},
679         { &hf_mgcp_param_localconnoptions_dqgi,
680           { "D-QoS GateID (dq-gi)", "mgcp.param.localconnectionoptions.dqgi", FT_STRING, BASE_DEC, NULL, 0x0,
681             "D-QoS GateID", HFILL }},
682         { &hf_mgcp_param_localconnoptions_dqrd,
683           { "D-QoS Reserve Destination (dq-rd)", "mgcp.param.localconnectionoptions.dqrd", FT_STRING, BASE_DEC, NULL, 0x0,
684             "D-QoS Reserve Destination", HFILL }},
685         { &hf_mgcp_param_localconnoptions_dqri,
686           { "D-QoS Resource ID (dq-ri)", "mgcp.param.localconnectionoptions.dqri", FT_STRING, BASE_DEC, NULL, 0x0,
687             "D-QoS Resource ID", HFILL }},
688         { &hf_mgcp_param_localconnoptions_dqrr,
689           { "D-QoS Resource Reservation (dq-rr)", "mgcp.param.localconnectionoptions.dqrr", FT_STRING, BASE_DEC, NULL, 0x0,
690             "D-QoS Resource Reservation", HFILL }},
691         { &hf_mgcp_param_localconnoptions_k,
692           { "Encryption Key (k)", "mgcp.param.localconnectionoptions.k", FT_STRING, BASE_DEC, NULL, 0x0,
693             "Encryption Key", HFILL }},
694         { &hf_mgcp_param_localconnoptions_gc,
695           { "Gain Control (gc)", "mgcp.param.localconnectionoptions.gc", FT_UINT32, BASE_DEC, NULL, 0x0,
696             "Gain Control", HFILL }},
697         { &hf_mgcp_param_localconnoptions_fmtp,
698           { "Media Format (fmtp)", "mgcp.param.localconnectionoptions.fmtp", FT_STRING, BASE_DEC, NULL, 0x0,
699             "Media Format", HFILL }},
700         { &hf_mgcp_param_localconnoptions_nt,
701           { "Network Type (nt)", "mgcp.param.localconnectionoptions.nt", FT_STRING, BASE_DEC, NULL, 0x0,
702             "Network Type", HFILL }},
703         { &hf_mgcp_param_localconnoptions_ofmtp,
704           { "Optional Media Format (o-fmtp)", "mgcp.param.localconnectionoptions.ofmtp", FT_STRING, BASE_DEC, NULL, 0x0,
705             "Optional Media Format", HFILL }},
706         { &hf_mgcp_param_localconnoptions_r,
707           { "Resource Reservation (r)", "mgcp.param.localconnectionoptions.r", FT_STRING, BASE_DEC, NULL, 0x0,
708             "Resource Reservation", HFILL }},
709         { &hf_mgcp_param_localconnoptions_t,
710           { "Type of Service (r)", "mgcp.param.localconnectionoptions.t", FT_STRING, BASE_DEC, NULL, 0x0,
711             "Type of Service", HFILL }},
712         { &hf_mgcp_param_localconnoptions_rcnf,
713           { "Reservation Confirmation (r-cnf)", "mgcp.param.localconnectionoptions.rcnf", FT_STRING, BASE_DEC, NULL, 0x0,
714             "Reservation Confirmation", HFILL }},
715         { &hf_mgcp_param_localconnoptions_rdir,
716           { "Reservation Direction (r-dir)", "mgcp.param.localconnectionoptions.rdir", FT_STRING, BASE_DEC, NULL, 0x0,
717             "Reservation Direction", HFILL }},
718         { &hf_mgcp_param_localconnoptions_rsh,
719           { "Resource Sharing (r-sh)", "mgcp.param.localconnectionoptions.rsh", FT_STRING, BASE_DEC, NULL, 0x0,
720             "Resource Sharing", HFILL }},
721         { &hf_mgcp_param_connectionmode,
722           { "ConnectionMode (M)", "mgcp.param.connectionmode", FT_STRING, BASE_DEC, NULL, 0x0,
723             "Connection Mode", HFILL }},
724         { &hf_mgcp_param_reqevents,
725           { "RequestedEvents (R)", "mgcp.param.reqevents", FT_STRING, BASE_DEC, NULL, 0x0,
726             "Requested Events", HFILL }},
727         { &hf_mgcp_param_signalreq,
728           { "SignalRequests (S)", "mgcp.param.signalreq", FT_STRING, BASE_DEC, NULL, 0x0,
729             "Signal Request", HFILL }},
730         { &hf_mgcp_param_restartmethod,
731           { "RestartMethod (RM)", "mgcp.param.restartmethod", FT_STRING, BASE_DEC, NULL, 0x0,
732             "Restart Method", HFILL }},
733         { &hf_mgcp_param_restartdelay,
734           { "RestartDelay (RD)", "mgcp.param.restartdelay", FT_STRING, BASE_DEC, NULL, 0x0,
735             "Restart Delay", HFILL }},
736         { &hf_mgcp_param_digitmap,
737           { "DigitMap (D)", "mgcp.param.digitmap", FT_STRING, BASE_DEC, NULL, 0x0,
738             "Digit Map", HFILL }},
739         { &hf_mgcp_param_observedevent,
740           { "ObservedEvents (O)", "mgcp.param.observedevents", FT_STRING, BASE_DEC, NULL, 0x0,
741             "Observed Events", HFILL }},
742         { &hf_mgcp_param_connectionparam,
743           { "ConnectionParameters (P)", "mgcp.param.connectionparam", FT_STRING, BASE_DEC, NULL, 0x0,
744             "Connection Parameters", HFILL }},
745         { &hf_mgcp_param_connectionparam_ps,
746           { "Packets sent (PS)", "mgcp.param.connectionparam.ps", FT_UINT32, BASE_DEC, NULL, 0x0,
747             "Packets sent (P:PS)", HFILL }},
748         { &hf_mgcp_param_connectionparam_os,
749           { "Octets sent (OS)", "mgcp.param.connectionparam.os", FT_UINT32, BASE_DEC, NULL, 0x0,
750             "Octets sent (P:OS)", HFILL }},
751         { &hf_mgcp_param_connectionparam_pr,
752           { "Packets received (PR)", "mgcp.param.connectionparam.pr", FT_UINT32, BASE_DEC, NULL, 0x0,
753             "Packets received (P:PR)", HFILL }},
754         { &hf_mgcp_param_connectionparam_or,
755           { "Octets received (OR)", "mgcp.param.connectionparam.or", FT_UINT32, BASE_DEC, NULL, 0x0,
756             "Octets received (P:OR)", HFILL }},
757         { &hf_mgcp_param_connectionparam_pl,
758           { "Packets lost (PL)", "mgcp.param.connectionparam.pl", FT_UINT32, BASE_DEC, NULL, 0x0,
759             "Packets lost (P:PL)", HFILL }},
760         { &hf_mgcp_param_connectionparam_ji,
761           { "Jitter (JI)", "mgcp.param.connectionparam.ji", FT_UINT32, BASE_DEC, NULL, 0x0,
762             "Average inter-packet arrival jitter in milliseconds (P:JI)", HFILL }},
763         { &hf_mgcp_param_connectionparam_la,
764           { "Latency (LA)", "mgcp.param.connectionparam.la", FT_UINT32, BASE_DEC, NULL, 0x0,
765             "Average latency in milliseconds (P:LA)", HFILL }},
766         { &hf_mgcp_param_connectionparam_pcrps,
767           { "Remote Packets sent (PC/RPS)", "mgcp.param.connectionparam.pcrps", FT_UINT32, BASE_DEC, NULL, 0x0,
768             "Remote Packets sent (P:PC/RPS)", HFILL }},
769         { &hf_mgcp_param_connectionparam_pcros,
770           { "Remote Octets sent (PC/ROS)", "mgcp.param.connectionparam.pcros", FT_UINT32, BASE_DEC, NULL, 0x0,
771             "Remote Octets sent (P:PC/ROS)", HFILL }},
772         { &hf_mgcp_param_connectionparam_pcrpl,
773           { "Remote Packets lost (PC/RPL)", "mgcp.param.connectionparam.pcrpl", FT_UINT32, BASE_DEC, NULL, 0x0,
774             "Remote Packets lost (P:PC/RPL)", HFILL }},
775         { &hf_mgcp_param_connectionparam_pcrji,
776           { "Remote Jitter (PC/RJI)", "mgcp.param.connectionparam.pcrji", FT_UINT32, BASE_DEC, NULL, 0x0,
777             "Remote Jitter (P:PC/RJI)", HFILL }},
778         { &hf_mgcp_param_connectionparam_x,
779           { "Vendor Extension", "mgcp.param.connectionparam.x", FT_STRING, BASE_DEC, NULL, 0x0,
780             "Vendor Extension (P:X-*)", HFILL }},
781         { &hf_mgcp_param_reasoncode,
782           { "ReasonCode (E)", "mgcp.param.reasoncode", FT_STRING, BASE_DEC, NULL, 0x0,
783             "Reason Code", HFILL }},
784         { &hf_mgcp_param_eventstates,
785           { "EventStates (ES)", "mgcp.param.eventstates", FT_STRING, BASE_DEC, NULL, 0x0,
786             "Event States", HFILL }},
787         { &hf_mgcp_param_specificendpoint,
788           { "SpecificEndpointID (Z)", "mgcp.param.specificendpointid", FT_STRING, BASE_DEC, NULL, 0x0,
789             "Specific Endpoint ID", HFILL }},
790         { &hf_mgcp_param_secondendpointid,
791           { "SecondEndpointID (Z2)", "mgcp.param.secondendpointid", FT_STRING, BASE_DEC, NULL, 0x0,
792             "Second Endpoing ID", HFILL }},
793         { &hf_mgcp_param_reqinfo,
794           { "RequestedInfo (F)", "mgcp.param.reqinfo", FT_STRING, BASE_DEC, NULL, 0x0,
795             "Requested Info", HFILL }},
796         { &hf_mgcp_param_quarantinehandling,
797           { "QuarantineHandling (Q)", "mgcp.param.quarantinehandling", FT_STRING, BASE_DEC, NULL, 0x0,
798             "Quarantine Handling", HFILL }},
799         { &hf_mgcp_param_detectedevents,
800           { "DetectedEvents (T)", "mgcp.param.detectedevents", FT_STRING, BASE_DEC, NULL, 0x0,
801             "Detected Events", HFILL }},
802         { &hf_mgcp_param_capabilities,
803           { "Capabilities (A)", "mgcp.param.capabilities", FT_STRING, BASE_DEC, NULL, 0x0,
804             "Capabilities", HFILL }},
805         { &hf_mgcp_param_maxmgcpdatagram,
806           {"MaxMGCPDatagram (MD)", "mgcp.param.maxmgcpdatagram", FT_STRING, BASE_DEC, NULL, 0x0,
807            "Maximum MGCP Datagram size", HFILL }},
808         { &hf_mgcp_param_packagelist,
809           {"PackageList (PL)", "mgcp.param.packagelist", FT_STRING, BASE_DEC, NULL, 0x0,
810            "Package List", HFILL }},
811         { &hf_mgcp_param_extension,
812           { "Extension Parameter (non-critical)", "mgcp.param.extension", FT_STRING, BASE_DEC, NULL, 0x0,
813             "Extension Parameter", HFILL }},
814         { &hf_mgcp_param_extension_critical,
815           { "Extension Parameter (critical)", "mgcp.param.extensioncritical", FT_STRING, BASE_DEC, NULL, 0x0,
816             "Critical Extension Parameter", HFILL }},
817         { &hf_mgcp_param_invalid,
818           { "Invalid Parameter", "mgcp.param.invalid", FT_STRING, BASE_DEC, NULL, 0x0,
819             "Invalid Parameter", HFILL }},
820         { &hf_mgcp_messagecount,
821           { "MGCP Message Count", "mgcp.messagecount", FT_UINT32, BASE_DEC, NULL, 0x0,
822             "Number of MGCP message in a packet", HFILL }},
823         { &hf_mgcp_dup,
824           { "Duplicate Message", "mgcp.dup", FT_UINT32, BASE_DEC, NULL, 0x0,
825             "Duplicate Message", HFILL }},
826         { &hf_mgcp_req_dup,
827           { "Duplicate Request", "mgcp.req.dup", FT_UINT32, BASE_DEC, NULL, 0x0,
828             "Duplicate Request", HFILL }},
829         { &hf_mgcp_rsp_dup,
830           { "Duplicate Response", "mgcp.rsp.dup", FT_UINT32, BASE_DEC, NULL, 0x0,
831             "Duplicate Response", HFILL }},
832     };
833
834     static gint *ett[] =
835     {
836         &ett_mgcp,
837         &ett_mgcp_param,
838         &ett_mgcp_param_connectionparam,
839         &ett_mgcp_param_localconnectionoptions
840     };
841
842     module_t *mgcp_module;
843
844     /* Register protocol */
845     proto_mgcp = proto_register_protocol("Media Gateway Control Protocol", "MGCP", "mgcp");
846     proto_register_field_array(proto_mgcp, hf, array_length(hf));
847     proto_register_subtree_array(ett, array_length(ett));
848     register_init_routine(&mgcp_init_protocol);
849
850     /* Register our configuration options */
851     mgcp_module = prefs_register_protocol(proto_mgcp, proto_reg_handoff_mgcp);
852
853     prefs_register_uint_preference(mgcp_module, "tcp.gateway_port",
854                                    "MGCP Gateway TCP Port",
855                                    "Set the UDP port for gateway messages "
856                                    "(if other than the default of 2427)",
857                                    10, &global_mgcp_gateway_tcp_port);
858
859     prefs_register_uint_preference(mgcp_module, "udp.gateway_port",
860                                    "MGCP Gateway UDP Port",
861                                    "Set the TCP port for gateway messages "
862                                    "(if other than the default of 2427)",
863                                    10, &global_mgcp_gateway_udp_port);
864
865     prefs_register_uint_preference(mgcp_module, "tcp.callagent_port",
866                                    "MGCP Callagent TCP Port",
867                                    "Set the TCP port for callagent messages "
868                                    "(if other than the default of 2727)",
869                                    10, &global_mgcp_callagent_tcp_port);
870
871     prefs_register_uint_preference(mgcp_module, "udp.callagent_port",
872                                    "MGCP Callagent UDP Port",
873                                    "Set the UDP port for callagent messages "
874                                    "(if other than the default of 2727)",
875                                    10, &global_mgcp_callagent_udp_port);
876
877
878     prefs_register_bool_preference(mgcp_module, "display_raw_text",
879                                   "Display raw text for MGCP message",
880                                   "Specifies that the raw text of the "
881                                   "MGCP message should be displayed "
882                                   "instead of (or in addition to) the "
883                                   "dissection tree",
884                                   &global_mgcp_raw_text);
885
886     prefs_register_obsolete_preference(mgcp_module, "display_dissect_tree");
887
888     prefs_register_bool_preference(mgcp_module, "display_mgcp_message_count",
889                                    "Display the number of MGCP messages",
890                                    "Display the number of MGCP messages "
891                                    "found in a packet in the protocol column.",
892                                    &global_mgcp_message_count);
893
894     mgcp_tap = register_tap("mgcp");
895 }
896
897 /* The registration hand-off routine */
898 void proto_reg_handoff_mgcp(void)
899 {
900         static int mgcp_prefs_initialized = FALSE;
901         static dissector_handle_t mgcp_handle;
902
903         /* Get a handle for the SDP dissector. */
904         sdp_handle = find_dissector("sdp");
905
906         if (!mgcp_prefs_initialized)
907         {
908                 mgcp_handle = create_dissector_handle(dissect_mgcp, proto_mgcp);
909                 mgcp_prefs_initialized = TRUE;
910         }
911         else
912         {
913                 dissector_delete("tcp.port", gateway_tcp_port, mgcp_handle);
914                 dissector_delete("udp.port", gateway_udp_port, mgcp_handle);
915                 dissector_delete("tcp.port", callagent_tcp_port, mgcp_handle);
916                 dissector_delete("udp.port", callagent_udp_port, mgcp_handle);
917         }
918
919         /* Set our port number for future use */
920         gateway_tcp_port = global_mgcp_gateway_tcp_port;
921         gateway_udp_port = global_mgcp_gateway_udp_port;
922
923         callagent_tcp_port = global_mgcp_callagent_tcp_port;
924         callagent_udp_port = global_mgcp_callagent_udp_port;
925
926         dissector_add("tcp.port", global_mgcp_gateway_tcp_port, mgcp_handle);
927         dissector_add("udp.port", global_mgcp_gateway_udp_port, mgcp_handle);
928         dissector_add("tcp.port", global_mgcp_callagent_tcp_port, mgcp_handle);
929         dissector_add("udp.port", global_mgcp_callagent_udp_port, mgcp_handle);
930 }
931
932 /*
933  * is_mgcp_verb - A function for determining whether there is a
934  *                MGCP verb at offset in tvb
935  *
936  * Parameter:
937  * tvb - The tvbuff in which we are looking for an MGCP verb
938  * offset - The offset in tvb at which we are looking for a MGCP verb
939  * maxlength - The maximum distance from offset we may look for the
940  *             characters that make up a MGCP verb.
941  * verb_name - The name for the verb code found (output)
942  *
943  * Return: TRUE if there is an MGCP verb at offset in tvb, otherwise FALSE
944  */
945 static gboolean is_mgcp_verb(tvbuff_t *tvb, gint offset, gint maxlength, const gchar **verb_name)
946 {
947         int returnvalue = FALSE;
948         guint8 word[5];
949
950         /* Read the string into 'word' and see if it looks like the start of a verb */
951         if ((maxlength >= 4) && tvb_get_nstringz0(tvb, offset, sizeof(word), word))
952         {
953                 if (((strncasecmp(word, "EPCF", 4) == 0) && (*verb_name = "EndpointConfiguration|")) ||
954                     ((strncasecmp(word, "CRCX", 4) == 0) && (*verb_name = "CreateConnection")) ||
955                     ((strncasecmp(word, "MDCX", 4) == 0) && (*verb_name = "ModifyConnection")) ||
956                     ((strncasecmp(word, "DLCX", 4) == 0) && (*verb_name = "DeleteConnection")) ||
957                     ((strncasecmp(word, "RQNT", 4) == 0) && (*verb_name = "NotificationRequest")) ||
958                     ((strncasecmp(word, "NTFY", 4) == 0) && (*verb_name = "Notify")) ||
959                     ((strncasecmp(word, "AUEP", 4) == 0) && (*verb_name = "AuditEndpoint")) ||
960                     ((strncasecmp(word, "AUCX", 4) == 0) && (*verb_name = "AuditConnection")) ||
961                     ((strncasecmp(word, "RSIP", 4) == 0) && (*verb_name = "RestartInProgress")) ||
962                     ((strncasecmp(word, "MESG", 4) == 0) && (*verb_name = "Message")) ||
963                     (word[0] == 'X' && is_rfc2234_alpha(word[1]) && is_rfc2234_alpha(word[2]) &&
964                                        is_rfc2234_alpha(word[3]) && (*verb_name = "*Experimental*")))
965                 {
966                         returnvalue = TRUE;
967                 }
968         }
969
970         /* May be whitespace after verb code - anything else is an error.. */
971         if (returnvalue && maxlength >= 5)
972         {
973                 char next = tvb_get_guint8(tvb,4);
974                 if ((next != ' ') && (next != '\t'))
975                 {
976                         returnvalue = FALSE;
977                 }
978         }
979
980         return returnvalue;
981 }
982
983 /*
984  * is_mgcp_rspcode - A function for determining whether something which
985  *                   looks roughly like a MGCP response code (3-digit number)
986  *                   is at 'offset' in tvb
987  *
988  * Parameters:
989  * tvb - The tvbuff in which we are looking for an MGCP response code
990  * offset - The offset in tvb at which we are looking for a MGCP response code
991  * maxlength - The maximum distance from offset we may look for the
992  *             characters that make up a MGCP response code.
993  *
994  * Return: TRUE if there is an MGCP response code at offset in tvb,
995  *         otherwise FALSE
996  */
997 static gboolean is_mgcp_rspcode(tvbuff_t *tvb, gint offset, gint maxlength)
998 {
999         int returnvalue = FALSE;
1000         guint8 word[4];
1001
1002         /* Do 1st 3 characters look like digits? */
1003         if (maxlength >= 3)
1004         {
1005                 tvb_get_nstringz0(tvb, offset, sizeof(word), word);
1006                 if (isdigit(word[0]) && isdigit(word[1]) && isdigit(word[2]))
1007                 {
1008                         returnvalue = TRUE;
1009                 }
1010         }
1011
1012         /* Maybe some white space after the 3rd digit - anything else is an error */
1013         if (returnvalue && maxlength >= 4)
1014         {
1015                 char next = tvb_get_guint8(tvb, 3);
1016                 if ((next != ' ') && (next != '\t'))
1017                 {
1018                         returnvalue = FALSE;
1019                 }
1020         }
1021
1022         return returnvalue;
1023 }
1024
1025 /*
1026  * is_rfc2234_alpha - Indicates whether the character c is an alphabetical
1027  *                    character.  This function is used instead of
1028  *                    isalpha because isalpha may deviate from the rfc2234
1029  *                    definition of ALPHA in some locales.
1030  *
1031  * Parameter:
1032  * c - The character being checked for being an alphabetical character.
1033  *
1034  * Return: TRUE if c is an upper or lower case alphabetical character,
1035  *         FALSE otherwise.
1036  */
1037 static gboolean is_rfc2234_alpha(guint8 c)
1038 {
1039         return ((c <= 'Z' && c >= 'A' ) || (c <= 'z' && c >= 'a'));
1040 }
1041
1042
1043 /*
1044  * tvb_parse_param - Parse the MGCP param into a type and a value.
1045  *
1046  * Parameters:
1047  * tvb - The tvbuff containing the MGCP param we are to parse.
1048  * offset - The offset in tvb at which we will begin looking for a
1049  *          MGCP parameter to parse.
1050  * len - The maximum distance from offset in tvb that we can look for
1051  *       an MGCP parameter to parse.
1052  * hf - The place to write a pointer to the integer representing the
1053  *      header field associated with the MGCP parameter parsed.
1054  *
1055  * Returns: The offset in tvb where the value of the MGCP parameter
1056  *          begins.
1057  */
1058 static gint tvb_parse_param(tvbuff_t* tvb, gint offset, gint len, int** hf)
1059 {
1060         gint returnvalue = -1, tvb_current_offset,counter;
1061         guint8 tempchar, plus_minus;
1062         gchar **buf;
1063
1064         tvb_current_offset = offset;
1065         *hf = NULL;
1066         buf=NULL;
1067
1068         if (len > 0)
1069         {
1070                 tempchar = tvb_get_guint8(tvb,tvb_current_offset);
1071
1072                 switch (tempchar)
1073                 {
1074                         case 'K':
1075                                 if (tvb_get_guint8(tvb,tvb_current_offset+1) != ':')
1076                                 {
1077                                         *hf = &hf_mgcp_param_invalid;
1078                                         break;
1079                                 }
1080                                 *hf = &hf_mgcp_param_rspack;
1081                                 break;
1082                         case 'B':
1083                                 if (tvb_get_guint8(tvb,tvb_current_offset+1) != ':')
1084                                 {
1085                                         *hf = &hf_mgcp_param_invalid;
1086                                         break;
1087                                 }
1088                                 *hf = &hf_mgcp_param_bearerinfo;
1089                                 break;
1090                         case 'C':
1091                                 if (tvb_get_guint8(tvb,tvb_current_offset+1) != ':')
1092                                 {
1093                                         *hf = &hf_mgcp_param_invalid;
1094                                         break;
1095                                 }
1096                                 *hf = &hf_mgcp_param_callid;
1097                                 break;
1098                         case 'I':
1099                                 tvb_current_offset++;
1100                                 if (len > (tvb_current_offset - offset) &&
1101                                    (tempchar = tvb_get_guint8(tvb,tvb_current_offset)) == ':')
1102                                 {
1103                                         *hf = &hf_mgcp_param_connectionid;
1104                                         tvb_current_offset--;
1105                                 }
1106                                 else
1107                                         if (tempchar == '2')
1108                                 {
1109                                         *hf = &hf_mgcp_param_secondconnectionid;
1110                                 }
1111                                 break;
1112                         case 'N':
1113                                 if (tvb_get_guint8(tvb,tvb_current_offset+1) != ':')
1114                                 {
1115                                         *hf = &hf_mgcp_param_invalid;
1116                                         break;
1117                                 }
1118                                 *hf = &hf_mgcp_param_notifiedentity;
1119                                 break;
1120                         case 'X':
1121                                 /* Move past 'X' */
1122                                 tvb_current_offset++;
1123
1124                                 /* X: is RequestIdentifier */
1125                                 if (len > (tvb_current_offset - offset) &&
1126                                    (tempchar = tvb_get_guint8(tvb,tvb_current_offset)) == ':')
1127                                 {
1128                                         *hf = &hf_mgcp_param_requestid;
1129                                         tvb_current_offset--;
1130                                 }
1131
1132                                 /* X+...: or X-....: are vendor extension parameters */
1133                                 else
1134                                 if (len > (tvb_current_offset - offset) &&
1135                                     ((plus_minus = tvb_get_guint8(tvb,tvb_current_offset)) == '-' ||
1136                                      (plus_minus == '+')))
1137                                 {
1138                                         /* Move past + or - */
1139                                         tvb_current_offset++;
1140
1141                                         /* Keep going, through possible vendor param name */
1142                                         for (counter = 1;
1143                                             ((len > (counter + tvb_current_offset-offset)) &&
1144                                             (is_rfc2234_alpha(tempchar = tvb_get_guint8(tvb, tvb_current_offset+counter)) ||
1145                                              isdigit(tempchar))) ;
1146                                              counter++);
1147
1148                                         if (tempchar == ':')
1149                                         {
1150                                                 /* Looks like a valid vendor param name */
1151                                                 tvb_current_offset += counter;
1152                                                 switch (plus_minus)
1153                                                 {
1154                                                         case '+':
1155                                                                 *hf = &hf_mgcp_param_extension_critical;
1156                                                                 break;
1157                                                         case '-':
1158                                                                 *hf = &hf_mgcp_param_extension;
1159                                                                 break;
1160                                                 }
1161                                         }
1162                                 }
1163                                 break;
1164                         case 'L':
1165                                 if (tvb_get_guint8(tvb,tvb_current_offset+1) != ':')
1166                                 {
1167                                         *hf = &hf_mgcp_param_invalid;
1168                                         break;
1169                                 }
1170                                 *hf = &hf_mgcp_param_localconnoptions;
1171                                 break;
1172                         case 'M':
1173                                 tvb_current_offset++;
1174                                 if (len > (tvb_current_offset - offset) &&
1175                                    (tempchar = tvb_get_guint8(tvb,tvb_current_offset)) == ':')
1176                                 {
1177                                         *hf = &hf_mgcp_param_connectionmode;
1178                                         tvb_current_offset--;
1179                                 }
1180                                 else
1181                                 if (tempchar == 'D')
1182                                 {
1183                                         *hf = &hf_mgcp_param_maxmgcpdatagram;
1184                                 }
1185                                 break;
1186                         case 'R':
1187                                 tvb_current_offset++;
1188                                 if (len > (tvb_current_offset - offset) &&
1189                                     (tempchar = tvb_get_guint8(tvb,tvb_current_offset)) == ':')
1190                                 {
1191                                         *hf = &hf_mgcp_param_reqevents;
1192                                         tvb_current_offset--;
1193                                 }
1194                                 else
1195                                 if ( tempchar == 'M')
1196                                 {
1197                                         *hf = &hf_mgcp_param_restartmethod;
1198                                 }
1199                                 else
1200                                 if (tempchar == 'D')
1201                                 {
1202                                         *hf = &hf_mgcp_param_restartdelay;
1203                                 }
1204                                 break;
1205                         case 'S':
1206                                 if (tvb_get_guint8(tvb,tvb_current_offset+1) != ':')
1207                                 {
1208                                         *hf = &hf_mgcp_param_invalid;
1209                                         break;
1210                                 }
1211                                 *hf = &hf_mgcp_param_signalreq;
1212                                 buf = &(mi->signalReq);
1213                                 break;
1214                         case 'D':
1215                                 if (tvb_get_guint8(tvb,tvb_current_offset+1) != ':')
1216                                 {
1217                                         *hf = &hf_mgcp_param_invalid;
1218                                         break;
1219                                 }
1220                                 *hf = &hf_mgcp_param_digitmap;
1221                                 mi->hasDigitMap = TRUE;
1222                                 break;
1223                         case 'O':
1224                                 if (tvb_get_guint8(tvb,tvb_current_offset+1) != ':')
1225                                 {
1226                                         *hf = &hf_mgcp_param_invalid;
1227                                         break;
1228                                 }
1229                                 *hf = &hf_mgcp_param_observedevent;
1230                                 buf = &(mi->observedEvents);
1231                                 break;
1232                         case 'P':
1233                                 tvb_current_offset++;
1234                                 if (len > (tvb_current_offset - offset) &&
1235                                     (tempchar = tvb_get_guint8(tvb,tvb_current_offset)) == ':')
1236                                 {
1237                                         *hf = &hf_mgcp_param_connectionparam;
1238                                         tvb_current_offset--;
1239                                 }
1240                                 else
1241                                 if ( tempchar == 'L')
1242                                 {
1243                                         *hf = &hf_mgcp_param_packagelist;
1244                                 }
1245                                 break;
1246                         case 'E':
1247                                 tvb_current_offset++;
1248                                 if (len > (tvb_current_offset - offset) &&
1249                                     (tempchar = tvb_get_guint8(tvb,tvb_current_offset)) == ':')
1250                                 {
1251                                         *hf = &hf_mgcp_param_reasoncode;
1252                                         tvb_current_offset--;
1253                                 }
1254                                 else
1255                                 if ( tempchar == 'S')
1256                                 {
1257                                         *hf = &hf_mgcp_param_eventstates;
1258                                 }
1259                                 break;
1260                         case 'Z':
1261                                 tvb_current_offset++;
1262                                 if (len > (tvb_current_offset - offset) &&
1263                                     (tempchar = tvb_get_guint8(tvb,tvb_current_offset)) == ':')
1264                                 {
1265                                         *hf = &hf_mgcp_param_specificendpoint;
1266                                         tvb_current_offset--;
1267                                 }
1268                                 else
1269                                 if (tempchar == '2')
1270                                 {
1271                                         *hf = &hf_mgcp_param_secondendpointid;
1272                                 }
1273                                 break;
1274                         case 'F':
1275                                 if (tvb_get_guint8(tvb,tvb_current_offset+1) != ':')
1276                                 {
1277                                         *hf = &hf_mgcp_param_invalid;
1278                                         break;
1279                                 }
1280                                 *hf = &hf_mgcp_param_reqinfo;
1281                                 break;
1282                         case 'Q':
1283                                 if (tvb_get_guint8(tvb,tvb_current_offset+1) != ':')
1284                                 {
1285                                         *hf = &hf_mgcp_param_invalid;
1286                                         break;
1287                                 }
1288                                 *hf = &hf_mgcp_param_quarantinehandling;
1289                                 break;
1290                         case 'T':
1291                                 if (tvb_get_guint8(tvb,tvb_current_offset+1) != ':')
1292                                 {
1293                                         *hf = &hf_mgcp_param_invalid;
1294                                         break;
1295                                 }
1296                                 *hf = &hf_mgcp_param_detectedevents;
1297                                 break;
1298                         case 'A':
1299                                 if (tvb_get_guint8(tvb,tvb_current_offset+1) != ':')
1300                                 {
1301                                         *hf = &hf_mgcp_param_invalid;
1302                                         break;
1303                                 }
1304                                 *hf = &hf_mgcp_param_capabilities;
1305                                 break;
1306
1307                         default:
1308                                 *hf = &hf_mgcp_param_invalid;
1309                                 break;
1310                 }
1311
1312                 /* Move to (hopefully) the colon */
1313                 tvb_current_offset++;
1314
1315                 /* Add a recognised parameter type if we have one */
1316                 if (*hf != NULL && len > (tvb_current_offset - offset) &&
1317                     (tempchar = tvb_get_guint8(tvb,tvb_current_offset)) == ':')
1318                 {
1319                         tvb_current_offset++;
1320                         tvb_current_offset = tvb_skip_wsp(tvb,tvb_current_offset, (len - tvb_current_offset + offset));
1321                         returnvalue = tvb_current_offset;
1322
1323                        /* set the observedEvents or signalReq used in Voip Calls analysis */
1324                        if (buf != NULL) {
1325                                *buf = tvb_get_ephemeral_string(tvb, tvb_current_offset, (len - tvb_current_offset + offset));
1326                        }
1327                 }
1328         }
1329         else
1330         {
1331                 /* Was an empty line */
1332                 *hf = &hf_mgcp_param_invalid;
1333         }
1334
1335         /* For these types, show the whole line */
1336         if ((*hf == &hf_mgcp_param_invalid) ||
1337             (*hf == &hf_mgcp_param_extension) || (*hf == &hf_mgcp_param_extension_critical))
1338         {
1339                 returnvalue = offset;
1340         }
1341
1342         return returnvalue;
1343 }
1344
1345
1346 /*
1347  * dissect_mgcp_firstline - Dissects the firstline of an MGCP message.
1348  *                          Adds the appropriate headers fields to
1349  *                          tree for the dissection of the first line
1350  *                          of an MGCP message.
1351  *
1352  * Parameters:
1353  * tvb - The tvb containing the first line of an MGCP message.  This
1354  *       tvb is presumed to ONLY contain the first line of the MGCP
1355  *       message.
1356  * pinfo - The packet info for the packet.  This is not really used
1357  *         by this function but is passed through so as to retain the
1358  *         style of a dissector.
1359  * tree - The tree from which to hang the structured information parsed
1360  *        from the first line of the MGCP message.
1361  */
1362 static void dissect_mgcp_firstline(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
1363 {
1364         gint tvb_current_offset,tvb_previous_offset,tvb_len,tvb_current_len;
1365         gint tokennum, tokenlen;
1366         char *transid = NULL;
1367         char *code = NULL;
1368         char *endpointId = NULL;
1369         mgcp_type_t mgcp_type = MGCP_OTHERS;
1370         conversation_t* conversation;
1371         mgcp_call_info_key mgcp_call_key;
1372         mgcp_call_info_key *new_mgcp_call_key = NULL;
1373         mgcp_call_t *mgcp_call = NULL;
1374         nstime_t delta;
1375         gint rspcode = 0;
1376         const gchar *verb_description = "";
1377         char code_with_verb[64] = "";  /* To fit "<4-letter-code> (<longest-verb>)" */
1378
1379         static address null_address = { AT_NONE, 0, NULL };
1380         tvb_previous_offset = 0;
1381         tvb_len = tvb_length(tvb);
1382         tvb_current_len = tvb_len;
1383         tvb_current_offset = tvb_previous_offset;
1384         mi->is_duplicate = FALSE;
1385         mi->request_available = FALSE;
1386
1387         if (tree)
1388         {
1389                 tokennum = 0;
1390
1391                 do
1392                 {
1393                         tvb_current_len = tvb_length_remaining(tvb,tvb_previous_offset);
1394                         tvb_current_offset = tvb_find_guint8(tvb, tvb_previous_offset, tvb_current_len, ' ');
1395                         if (tvb_current_offset == -1)
1396                         {
1397                                 tvb_current_offset = tvb_len;
1398                                 tokenlen = tvb_current_len;
1399                         }
1400                         else
1401                         {
1402                                 tokenlen = tvb_current_offset - tvb_previous_offset;
1403                         }
1404                         if (tokennum == 0)
1405                         {
1406                                 if (tokenlen > 4)
1407                                         THROW(ReportedBoundsError);
1408                                 code = tvb_format_text(tvb,tvb_previous_offset,tokenlen);
1409                                 strncpy(mi->code,code,4);
1410                                 mi->code[4] = '\0';
1411                                 if (is_mgcp_verb(tvb,tvb_previous_offset,tvb_current_len,&verb_description))
1412                                 {
1413                                         mgcp_type = MGCP_REQUEST;
1414                                         if (verb_description != NULL)
1415                                         {
1416                                                 /* Can show verb along with code if known */
1417                                                 sprintf(code_with_verb, "%s (%s)", code, verb_description);
1418                                         }
1419
1420                                         proto_tree_add_string_format(tree, hf_mgcp_req_verb, tvb,
1421                                                                      tvb_previous_offset, tokenlen,
1422                                                                      code, "%s",
1423                                                                      strlen(code_with_verb) ? code_with_verb : code);
1424                                 }
1425                                 else
1426                                 if (is_mgcp_rspcode(tvb,tvb_previous_offset,tvb_current_len))
1427                                 {
1428                                         mgcp_type = MGCP_RESPONSE;
1429                                         rspcode = atoi(code);
1430                                         mi->rspcode = rspcode;
1431                                         proto_tree_add_uint(tree,hf_mgcp_rsp_rspcode, tvb,
1432                                                             tvb_previous_offset, tokenlen, rspcode);
1433                                 }
1434                                 else
1435                                 {
1436                                         break;
1437                                 }
1438                         }
1439                         if (tokennum == 1)
1440                         {
1441                                 transid = tvb_format_text(tvb,tvb_previous_offset,tokenlen);
1442                                 /* XXX - what if this isn't a valid text string? */
1443                                 mi->transid = atol(transid);
1444                                 proto_tree_add_string(tree, hf_mgcp_transid, tvb,
1445                                                       tvb_previous_offset, tokenlen, transid);
1446                         }
1447                         if (tokennum == 2)
1448                         {
1449                                 if (mgcp_type == MGCP_REQUEST)
1450                                 {
1451                                         endpointId = tvb_format_text(tvb, tvb_previous_offset,tokenlen);
1452                                         mi->endpointId = ep_strdup(endpointId);
1453                                         proto_tree_add_string(tree,hf_mgcp_req_endpoint, tvb,
1454                                                               tvb_previous_offset, tokenlen, endpointId);
1455                                 }
1456                                 else
1457                                 if (mgcp_type == MGCP_RESPONSE)
1458                                 {
1459                                         if (tvb_current_offset < tvb_len)
1460                                         {
1461                                                 tokenlen = tvb_find_line_end(tvb, tvb_previous_offset,
1462                                                                              -1, &tvb_current_offset, FALSE);
1463                                         }
1464                                         else
1465                                         {
1466                                                 tokenlen = tvb_current_len;
1467                                         }
1468                                         proto_tree_add_string(tree, hf_mgcp_rsp_rspstring, tvb,
1469                                                               tvb_previous_offset, tokenlen,
1470                                                               tvb_format_text(tvb, tvb_previous_offset,
1471                                                               tokenlen));
1472                                         break;
1473                                 }
1474                         }
1475       
1476                         if ((tokennum == 3 && mgcp_type == MGCP_REQUEST))
1477                         {
1478                                 if (tvb_current_offset < tvb_len )
1479                                 {
1480                                         tokenlen = tvb_find_line_end(tvb, tvb_previous_offset,
1481                                                                      -1, &tvb_current_offset,FALSE);
1482                                 }
1483                                 else
1484                                 {
1485                                         tokenlen = tvb_current_len;
1486                                 }
1487                                 proto_tree_add_string(tree,hf_mgcp_version, tvb,
1488                                                       tvb_previous_offset, tokenlen,
1489                                                       tvb_format_text(tvb,tvb_previous_offset,
1490                                                       tokenlen));
1491                                 break;
1492                         }
1493                         if (tvb_current_offset < tvb_len)
1494                         {
1495                                 tvb_previous_offset = tvb_skip_wsp(tvb, tvb_current_offset,
1496                                                                    tvb_current_len);
1497                         }
1498                         tokennum++;
1499                 } while (tvb_current_offset < tvb_len && tvb_previous_offset < tvb_len && tokennum <= 3);
1500
1501                 switch (mgcp_type)
1502                 {
1503                         case MGCP_RESPONSE:
1504                                 proto_tree_add_boolean_hidden(tree, hf_mgcp_rsp, tvb, 0, 0, TRUE);
1505                                 /* Check for MGCP response.  A response must match a call that
1506                                    we've seen, and the response must be sent to the same
1507                                    port and address that the call came from, and must
1508                                    come from the port to which the call was sent.
1509
1510                                    If the transport is connection-oriented (we check, for
1511                                    now, only for "pinfo->ptype" of PT_TCP), we take
1512                                    into account the address from which the call was sent
1513                                    and the address to which the call was sent, because
1514                                    the addresses of the two endpoints should be the same
1515                                    for all calls and replies.
1516
1517                                    If the transport is connectionless, we don't worry
1518                                    about the address to which the call was sent and from
1519                                    which the reply was sent, because there's no
1520                                    guarantee that the reply will come from the address
1521                                    to which the call was sent. */
1522                                 if (pinfo->ptype == PT_TCP)
1523                                 {
1524                                         conversation = find_conversation(pinfo->fd->num, &pinfo->src,
1525                                                                          &pinfo->dst, pinfo->ptype, pinfo->srcport,
1526                                                                          pinfo->destport, 0);
1527                                 }
1528                                 else
1529                                 {
1530                                         /* XXX - can we just use NO_ADDR_B?  Unfortunately,
1531                                          * you currently still have to pass a non-null
1532                                          * pointer for the second address argument even
1533                                          * if you do that.
1534                                          */
1535                                         conversation = find_conversation(pinfo->fd->num, &null_address,
1536                                                                          &pinfo->dst, pinfo->ptype, pinfo->srcport,
1537                                                                          pinfo->destport, 0);
1538                                 }
1539                                 if (conversation != NULL)
1540                                 {
1541                                         /* Look only for matching request, if
1542                                            matching conversation is available. */
1543                                         mgcp_call_key.transid = mi->transid;
1544                                         mgcp_call_key.conversation = conversation;
1545                                         mgcp_call = g_hash_table_lookup(mgcp_calls, &mgcp_call_key);
1546                                         if (mgcp_call)
1547                                         {
1548                                                 /* Indicate the frame to which this is a reply. */
1549                                                 if (mgcp_call->req_num)
1550                                                 {
1551                                                         proto_item* item;
1552                                                         mi->request_available = TRUE;
1553                                                         mgcp_call->responded = TRUE;
1554                                                         mi->req_num = mgcp_call->req_num;
1555                                                         strcpy(mi->code,mgcp_call->code);
1556                                                         item = proto_tree_add_uint_format(tree, hf_mgcp_req_frame,
1557                                                                                           tvb, 0, 0, mgcp_call->req_num,
1558                                                                                           "This is a response to a request in frame %u",
1559                                                                                           mgcp_call->req_num);
1560                                                         PROTO_ITEM_SET_GENERATED(item);
1561                                                         nstime_delta(&delta, &pinfo->fd->abs_ts, &mgcp_call->req_time);
1562                                                         item = proto_tree_add_time(tree, hf_mgcp_time, tvb, 0, 0, &delta);
1563                                                         PROTO_ITEM_SET_GENERATED(item);
1564                                                 }
1565
1566                                                 if (mgcp_call->rsp_num == 0)
1567                                                 {
1568                                                         /* We have not yet seen a response to that call, so
1569                                                            this must be the first response; remember its
1570                                                            frame number. */
1571                                                         mgcp_call->rsp_num = pinfo->fd->num;
1572                                                 }
1573                                                 else
1574                                                 {
1575                                                         /* We have seen a response to this call - but was it
1576                                                            *this* response? (disregard provisional responses) */
1577                                                         if ((mgcp_call->rsp_num != pinfo->fd->num) &&
1578                                                             (mi->rspcode >= 200) &&
1579                                                             (mi->rspcode == mgcp_call->rspcode))
1580                                                         {
1581                                                                 /* No, so it's a duplicate response. Mark it as such. */
1582                                                                 mi->is_duplicate = TRUE;
1583                                                                 if (check_col(pinfo->cinfo, COL_INFO))
1584                                                                 {
1585                                                                         col_append_fstr(pinfo->cinfo, COL_INFO,
1586                                                                                         ", Duplicate Response %u",
1587                                                                                         mi->transid);
1588                                                                 }
1589                                                                 if (tree)
1590                                                                 {
1591                                                                         proto_item* item;
1592                                                                         proto_tree_add_uint_hidden(tree, hf_mgcp_dup, tvb, 0,0, mi->transid);
1593                                                                         item = proto_tree_add_uint(tree, hf_mgcp_rsp_dup,
1594                                                                                                    tvb, 0, 0, mi->transid);
1595                                                                         PROTO_ITEM_SET_GENERATED(item);
1596                                                                 }
1597                                                         }
1598                                                 }
1599                                                 /* Now store the response code (after comparison above) */
1600                                                 mgcp_call->rspcode = mi->rspcode;
1601                                         }
1602                                 }
1603                                 break;
1604                         case MGCP_REQUEST:
1605                                 proto_tree_add_boolean_hidden(tree, hf_mgcp_req, tvb, 0, 0, TRUE);
1606                                 /* Keep track of the address and port whence the call came,
1607                                  * and the port to which the call is being sent, so that
1608                                  * we can match up calls with replies.
1609                                  *
1610                                  * If the transport is connection-oriented (we check, for
1611                                  * now, only for "pinfo->ptype" of PT_TCP), we take
1612                                  * into account the address from which the call was sent
1613                                  * and the address to which the call was sent, because
1614                                  * the addresses of the two endpoints should be the same
1615                                  * for all calls and replies.
1616                                  *
1617                                  * If the transport is connectionless, we don't worry
1618                                  * about the address to which the call was sent and from
1619                                  * which the reply was sent, because there's no
1620                                  * guarantee that the reply will come from the address
1621                                  * to which the call was sent.
1622                                  */
1623                                 if (pinfo->ptype == PT_TCP)
1624                                 {
1625                                         conversation = find_conversation(pinfo->fd->num, &pinfo->src,
1626                                                                          &pinfo->dst, pinfo->ptype, pinfo->srcport,
1627                                                                          pinfo->destport, 0);
1628                                 }
1629                                 else
1630                                 {
1631                                         /*
1632                                          * XXX - can we just use NO_ADDR_B?  Unfortunately,
1633                                          * you currently still have to pass a non-null
1634                                          * pointer for the second address argument even
1635                                          * if you do that.
1636                                          */
1637                                         conversation = find_conversation(pinfo->fd->num, &pinfo->src,
1638                                                                          &null_address, pinfo->ptype, pinfo->srcport,
1639                                                                          pinfo->destport, 0);
1640                                 }
1641                                 if (conversation == NULL)
1642                                 {
1643                                         /* It's not part of any conversation - create a new one. */
1644                                         if (pinfo->ptype == PT_TCP)
1645                                         {
1646                                                 conversation = conversation_new(pinfo->fd->num, &pinfo->src,
1647                                                                                 &pinfo->dst, pinfo->ptype, pinfo->srcport,
1648                                                                                 pinfo->destport, 0);
1649                                         }
1650                                         else
1651                                         {
1652                                                 conversation = conversation_new(pinfo->fd->num, &pinfo->src,
1653                                                                                 &null_address, pinfo->ptype, pinfo->srcport,
1654                                                                                 pinfo->destport, 0);
1655                                         }
1656                                 }
1657
1658                                 /* Prepare the key data */
1659                                 mgcp_call_key.transid = mi->transid;
1660                                 mgcp_call_key.conversation = conversation;
1661
1662                                 /* Look up the request */
1663                                 mgcp_call = g_hash_table_lookup(mgcp_calls, &mgcp_call_key);
1664                                 if (mgcp_call != NULL)
1665                                 {
1666                                         /* We've seen a request with this TRANSID, with the same
1667                                            source and destination, before - but was it
1668                                            *this* request? */
1669                                         if (pinfo->fd->num != mgcp_call->req_num)
1670                                         {
1671                                                 /* No, so it's a duplicate request. Mark it as such. */
1672                                                 mi->is_duplicate = TRUE;
1673                                                 mi->req_num = mgcp_call->req_num;
1674                                                 if (check_col(pinfo->cinfo, COL_INFO))
1675                                                 {
1676                                                         col_append_fstr(pinfo->cinfo, COL_INFO,
1677                                                                         ", Duplicate Request %u",
1678                                                                         mi->transid);
1679                                                 }
1680                                                 if (tree)
1681                                                 {
1682                                                         proto_item* item;
1683                                                         proto_tree_add_uint_hidden(tree, hf_mgcp_dup, tvb, 0,0, mi->transid);
1684                                                         item = proto_tree_add_uint(tree, hf_mgcp_req_dup, tvb, 0,0, mi->transid);
1685                                                         PROTO_ITEM_SET_GENERATED(item);
1686                                                 }
1687                                         }
1688                                 }
1689                                 else
1690                                 {
1691                                         /* Prepare the value data.
1692                                            "req_num" and "rsp_num" are frame numbers;
1693                                            frame numbers are 1-origin, so we use 0
1694                                            to mean "we don't yet know in which frame
1695                                            the reply for this call appears". */
1696                                         new_mgcp_call_key = g_mem_chunk_alloc(mgcp_call_info_key_chunk);
1697                                         *new_mgcp_call_key = mgcp_call_key;
1698                                         mgcp_call = g_mem_chunk_alloc(mgcp_call_info_value_chunk);
1699                                         mgcp_call->req_num = pinfo->fd->num;
1700                                         mgcp_call->rsp_num = 0;
1701                                         mgcp_call->transid = mi->transid;
1702                                         mgcp_call->responded = FALSE;
1703                                         mgcp_call->req_time=pinfo->fd->abs_ts;
1704                                         strcpy(mgcp_call->code,mi->code);
1705
1706                                         /* Store it */
1707                                         g_hash_table_insert(mgcp_calls, new_mgcp_call_key, mgcp_call);
1708                                 }
1709                                 if (mgcp_call && mgcp_call->rsp_num)
1710                                 {
1711                                         proto_item* item = proto_tree_add_uint_format(tree, hf_mgcp_rsp_frame,
1712                                                                                       tvb, 0, 0, mgcp_call->rsp_num,
1713                                                                                       "The response to this request is in frame %u",
1714                                                                                       mgcp_call->rsp_num);
1715                                         PROTO_ITEM_SET_GENERATED(item);
1716                                 }
1717                                 break;
1718                         default:
1719                                 break;
1720                 }
1721
1722                 mi->mgcp_type = mgcp_type;
1723                 if (mgcp_call)
1724                 {
1725                         mi->req_time.secs=mgcp_call->req_time.secs;
1726                         mi->req_time.nsecs=mgcp_call->req_time.nsecs;
1727                 }
1728         }
1729
1730         tap_queue_packet(mgcp_tap, pinfo, mi);
1731 }
1732
1733 /*
1734  * dissect_mgcp_params - Dissects the parameters of an MGCP message.
1735  *                       Adds the appropriate headers fields to
1736  *                       tree for the dissection of the parameters
1737  *                       of an MGCP message.
1738  *
1739  * Parameters:
1740  * tvb - The tvb containing the parameters of an MGCP message.  This
1741  *       tvb is presumed to ONLY contain the part of the MGCP
1742  *       message which contains the MGCP parameters.
1743  * tree - The tree from which to hang the structured information parsed
1744  *        from the parameters of the MGCP message.
1745  */
1746 static void dissect_mgcp_params(tvbuff_t *tvb, proto_tree *tree)
1747 {
1748         int linelen, tokenlen, *my_param;
1749         gint tvb_lineend,tvb_current_len, tvb_linebegin,tvb_len,old_lineend;
1750         gint tvb_tokenbegin;
1751         proto_tree *mgcp_param_ti, *mgcp_param_tree;
1752
1753         tvb_len = tvb_length(tvb);
1754         tvb_linebegin = 0;
1755         tvb_current_len = tvb_length_remaining(tvb,tvb_linebegin);
1756         tvb_lineend = tvb_linebegin;
1757
1758         if (tree)
1759         {
1760                 mgcp_param_ti = proto_tree_add_item(tree, hf_mgcp_params, tvb,
1761                                                     tvb_linebegin, tvb_len, FALSE);
1762                 proto_item_set_text(mgcp_param_ti, "Parameters");
1763                 mgcp_param_tree = proto_item_add_subtree(mgcp_param_ti, ett_mgcp_param);
1764
1765                 /* Parse the parameters */
1766                 while (tvb_lineend < tvb_len)
1767                 {
1768                         old_lineend = tvb_lineend;
1769                         linelen = tvb_find_line_end(tvb, tvb_linebegin, -1,&tvb_lineend,FALSE);
1770                         tvb_tokenbegin = tvb_parse_param(tvb, tvb_linebegin, linelen, &my_param);
1771
1772                         if (my_param)
1773                         {
1774                                 if (*my_param == hf_mgcp_param_connectionparam)
1775                                 {
1776                                         tokenlen = tvb_find_line_end(tvb,tvb_tokenbegin,-1,&tvb_lineend,FALSE);
1777                                         dissect_mgcp_connectionparams(mgcp_param_tree, tvb, tvb_linebegin,
1778                                                                       tvb_tokenbegin - tvb_linebegin, tokenlen);
1779                                 }
1780                                 else
1781                                 if (*my_param == hf_mgcp_param_localconnoptions)
1782                                 {
1783                                         tokenlen = tvb_find_line_end(tvb,tvb_tokenbegin,-1,&tvb_lineend,FALSE);
1784                                         dissect_mgcp_localconnectionoptions(mgcp_param_tree, tvb, tvb_linebegin,
1785                                                                             tvb_tokenbegin - tvb_linebegin, tokenlen);
1786                                 }
1787                                 else
1788                                 {
1789                                         tokenlen = tvb_find_line_end(tvb,tvb_tokenbegin,-1,&tvb_lineend,FALSE);
1790                                         proto_tree_add_string(mgcp_param_tree,*my_param, tvb,
1791                                                               tvb_linebegin, linelen,
1792                                                               tvb_format_text(tvb,tvb_tokenbegin, tokenlen));
1793                                 }
1794                         }
1795
1796                         tvb_linebegin = tvb_lineend;
1797                         /* Its a infinite loop if we didn't advance (or went backwards) */
1798                         if (old_lineend >= tvb_lineend)
1799                         {
1800                                 THROW(ReportedBoundsError);
1801                         }
1802                 }
1803         }
1804 }
1805
1806 /* Dissect the connection params */
1807 static void
1808 dissect_mgcp_connectionparams(proto_tree *parent_tree, tvbuff_t *tvb, gint offset, gint param_type_len, gint param_val_len)
1809 {
1810         proto_tree *tree = parent_tree;
1811         proto_item *item = NULL;
1812
1813         gchar *tokenline = NULL;
1814         gchar **tokens = NULL;
1815         gchar **typval = NULL;
1816         guint i = 0;
1817         guint tokenlen = 0;
1818         int hf_uint = -1;
1819         int hf_string = -1;
1820
1821         if (parent_tree)
1822         {
1823                 item = proto_tree_add_item(parent_tree, hf_mgcp_param_connectionparam, tvb, offset, param_type_len+param_val_len, FALSE);
1824                 tree = proto_item_add_subtree(item, ett_mgcp_param_connectionparam);
1825         }
1826
1827         /* The P: line */
1828         offset += param_type_len; /* skip the P: */
1829         tokenline = tvb_get_ephemeral_string(tvb, offset, param_val_len);
1830
1831         /* Split into type=value pairs separated by comma */
1832         tokens = ep_strsplit(tokenline, ",", -1);
1833         
1834         for (i = 0; tokens[i] != NULL; i++)
1835         {
1836                 tokenlen = strlen(tokens[i]);
1837                 typval = ep_strsplit(tokens[i], "=", 2);
1838                 if ((typval[0] != NULL) && (typval[1] != NULL))
1839                 {
1840                         if (!strcasecmp(g_strstrip(typval[0]), "PS"))
1841                         {
1842                                 hf_uint = hf_mgcp_param_connectionparam_ps;
1843                         }
1844                         else if (!strcasecmp(g_strstrip(typval[0]), "OS"))
1845                         {
1846                                 hf_uint = hf_mgcp_param_connectionparam_os;
1847                         }
1848                         else if (!strcasecmp(g_strstrip(typval[0]), "PR"))
1849                         {
1850                                 hf_uint = hf_mgcp_param_connectionparam_pr;
1851                         }
1852                         else if (!strcasecmp(g_strstrip(typval[0]), "OR"))
1853                         {
1854                                 hf_uint = hf_mgcp_param_connectionparam_or;
1855                         }
1856                         else if (!strcasecmp(g_strstrip(typval[0]), "PL"))
1857                         {
1858                                 hf_uint = hf_mgcp_param_connectionparam_pl;
1859                         }
1860                         else if (!strcasecmp(g_strstrip(typval[0]), "JI"))
1861                         {
1862                                 hf_uint = hf_mgcp_param_connectionparam_ji;
1863                         }
1864                         else if (!strcasecmp(g_strstrip(typval[0]), "LA"))
1865                         {
1866                                 hf_uint = hf_mgcp_param_connectionparam_la;
1867                         }
1868                         else if (!strcasecmp(g_strstrip(typval[0]), "PC/RPS"))
1869                         {
1870                                 hf_uint = hf_mgcp_param_connectionparam_pcrps;
1871                         } else if (!strcasecmp(g_strstrip(typval[0]), "PC/ROS"))
1872                         {
1873                                 hf_uint = hf_mgcp_param_connectionparam_pcros;
1874                         }
1875                         else if (!strcasecmp(g_strstrip(typval[0]), "PC/RPL"))
1876                         {
1877                                 hf_uint = hf_mgcp_param_connectionparam_pcrpl;
1878                         }
1879                         else if (!strcasecmp(g_strstrip(typval[0]), "PC/RJI"))
1880                         {
1881                                 hf_uint = hf_mgcp_param_connectionparam_pcrji;
1882                         }
1883                         else if (!strncasecmp(g_strstrip(typval[0]), "X-", 2))
1884                         {
1885                                 hf_string = hf_mgcp_param_connectionparam_x;
1886                         }
1887                         else
1888                         {
1889                                 hf_uint = -1;
1890                                 hf_string = -1;
1891                         }
1892
1893                         if (tree)
1894                         {
1895                                 if (hf_uint != -1)
1896                                 {
1897                                         proto_tree_add_uint(tree, hf_uint, tvb, offset, tokenlen, atol(typval[1]));
1898                                 }
1899                                 else if (hf_string != -1)
1900                                 {
1901                                         proto_tree_add_string(tree, hf_string, tvb, offset, tokenlen, g_strstrip(typval[1]));
1902                                 }
1903                                 else
1904                                 {
1905                                         proto_tree_add_text(tree, tvb, offset, tokenlen, "Unknown parameter: %s", tokens[i]);
1906                                 }
1907                         }
1908                 }
1909                 else if (tree)
1910                 {
1911                         proto_tree_add_text(tree, tvb, offset, tokenlen, "Malformed parameter: %s", tokens[i]);
1912                 }
1913                 offset += tokenlen + 1; /* 1 extra for the delimiter */
1914         }
1915
1916 }
1917
1918 /* Dissect the local connection option */
1919 static void
1920 dissect_mgcp_localconnectionoptions(proto_tree *parent_tree, tvbuff_t *tvb, gint offset, gint param_type_len, gint param_val_len)
1921 {
1922         proto_tree *tree = parent_tree;
1923         proto_item *item = NULL;
1924
1925         gchar *tokenline = NULL;
1926         gchar **tokens = NULL;
1927         gchar **typval = NULL;
1928         guint i = 0;
1929         guint tokenlen = 0;
1930         int hf_uint = -1;
1931         int hf_string = -1;
1932
1933         if (parent_tree)
1934         {
1935                 item = proto_tree_add_item(parent_tree, hf_mgcp_param_localconnoptions, tvb, offset, param_type_len+param_val_len, FALSE);
1936                 tree = proto_item_add_subtree(item, ett_mgcp_param_localconnectionoptions);
1937         }
1938
1939         /* The L: line */
1940         offset += param_type_len; /* skip the L: */
1941         tokenline = tvb_get_ephemeral_string(tvb, offset, param_val_len);
1942
1943         /* Split into type=value pairs separated by comma */
1944         tokens = ep_strsplit(tokenline, ",", -1);
1945         for (i = 0; tokens[i] != NULL; i++)
1946         {
1947                 hf_uint = -1;
1948                 hf_string = -1;
1949         
1950                 tokenlen = strlen(tokens[i]);
1951                 typval = ep_strsplit(tokens[i], ":", 2);
1952                 if ((typval[0] != NULL) && (typval[1] != NULL))
1953                 {
1954                         if (!strcasecmp(g_strstrip(typval[0]), "p"))
1955                         {
1956                                 hf_uint = hf_mgcp_param_localconnoptions_p;
1957                         }
1958                         else if (!strcasecmp(g_strstrip(typval[0]), "a"))
1959                         {
1960                                 hf_string = hf_mgcp_param_localconnoptions_a;
1961                         }
1962                         else if (!strcasecmp(g_strstrip(typval[0]), "s"))
1963                         {
1964                                 hf_string = hf_mgcp_param_localconnoptions_s;
1965                         }
1966                         else if (!strcasecmp(g_strstrip(typval[0]), "e"))
1967                         {
1968                                 hf_string = hf_mgcp_param_localconnoptions_e;
1969                         }
1970                         else if (!strcasecmp(g_strstrip(typval[0]), "sc-rtp"))
1971                         {
1972                                 hf_string = hf_mgcp_param_localconnoptions_scrtp;
1973                         }
1974                         else if (!strcasecmp(g_strstrip(typval[0]), "sc-rtcp"))
1975                         {
1976                                 hf_string = hf_mgcp_param_localconnoptions_scrtcp;
1977                         }
1978                         else if (!strcasecmp(g_strstrip(typval[0]), "b"))
1979                         {
1980                                 hf_string = hf_mgcp_param_localconnoptions_b;
1981                         }
1982                         else if (!strcasecmp(g_strstrip(typval[0]), "es-ccd"))
1983                         {
1984                                 hf_string = hf_mgcp_param_localconnoptions_esccd;
1985                         }
1986                         else if (!strcasecmp(g_strstrip(typval[0]), "es-cci"))
1987                         {
1988                                 hf_string = hf_mgcp_param_localconnoptions_escci;
1989                         }
1990                         else if (!strcasecmp(g_strstrip(typval[0]), "dq-gi"))
1991                         {
1992                                 hf_string = hf_mgcp_param_localconnoptions_dqgi;
1993                         }
1994                         else if (!strcasecmp(g_strstrip(typval[0]), "dq-rd"))
1995                         {
1996                                 hf_string = hf_mgcp_param_localconnoptions_dqrd;
1997                         }
1998                         else if (!strcasecmp(g_strstrip(typval[0]), "dq-ri"))
1999                         {
2000                                 hf_string = hf_mgcp_param_localconnoptions_dqri;
2001                         }
2002                         else if (!strcasecmp(g_strstrip(typval[0]), "dq-rr"))
2003                         {
2004                                 hf_string = hf_mgcp_param_localconnoptions_dqrr;
2005                         }
2006                         else if (!strcasecmp(g_strstrip(typval[0]), "k"))
2007                         {
2008                                 hf_string = hf_mgcp_param_localconnoptions_k;
2009                         }
2010                         else if (!strcasecmp(g_strstrip(typval[0]), "gc"))
2011                         {
2012                                 hf_uint = hf_mgcp_param_localconnoptions_gc;
2013                         }
2014                         else if (!strcasecmp(g_strstrip(typval[0]), "fmtp"))
2015                         {
2016                                 hf_string = hf_mgcp_param_localconnoptions_fmtp;
2017                         }
2018                         else if (!strcasecmp(g_strstrip(typval[0]), "nt"))
2019                         {
2020                                 hf_string = hf_mgcp_param_localconnoptions_nt;
2021                         }
2022                         else if (!strcasecmp(g_strstrip(typval[0]), "o-fmtp"))
2023                         {
2024                                 hf_string = hf_mgcp_param_localconnoptions_ofmtp;
2025                         }
2026                         else if (!strcasecmp(g_strstrip(typval[0]), "r"))
2027                         {
2028                                 hf_string = hf_mgcp_param_localconnoptions_r;
2029                         }
2030                         else if (!strcasecmp(g_strstrip(typval[0]), "t"))
2031                         {
2032                                 hf_string = hf_mgcp_param_localconnoptions_t;
2033                         }
2034                         else if (!strcasecmp(g_strstrip(typval[0]), "r-cnf"))
2035                         {
2036                                 hf_string = hf_mgcp_param_localconnoptions_rcnf;
2037                         }
2038                         else if (!strcasecmp(g_strstrip(typval[0]), "r-dir"))
2039                         {
2040                                 hf_string = hf_mgcp_param_localconnoptions_rdir;
2041                         }
2042                         else if (!strcasecmp(g_strstrip(typval[0]), "r-sh"))
2043                         {
2044                                 hf_string = hf_mgcp_param_localconnoptions_rsh;
2045                         }
2046                         else
2047                         {
2048                                 hf_uint = -1;
2049                                 hf_string = -1;
2050                         }
2051
2052                         /* Add item */
2053                         if (tree)
2054                         {
2055                                 if (hf_uint != -1)
2056                                 {
2057                                         proto_tree_add_uint(tree, hf_uint, tvb, offset, tokenlen, atol(typval[1]));
2058                                 }
2059                                 else if (hf_string != -1)
2060                                 {
2061                                         proto_tree_add_string(tree, hf_string, tvb, offset, tokenlen, g_strstrip(typval[1]));
2062                                 }
2063                                 else
2064                                 {
2065                                         proto_tree_add_text(tree, tvb, offset, tokenlen, "Unknown parameter: %s", tokens[i]);
2066                                 }
2067                         }
2068                 }
2069                 else if (tree)
2070                 {
2071                         proto_tree_add_text(tree, tvb, offset, tokenlen, "Malformed parameter: %s", tokens[i]);
2072                 }
2073                 offset += tokenlen + 1; /* 1 extra for the delimiter */
2074         }
2075 }
2076
2077
2078
2079 /*
2080  * tvb_skip_wsp - Returns the position in tvb of the first non-whitespace
2081  *                character following offset or offset + maxlength -1 whichever
2082  *                is smaller.
2083  *
2084  * Parameters:
2085  * tvb - The tvbuff in which we are skipping whitespace.
2086  * offset - The offset in tvb from which we begin trying to skip whitespace.
2087  * maxlength - The maximum distance from offset that we may try to skip
2088  * whitespace.
2089  *
2090  * Returns: The position in tvb of the first non-whitespace
2091  *          character following offset or offset + maxlength -1 whichever
2092  *          is smaller.
2093  */
2094 static gint tvb_skip_wsp(tvbuff_t* tvb, gint offset, gint maxlength)
2095 {
2096         gint counter = offset;
2097         gint end = offset + maxlength,tvb_len;
2098         guint8 tempchar;
2099
2100         /* Get the length remaining */
2101         tvb_len = tvb_length(tvb);
2102         end = offset + maxlength;
2103         if (end >= tvb_len)
2104         {
2105                 end = tvb_len;
2106         }
2107
2108         /* Skip past spaces and tabs until run out or meet something else */
2109         for (counter = offset;
2110              counter < end &&
2111               ((tempchar = tvb_get_guint8(tvb,counter)) == ' ' ||
2112               tempchar == '\t');
2113              counter++);
2114
2115         return (counter);
2116 }
2117
2118 /*
2119  * tvb_find_null_line - Returns the length from offset to the first null
2120  *                      line found (a null line is a line that begins
2121  *                      with a CR or LF.  The offset to the first character
2122  *                      after the null line is written into the gint pointed
2123  *                      to by next_offset.
2124  *
2125  * Parameters:
2126  * tvb - The tvbuff in which we are looking for a null line.
2127  * offset - The offset in tvb at which we will begin looking for
2128  *          a null line.
2129  * len - The maximum distance from offset in tvb that we will look for
2130  *       a null line.  If it is -1 we will look to the end of the buffer.
2131  *
2132  * next_offset - The location to write the offset of first character
2133  *               FOLLOWING the null line.
2134  *
2135  * Returns: The length from offset to the first character BEFORE
2136  *          the null line..
2137  */
2138 static gint tvb_find_null_line(tvbuff_t* tvb, gint offset, gint len, gint* next_offset)
2139 {
2140         gint tvb_lineend,tvb_current_len,tvb_linebegin,maxoffset;
2141         guint tempchar;
2142
2143         tvb_linebegin = offset;
2144         tvb_lineend = tvb_linebegin;
2145
2146         /* Simple setup to allow for the traditional -1 search to the end of the tvbuff */
2147         if (len != -1)
2148         {
2149                 tvb_current_len = len;
2150         }
2151         else
2152         {
2153                 tvb_current_len = tvb_length_remaining(tvb,offset);
2154         }
2155
2156         maxoffset = (tvb_current_len - 1) + offset;
2157
2158         /* Loop around until we either find a line begining with a carriage return
2159            or newline character or until we hit the end of the tvbuff. */
2160         do
2161         {
2162                 tvb_linebegin = tvb_lineend;
2163                 tvb_current_len = tvb_length_remaining(tvb,tvb_linebegin);
2164                 tvb_find_line_end(tvb, tvb_linebegin, tvb_current_len, &tvb_lineend,FALSE);
2165                 tempchar = tvb_get_guint8(tvb,tvb_linebegin);
2166         } while (tempchar != '\r' && tempchar != '\n' && tvb_lineend <= maxoffset);
2167
2168
2169         *next_offset = tvb_lineend;
2170
2171         if (tvb_lineend <= maxoffset)
2172         {
2173                 tvb_current_len = tvb_linebegin - offset;
2174         }
2175         else
2176         {
2177                 tvb_current_len = tvb_length_remaining(tvb,offset);
2178         }
2179
2180         return tvb_current_len;
2181 }
2182
2183 /*
2184  * tvb_find_dot_line -  Returns the length from offset to the first line
2185  *                      containing only a dot (.) character.  A line
2186  *                      containing only a dot is used to indicate a
2187  *                      separation between multiple MGCP messages
2188  *                      piggybacked in the same UDP packet.
2189  *
2190  * Parameters:
2191  * tvb - The tvbuff in which we are looking for a dot line.
2192  * offset - The offset in tvb at which we will begin looking for
2193  *          a dot line.
2194  * len - The maximum distance from offset in tvb that we will look for
2195  *       a dot line.  If it is -1 we will look to the end of the buffer.
2196  *
2197  * next_offset - The location to write the offset of first character
2198  *               FOLLOWING the dot line.
2199  *
2200  * Returns: The length from offset to the first character BEFORE
2201  *          the dot line or -1 if the character at offset is a .
2202  *          followed by a newline or a carriage return.
2203  */
2204 static gint tvb_find_dot_line(tvbuff_t* tvb, gint offset, gint len, gint* next_offset)
2205 {
2206         gint tvb_current_offset, tvb_current_len, maxoffset,tvb_len;
2207         guint8 tempchar;
2208         tvb_current_offset = offset;
2209         tvb_current_len = len;
2210         tvb_len = tvb_length(tvb);
2211
2212         if (len == -1)
2213         {
2214                 maxoffset = tvb_len - 1;
2215         }
2216         else
2217         {
2218                 maxoffset = (len - 1) + offset;
2219         }
2220         tvb_current_offset = offset -1;
2221
2222         do
2223         {
2224                 tvb_current_offset = tvb_find_guint8(tvb, tvb_current_offset+1,
2225                                                      tvb_current_len, '.');
2226                 tvb_current_len = maxoffset - tvb_current_offset + 1;
2227
2228                 /* If we didn't find a . then break out of the loop */
2229                 if (tvb_current_offset == -1)
2230                 {
2231                         break;
2232                 }
2233
2234                 /* Do we have and characters following the . ? */
2235                 if (tvb_current_offset < maxoffset)
2236                 {
2237                         tempchar = tvb_get_guint8(tvb,tvb_current_offset+1);
2238                         /* Are the characters that follow the dot a newline or carriage return ? */
2239                         if (tempchar == '\r' || tempchar == '\n')
2240                         {
2241                                 /* Do we have any charaters that proceed the . ? */
2242                                 if (tvb_current_offset == 0)
2243                                 {
2244                                         break;
2245                                 }
2246                                 else
2247                                 {
2248                                         tempchar = tvb_get_guint8(tvb,tvb_current_offset-1);
2249
2250                                         /* Are the characters that follow the dot a newline or a
2251                                            carriage return ? */
2252                                         if (tempchar == '\r' || tempchar == '\n')
2253                                         {
2254                                                 break;
2255                                         }
2256                                 }
2257                         }
2258                 }
2259                 else
2260                 if (tvb_current_offset == maxoffset)
2261                 {
2262                         if (tvb_current_offset == 0)
2263                         {
2264                                 break;
2265                         }
2266                         else
2267                         {
2268                                 tempchar = tvb_get_guint8(tvb,tvb_current_offset-1);
2269                                 if (tempchar == '\r' || tempchar == '\n')
2270                                 {
2271                                         break;
2272                                 }
2273                         }
2274                 }
2275         } while (tvb_current_offset < maxoffset);
2276
2277
2278         /*
2279          * So now we either have the tvb_current_offset of a . in a dot line
2280          * or a tvb_current_offset of -1
2281          */
2282         if (tvb_current_offset == -1)
2283         {
2284                 tvb_current_offset = maxoffset +1;
2285                 *next_offset = maxoffset + 1;
2286         }
2287         else
2288         {
2289                 tvb_find_line_end(tvb,tvb_current_offset,tvb_current_len,next_offset,FALSE);
2290         }
2291
2292         if (tvb_current_offset == offset)
2293         {
2294                 tvb_current_len = -1;
2295         }
2296         else
2297         {
2298                 tvb_current_len = tvb_current_offset - offset;
2299         }
2300
2301         return tvb_current_len;
2302 }
2303