For response packets, add a field showing the number of the frame
[obnox/wireshark/wip.git] / packet-ncp2222.inc
1 /* packet-ncp2222.inc
2  *
3  * Routines for NetWare Core Protocol. This C code gets #include'd
4  * into packet-ncp2222.c, which is generated from ncp2222.py. It's
5  * #include'd instead of being in a separate compilation unit so
6  * that all the data tables in packet-ncp2222.c can remain static.
7  *
8  * Gilbert Ramirez <gram@alumni.rice.edu>
9  *
10  * $Id: packet-ncp2222.inc,v 1.12 2002/05/16 03:31:34 gram Exp $
11  *
12  * Ethereal - Network traffic analyzer
13  * By Gerald Combs <gerald@ethereal.com>
14  * Copyright 2000 Gerald Combs
15  *
16  * This program is free software; you can redistribute it and/or
17  * modify it under the terms of the GNU General Public License
18  * as published by the Free Software Foundation; either version 2
19  * of the License, or (at your option) any later version.
20  * 
21  * This program is distributed in the hope that it will be useful,
22  * but WITHOUT ANY WARRANTY; without even the implied warranty of
23  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24  * GNU General Public License for more details.
25  * 
26  * You should have received a copy of the GNU General Public License
27  * along with this program; if not, write to the Free Software
28  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
29  */
30
31 #define NCP_PACKET_INIT_COUNT   200
32 #define PROTO_LENGTH_UNTIL_END -1
33
34 static void
35 process_ptvc_record(ptvcursor_t *ptvc, const ptvc_record *rec,
36                 int *req_cond_results, gboolean really_decode,
37                 const ncp_record *ncp_rec);
38
39 /* NCP packets come in request/reply pairs. The request packets tell the type
40  * of NCP request and give a sequence ID. The response, unfortunately, only
41  * identifies itself via the sequence ID; you have to know what type of NCP
42  * request the request packet contained in order to successfully parse the NCP
43  * response. A global method for doing this does not exist in ethereal yet
44  * (NFS also requires it), so for now the NCP section will keep its own hash
45  * table keeping track of NCP packet types.
46  *
47  * We construct a conversation specified by the client and server
48  * addresses and the connection number; the key representing the unique
49  * NCP request then is composed of the pointer to the conversation
50  * structure, cast to a "guint" (which may throw away the upper 32
51  * bits of the pointer on a P64 platform, but the low-order 32 bits
52  * are more likely to differ between conversations than the upper 32 bits),
53  * and the sequence number.
54  *
55  * The value stored in the hash table is the ncp_req_hash_value pointer. This
56  * struct tells us the NCP type and gives the ncp2222_record pointer, if
57  * ncp_type == 0x2222.
58  */
59 typedef struct {
60         conversation_t  *conversation;
61         guint8          nw_sequence;
62 } ncp_req_hash_key;
63
64 typedef struct {
65         const ncp_record        *ncp_rec;
66         gboolean                *req_cond_results;
67         guint32                 req_frame_num;
68 } ncp_req_hash_value;
69
70 static GHashTable *ncp_req_hash = NULL;
71 static GMemChunk *ncp_req_hash_keys = NULL;
72 static GMemChunk *ncp_req_hash_values = NULL;
73
74 /* Hash Functions */
75 gint
76 ncp_equal(gconstpointer v, gconstpointer v2)
77 {
78         ncp_req_hash_key        *val1 = (ncp_req_hash_key*)v;
79         ncp_req_hash_key        *val2 = (ncp_req_hash_key*)v2;
80
81         if (val1->conversation == val2->conversation &&
82             val1->nw_sequence  == val2->nw_sequence ) {
83                 return 1;
84         }
85         return 0;
86 }
87
88 guint
89 ncp_hash(gconstpointer v)
90 {
91         ncp_req_hash_key        *ncp_key = (ncp_req_hash_key*)v;
92         return GPOINTER_TO_UINT(ncp_key->conversation) + ncp_key->nw_sequence;
93 }
94
95 /* Frees memory used by the ncp_req_hash_value's */
96 static void
97 ncp_req_hash_cleanup(gpointer key _U_, gpointer value, gpointer user_data _U_)
98 {
99         ncp_req_hash_value      *request_value = (ncp_req_hash_value*) value;
100
101         if (request_value->req_cond_results) {
102                 g_free(request_value->req_cond_results);
103         }
104 }
105
106 /* Initializes the hash table and the mem_chunk area each time a new
107  * file is loaded or re-loaded in ethereal */
108 static void
109 ncp_init_protocol(void)
110 {
111         if (ncp_req_hash) {
112                 g_hash_table_foreach(ncp_req_hash, ncp_req_hash_cleanup, NULL);
113                 g_hash_table_destroy(ncp_req_hash);
114         }
115         if (ncp_req_hash_keys)
116                 g_mem_chunk_destroy(ncp_req_hash_keys);
117         if (ncp_req_hash_values)
118                 g_mem_chunk_destroy(ncp_req_hash_values);
119
120         ncp_req_hash = g_hash_table_new(ncp_hash, ncp_equal);
121         ncp_req_hash_keys = g_mem_chunk_new("ncp_req_hash_keys",
122                         sizeof(ncp_req_hash_key),
123                         NCP_PACKET_INIT_COUNT * sizeof(ncp_req_hash_key),
124                         G_ALLOC_ONLY);
125         ncp_req_hash_values = g_mem_chunk_new("ncp_req_hash_values",
126                         sizeof(ncp_req_hash_value),
127                         NCP_PACKET_INIT_COUNT * sizeof(ncp_req_hash_value),
128                         G_ALLOC_ONLY);
129 }
130
131 /* After the sequential run, we don't need the ncp_request hash and keys
132  * anymore; the lookups have already been done and the vital info
133  * saved in the reply-packets' private_data in the frame_data struct. */
134 static void
135 ncp_postseq_cleanup(void)
136 {
137         if (ncp_req_hash) {
138                 /* Destroy the hash, but don't clean up request_condition data. */
139                 g_hash_table_destroy(ncp_req_hash);
140                 ncp_req_hash = NULL;
141         }
142         if (ncp_req_hash_keys) {
143                 g_mem_chunk_destroy(ncp_req_hash_keys);
144                 ncp_req_hash_keys = NULL;
145         }
146         /* Don't free the ncp_req_hash_values, as they're
147          * needed during random-access processing of the proto_tree.*/
148 }
149
150 ncp_req_hash_value*
151 ncp_hash_insert(conversation_t *conversation, guint8 nw_sequence,
152                 const ncp_record *ncp_rec)
153 {
154         ncp_req_hash_key                *request_key;
155         ncp_req_hash_value              *request_value;
156
157         /* Now remember the request, so we can find it if we later
158            a reply to it. */
159         request_key = g_mem_chunk_alloc(ncp_req_hash_keys);
160         request_key->conversation = conversation;
161         request_key->nw_sequence = nw_sequence;
162
163         request_value = g_mem_chunk_alloc(ncp_req_hash_values);
164         request_value->ncp_rec = ncp_rec;
165         request_value->req_cond_results = NULL;
166
167         g_hash_table_insert(ncp_req_hash, request_key, request_value);
168
169         return request_value;
170 }
171
172 /* Returns the ncp_rec*, or NULL if not found. */
173 ncp_req_hash_value*
174 ncp_hash_lookup(conversation_t *conversation, guint8 nw_sequence)
175 {
176         ncp_req_hash_key                request_key;
177
178         request_key.conversation = conversation;
179         request_key.nw_sequence = nw_sequence;
180
181         return g_hash_table_lookup(ncp_req_hash, &request_key);
182 }
183
184 /* Does NCP func require a subfunction code? */
185 static gboolean
186 ncp_requires_subfunc(guint8 func)
187 {
188         const guint8 *ncp_func_requirement = ncp_func_requires_subfunc;
189
190         while (*ncp_func_requirement != 0) {
191                 if (*ncp_func_requirement == func) {
192                         return TRUE;
193                 }
194                 ncp_func_requirement++;
195         }
196         return FALSE;
197 }
198
199 /* Does the NCP func have a length parameter? */
200 static gboolean
201 ncp_has_length_parameter(guint8 func)
202 {
203         const guint8 *ncp_func_requirement = ncp_func_has_no_length_parameter;
204
205         while (*ncp_func_requirement != 0) {
206                 if (*ncp_func_requirement == func) {
207                         return FALSE;
208                 }
209                 ncp_func_requirement++;
210         }
211         return TRUE;
212 }
213                 
214
215 /* Return a ncp_record* based on func and possibly subfunc */
216 static const ncp_record *
217 ncp_record_find(guint8 func, guint8 subfunc)
218 {
219         const ncp_record *ncp_rec = ncp_packets;
220
221         while(ncp_rec->func != 0 || ncp_rec->subfunc != 0 ||
222                 ncp_rec->name != NULL ) {
223                 if (ncp_rec->func == func) {
224                         if (ncp_rec->has_subfunc) {
225                                 if (ncp_rec->subfunc == subfunc) {
226                                         return ncp_rec;
227                                 }
228                         }
229                         else {
230                                 return ncp_rec;
231                         }
232                 }
233                 ncp_rec++;
234         }
235         return NULL;
236 }
237
238 /* Given a proto_item*, assume it contains an integer value
239  * and return a guint from it. */
240 guint
241 get_item_value(proto_item *item)
242 {
243         return fvalue_get_integer(PITEM_FINFO(item)->value);
244 }
245
246 char*
247 get_item_name(proto_item *item)
248 {
249         return PITEM_FINFO(item)->hfinfo->name;
250 }
251
252
253 typedef proto_item* (*padd_func_t)(ptvcursor_t*, const ptvc_record*);
254
255 /*
256  * XXX - are these just DOS-format dates and times?
257  *
258  * Should we put code to understand various date and time formats (UNIX,
259  * DOS, SMB weird mutant UNIX, NT, Mac, etc. into libethereal, and have
260  * the "display" member of an HF_ABSOLUTE_TIME field specify whether
261  * it's DOS date/DOS time, DOS time/DOS date, NT time, UNIX time_t,
262  * UNIX "struct timeval", NFSv3/NFSv4 seconds/nanoseconds, Mac, etc.?
263  *
264  * What about hijacking the "bitmask" field to specify the precision of
265  * the time stamp, or putting a combination of precision and format
266  * into the "display" member?
267  *
268  * What about relative times?  Should they have units (seconds, milliseconds,
269  * microseconds, nanoseconds, etc.), precision, and format in there?
270  */
271 typedef struct {
272         guint   year;
273         guint   month;
274         guint   day;
275 } nw_date_t;
276
277 typedef struct {
278         guint   hour;
279         guint   minute;
280         guint   second;
281 } nw_time_t;
282
283 /* Given an integer, fill in a nw_date_t struct. */
284 static void
285 uint_to_nwdate(guint data, nw_date_t *nwdate)
286 {
287         nwdate->day   =  data & 0x001f;
288         nwdate->month = (data & 0x01e0) >> 5;
289         nwdate->year  = ((data & 0xfe00) >> 9) + 1980;
290 }
291
292 /* Given an integer, fill in a nw_time_t struct. */
293 static void
294 uint_to_nwtime(guint data, nw_time_t *nwtime)
295 {
296         /* 2-second resolution */
297         nwtime->second = (data & 0x001f) * 2;
298         nwtime->minute = ((data & 0x07e0) >> 5) + 1;
299         nwtime->hour   = ((data & 0xf800) >> 11) + 1;
300 }
301
302
303 static proto_item*
304 padd_normal(ptvcursor_t *ptvc, const ptvc_record *rec)
305 {
306         return ptvcursor_add(ptvc, *rec->hf_ptr,
307                 rec->length, rec->endianness);
308 }
309
310
311 static proto_item*
312 padd_date(ptvcursor_t *ptvc, const ptvc_record *rec)
313 {
314         proto_item      *item;
315         nw_date_t       nw_date;
316         gint            offset;
317
318         offset = ptvcursor_current_offset(ptvc);
319
320         item = ptvcursor_add(ptvc, *rec->hf_ptr,
321                 rec->length, rec->endianness);
322
323         uint_to_nwdate(get_item_value(item), &nw_date);
324         
325         proto_item_set_text(item, get_item_name(item)); 
326         proto_item_append_text(item, ": %04u/%02u/%02u",
327                         nw_date.year, nw_date.month, nw_date.day);
328         return item;
329 }
330
331 static proto_item*
332 padd_time(ptvcursor_t *ptvc, const ptvc_record *rec)
333 {
334         proto_item      *item;
335         nw_time_t       nw_time;
336         gint            offset;
337
338         offset = ptvcursor_current_offset(ptvc);
339
340         item = ptvcursor_add(ptvc, *rec->hf_ptr,
341                 rec->length, rec->endianness);
342
343         uint_to_nwtime(get_item_value(item), &nw_time);
344         
345         proto_item_set_text(item, get_item_name(item)); 
346         proto_item_append_text(item, ": %02u:%02u:%02u",
347                         nw_time.hour, nw_time.minute, nw_time.second);
348         return item;
349 }
350
351
352
353 /* Add a value for a ptvc_record, and process the sub-ptvc_record
354  * that it points to. */
355 static void
356 process_bitfield_sub_ptvc_record(ptvcursor_t *ptvc, const ptvc_record *rec,
357                 gboolean really_decode)
358 {
359         proto_item              *item;
360         proto_tree              *sub_tree;
361         const ptvc_record       *sub_rec;
362         int                     current_offset;
363         gint                    ett;
364         ptvcursor_t             *sub_ptvc;
365
366         if (really_decode) {
367                 /* Save the current offset */
368                 current_offset = ptvcursor_current_offset(ptvc);
369
370                 /* Add the item */
371                 item = ptvcursor_add(ptvc, *rec->hf_ptr, rec->length,
372                                 rec->endianness);
373
374                 ett = *rec->sub_ptvc_rec->ett;
375
376                 /* Make a new protocol sub-tree */
377                 sub_tree = proto_item_add_subtree(item, ett);
378
379                 /* Make a new ptvcursor */
380                 sub_ptvc = ptvcursor_new(sub_tree, ptvcursor_tvbuff(ptvc),
381                                 current_offset);
382
383                 /* Use it */
384                 sub_rec = rec->sub_ptvc_rec->ptvc_rec;
385                 while(sub_rec->hf_ptr != NULL) {
386                         g_assert(!sub_rec->sub_ptvc_rec);
387                         ptvcursor_add_no_advance(sub_ptvc, *sub_rec->hf_ptr,
388                                         sub_rec->length, sub_rec->endianness);
389                         sub_rec++;
390                 }
391
392                 /* Free it. */
393                 ptvcursor_free(sub_ptvc);
394         }
395         else {
396                 ptvcursor_advance(ptvc, rec->length);
397         }
398 }
399
400 /* Process a sub-ptvc_record that points to a "struct" ptvc_record. */
401 static void
402 process_struct_sub_ptvc_record(ptvcursor_t *ptvc, const ptvc_record *rec,
403                 int *req_cond_results, gboolean really_decode,
404                 const ncp_record *ncp_rec)
405 {
406         const ptvc_record       *sub_rec;
407         gint                    ett;
408         proto_tree              *old_tree=NULL, *new_tree;
409         proto_item              *item=NULL;
410         gint                    offset=0;
411
412         /* Create a sub-proto_tree? */
413         if (rec->sub_ptvc_rec->descr) {
414                 ett = *rec->sub_ptvc_rec->ett;
415                 old_tree = ptvcursor_tree(ptvc);
416                 offset = ptvcursor_current_offset(ptvc);
417                 item = proto_tree_add_text(old_tree, ptvcursor_tvbuff(ptvc),
418                                 offset, PROTO_LENGTH_UNTIL_END,
419                                 rec->sub_ptvc_rec->descr);
420                 new_tree = proto_item_add_subtree(item, ett);
421                 ptvcursor_set_tree(ptvc, new_tree);
422         }
423
424         /* Get the ptvc_record for the struct and call our caller
425          * to process it. */
426         sub_rec = rec->sub_ptvc_rec->ptvc_rec;
427         process_ptvc_record(ptvc, sub_rec, req_cond_results, really_decode, ncp_rec);
428
429         /* Re-set the tree */
430         if (rec->sub_ptvc_rec->descr) {
431                 proto_item_set_len(item, ptvcursor_current_offset(ptvc) - offset);
432                 ptvcursor_set_tree(ptvc, old_tree);
433         }
434 }
435
436 /* Run through the table of ptvc_record's and add info to the tree. This
437  * is the work-horse of process_ptvc_record(). */
438 static void
439 _process_ptvc_record(ptvcursor_t *ptvc, const ptvc_record *rec,
440                 int *req_cond_results, gboolean really_decode,
441                 const ncp_record *ncp_rec)
442 {
443         proto_item      *item;
444         guint           i, repeat_count;
445         padd_func_t     func = NULL;
446
447         if (rec->sub_ptvc_rec) {
448                 /* Repeat this? */
449                 if (rec->repeat_index == NO_REPEAT) {
450                         if (rec->hf_ptr == PTVC_STRUCT) {
451                                 process_struct_sub_ptvc_record(ptvc, rec,
452                                                 req_cond_results, really_decode,
453                                                 ncp_rec);
454                         }
455                         else {
456                                 process_bitfield_sub_ptvc_record(ptvc, rec,
457                                                 really_decode);
458                         }
459                 }
460                 else {
461                         repeat_count = repeat_vars[rec->repeat_index];
462                         for (i = 0; i < repeat_count; i++ ) {
463                                 if (rec->hf_ptr == PTVC_STRUCT) {
464                                         process_struct_sub_ptvc_record(ptvc, rec,
465                                                 req_cond_results, really_decode,
466                                                 ncp_rec);
467                                 }
468                                 else {
469                                         process_bitfield_sub_ptvc_record(ptvc, rec,
470                                                         really_decode);
471                                 }
472                         }
473                 }
474         }
475         else {
476                 /* If we can't repeat this field, we might use it
477                  * to set a 'var'. */
478                 if (rec->repeat_index == NO_REPEAT) {
479                         if (really_decode) {
480                                 /* Handle any special formatting. */
481                                 switch(rec->special_fmt) {
482                                         case NCP_FMT_NONE:
483                                                 func = padd_normal;
484                                                 break;
485                                         case NCP_FMT_NW_DATE:
486                                                 func = padd_date;
487                                                 break;
488                                         case NCP_FMT_NW_TIME:
489                                                 func = padd_time;
490                                                 break;
491                                         default:
492                                                 g_assert_not_reached();
493                                 }
494                                 item = func(ptvc, rec);
495
496                                 /* Set the value as a 'var' ? */
497                                 if (rec->var_index != NO_VAR) {
498                                         repeat_vars[rec->var_index] = get_item_value(item);
499                                 }
500                         }
501                         else {
502                                 /* If we don't decode the field, we
503                                  * better not use the value to set a var.
504                                  * Actually, we could, as long as we don't
505                                  * *use* that var; for now keep this assert in
506                                  * place. */
507                                 g_assert(rec->var_index == NO_VAR);
508                                 ptvcursor_advance(ptvc, rec->length);
509                         }
510                 }
511                 else {
512                         /* We do repeat this field. */
513                         repeat_count = repeat_vars[rec->repeat_index];
514                         if (really_decode) {
515                                 /* Handle any special formatting. */
516                                 switch(rec->special_fmt) {
517                                         case NCP_FMT_NONE:
518                                                 func = padd_normal;
519                                                 break;
520                                         case NCP_FMT_NW_DATE:
521                                                 func = padd_date;
522                                                 break;
523                                         case NCP_FMT_NW_TIME:
524                                                 func = padd_time;
525                                                 break;
526                                         default:
527                                                 g_assert_not_reached();
528                                 }
529                                 for (i = 0; i < repeat_count; i++ ) {
530                                         func(ptvc, rec);
531                                 }
532                         }
533                         else {
534                                 for (i = 0; i < repeat_count; i++ ) {
535                                         ptvcursor_advance(ptvc, rec->length);
536                                 }
537                         }
538                 }
539         }
540 }
541
542 /* Run through the table of ptvc_record's and add info to the tree.
543  * Honor a request condition result. */
544 static void
545 process_ptvc_record(ptvcursor_t *ptvc, const ptvc_record *rec,
546                 int *req_cond_results, gboolean really_decode,
547                 const ncp_record *ncp_rec)
548 {
549         gboolean decode;
550
551         while(rec->hf_ptr != NULL) {
552                 decode = really_decode;
553                 /* If we're supposed to decode, check the request condition
554                  * results to see if we should override this and *not* decode. */
555                 if (decode && req_cond_results) {
556                         if (rec->req_cond_index != NO_REQ_COND) {
557                                 if (req_cond_results[rec->req_cond_index] == FALSE) {
558                                         decode = FALSE;
559                                 }
560                         }
561                 }
562                 if (decode || ncp_rec->req_cond_size_type == REQ_COND_SIZE_CONSTANT) {
563                         _process_ptvc_record(ptvc, rec, req_cond_results, decode, ncp_rec);
564                 }
565                 rec++;
566         }
567 }
568
569
570
571 /* Clear the repeat_vars array. */
572 static void
573 clear_repeat_vars(void)
574 {
575         guint i;
576
577         for (i = 0 ; i < NUM_REPEAT_VARS; i++ ) {
578                 repeat_vars[i] = 0;
579         }
580 }
581
582 /* Given an error_equivalency table and a completion code, return
583  * the string representing the error. */
584 static const char*
585 ncp_error_string(const error_equivalency *errors, guint8 completion_code)
586 {
587         while (errors->ncp_error_index != -1) {
588                 if (errors->error_in_packet == completion_code) {
589                         return ncp_errors[errors->ncp_error_index];
590                 }
591                 errors++;
592         }
593
594         return "Unknown";
595 }
596
597 static const ncp_record ncp1111_request =
598         { 0x01, 0x00, NO_SUBFUNC, "Create Connection Service", NCP_GROUP_CONNECTION,
599                 NULL, NULL, ncp_0x2_errors, NULL, NO_REQ_COND_SIZE, NULL };
600
601 /* Wrapper around proto_tree_free() */
602 void free_proto_tree(void *tree)
603 {
604         if (tree) {
605                 proto_tree_free((proto_tree*) tree);
606         }
607 }
608
609 void
610 dissect_ncp_request(tvbuff_t *tvb, packet_info *pinfo,
611                 guint16 nw_connection, guint8 sequence,
612                 guint16 type, proto_tree *ncp_tree)
613 {
614         guint8                  func, subfunc = 0;
615         gboolean                requires_subfunc;
616         gboolean                has_length = TRUE;
617         ncp_req_hash_value      *request_value = NULL;
618         const ncp_record        *ncp_rec = NULL;
619         conversation_t          *conversation;
620         ptvcursor_t             *ptvc = NULL;
621         proto_tree              *temp_tree = NULL;
622         gboolean                run_req_cond = FALSE;
623         gboolean                run_info_str = FALSE;
624
625         func = tvb_get_guint8(tvb, 6);
626
627         requires_subfunc = ncp_requires_subfunc(func);
628         has_length = ncp_has_length_parameter(func);
629         if (requires_subfunc) {
630                 if (has_length) {
631                         subfunc = tvb_get_guint8(tvb, 9);
632                 }
633                 else {
634                         subfunc = tvb_get_guint8(tvb, 7);
635                 }
636         }
637
638         /* Determine which ncp_record to use. */
639         switch (type) {
640                 case 0x1111:
641                         ncp_rec = &ncp1111_request;
642                         break;
643                 case 0x2222:
644                         ncp_rec = ncp_record_find(func, subfunc);
645                         break;
646                 default:
647                         ncp_rec = NULL;
648         }
649
650         /* Fill in the INFO column. */
651         if (check_col(pinfo->cinfo, COL_INFO)) {
652                 if (ncp_rec) {
653                         col_add_fstr(pinfo->cinfo, COL_INFO, "C %s", ncp_rec->name);
654                 }
655                 else {
656                         if (requires_subfunc) {
657                                 col_add_fstr(pinfo->cinfo, COL_INFO,
658                                         "C Unknown Function %d %d (0x%02X/0x%02x)",
659                                         func, subfunc, func, subfunc);
660                         }
661                         else {
662                                 col_add_fstr(pinfo->cinfo, COL_INFO,
663                                         "C Unknown Function %d (0x%02x)",
664                                         func, func);
665                         }
666                 }
667         }
668
669         if (!pinfo->fd->flags.visited) {
670                 /* This is the first time we've looked at this packet.
671                    Keep track of the address and connection whence the request
672                    came, and the address and connection to which the request
673                    is being sent, so that we can match up calls with replies.
674                    (We don't include the sequence number, as we may want
675                    to have all packets over the same connection treated
676                    as being part of a single conversation so that we can
677                    let the user select that conversation to be displayed.) */
678                 conversation = find_conversation(&pinfo->src, &pinfo->dst,
679                     PT_NCP, nw_connection, nw_connection, 0);
680
681                 if (conversation == NULL) {
682                         /* It's not part of any conversation - create a new one. */
683                         conversation = conversation_new(&pinfo->src, &pinfo->dst,
684                             PT_NCP, nw_connection, nw_connection, 0);
685                 }
686                 request_value = ncp_hash_insert(conversation, sequence, ncp_rec);
687                 request_value->req_frame_num = pinfo->fd->num;
688
689                 /* If this is the first time we're examining the packet,
690                  * check to see if this NCP type uses a "request condition".
691                  * If so, we have to build a proto_tree because request conditions
692                  * use display filters to work, and without a proto_tree,
693                  * display filters can't possibly work. If we already have
694                  * a proto_tree, then wonderful. If we don't, we need to build
695                  * one. */
696                 if (ncp_rec) {
697                         if (ncp_rec->req_cond_indexes) {
698                                 run_req_cond = TRUE;
699                         }
700                         /* Only create info string if COL_INFO is available. */
701                         if (ncp_rec->req_info_str && check_col(pinfo->cinfo, COL_INFO)) {
702                                 run_info_str = TRUE;
703                         }
704                         /* We also have to use a tree if we have to construct an info_str */
705                         if ((run_info_str || run_req_cond) && !ncp_tree) {
706                                 proto_item *ti;
707
708                                 temp_tree = proto_tree_create_root();
709                                 proto_tree_set_visible(temp_tree, FALSE);
710                                 ti = proto_tree_add_item(temp_tree, proto_ncp, tvb, 0, -1, FALSE);
711                                 ncp_tree = proto_item_add_subtree(ti, ett_ncp);
712                         }
713                 }
714         }
715
716         if (ncp_tree) {
717                 /* If the dissection throws an exception, be sure to free
718                  * the temporary proto_tree that was created. Because of the
719                  * way the CLEANUP_PUSH macro works, we can't put it in an 'if'
720                  * block; it has to be in the same scope as the terminating
721                  * CLEANUP_POP or CLEANUP_POP_AND_ALLOC. So, we always
722                  * call CLEANUP_POP and friends, but the value of temp_tree is
723                  * NULL if no cleanup is needed, and non-null if cleanup is needed. */
724                 CLEANUP_PUSH(free_proto_tree, temp_tree);
725
726                 /* Before the dissection, if we're saving data for a request
727                  * condition, we have to prime the proto tree using the
728                  * dfilter information */
729                 if (run_req_cond) {
730                         const int       *needed;
731                         dfilter_t       *dfilter;
732
733                         needed = ncp_rec->req_cond_indexes;
734
735                         while (*needed != -1) {
736                                 dfilter = req_conds[*needed].dfilter;
737                                 /* Prime the proto_tree with "interesting fields". */
738                                 dfilter_prime_proto_tree(dfilter, ncp_tree);
739                                 needed++;
740                         }
741                 }
742
743                 /* Before the dissection, if we need a field for the info_str,
744                  * prime the tree. */
745                 if (run_info_str) {
746                         proto_tree_prime_hfid(ncp_tree, *ncp_rec->req_info_str->hf_ptr);
747                 }
748
749                 conversation = find_conversation(&pinfo->src, &pinfo->dst,
750                     PT_NCP, nw_connection, nw_connection, 0);
751
752                 switch (type) {
753                         case 0x1111:
754                                 ; /* nothing */
755                                 break;
756
757                         case 0x2222:
758                                 proto_tree_add_uint_format(ncp_tree, hf_ncp_func, tvb, 6, 1,
759                                         func, "Function: %d (0x%02X), %s",
760                                         func, func, ncp_rec ? ncp_rec->name : "Unknown");
761                                 break;
762
763                         default:
764                                 ; /* nothing */
765                                 break;
766                 }
767
768                 if (requires_subfunc) {
769                         if (has_length) {
770                                 proto_tree_add_item(ncp_tree, hf_ncp_length, tvb, 7,
771                                         2, FALSE);
772                                 proto_tree_add_uint_format(ncp_tree, hf_ncp_subfunc, tvb, 9, 1,
773                                         subfunc, "SubFunction: %d (0x%02x)",
774                                         subfunc, subfunc);
775                                 ptvc = ptvcursor_new(ncp_tree, tvb, 10);
776                         }
777                         else {
778                                 proto_tree_add_uint_format(ncp_tree, hf_ncp_subfunc, tvb, 7, 1,
779                                         subfunc, "SubFunction: %d (0x%02x)",
780                                         subfunc, subfunc);
781                                 ptvc = ptvcursor_new(ncp_tree, tvb, 8);
782                         }
783                 }
784                 else {
785                         ptvc = ptvcursor_new(ncp_tree, tvb, 7);
786                 }
787
788                 /* The group is not part of the packet, but it's useful
789                  * information to display anyway. */
790                 if (ncp_rec) {
791                         proto_tree_add_text(ncp_tree, tvb, 6, 1, "Group: %s",
792                                         ncp_groups[ncp_rec->group]);
793                 }
794
795                 if (ncp_rec && ncp_rec->request_ptvc) {
796                         clear_repeat_vars();
797                         process_ptvc_record(ptvc, ncp_rec->request_ptvc, NULL, TRUE, ncp_rec);
798                 }
799                 ptvcursor_free(ptvc);
800
801                 /* Now that the dissection is done, do we need to run
802                  * some display filters on the resulting tree in order
803                  * to save results for "request conditions" ? */
804                 if (run_req_cond) {
805                         const int       *needed;
806                         gboolean        *results;
807                         dfilter_t       *dfilter;
808
809                         results = g_new0(gboolean, NUM_REQ_CONDS);
810                         needed = ncp_rec->req_cond_indexes;
811
812                         while (*needed != -1) {
813                                 /* ncp_tree is not a root proto_tree, but
814                                  * dfilters will still work on it. */
815                                 dfilter = req_conds[*needed].dfilter;
816                                 results[*needed] = dfilter_apply(dfilter, ncp_tree);
817                                 needed++;
818                         }
819
820                         /* Save the results so the reply packet dissection
821                          * get to them. */
822                         request_value->req_cond_results = results;
823                 }
824
825                 /* Construct the info string if necessary */
826                 if (run_info_str) {
827                         GPtrArray *parray;
828                         int i, len;
829                         field_info *finfo;
830
831                         parray = proto_get_finfo_ptr_array(ncp_tree,
832                                 *ncp_rec->req_info_str->hf_ptr);
833                         len = g_ptr_array_len(parray);
834                         if (len > 0) {
835                                 col_set_str(pinfo->cinfo, COL_INFO, "C ");
836
837                                 finfo = g_ptr_array_index(parray, 0);
838                                 col_append_fstr(pinfo->cinfo, COL_INFO,
839                                         (gchar*) ncp_rec->req_info_str->first_string,
840                                         /* XXX - this only works for certain ftypes */
841                                         fvalue_get(finfo->value));
842                         }
843                         if (len > 1) {
844                                 for (i = 1; i < len; i++) {
845                                         finfo = g_ptr_array_index(parray, i);
846                                         col_append_fstr(pinfo->cinfo, COL_INFO,
847                                                 (gchar*) ncp_rec->req_info_str->repeat_string,
848                                                 /* XXX - this only works for certain ftypes */
849                                                 fvalue_get(finfo->value));
850                                 }
851                         }
852                 }
853
854
855                 /* Free the temporary proto_tree */
856                 CLEANUP_CALL_AND_POP;
857         }
858 }
859
860
861 void
862 dissect_ncp_reply(tvbuff_t *tvb, packet_info *pinfo,
863         guint16 nw_connection, guint8 sequence, proto_tree *ncp_tree)
864 {
865         conversation_t                  *conversation;
866         ncp_req_hash_value              *request_value = NULL;
867         const ncp_record                *ncp_rec = NULL;
868         int                             *req_cond_results;
869         guint8                          completion_code;
870         guint                           length;
871         ptvcursor_t                     *ptvc = NULL;
872         const char                      *error_string;
873
874         if (!pinfo->fd->flags.visited) {
875                 /* Find the conversation whence the request would have come. */
876                 conversation = find_conversation(&pinfo->src, &pinfo->dst,
877                             PT_NCP, nw_connection, nw_connection, 0);
878                 if (conversation != NULL) {
879                         /* find the record telling us the request made that caused
880                            this reply */
881                         request_value = ncp_hash_lookup(conversation, sequence);
882                         if (request_value) {
883                                 ncp_rec = request_value->ncp_rec;
884                         }
885                         p_add_proto_data(pinfo->fd, proto_ncp, (void*) request_value);
886                 }
887                 /* else... we haven't seen an NCP Request for that conversation and sequence. */
888         }
889         else {
890                 request_value = p_get_proto_data(pinfo->fd, proto_ncp);
891                 if (request_value) {
892                         ncp_rec = request_value->ncp_rec;
893                 }
894         }
895
896         /* A completion code of 0 always means OK. Non-zero means failure,
897          * but each non-zero value has a different meaning. And the same value
898          * can have different meanings, depending on the ncp.func (and ncp.subfunc)
899          * value. */
900         completion_code = tvb_get_guint8(tvb, 6);
901         if (ncp_rec && ncp_rec->errors) {
902                 error_string = ncp_error_string(ncp_rec->errors, completion_code);
903         }
904         else if (completion_code == 0) {
905                 error_string = "OK";
906         }
907         else {
908                 error_string = "Not OK";
909         }
910
911         if (check_col(pinfo->cinfo, COL_INFO)) {
912                 col_add_fstr(pinfo->cinfo, COL_INFO, "R %s", error_string);
913         }
914
915         if (ncp_tree) {
916
917                 if (request_value) {
918                         proto_tree_add_uint(ncp_tree, hf_ncp_req_frame_num, tvb, 0, 0,
919                                 request_value->req_frame_num);
920                 }
921
922
923                 /* Put the func (and maybe subfunc) from the request packet
924                  * in the proto tree, but hidden. That way filters on ncp.func
925                  * or ncp.subfunc will find both the requests and the replies.
926                  */
927                 if (ncp_rec) {
928                         proto_tree_add_uint_format(ncp_tree, hf_ncp_func, tvb, 6, 0,
929                                 ncp_rec->func, "Function: %d (0x%02X), %s",
930                                 ncp_rec->func, ncp_rec->func, ncp_rec->name);
931                         if (ncp_requires_subfunc(ncp_rec->func)) {
932                                 proto_tree_add_uint_format(ncp_tree, hf_ncp_subfunc, tvb, 6, 0,
933                                         ncp_rec->subfunc, "SubFunction: %d (0x%02x)",
934                                         ncp_rec->subfunc, ncp_rec->subfunc);
935                         }
936                 }
937
938                 proto_tree_add_uint_format(ncp_tree, hf_ncp_completion_code, tvb, 6, 1,
939                         completion_code, "Completion Code: %d (0x%02x), %s",
940                         completion_code, completion_code, error_string);
941
942                 proto_tree_add_item(ncp_tree, hf_ncp_connection_status, tvb, 7, 1, FALSE);
943
944                 length = tvb_length(tvb);
945                 if (!ncp_rec && length > 8) {
946                         proto_tree_add_text(ncp_tree, tvb, 8, length - 8,
947                                         "No request record found. Parsing is impossible.");
948                 }
949                 else if (ncp_rec && ncp_rec->reply_ptvc) {
950                         /* If a non-zero completion code was found, it is
951                          * legal to not have any fields, even if the packet
952                          * type is defined as having fields. */
953                         if (completion_code != 0 && tvb_length(tvb) == 8) {
954                                 return;
955                         }
956                         /*printf("func=0x%x subfunc=0x%x\n", ncp_rec->func, ncp_rec->subfunc);*/
957
958                         /* Any request condition results? */
959                         if (request_value) {
960                                 req_cond_results = request_value->req_cond_results;
961                         }
962                         else {
963                                 req_cond_results = NULL;
964                         }
965
966                         clear_repeat_vars();
967                         ptvc = ptvcursor_new(ncp_tree, tvb, 8);
968                         process_ptvc_record(ptvc, ncp_rec->reply_ptvc, req_cond_results,
969                                         TRUE, ncp_rec);
970                         ptvcursor_free(ptvc);
971                 }
972         }
973 }