rerun pidl
[metze/wireshark/wip.git] / epan / gcp.c
1 /*
2  * gcp.c
3  * Gateway Control Protocol -- Context Tracking
4  *
5  * $Id$
6  *
7  * Wireshark - Network traffic analyzer
8  * By Gerald Combs <gerald@wireshark.org>
9  * Copyright 1998 Gerald Combs
10  *
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU General Public License
13  * as published by the Free Software Foundation; either version 2
14  * of the License, or (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24  */
25
26  /*
27   * TO DO:
28   *  - handle text-encoded termination wildcards adequtelly
29   *  - avoid persistent tracking of NULL and ALL contexts
30   */
31
32 #include "config.h"
33
34 #include <epan/emem.h>
35
36 #include "gcp.h"
37
38 static wmem_tree_t* msgs = NULL;
39 static wmem_tree_t* trxs = NULL;
40 static wmem_tree_t* ctxs_by_trx = NULL;
41 static wmem_tree_t* ctxs = NULL;
42
43 const value_string gcp_cmd_type[] = {
44     { GCP_CMD_NONE, "NoCommand"},
45     { GCP_CMD_ADD_REQ, "addReq"},
46     { GCP_CMD_MOVE_REQ, "moveReq"},
47     { GCP_CMD_MOD_REQ, "modReq"},
48     { GCP_CMD_SUB_REQ, "subtractReq"},
49     { GCP_CMD_AUDITCAP_REQ, "auditCapRequest"},
50     { GCP_CMD_AUDITVAL_REQ, "auditValueRequest"},
51     { GCP_CMD_NOTIFY_REQ, "notifyReq"},
52     { GCP_CMD_SVCCHG_REQ, "serviceChangeReq"},
53     { GCP_CMD_TOPOLOGY_REQ, "topologyReq"},
54     { GCP_CMD_CTX_ATTR_AUDIT_REQ, "ctxAttrAuditReq"},
55     { GCP_CMD_ADD_REPLY, "addReply"},
56     { GCP_CMD_MOVE_REPLY, "moveReply"},
57     { GCP_CMD_MOD_REPLY, "modReply"},
58     { GCP_CMD_SUB_REPLY, "subtractReply"},
59     { GCP_CMD_AUDITCAP_REPLY, "auditCapReply"},
60     { GCP_CMD_AUDITVAL_REPLY, "auditValReply"},
61     { GCP_CMD_NOTIFY_REPLY, "notifyReply"},
62     { GCP_CMD_SVCCHG_REPLY, "serviceChangeReply"},
63     { GCP_CMD_TOPOLOGY_REPLY, "topologyReply"},
64     { 0, NULL }
65 };
66
67 const value_string gcp_term_types[] = {
68     { GCP_TERM_TYPE_AAL1, "aal1" },
69     { GCP_TERM_TYPE_AAL2, "aal2" },
70     { GCP_TERM_TYPE_AAL1_STRUCT, "aal1struct" },
71     { GCP_TERM_TYPE_IP_RTP, "ipRtp" },
72     { GCP_TERM_TYPE_TDM, "tdm" },
73     { 0, NULL }
74 };
75
76
77 void gcp_init(void) {
78     static gboolean gcp_initialized = FALSE;
79
80     if (gcp_initialized)
81         return;
82
83     msgs = wmem_tree_new_autoreset(wmem_epan_scope(), wmem_file_scope());
84     trxs = wmem_tree_new_autoreset(wmem_epan_scope(), wmem_file_scope());
85     ctxs_by_trx = wmem_tree_new_autoreset(wmem_epan_scope(), wmem_file_scope());
86     ctxs = wmem_tree_new_autoreset(wmem_epan_scope(), wmem_file_scope());
87     gcp_initialized = TRUE;
88 }
89
90 gcp_msg_t* gcp_msg(packet_info* pinfo, int o, gboolean keep_persistent_data) {
91     gcp_msg_t* m;
92     guint32 framenum = (guint32)pinfo->fd->num;
93     guint32 offset = (guint32)o;
94     address* src = &(pinfo->src);
95     address* dst = &(pinfo->dst);
96     address* lo_addr;
97     address* hi_addr;
98
99     if (keep_persistent_data) {
100                 wmem_tree_key_t key[3];
101
102                 key[0].length = 1;
103                 key[0].key = &(framenum);
104                 key[1].length = 1;
105                 key[1].key = &offset;
106                 key[2].length = 0;
107                 key[2].key =NULL;
108
109         if (( m = (gcp_msg_t *)wmem_tree_lookup32_array(msgs,key) )) {
110             m->commited = TRUE;
111             return m;
112         } else {
113             m = wmem_new(wmem_file_scope(), gcp_msg_t);
114             m->framenum = framenum;
115             m->time = pinfo->fd->abs_ts;
116             m->trxs = NULL;
117             m->commited = FALSE;
118
119             wmem_tree_insert32_array(msgs,key,m);
120         }
121     } else {
122         m = wmem_new0(wmem_packet_scope(), gcp_msg_t);
123         m->framenum = framenum;
124         m->trxs = NULL;
125         m->commited = FALSE;
126     }
127
128     if (CMP_ADDRESS(src, dst) < 0)  {
129         lo_addr = src;
130         hi_addr = dst;
131     } else {
132         lo_addr = dst;
133         hi_addr = src;
134     }
135
136     switch(lo_addr->type) {
137         case AT_NONE:
138             m->lo_addr = 0;
139             m->hi_addr = 0;
140             break;
141         case AT_IPv4:
142             memcpy((guint8*)&(m->hi_addr),hi_addr->data,4);
143             memcpy((guint8*)&(m->lo_addr),lo_addr->data,4);
144             break;
145         case AT_SS7PC:
146             m->hi_addr = mtp3_pc_hash((const mtp3_addr_pc_t *)hi_addr->data);
147             m->lo_addr = mtp3_pc_hash((const mtp3_addr_pc_t *)lo_addr->data);
148             break;
149         default:
150             /* XXX: heuristic and error prone */
151             m->hi_addr = g_str_hash(ep_address_to_str(hi_addr));
152             m->lo_addr = g_str_hash(ep_address_to_str(lo_addr));
153         break;
154     }
155
156     return m;
157 }
158
159 gcp_trx_t* gcp_trx(gcp_msg_t* m ,guint32 t_id , gcp_trx_type_t type, gboolean keep_persistent_data) {
160     gcp_trx_t* t = NULL;
161     gcp_trx_msg_t* trxmsg;
162
163     if ( !m ) return NULL;
164
165     if (keep_persistent_data) {
166         if (m->commited) {
167
168             for ( trxmsg = m->trxs; trxmsg; trxmsg = trxmsg->next) {
169                 if (trxmsg->trx && trxmsg->trx->id == t_id) {
170                     return trxmsg->trx;
171                 }
172             }
173             DISSECTOR_ASSERT_NOT_REACHED();
174         } else {
175             wmem_tree_key_t key[4];
176
177             key[0].length = 1;
178             key[0].key = &(m->hi_addr);
179             key[1].length = 1;
180             key[1].key = &(m->lo_addr);
181             key[2].length = 1;
182             key[2].key = &(t_id);
183             key[3].length = 0;
184             key[3].key = NULL;
185
186             trxmsg = wmem_new(wmem_file_scope(), gcp_trx_msg_t);
187             t = (gcp_trx_t *)wmem_tree_lookup32_array(trxs,key);
188
189             if (!t) {
190                 t = wmem_new(wmem_file_scope(), gcp_trx_t);
191                 t->initial = m;
192                 t->id = t_id;
193                 t->type = type;
194                 t->pendings = 0;
195                 t->error = 0;
196                 t->cmds = NULL;
197
198                 wmem_tree_insert32_array(trxs,key,t);
199             }
200
201             /* XXX: request, reply and ack + point to frames where they are */
202             switch ( type ) {
203                 case GCP_TRX_PENDING:
204                     t->pendings++;
205                     break;
206                 default:
207                     break;
208             }
209
210         }
211     } else {
212         t = wmem_new(wmem_packet_scope(), gcp_trx_t);
213         trxmsg = wmem_new(wmem_packet_scope(), gcp_trx_msg_t);
214         t->initial = NULL;
215         t->id = t_id;
216         t->type = type;
217         t->pendings = 0;
218         t->error = 0;
219         t->cmds = NULL;
220     }
221
222     DISSECTOR_ASSERT(trxmsg);
223
224     trxmsg->trx = t;
225     trxmsg->next = NULL;
226     trxmsg->last = trxmsg;
227
228     if (m->trxs) {
229         m->trxs->last = m->trxs->last->next = trxmsg;
230     } else {
231         m->trxs = trxmsg;
232     }
233
234     return t;
235 }
236
237
238 gcp_ctx_t* gcp_ctx(gcp_msg_t* m, gcp_trx_t* t, guint32 c_id, gboolean persistent) {
239     gcp_ctx_t* context = NULL;
240     gcp_ctx_t** context_p = NULL;
241
242     if ( !m || !t ) return NULL;
243
244     if (persistent) {
245
246         wmem_tree_key_t ctx_key[4];
247         wmem_tree_key_t trx_key[4];
248
249         ctx_key[0].length = 1;
250         ctx_key[0].key = &(m->hi_addr);
251         ctx_key[1].length = 1;
252         ctx_key[1].key = &(m->lo_addr);
253         ctx_key[2].length = 1;
254         ctx_key[2].key = &(c_id);
255         ctx_key[3].length = 0;
256         ctx_key[3].key = NULL;
257
258         trx_key[0].length = 1;
259         trx_key[0].key = &(m->hi_addr);
260         trx_key[1].length = 1;
261         trx_key[1].key = &(m->lo_addr);
262         trx_key[2].length = 1;
263         trx_key[2].key = &(t->id);
264         trx_key[3].length = 0;
265         trx_key[3].key = NULL;
266
267         if (m->commited) {
268             if (( context = (gcp_ctx_t *)wmem_tree_lookup32_array(ctxs_by_trx,trx_key) )) {
269                 return context;
270             } if ((context_p = (gcp_ctx_t **)wmem_tree_lookup32_array(ctxs,ctx_key))) {
271                 context = *context_p;
272
273                 do {
274                     if (context->initial->framenum <= m->framenum) {
275                         return context;
276                     }
277                 } while(( context = context->prev ));
278
279                 DISSECTOR_ASSERT(! "a context should exist");
280             }
281         } else {
282             if (c_id == CHOOSE_CONTEXT) {
283                 if (! ( context = (gcp_ctx_t *)wmem_tree_lookup32_array(ctxs_by_trx,trx_key))) {
284                     context = wmem_new(wmem_file_scope(), gcp_ctx_t);
285                     context->initial = m;
286                     context->cmds = NULL;
287                     context->id = c_id;
288                     context->terms.last = &(context->terms);
289                     context->terms.next = NULL;
290                     context->terms.term = NULL;
291
292                     wmem_tree_insert32_array(ctxs_by_trx,trx_key,context);
293                 }
294             } else {
295                 if (( context = (gcp_ctx_t *)wmem_tree_lookup32_array(ctxs_by_trx,trx_key) )) {
296                     if (( context_p = (gcp_ctx_t **)wmem_tree_lookup32_array(ctxs,ctx_key) )) {
297                         if (context != *context_p) {
298                             if(context->id != CHOOSE_CONTEXT) {
299                                 context = wmem_new(wmem_file_scope(), gcp_ctx_t);
300                             }
301                             context->initial = m;
302                             context->id = c_id;
303                             context->cmds = NULL;
304                             context->terms.last = &(context->terms);
305                             context->terms.next = NULL;
306                             context->terms.term = NULL;
307
308                             context->prev = *context_p;
309                             *context_p = context;
310                         }
311                     } else {
312                         context_p = wmem_new(wmem_file_scope(), gcp_ctx_t*);
313                         *context_p = context;
314                         context->initial = m;
315                         context->id = c_id;
316                         wmem_tree_insert32_array(ctxs,ctx_key,context_p);
317                     }
318                 } else if (! ( context_p = (gcp_ctx_t**)wmem_tree_lookup32_array(ctxs,ctx_key) )) {
319                     context = wmem_new(wmem_file_scope(), gcp_ctx_t);
320                     context->initial = m;
321                     context->id = c_id;
322                     context->cmds = NULL;
323                     context->terms.last = &(context->terms);
324                     context->terms.next = NULL;
325                     context->terms.term = NULL;
326
327                     context_p = wmem_new(wmem_file_scope(), gcp_ctx_t*);
328                     *context_p = context;
329                     wmem_tree_insert32_array(ctxs,ctx_key,context_p);
330                 } else {
331                     context = *context_p;
332                 }
333             }
334         }
335     } else {
336         context = wmem_new(wmem_packet_scope(), gcp_ctx_t);
337         context->initial = m;
338         context->cmds = NULL;
339         context->id = c_id;
340         context->terms.last = &(context->terms);
341         context->terms.next = NULL;
342         context->terms.term = NULL;
343     }
344
345     return context;
346 }
347
348 gcp_cmd_t* gcp_cmd(gcp_msg_t* m, gcp_trx_t* t, gcp_ctx_t* c, gcp_cmd_type_t type, guint offset, gboolean persistent) {
349     gcp_cmd_t* cmd;
350     gcp_cmd_msg_t* cmdtrx;
351     gcp_cmd_msg_t* cmdctx;
352
353     if ( !m || !t || !c ) return NULL;
354
355     if (persistent) {
356         if (m->commited) {
357             DISSECTOR_ASSERT(t->cmds != NULL);
358
359             for (cmdctx = t->cmds; cmdctx; cmdctx = cmdctx->next) {
360                 cmd = cmdctx->cmd;
361                 if (cmd->msg == m && cmd->offset == offset) {
362                     return cmd;
363                 }
364             }
365
366             DISSECTOR_ASSERT(!"called for a command that does not exist!");
367
368             return NULL;
369         } else {
370             cmd = wmem_new(wmem_file_scope(), gcp_cmd_t);
371             cmdtrx = wmem_new(wmem_file_scope(), gcp_cmd_msg_t);
372             cmdctx = wmem_new(wmem_file_scope(), gcp_cmd_msg_t);
373         }
374     } else {
375         cmd = wmem_new(wmem_packet_scope(), gcp_cmd_t);
376         cmdtrx = wmem_new(wmem_packet_scope(), gcp_cmd_msg_t);
377         cmdctx = wmem_new(wmem_packet_scope(), gcp_cmd_msg_t);
378     }
379
380     cmd->type = type;
381     cmd->offset = offset;
382     cmd->terms.term = NULL;
383     cmd->terms.next = NULL;
384     cmd->terms.last = &(cmd->terms);
385     cmd->str = NULL;
386     cmd->msg = m;
387     cmd->trx = t;
388     cmd->ctx = c;
389     cmd->error = 0;
390
391     cmdctx->cmd = cmdtrx->cmd = cmd;
392     cmdctx->next =  cmdtrx->next = NULL;
393     cmdctx->last = cmdtrx->last = NULL;
394
395     if (t->cmds) {
396         t->cmds->last->next = cmdtrx;
397         t->cmds->last = cmdtrx;
398     } else {
399         t->cmds = cmdtrx;
400         t->cmds->last = cmdtrx;
401     }
402
403     if (c->cmds) {
404         c->cmds->last->next = cmdctx;
405         c->cmds->last = cmdctx;
406     } else {
407         c->cmds = cmdctx;
408         c->cmds->last = cmdctx;
409     }
410
411     return cmd;
412 }
413
414
415 gcp_term_t* gcp_cmd_add_term(gcp_msg_t* m, gcp_trx_t* tr, gcp_cmd_t* c, gcp_term_t* t, gcp_wildcard_t wildcard, gboolean persistent) {
416     gcp_terms_t* ct;
417     gcp_terms_t* ct2;
418
419     static gcp_term_t all_terms = {"$",(const guint8*)"",1,GCP_TERM_TYPE_UNKNOWN,NULL,NULL,NULL};
420
421     if ( !c ) return NULL;
422
423     if ( wildcard == GCP_WILDCARD_CHOOSE) {
424         return &all_terms;
425     }
426
427     if (persistent) {
428         if ( c->msg->commited ) {
429             if (wildcard == GCP_WILDCARD_ALL) {
430                 for (ct = c->ctx->terms.next; ct; ct = ct->next) {
431                     /* XXX not handling more wilcards in one msg */
432                     if ( ct->term->start == m ) {
433                         return ct->term;
434                     }
435                 }
436                 return NULL;
437             } else {
438                 for (ct = c->ctx->terms.next; ct; ct = ct->next) {
439                     if ( g_str_equal(ct->term->str,t->str) ) {
440                         return ct->term;
441                     }
442                 }
443                 return NULL;
444             }
445         } else {
446
447             for (ct = c->ctx->terms.next; ct; ct = ct->next) {
448                 if ( g_str_equal(ct->term->str,t->str) || ct->term->start == m) {
449                     break;
450                 }
451             }
452
453             if ( ! ct ) {
454
455                 if (wildcard == GCP_WILDCARD_ALL) {
456                     ct = wmem_new(wmem_file_scope(), gcp_terms_t);
457                     ct->next = NULL;
458                     ct->term = wmem_new0(wmem_file_scope(), gcp_term_t);
459
460                     ct->term->start = m;
461                     ct->term->str = "*";
462                     ct->term->buffer = NULL;
463                     ct->term->len = 0;
464
465                     c->terms.last = c->terms.last->next = ct;
466
467                     ct2 = wmem_new0(wmem_file_scope(), gcp_terms_t);
468                     ct2->term = ct->term;
469
470                     c->ctx->terms.last->next = ct2;
471                     c->ctx->terms.last = ct2;
472
473                     return ct->term;
474                 } else {
475                     for (ct = c->ctx->terms.next; ct; ct = ct->next) {
476                         /* XXX not handling more wilcards in one msg */
477                         if ( ct->term->buffer == NULL && tr->cmds->cmd->msg == ct->term->start ) {
478                             ct->term->str = wmem_strdup(wmem_file_scope(), t->str);
479                             ct->term->buffer = (const guint8 *)wmem_memdup(wmem_file_scope(), t->buffer,t->len);
480                             ct->term->len = t->len;
481
482                             ct2 = wmem_new0(wmem_file_scope(), gcp_terms_t);
483                             ct2->term = ct->term;
484
485                             c->terms.last = c->terms.last->next = ct2;
486
487                             return ct->term;
488                         }
489
490                         if  ( g_str_equal(ct->term->str,t->str) ) {
491                             ct2 = wmem_new0(wmem_file_scope(), gcp_terms_t);
492                             ct2->term = ct->term;
493
494                             c->terms.last = c->terms.last->next = ct2;
495
496                             return ct->term;
497                         }
498                     }
499
500                     ct = wmem_new(wmem_file_scope(), gcp_terms_t);
501                     ct->next = NULL;
502                     ct->term = wmem_new0(wmem_file_scope(), gcp_term_t);
503
504                     ct->term->start = m;
505                     ct->term->str = wmem_strdup(wmem_file_scope(), t->str);
506                     ct->term->buffer = (const guint8 *)wmem_memdup(wmem_file_scope(), t->buffer,t->len);
507                     ct->term->len = t->len;
508
509                     ct2 = wmem_new0(wmem_file_scope(), gcp_terms_t);
510                     ct2->term = ct->term;
511
512                     c->terms.last = c->terms.last->next = ct2;
513
514                     ct2 = wmem_new0(wmem_file_scope(), gcp_terms_t);
515                     ct2->term = ct->term;
516
517                     c->ctx->terms.last = c->ctx->terms.last->next = ct2;
518
519                     return ct->term;
520                 }
521             } else {
522                 ct2 = wmem_new0(wmem_file_scope(), gcp_terms_t);
523                 ct2->term = ct->term;
524
525                 c->terms.last = c->terms.last->next = ct2;
526                 return ct->term;
527             }
528
529             DISSECTOR_ASSERT_NOT_REACHED();
530         }
531     } else {
532         ct = wmem_new(wmem_packet_scope(), gcp_terms_t);
533         ct->term = t;
534         ct->next = NULL;
535         c->terms.last = c->terms.last->next = ct;
536
537         return t;
538     }
539
540 }
541
542 const gchar* gcp_cmd_to_str(gcp_cmd_t* c, gboolean persistent) {
543     const gchar* s;
544     gcp_terms_t* term;
545
546     if ( !c ) return "-";
547
548     switch (c->type) {
549         case GCP_CMD_NONE:
550             return "-";
551             break;
552         case GCP_CMD_ADD_REQ:
553             s = "AddReq {";
554             break;
555         case GCP_CMD_MOVE_REQ:
556             s = "MoveReq {";
557             break;
558         case GCP_CMD_MOD_REQ:
559             s = "ModReq {";
560             break;
561         case GCP_CMD_SUB_REQ:
562             s = "SubReq {";
563             break;
564         case GCP_CMD_AUDITCAP_REQ:
565             s = "AuditCapReq {";
566             break;
567         case GCP_CMD_AUDITVAL_REQ:
568             s = "AuditValReq {";
569             break;
570         case GCP_CMD_NOTIFY_REQ:
571             s = "NotifyReq {";
572             break;
573         case GCP_CMD_SVCCHG_REQ:
574             s = "SvcChgReq {";
575             break;
576         case GCP_CMD_TOPOLOGY_REQ:
577             s = "TopologyReq {";
578             break;
579         case GCP_CMD_CTX_ATTR_AUDIT_REQ:
580             s = "CtxAttribAuditReq {";
581             break;
582         case GCP_CMD_ADD_REPLY:
583             s = "AddReply {";
584             break;
585         case GCP_CMD_MOVE_REPLY:
586             s = "MoveReply {";
587             break;
588         case GCP_CMD_MOD_REPLY:
589             s = "ModReply {";
590             break;
591         case GCP_CMD_SUB_REPLY:
592             s = "SubReply {";
593             break;
594         case GCP_CMD_AUDITCAP_REPLY:
595             s = "AuditCapReply {";
596             break;
597         case GCP_CMD_AUDITVAL_REPLY:
598             s = "AuditValReply {";
599             break;
600         case GCP_CMD_NOTIFY_REPLY:
601             s = "NotifyReply {";
602             break;
603         case GCP_CMD_SVCCHG_REPLY:
604             s = "SvcChgReply {";
605             break;
606         case GCP_CMD_TOPOLOGY_REPLY:
607             s = "TopologyReply {";
608             break;
609         case GCP_CMD_REPLY:
610             s = "ActionReply {";
611             break;
612         case GCP_CMD_OTHER_REQ:
613             s = "Request {";
614             break;
615         default:
616             s = "-";
617             break;
618     }
619
620     for (term = c->terms.next; term; term = term->next) {
621         s = wmem_strdup_printf(wmem_packet_scope(), "%s %s",s,term->term->str);
622     }
623
624     if (c->error) {
625         s = wmem_strdup_printf(wmem_packet_scope(), "%s Error=%i",s,c->error);
626     }
627
628     s = wmem_strdup_printf(wmem_packet_scope(), "%s }", s);
629
630     if (persistent) {
631         if (! c->str) c->str = wmem_strdup(wmem_file_scope(), s);
632     } else {
633         c->str = s;
634     }
635
636     return s;
637 }
638
639 static const gchar* gcp_trx_to_str(gcp_msg_t* m, gcp_trx_t* t, gboolean persistent) {
640     gchar* s;
641     gcp_cmd_msg_t* c;
642
643     if ( !m || !t ) return "-";
644
645     s = wmem_strdup_printf(wmem_packet_scope(), "T %x { ",t->id);
646
647     if (t->cmds) {
648         if (t->cmds->cmd->ctx) {
649             s = wmem_strdup_printf(wmem_packet_scope(), "%s C %x {",s,t->cmds->cmd->ctx->id);
650
651             for (c = t->cmds; c; c = c->next) {
652                 if (c->cmd->msg == m) {
653                     s = wmem_strdup_printf(wmem_packet_scope(), "%s %s",s,gcp_cmd_to_str(c->cmd,persistent));
654                 }
655             }
656
657             s = wmem_strdup_printf(wmem_packet_scope(), "%s %s",s,"}");
658         }
659     }
660
661     if (t->error) {
662         s = wmem_strdup_printf(wmem_packet_scope(), "%s Error=%i",s,t->error);
663     }
664
665     return wmem_strdup_printf(wmem_packet_scope(), "%s %s",s,"}");
666 }
667
668 const gchar* gcp_msg_to_str(gcp_msg_t* m, gboolean persistent) {
669     gcp_trx_msg_t* t;
670     const gchar* s = "";
671
672     if ( !m ) return "-";
673
674     for (t = m->trxs; t; t = t->next) {
675         s = wmem_strdup_printf(wmem_packet_scope(), "%s %s",s,gcp_trx_to_str(m,t->trx, persistent));
676     }
677
678     return s;
679 }
680
681 typedef struct _gcp_ctxs_t {
682     struct _gcp_ctx_t* ctx;
683     struct _gcp_ctxs_t* next;
684 } gcp_ctxs_t;
685
686 /*static const gchar* trx_types[] = {"None","Req","Reply","Pending","Ack"};*/
687
688 void gcp_analyze_msg(proto_tree* gcp_tree, packet_info* pinfo, tvbuff_t* gcp_tvb, gcp_msg_t* m, gcp_hf_ett_t* ids, expert_field* command_err) {
689     gcp_trx_msg_t* t;
690     gcp_ctxs_t contexts = {NULL,NULL};
691     gcp_ctxs_t* ctx_node;
692     gcp_cmd_msg_t* c;
693
694
695     for (t = m->trxs; t; t = t->next) {
696         for (c = t->trx->cmds; c; c = c->next) {
697             gcp_ctx_t* ctx = c->cmd->ctx;
698
699             for (ctx_node = contexts.next; ctx_node; ctx_node = ctx_node->next) {
700                 if (ctx_node->ctx->id == ctx->id) {
701                     break;
702                 }
703             }
704
705             if (! ctx_node) {
706                 ctx_node = wmem_new(wmem_packet_scope(), gcp_ctxs_t);
707                 ctx_node->ctx = ctx;
708                 ctx_node->next = contexts.next;
709                 contexts.next = ctx_node;
710             }
711         }
712     }
713
714     for (ctx_node = contexts.next; ctx_node; ctx_node = ctx_node->next) {
715         gcp_ctx_t* ctx = ctx_node->ctx;
716         proto_item* ctx_item = proto_tree_add_uint(gcp_tree,ids->hf.ctx,gcp_tvb,0,0,ctx->id);
717         proto_tree* ctx_tree = proto_item_add_subtree(ctx_item,ids->ett.ctx);
718         gcp_terms_t *ctx_term;
719
720         PROTO_ITEM_SET_GENERATED(ctx_item);
721
722         if (ctx->cmds) {
723             proto_item* history_item = proto_tree_add_text(ctx_tree,gcp_tvb,0,0,"[ Command History ]");
724             proto_tree* history_tree = proto_item_add_subtree(history_item,ids->ett.ctx_cmds);
725
726             for (c = ctx->cmds; c; c = c->next) {
727                 proto_item* cmd_item = proto_tree_add_uint(history_tree,ids->hf.ctx_cmd,gcp_tvb,0,0,c->cmd->msg->framenum);
728                 if (c->cmd->str) proto_item_append_text(cmd_item,"  %s ",c->cmd->str);
729                 PROTO_ITEM_SET_GENERATED(cmd_item);
730                 if (c->cmd->error) {
731                     expert_add_info(pinfo, cmd_item, command_err);
732                 }
733             }
734         }
735
736         if (( ctx_term = ctx->terms.next )) {
737             proto_item* terms_item = proto_tree_add_text(ctx_tree,gcp_tvb,0,0,"[ Terminations Used ]");
738             proto_tree* terms_tree = proto_item_add_subtree(terms_item,ids->ett.ctx_terms);
739
740             for (; ctx_term; ctx_term = ctx_term->next ) {
741                 if ( ctx_term->term && ctx_term->term->str) {
742                     proto_item* pi = proto_tree_add_string(terms_tree,ids->hf.ctx_term,gcp_tvb,0,0,ctx_term->term->str);
743                     proto_tree* term_tree = proto_item_add_subtree(pi,ids->ett.ctx_term);
744
745                     PROTO_ITEM_SET_GENERATED(pi);
746
747                     if (ctx_term->term->type) {
748                         pi = proto_tree_add_uint(term_tree,ids->hf.ctx_term_type,gcp_tvb,0,0,ctx_term->term->type);
749                         PROTO_ITEM_SET_GENERATED(pi);
750                     }
751
752                     if (ctx_term->term->bir) {
753                         pi = proto_tree_add_string(term_tree,ids->hf.ctx_term_bir,gcp_tvb,0,0,ctx_term->term->bir);
754                         PROTO_ITEM_SET_GENERATED(pi);
755                     }
756
757                     if (ctx_term->term->nsap) {
758                         pi = proto_tree_add_string(term_tree,ids->hf.ctx_term_nsap,gcp_tvb,0,0,ctx_term->term->nsap);
759                         PROTO_ITEM_SET_GENERATED(pi);
760                     }
761
762                     if (ctx_term->term->bir && ctx_term->term->nsap) {
763                         gchar* tmp_key = wmem_strdup_printf(wmem_packet_scope(), "%s:%s",ctx_term->term->nsap,ctx_term->term->bir);
764                         gchar* key = g_ascii_strdown(tmp_key, -1);
765                         alcap_tree_from_bearer_key(term_tree, gcp_tvb, pinfo, key);
766                         g_free(key);
767                     }
768                 }
769             }
770         }
771     }
772 }