From Jakub Zawadzki via bug #4289: (Fix for) Frame arrival times (pcap)
[obnox/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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, 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 #ifdef HAVE_CONFIG_H
33 # include "config.h"
34 #endif
35
36 #include "gcp.h"
37
38 static emem_tree_t* msgs = NULL;
39 static emem_tree_t* trxs = NULL;
40 static emem_tree_t* ctxs_by_trx = NULL;
41 static emem_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 = se_tree_create(EMEM_TREE_TYPE_RED_BLACK, "gcp_msgs");
84   trxs = se_tree_create(EMEM_TREE_TYPE_RED_BLACK, "gcp_trxs");
85   ctxs_by_trx = se_tree_create(EMEM_TREE_TYPE_RED_BLACK, "gcp_ctxs_by_trx");
86   ctxs = se_tree_create(EMEM_TREE_TYPE_RED_BLACK, "gcp_ctxs");
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
100     if (keep_persistent_data) {
101                 emem_tree_key_t key[] = {
102                         {1,&(framenum)},
103                         {1,&offset},
104                         {0,NULL},
105                 };
106
107         if (( m = se_tree_lookup32_array(msgs,key) )) {
108             m->commited = TRUE;
109                         return m;
110         } else {
111             m = se_alloc(sizeof(gcp_msg_t));
112             m->framenum = framenum;
113             m->time = pinfo->fd->abs_ts;
114             m->trxs = NULL;
115             m->commited = FALSE;
116
117             se_tree_insert32_array(msgs,key,m);
118         }
119     } else {
120         m = ep_new0(gcp_msg_t);
121         m->framenum = framenum;
122         m->trxs = NULL;
123         m->commited = FALSE;
124     }
125
126         if (CMP_ADDRESS(src, dst) < 0)  {
127                 lo_addr = src;
128                 hi_addr = dst;
129         } else {
130                 lo_addr = dst;
131                 hi_addr = src;
132         }
133
134         switch(lo_addr->type) {
135                 case AT_NONE:
136                         m->lo_addr = 0;
137                         m->hi_addr = 0;
138                         break;
139                 case AT_IPv4:
140                         memcpy((guint8*)&(m->hi_addr),hi_addr->data,4);
141                         memcpy((guint8*)&(m->lo_addr),lo_addr->data,4);
142                         break;
143                 case AT_SS7PC:
144                         m->hi_addr = mtp3_pc_hash((const mtp3_addr_pc_t *)hi_addr->data);
145                         m->lo_addr = mtp3_pc_hash((const mtp3_addr_pc_t *)lo_addr->data);
146                         break;
147                 default:
148                         /* XXX: heuristic and error prone */
149                         m->hi_addr = g_str_hash(ep_address_to_str(hi_addr));
150                         m->lo_addr = g_str_hash(ep_address_to_str(lo_addr));
151                         break;
152         }
153
154     return m;
155 }
156
157 gcp_trx_t* gcp_trx(gcp_msg_t* m ,guint32 t_id , gcp_trx_type_t type, gboolean keep_persistent_data) {
158     gcp_trx_t* t = NULL;
159     gcp_trx_msg_t* trxmsg;
160
161     if ( !m ) return NULL;
162
163     if (keep_persistent_data) {
164         if (m->commited) {
165
166             for ( trxmsg = m->trxs; trxmsg; trxmsg = trxmsg->next) {
167                 if (trxmsg->trx && trxmsg->trx->id == t_id) {
168                     return trxmsg->trx;
169                 }
170             }
171             DISSECTOR_ASSERT_NOT_REACHED();
172         } else {
173                         emem_tree_key_t key[] = {
174                                 {1,&(m->hi_addr)},
175                                 {1,&(m->lo_addr)},
176                                 {1,&(t_id)},
177                                 {0,NULL}
178                         };
179
180             trxmsg = se_alloc(sizeof(gcp_trx_msg_t));
181             t = se_tree_lookup32_array(trxs,key);
182
183             if (!t) {
184                 t = se_alloc(sizeof(gcp_trx_t));
185                 t->initial = m;
186                 t->id = t_id;
187                 t->type = type;
188                 t->pendings = 0;
189                 t->error = 0;
190                 t->cmds = NULL;
191
192                 se_tree_insert32_array(trxs,key,t);
193             }
194
195             /* XXX: request, reply and ack + point to frames where they are */
196             switch ( type ) {
197                 case GCP_TRX_PENDING:
198                     t->pendings++;
199                     break;
200                 default:
201                     break;
202             }
203
204         }
205     } else {
206         t = ep_new(gcp_trx_t);
207         trxmsg = ep_new(gcp_trx_msg_t);
208         t->initial = NULL;
209         t->id = t_id;
210         t->type = type;
211         t->pendings = 0;
212         t->error = 0;
213         t->cmds = NULL;
214     }
215
216     DISSECTOR_ASSERT(trxmsg);
217
218     trxmsg->trx = t;
219     trxmsg->next = NULL;
220     trxmsg->last = trxmsg;
221
222     if (m->trxs) {
223         m->trxs->last = m->trxs->last->next = trxmsg;
224     } else {
225         m->trxs = trxmsg;
226     }
227
228     return t;
229 }
230
231
232 gcp_ctx_t* gcp_ctx(gcp_msg_t* m, gcp_trx_t* t, guint32 c_id, gboolean persistent) {
233     gcp_ctx_t* context = NULL;
234     gcp_ctx_t** context_p = NULL;
235
236     if ( !m || !t ) return NULL;
237
238     if (persistent) {
239
240                 emem_tree_key_t ctx_key[] = {
241                 {1,&(m->hi_addr)},
242                 {1,&(m->lo_addr)},
243                 {1,&(c_id)},
244                 {0,NULL}
245                 };
246
247                 emem_tree_key_t trx_key[] = {
248                 {1,&(m->hi_addr)},
249                 {1,&(m->lo_addr)},
250                 {1,&(t->id)},
251                 {0,NULL}
252                 };
253
254         if (m->commited) {
255             if (( context = se_tree_lookup32_array(ctxs_by_trx,trx_key) )) {
256                 return context;
257             } if ((context_p = se_tree_lookup32_array(ctxs,ctx_key))) {
258                 context = *context_p;
259
260                 do {
261                     if (context->initial->framenum <= m->framenum) {
262                         return context;
263                     }
264                 } while(( context = context->prev ));
265
266                 DISSECTOR_ASSERT(! "a context should exist");
267             }
268         } else {
269             if (c_id == CHOOSE_CONTEXT) {
270                 if (! ( context = se_tree_lookup32_array(ctxs_by_trx,trx_key))) {
271                     context = se_alloc(sizeof(gcp_ctx_t));
272                     context->initial = m;
273                     context->cmds = NULL;
274                     context->id = c_id;
275                     context->terms.last = &(context->terms);
276                     context->terms.next = NULL;
277                     context->terms.term = NULL;
278
279                     se_tree_insert32_array(ctxs_by_trx,trx_key,context);
280                 }
281             } else {
282                 if (( context = se_tree_lookup32_array(ctxs_by_trx,trx_key) )) {
283                     if (( context_p = se_tree_lookup32_array(ctxs,ctx_key) )) {
284                         if (context != *context_p) {
285                             context = se_alloc(sizeof(gcp_ctx_t));
286                             context->initial = m;
287                             context->id = c_id;
288                             context->cmds = NULL;
289                             context->terms.last = &(context->terms);
290                             context->terms.next = NULL;
291                             context->terms.term = NULL;
292
293                             context->prev = *context_p;
294                             *context_p = context;
295                         }
296                     } else {
297                         context_p = se_alloc(sizeof(void*));
298                         *context_p = context;
299                         context->initial = m;
300                         context->id = c_id;
301                         se_tree_insert32_array(ctxs,ctx_key,context_p);
302                     }
303                 } else if (! ( context_p = se_tree_lookup32_array(ctxs,ctx_key) )) {
304                     context = se_alloc(sizeof(gcp_ctx_t));
305                     context->initial = m;
306                     context->id = c_id;
307                     context->cmds = NULL;
308                     context->terms.last = &(context->terms);
309                     context->terms.next = NULL;
310                     context->terms.term = NULL;
311
312                     context_p = se_alloc(sizeof(void*));
313                     *context_p = context;
314                     se_tree_insert32_array(ctxs,ctx_key,context_p);
315                 } else {
316                     context = *context_p;
317                 }
318             }
319         }
320     } else {
321         context = ep_new(gcp_ctx_t);
322         context->initial = m;
323         context->cmds = NULL;
324         context->id = c_id;
325         context->terms.last = &(context->terms);
326         context->terms.next = NULL;
327         context->terms.term = NULL;
328     }
329
330     return context;
331 }
332
333 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) {
334     gcp_cmd_t* cmd;
335     gcp_cmd_msg_t* cmdtrx;
336     gcp_cmd_msg_t* cmdctx;
337
338     if ( !m || !t || !c) return NULL;
339
340     if (persistent) {
341         if (m->commited) {
342             DISSECTOR_ASSERT(t->cmds != NULL);
343
344             for (cmdctx = t->cmds; cmdctx; cmdctx = cmdctx->next) {
345                 cmd = cmdctx->cmd;
346                 if (cmd->msg == m && cmd->offset == offset) {
347                     return cmd;
348                 }
349             }
350
351             DISSECTOR_ASSERT(!"called for a command that does not exist!");
352
353             return NULL;
354         } else {
355             cmd = se_alloc(sizeof(gcp_cmd_t));
356             cmdtrx = se_alloc(sizeof(gcp_cmd_msg_t));
357             cmdctx = se_alloc(sizeof(gcp_cmd_msg_t));
358         }
359     } else {
360         cmd = ep_new(gcp_cmd_t);
361         cmdtrx = ep_new(gcp_cmd_msg_t);
362         cmdctx = ep_new(gcp_cmd_msg_t);
363     }
364
365     cmd->type = type;
366     cmd->offset = offset;
367     cmd->terms.term = NULL;
368     cmd->terms.next = NULL;
369     cmd->terms.last = &(cmd->terms);
370     cmd->str = NULL;
371     cmd->msg = m;
372     cmd->trx = t;
373     cmd->ctx = c;
374     cmd->error = 0;
375
376     cmdctx->cmd = cmdtrx->cmd = cmd;
377     cmdctx->next =  cmdtrx->next = NULL;
378     cmdctx->last = cmdtrx->last = NULL;
379
380     if (t->cmds) {
381         t->cmds->last->next = cmdtrx;
382         t->cmds->last = cmdtrx;
383     } else {
384         t->cmds = cmdtrx;
385         t->cmds->last = cmdtrx;
386     }
387
388     if (c->cmds) {
389         c->cmds->last->next = cmdctx;
390         c->cmds->last = cmdctx;
391     } else {
392         c->cmds = cmdctx;
393         c->cmds->last = cmdctx;
394     }
395
396     return cmd;
397 }
398
399
400 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) {
401     gcp_terms_t* ct;
402     gcp_terms_t* ct2;
403
404     static gcp_term_t all_terms = {"$",(guint8*)"",1,GCP_TERM_TYPE_UNKNOWN,NULL,NULL,NULL};
405
406     if ( !c ) return NULL;
407
408     if ( wildcard == GCP_WILDCARD_CHOOSE) {
409         return &all_terms;
410     }
411
412     if (persistent) {
413         if ( c->msg->commited ) {
414             if (wildcard == GCP_WILDCARD_ALL) {
415                 for (ct = c->ctx->terms.next; ct; ct = ct->next) {
416                     /* XXX not handling more wilcards in one msg */
417                     if ( ct->term->start == m ) {
418                         return ct->term;
419                     }
420                 }
421                 return NULL;
422             } else {
423                 for (ct = c->ctx->terms.next; ct; ct = ct->next) {
424                     if ( g_str_equal(ct->term->str,t->str) ) {
425                         return ct->term;
426                     }
427                 }
428                 return NULL;
429             }
430         } else {
431
432             for (ct = c->ctx->terms.next; ct; ct = ct->next) {
433                 if ( g_str_equal(ct->term->str,t->str) || ct->term->start == m) {
434                     break;
435                 }
436             }
437
438             if ( ! ct ) {
439
440                 if (wildcard == GCP_WILDCARD_ALL) {
441                     ct = se_alloc(sizeof(gcp_terms_t));
442                     ct->next = NULL;
443                     ct->term = se_alloc0(sizeof(gcp_term_t));
444
445                     ct->term->start = m;
446                     ct->term->str = "*";
447                     ct->term->buffer = NULL;
448                     ct->term->len = 0;
449
450                     c->terms.last = c->terms.last->next = ct;
451
452                     ct2 = se_alloc0(sizeof(gcp_terms_t));
453                     ct2->term = ct->term;
454
455                     c->ctx->terms.last->next = ct2;
456                     c->ctx->terms.last = ct2;
457
458                     return ct->term;
459                 } else {
460                     for (ct = c->ctx->terms.next; ct; ct = ct->next) {
461                         /* XXX not handling more wilcards in one msg */
462                         if ( ct->term->buffer == NULL && tr->cmds->cmd->msg == ct->term->start ) {
463                             ct->term->str = se_strdup(t->str);
464                             ct->term->buffer = se_memdup(t->buffer,t->len);
465                             ct->term->len = t->len;
466
467                             ct2 = se_alloc0(sizeof(gcp_terms_t));
468                             ct2->term = ct->term;
469
470                             c->terms.last = c->terms.last->next = ct2;
471
472                             return ct->term;
473                         }
474
475                         if  ( g_str_equal(ct->term->str,t->str) ) {
476                             ct2 = se_alloc0(sizeof(gcp_terms_t));
477                             ct2->term = ct->term;
478
479                             c->terms.last = c->terms.last->next = ct2;
480
481                             return ct->term;
482                         }
483                     }
484
485                     ct = se_alloc(sizeof(gcp_terms_t));
486                     ct->next = NULL;
487                     ct->term = se_alloc0(sizeof(gcp_term_t));
488
489                     ct->term->start = m;
490                     ct->term->str = se_strdup(t->str);
491                     ct->term->buffer = se_memdup(t->buffer,t->len);
492                     ct->term->len = t->len;
493
494                     ct2 = se_alloc0(sizeof(gcp_terms_t));
495                     ct2->term = ct->term;
496
497                     c->terms.last = c->terms.last->next = ct2;
498
499                     ct2 = se_alloc0(sizeof(gcp_terms_t));
500                     ct2->term = ct->term;
501
502                     c->ctx->terms.last = c->ctx->terms.last->next = ct2;
503
504                     return ct->term;
505                 }
506             } else {
507                 ct2 = se_alloc0(sizeof(gcp_terms_t));
508                 ct2->term = ct->term;
509
510                 c->terms.last = c->terms.last->next = ct2;
511                 return ct->term;
512             }
513
514             DISSECTOR_ASSERT_NOT_REACHED();
515             return NULL;
516         }
517     } else {
518         ct = ep_new(gcp_terms_t);
519         ct->term = t;
520         ct->next = NULL;
521         c->terms.last = c->terms.last->next = ct;
522
523         return t;
524     }
525
526 }
527
528 gchar* gcp_cmd_to_str(gcp_cmd_t* c, gboolean persistent) {
529     gchar* s = "-";
530     gcp_terms_t* term;
531
532     if ( !c ) return "-";
533
534     switch (c->type) {
535         case GCP_CMD_NONE:
536             return "-";
537             break;
538         case GCP_CMD_ADD_REQ:
539             s = "AddReq {";
540             break;
541         case GCP_CMD_MOVE_REQ:
542             s = "MoveReq {";
543             break;
544         case GCP_CMD_MOD_REQ:
545             s = "ModReq {";
546             break;
547         case GCP_CMD_SUB_REQ:
548             s = "SubReq {";
549             break;
550         case GCP_CMD_AUDITCAP_REQ:
551             s = "AuditCapReq {";
552             break;
553         case GCP_CMD_AUDITVAL_REQ:
554             s = "AuditValReq {";
555             break;
556         case GCP_CMD_NOTIFY_REQ:
557             s = "NotifyReq {";
558             break;
559         case GCP_CMD_SVCCHG_REQ:
560             s = "SvcChgReq {";
561             break;
562         case GCP_CMD_TOPOLOGY_REQ:
563             s = "TopologyReq {";
564             break;
565         case GCP_CMD_CTX_ATTR_AUDIT_REQ:
566             s = "CtxAttribAuditReq {";
567             break;
568         case GCP_CMD_ADD_REPLY:
569             s = "AddReply {";
570             break;
571         case GCP_CMD_MOVE_REPLY:
572             s = "MoveReply {";
573             break;
574         case GCP_CMD_MOD_REPLY:
575             s = "ModReply {";
576             break;
577         case GCP_CMD_SUB_REPLY:
578             s = "SubReply {";
579             break;
580         case GCP_CMD_AUDITCAP_REPLY:
581             s = "AuditCapReply {";
582             break;
583         case GCP_CMD_AUDITVAL_REPLY:
584             s = "AuditValReply {";
585             break;
586         case GCP_CMD_NOTIFY_REPLY:
587             s = "NotifyReply {";
588             break;
589         case GCP_CMD_SVCCHG_REPLY:
590             s = "SvcChgReply {";
591             break;
592         case GCP_CMD_TOPOLOGY_REPLY:
593             s = "TopologyReply {";
594             break;
595         case GCP_CMD_REPLY:
596             s = "ActionReply {";
597             break;
598         case GCP_CMD_OTHER_REQ:
599             s = "Request {";
600             break;
601
602     }
603
604     for (term = c->terms.next; term; term = term->next) {
605         s = ep_strdup_printf("%s %s",s,term->term->str);
606     };
607
608     if (c->error) {
609         s = ep_strdup_printf("%s Error=%i",s,c->error);
610     }
611
612         s = ep_strdup_printf("%s }", s);
613
614         if (persistent) {
615                 if (! c->str) c->str = se_strdup(s);
616         } else {
617                 c->str = s;
618         }
619
620     return s;
621 }
622
623 static gchar* gcp_trx_to_str(gcp_msg_t* m, gcp_trx_t* t, gboolean persistent) {
624     gchar* s;
625     gcp_cmd_msg_t* c;
626
627     if ( !m || !t ) return "-";
628
629         s = ep_strdup_printf("T %x { ",t->id);
630
631     if (t->cmds) {
632         if (t->cmds->cmd->ctx) {
633             s = ep_strdup_printf("%s C %x {",s,t->cmds->cmd->ctx->id);
634
635             for (c = t->cmds; c; c = c->next) {
636                 if (c->cmd->msg == m)
637                     s = ep_strdup_printf("%s %s",s,gcp_cmd_to_str(c->cmd,persistent));
638             }
639
640             s = ep_strdup_printf("%s %s",s,"}");
641         }
642     }
643
644     if (t->error) {
645         s = ep_strdup_printf("%s Error=%i",s,t->error);
646     }
647
648     return ep_strdup_printf("%s %s",s,"}");
649 }
650
651 gchar* gcp_msg_to_str(gcp_msg_t* m, gboolean persistent) {
652     gcp_trx_msg_t* t;
653     gchar* s = "";
654
655     if ( !m ) return "-";
656
657     for (t = m->trxs; t; t = t->next) {
658         s = ep_strdup_printf("%s %s",s,gcp_trx_to_str(m,t->trx, persistent));
659     };
660
661     return s;
662 }
663
664 typedef struct _gcp_ctxs_t {
665     struct _gcp_ctx_t* ctx;
666     struct _gcp_ctxs_t* next;
667 } gcp_ctxs_t;
668
669 /*static const gchar* trx_types[] = {"None","Req","Reply","Pending","Ack"};*/
670
671 void gcp_analyze_msg(proto_tree* gcp_tree, tvbuff_t* gcp_tvb, gcp_msg_t* m, gcp_hf_ett_t* ids) {
672     gcp_trx_msg_t* t;
673     gcp_ctxs_t contexts = {NULL,NULL};
674     gcp_ctxs_t* ctx_node;
675     gcp_cmd_msg_t* c;
676
677
678     for (t = m->trxs; t; t = t->next) {
679         for (c = t->trx->cmds; c; c = c->next) {
680             gcp_ctx_t* ctx = c->cmd->ctx;
681
682             for (ctx_node = contexts.next; ctx_node; ctx_node = ctx_node->next) {
683                 if (ctx_node->ctx->id == ctx->id) {
684                     break;
685                 }
686             }
687
688             if (! ctx_node) {
689                 ctx_node = ep_new(gcp_ctxs_t);
690                 ctx_node->ctx = ctx;
691                 ctx_node->next = contexts.next;
692                 contexts.next = ctx_node;
693             }
694
695         }
696     }
697
698     for (ctx_node = contexts.next; ctx_node; ctx_node = ctx_node->next) {
699         gcp_ctx_t* ctx = ctx_node->ctx;
700         proto_item* ctx_item = proto_tree_add_uint(gcp_tree,ids->hf.ctx,gcp_tvb,0,0,ctx->id);
701         proto_tree* ctx_tree = proto_item_add_subtree(ctx_item,ids->ett.ctx);
702         gcp_terms_t *ctx_term;
703
704         PROTO_ITEM_SET_GENERATED(ctx_item);
705
706         if (ctx->cmds) {
707             proto_item* history_item = proto_tree_add_text(ctx_tree,gcp_tvb,0,0,"[ Command History ]");
708             proto_tree* history_tree = proto_item_add_subtree(history_item,ids->ett.ctx_cmds);
709
710             for (c = ctx->cmds; c; c = c->next) {
711                 proto_item* cmd_item = proto_tree_add_uint(history_tree,ids->hf.ctx_cmd,gcp_tvb,0,0,c->cmd->msg->framenum);
712                 if (c->cmd->str) proto_item_append_text(cmd_item,"  %s ",c->cmd->str);
713                 PROTO_ITEM_SET_GENERATED(cmd_item);
714                 if (c->cmd->error) {
715                     proto_item_set_expert_flags(cmd_item, PI_RESPONSE_CODE, PI_WARN);
716                 }
717             }
718         }
719
720         if (( ctx_term = ctx->terms.next )) {
721             proto_item* terms_item = proto_tree_add_text(ctx_tree,gcp_tvb,0,0,"[ Terminations Used ]");
722             proto_tree* terms_tree = proto_item_add_subtree(terms_item,ids->ett.ctx_terms);
723
724             for (; ctx_term; ctx_term = ctx_term->next ) {
725                 if ( ctx_term->term && ctx_term->term->str) {
726                     proto_item* pi = proto_tree_add_string(terms_tree,ids->hf.ctx_term,gcp_tvb,0,0,ctx_term->term->str);
727                     proto_tree* term_tree = proto_item_add_subtree(pi,ids->ett.ctx_term);
728
729                     PROTO_ITEM_SET_GENERATED(pi);
730
731                     if (ctx_term->term->type) {
732                         pi = proto_tree_add_uint(term_tree,ids->hf.ctx_term_type,gcp_tvb,0,0,ctx_term->term->type);
733                         PROTO_ITEM_SET_GENERATED(pi);
734                     }
735
736                     if (ctx_term->term->bir) {
737                         pi = proto_tree_add_string(term_tree,ids->hf.ctx_term_bir,gcp_tvb,0,0,ctx_term->term->bir);
738                         PROTO_ITEM_SET_GENERATED(pi);
739                     }
740
741                     if (ctx_term->term->nsap) {
742                         pi = proto_tree_add_string(term_tree,ids->hf.ctx_term_nsap,gcp_tvb,0,0,ctx_term->term->nsap);
743                         PROTO_ITEM_SET_GENERATED(pi);
744                     }
745
746                     if (ctx_term->term->bir && ctx_term->term->nsap) {
747                         gchar* key = ep_strdup_printf("%s:%s",ctx_term->term->nsap,ctx_term->term->bir);
748                         g_strdown(key);
749                         alcap_tree_from_bearer_key(term_tree, gcp_tvb, key);
750                     }
751                 }
752             }
753         }
754     }
755 }