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.
8 * Gilbert Ramirez <gram@alumni.rice.edu>
10 * $Id: packet-ncp2222.inc,v 1.12 2002/05/16 03:31:34 gram Exp $
12 * Ethereal - Network traffic analyzer
13 * By Gerald Combs <gerald@ethereal.com>
14 * Copyright 2000 Gerald Combs
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.
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.
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.
31 #define NCP_PACKET_INIT_COUNT 200
32 #define PROTO_LENGTH_UNTIL_END -1
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);
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.
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.
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
60 conversation_t *conversation;
65 const ncp_record *ncp_rec;
66 gboolean *req_cond_results;
67 guint32 req_frame_num;
70 static GHashTable *ncp_req_hash = NULL;
71 static GMemChunk *ncp_req_hash_keys = NULL;
72 static GMemChunk *ncp_req_hash_values = NULL;
76 ncp_equal(gconstpointer v, gconstpointer v2)
78 ncp_req_hash_key *val1 = (ncp_req_hash_key*)v;
79 ncp_req_hash_key *val2 = (ncp_req_hash_key*)v2;
81 if (val1->conversation == val2->conversation &&
82 val1->nw_sequence == val2->nw_sequence ) {
89 ncp_hash(gconstpointer v)
91 ncp_req_hash_key *ncp_key = (ncp_req_hash_key*)v;
92 return GPOINTER_TO_UINT(ncp_key->conversation) + ncp_key->nw_sequence;
95 /* Frees memory used by the ncp_req_hash_value's */
97 ncp_req_hash_cleanup(gpointer key _U_, gpointer value, gpointer user_data _U_)
99 ncp_req_hash_value *request_value = (ncp_req_hash_value*) value;
101 if (request_value->req_cond_results) {
102 g_free(request_value->req_cond_results);
106 /* Initializes the hash table and the mem_chunk area each time a new
107 * file is loaded or re-loaded in ethereal */
109 ncp_init_protocol(void)
112 g_hash_table_foreach(ncp_req_hash, ncp_req_hash_cleanup, NULL);
113 g_hash_table_destroy(ncp_req_hash);
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);
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),
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),
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. */
135 ncp_postseq_cleanup(void)
138 /* Destroy the hash, but don't clean up request_condition data. */
139 g_hash_table_destroy(ncp_req_hash);
142 if (ncp_req_hash_keys) {
143 g_mem_chunk_destroy(ncp_req_hash_keys);
144 ncp_req_hash_keys = NULL;
146 /* Don't free the ncp_req_hash_values, as they're
147 * needed during random-access processing of the proto_tree.*/
151 ncp_hash_insert(conversation_t *conversation, guint8 nw_sequence,
152 const ncp_record *ncp_rec)
154 ncp_req_hash_key *request_key;
155 ncp_req_hash_value *request_value;
157 /* Now remember the request, so we can find it if we later
159 request_key = g_mem_chunk_alloc(ncp_req_hash_keys);
160 request_key->conversation = conversation;
161 request_key->nw_sequence = nw_sequence;
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;
167 g_hash_table_insert(ncp_req_hash, request_key, request_value);
169 return request_value;
172 /* Returns the ncp_rec*, or NULL if not found. */
174 ncp_hash_lookup(conversation_t *conversation, guint8 nw_sequence)
176 ncp_req_hash_key request_key;
178 request_key.conversation = conversation;
179 request_key.nw_sequence = nw_sequence;
181 return g_hash_table_lookup(ncp_req_hash, &request_key);
184 /* Does NCP func require a subfunction code? */
186 ncp_requires_subfunc(guint8 func)
188 const guint8 *ncp_func_requirement = ncp_func_requires_subfunc;
190 while (*ncp_func_requirement != 0) {
191 if (*ncp_func_requirement == func) {
194 ncp_func_requirement++;
199 /* Does the NCP func have a length parameter? */
201 ncp_has_length_parameter(guint8 func)
203 const guint8 *ncp_func_requirement = ncp_func_has_no_length_parameter;
205 while (*ncp_func_requirement != 0) {
206 if (*ncp_func_requirement == func) {
209 ncp_func_requirement++;
215 /* Return a ncp_record* based on func and possibly subfunc */
216 static const ncp_record *
217 ncp_record_find(guint8 func, guint8 subfunc)
219 const ncp_record *ncp_rec = ncp_packets;
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) {
238 /* Given a proto_item*, assume it contains an integer value
239 * and return a guint from it. */
241 get_item_value(proto_item *item)
243 return fvalue_get_integer(PITEM_FINFO(item)->value);
247 get_item_name(proto_item *item)
249 return PITEM_FINFO(item)->hfinfo->name;
253 typedef proto_item* (*padd_func_t)(ptvcursor_t*, const ptvc_record*);
256 * XXX - are these just DOS-format dates and times?
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.?
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?
268 * What about relative times? Should they have units (seconds, milliseconds,
269 * microseconds, nanoseconds, etc.), precision, and format in there?
283 /* Given an integer, fill in a nw_date_t struct. */
285 uint_to_nwdate(guint data, nw_date_t *nwdate)
287 nwdate->day = data & 0x001f;
288 nwdate->month = (data & 0x01e0) >> 5;
289 nwdate->year = ((data & 0xfe00) >> 9) + 1980;
292 /* Given an integer, fill in a nw_time_t struct. */
294 uint_to_nwtime(guint data, nw_time_t *nwtime)
296 /* 2-second resolution */
297 nwtime->second = (data & 0x001f) * 2;
298 nwtime->minute = ((data & 0x07e0) >> 5) + 1;
299 nwtime->hour = ((data & 0xf800) >> 11) + 1;
304 padd_normal(ptvcursor_t *ptvc, const ptvc_record *rec)
306 return ptvcursor_add(ptvc, *rec->hf_ptr,
307 rec->length, rec->endianness);
312 padd_date(ptvcursor_t *ptvc, const ptvc_record *rec)
318 offset = ptvcursor_current_offset(ptvc);
320 item = ptvcursor_add(ptvc, *rec->hf_ptr,
321 rec->length, rec->endianness);
323 uint_to_nwdate(get_item_value(item), &nw_date);
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);
332 padd_time(ptvcursor_t *ptvc, const ptvc_record *rec)
338 offset = ptvcursor_current_offset(ptvc);
340 item = ptvcursor_add(ptvc, *rec->hf_ptr,
341 rec->length, rec->endianness);
343 uint_to_nwtime(get_item_value(item), &nw_time);
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);
353 /* Add a value for a ptvc_record, and process the sub-ptvc_record
354 * that it points to. */
356 process_bitfield_sub_ptvc_record(ptvcursor_t *ptvc, const ptvc_record *rec,
357 gboolean really_decode)
360 proto_tree *sub_tree;
361 const ptvc_record *sub_rec;
364 ptvcursor_t *sub_ptvc;
367 /* Save the current offset */
368 current_offset = ptvcursor_current_offset(ptvc);
371 item = ptvcursor_add(ptvc, *rec->hf_ptr, rec->length,
374 ett = *rec->sub_ptvc_rec->ett;
376 /* Make a new protocol sub-tree */
377 sub_tree = proto_item_add_subtree(item, ett);
379 /* Make a new ptvcursor */
380 sub_ptvc = ptvcursor_new(sub_tree, ptvcursor_tvbuff(ptvc),
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);
393 ptvcursor_free(sub_ptvc);
396 ptvcursor_advance(ptvc, rec->length);
400 /* Process a sub-ptvc_record that points to a "struct" ptvc_record. */
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)
406 const ptvc_record *sub_rec;
408 proto_tree *old_tree=NULL, *new_tree;
409 proto_item *item=NULL;
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);
424 /* Get the ptvc_record for the struct and call our caller
426 sub_rec = rec->sub_ptvc_rec->ptvc_rec;
427 process_ptvc_record(ptvc, sub_rec, req_cond_results, really_decode, ncp_rec);
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);
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(). */
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)
444 guint i, repeat_count;
445 padd_func_t func = NULL;
447 if (rec->sub_ptvc_rec) {
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,
456 process_bitfield_sub_ptvc_record(ptvc, rec,
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,
469 process_bitfield_sub_ptvc_record(ptvc, rec,
476 /* If we can't repeat this field, we might use it
478 if (rec->repeat_index == NO_REPEAT) {
480 /* Handle any special formatting. */
481 switch(rec->special_fmt) {
485 case NCP_FMT_NW_DATE:
488 case NCP_FMT_NW_TIME:
492 g_assert_not_reached();
494 item = func(ptvc, rec);
496 /* Set the value as a 'var' ? */
497 if (rec->var_index != NO_VAR) {
498 repeat_vars[rec->var_index] = get_item_value(item);
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
507 g_assert(rec->var_index == NO_VAR);
508 ptvcursor_advance(ptvc, rec->length);
512 /* We do repeat this field. */
513 repeat_count = repeat_vars[rec->repeat_index];
515 /* Handle any special formatting. */
516 switch(rec->special_fmt) {
520 case NCP_FMT_NW_DATE:
523 case NCP_FMT_NW_TIME:
527 g_assert_not_reached();
529 for (i = 0; i < repeat_count; i++ ) {
534 for (i = 0; i < repeat_count; i++ ) {
535 ptvcursor_advance(ptvc, rec->length);
542 /* Run through the table of ptvc_record's and add info to the tree.
543 * Honor a request condition result. */
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)
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) {
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);
571 /* Clear the repeat_vars array. */
573 clear_repeat_vars(void)
577 for (i = 0 ; i < NUM_REPEAT_VARS; i++ ) {
582 /* Given an error_equivalency table and a completion code, return
583 * the string representing the error. */
585 ncp_error_string(const error_equivalency *errors, guint8 completion_code)
587 while (errors->ncp_error_index != -1) {
588 if (errors->error_in_packet == completion_code) {
589 return ncp_errors[errors->ncp_error_index];
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 };
601 /* Wrapper around proto_tree_free() */
602 void free_proto_tree(void *tree)
605 proto_tree_free((proto_tree*) tree);
610 dissect_ncp_request(tvbuff_t *tvb, packet_info *pinfo,
611 guint16 nw_connection, guint8 sequence,
612 guint16 type, proto_tree *ncp_tree)
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;
625 func = tvb_get_guint8(tvb, 6);
627 requires_subfunc = ncp_requires_subfunc(func);
628 has_length = ncp_has_length_parameter(func);
629 if (requires_subfunc) {
631 subfunc = tvb_get_guint8(tvb, 9);
634 subfunc = tvb_get_guint8(tvb, 7);
638 /* Determine which ncp_record to use. */
641 ncp_rec = &ncp1111_request;
644 ncp_rec = ncp_record_find(func, subfunc);
650 /* Fill in the INFO column. */
651 if (check_col(pinfo->cinfo, COL_INFO)) {
653 col_add_fstr(pinfo->cinfo, COL_INFO, "C %s", ncp_rec->name);
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);
662 col_add_fstr(pinfo->cinfo, COL_INFO,
663 "C Unknown Function %d (0x%02x)",
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);
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);
686 request_value = ncp_hash_insert(conversation, sequence, ncp_rec);
687 request_value->req_frame_num = pinfo->fd->num;
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
697 if (ncp_rec->req_cond_indexes) {
700 /* Only create info string if COL_INFO is available. */
701 if (ncp_rec->req_info_str && check_col(pinfo->cinfo, COL_INFO)) {
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) {
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);
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);
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 */
733 needed = ncp_rec->req_cond_indexes;
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);
743 /* Before the dissection, if we need a field for the info_str,
746 proto_tree_prime_hfid(ncp_tree, *ncp_rec->req_info_str->hf_ptr);
749 conversation = find_conversation(&pinfo->src, &pinfo->dst,
750 PT_NCP, nw_connection, nw_connection, 0);
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");
768 if (requires_subfunc) {
770 proto_tree_add_item(ncp_tree, hf_ncp_length, tvb, 7,
772 proto_tree_add_uint_format(ncp_tree, hf_ncp_subfunc, tvb, 9, 1,
773 subfunc, "SubFunction: %d (0x%02x)",
775 ptvc = ptvcursor_new(ncp_tree, tvb, 10);
778 proto_tree_add_uint_format(ncp_tree, hf_ncp_subfunc, tvb, 7, 1,
779 subfunc, "SubFunction: %d (0x%02x)",
781 ptvc = ptvcursor_new(ncp_tree, tvb, 8);
785 ptvc = ptvcursor_new(ncp_tree, tvb, 7);
788 /* The group is not part of the packet, but it's useful
789 * information to display anyway. */
791 proto_tree_add_text(ncp_tree, tvb, 6, 1, "Group: %s",
792 ncp_groups[ncp_rec->group]);
795 if (ncp_rec && ncp_rec->request_ptvc) {
797 process_ptvc_record(ptvc, ncp_rec->request_ptvc, NULL, TRUE, ncp_rec);
799 ptvcursor_free(ptvc);
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" ? */
809 results = g_new0(gboolean, NUM_REQ_CONDS);
810 needed = ncp_rec->req_cond_indexes;
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);
820 /* Save the results so the reply packet dissection
822 request_value->req_cond_results = results;
825 /* Construct the info string if necessary */
831 parray = proto_get_finfo_ptr_array(ncp_tree,
832 *ncp_rec->req_info_str->hf_ptr);
833 len = g_ptr_array_len(parray);
835 col_set_str(pinfo->cinfo, COL_INFO, "C ");
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));
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));
855 /* Free the temporary proto_tree */
856 CLEANUP_CALL_AND_POP;
862 dissect_ncp_reply(tvbuff_t *tvb, packet_info *pinfo,
863 guint16 nw_connection, guint8 sequence, proto_tree *ncp_tree)
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;
871 ptvcursor_t *ptvc = NULL;
872 const char *error_string;
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
881 request_value = ncp_hash_lookup(conversation, sequence);
883 ncp_rec = request_value->ncp_rec;
885 p_add_proto_data(pinfo->fd, proto_ncp, (void*) request_value);
887 /* else... we haven't seen an NCP Request for that conversation and sequence. */
890 request_value = p_get_proto_data(pinfo->fd, proto_ncp);
892 ncp_rec = request_value->ncp_rec;
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)
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);
904 else if (completion_code == 0) {
908 error_string = "Not OK";
911 if (check_col(pinfo->cinfo, COL_INFO)) {
912 col_add_fstr(pinfo->cinfo, COL_INFO, "R %s", error_string);
918 proto_tree_add_uint(ncp_tree, hf_ncp_req_frame_num, tvb, 0, 0,
919 request_value->req_frame_num);
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.
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);
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);
942 proto_tree_add_item(ncp_tree, hf_ncp_connection_status, tvb, 7, 1, FALSE);
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.");
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) {
956 /*printf("func=0x%x subfunc=0x%x\n", ncp_rec->func, ncp_rec->subfunc);*/
958 /* Any request condition results? */
960 req_cond_results = request_value->req_cond_results;
963 req_cond_results = NULL;
967 ptvc = ptvcursor_new(ncp_tree, tvb, 8);
968 process_ptvc_record(ptvc, ncp_rec->reply_ptvc, req_cond_results,
970 ptvcursor_free(ptvc);