For proto_tree_add_item(..., proto_xxx, ...)use ENC_NA as the encoding arg.
[obnox/wireshark/wip.git] / epan / dissectors / packet-ncp.c
1 /* packet-ncp.c
2  * Routines for NetWare Core Protocol
3  * Gilbert Ramirez <gram@alumni.rice.edu>
4  * Modified to allow NCP over TCP/IP decodes by James Coe <jammer@cin.net>
5  * Modified to decode server op-lock, packet signature,
6  * & NDS packets by Greg Morris <gmorris@novell.com>
7  *
8  * Portions Copyright (c) by Gilbert Ramirez 2000-2002
9  * Portions Copyright (c) by James Coe 2000-2002
10  * Portions Copyright (c) Novell, Inc. 2000-2003
11  *
12  * $Id$
13  *
14  * Wireshark - Network traffic analyzer
15  * By Gerald Combs <gerald@wireshark.org>
16  * Copyright 2000 Gerald Combs
17  *
18  * This program is free software; you can redistribute it and/or
19  * modify it under the terms of the GNU General Public License
20  * as published by the Free Software Foundation; either version 2
21  * of the License, or (at your option) any later version.
22  *
23  * This program is distributed in the hope that it will be useful,
24  * but WITHOUT ANY WARRANTY; without even the implied warranty of
25  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
26  * GNU General Public License for more details.
27  *
28  * You should have received a copy of the GNU General Public License
29  * along with this program; if not, write to the Free Software
30  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
31  */
32
33 /* XXX:
34    ToDo: Find and fix possible memory leak(s):
35
36    Example:
37
38    A 40M capture file with mostly NCP frames results
39    in a 400K-800K memory usage increase each time the file is reloaded.
40
41    (If the NCP dissection is disabled, there is minimal memory usage
42    increase each time the file is reloaded).
43 */
44
45 #ifdef HAVE_CONFIG_H
46 # include "config.h"
47 #endif
48
49 #ifdef HAVE_SYS_TYPES_H
50 # include <sys/types.h>
51 #endif
52
53 #ifdef HAVE_NETINET_IN_H
54 # include <netinet/in.h>
55 #endif
56
57 #include <string.h>
58 #include <glib.h>
59 #include <epan/packet.h>
60 #include <epan/emem.h>
61 #include <epan/prefs.h>
62 #include "packet-ipx.h"
63 #include "packet-tcp.h"
64 #include "packet-ncp-int.h"
65 #include <epan/reassemble.h>
66 #include <epan/conversation.h>
67 #include <epan/tap.h>
68
69 int proto_ncp = -1;
70 static int hf_ncp_ip_ver = -1;
71 static int hf_ncp_ip_length = -1;
72 static int hf_ncp_ip_rplybufsize = -1;
73 static int hf_ncp_ip_sig = -1;
74 static int hf_ncp_ip_packetsig = -1;
75 static int hf_ncp_type = -1;
76 static int hf_ncp_seq = -1;
77 static int hf_ncp_connection = -1;
78 static int hf_ncp_task = -1;
79 static int hf_ncp_stream_type = -1;
80 static int hf_ncp_system_flags = -1;
81 static int hf_ncp_system_flags_abt = -1;
82 static int hf_ncp_system_flags_eob = -1;
83 static int hf_ncp_system_flags_sys = -1;
84 static int hf_ncp_system_flags_bsy = -1;
85 static int hf_ncp_system_flags_lst = -1;
86 static int hf_ncp_src_connection = -1;
87 static int hf_ncp_dst_connection = -1;
88 static int hf_ncp_packet_seqno = -1;
89 static int hf_ncp_delay_time = -1;
90 static int hf_ncp_burst_seqno = -1;
91 static int hf_ncp_ack_seqno = -1;
92 static int hf_ncp_burst_len = -1;
93 static int hf_ncp_burst_offset = -1;
94 static int hf_ncp_data_offset = -1;
95 static int hf_ncp_data_bytes = -1;
96 static int hf_ncp_missing_fraglist_count = -1;
97 static int hf_ncp_missing_data_offset = -1;
98 static int hf_ncp_missing_data_count = -1;
99 static int hf_ncp_oplock_flag = -1;
100 static int hf_ncp_oplock_handle = -1;
101 static int hf_ncp_completion_code = -1;
102 static int hf_ncp_connection_status = -1;
103 static int hf_ncp_slot = -1;
104 static int hf_ncp_control_code = -1;
105 static int hf_ncp_fragment_handle = -1;
106 static int hf_lip_echo = -1;
107 static int hf_ncp_burst_command = -1;
108 static int hf_ncp_burst_file_handle = -1;
109 static int hf_ncp_burst_reserved = -1;
110
111 gint ett_ncp = -1;
112 gint ett_nds = -1;
113 gint ett_nds_segments = -1;
114 gint ett_nds_segment = -1;
115 static gint ett_ncp_system_flags = -1;
116
117 static struct novell_tap ncp_tap;
118 static struct ncp_common_header     header;
119 static struct ncp_common_header    *ncp_hdr;
120
121 /* Tables for reassembly of fragments. */
122 GHashTable *nds_fragment_table = NULL;
123 GHashTable *nds_reassembled_table = NULL;
124 dissector_handle_t nds_data_handle;
125
126 /* desegmentation of NCP over TCP */
127 static gboolean ncp_desegment = TRUE;
128
129 static dissector_handle_t data_handle;
130
131 #define TCP_PORT_NCP            524
132 #define UDP_PORT_NCP            524
133
134 #define NCP_RQST_HDR_LENGTH     7
135 #define NCP_RPLY_HDR_LENGTH     8
136
137 /* These are the header structures to handle NCP over IP */
138 #define NCPIP_RQST      0x446d6454      /* "DmdT" */
139 #define NCPIP_RPLY      0x744e6350      /* "tNcP" */
140
141 struct ncp_ip_header {
142     guint32 signature;
143     guint32 length;
144 };
145
146 /* This header only appears on NCP over IP request packets */
147 struct ncp_ip_rqhdr {
148     guint32 version;
149     guint32 rplybufsize;
150 };
151
152 static const value_string ncp_ip_signature[] = {
153     { NCPIP_RQST, "Demand Transport (Request)" },
154     { NCPIP_RPLY, "Transport is NCP (Reply)" },
155     { 0, NULL },
156 };
157
158 static const value_string burst_command[] = {
159     { 0x01000000, "Burst Read" },
160     { 0x02000000, "Burst Write" },
161     { 0, NULL },
162 };
163
164 /* The information in this module comes from:
165    NetWare LAN Analysis, Second Edition
166    Laura A. Chappell and Dan E. Hakes
167    (c) 1994 Novell, Inc.
168    Novell Press, San Jose.
169    ISBN: 0-7821-1362-1
170
171    And from the ncpfs source code by Volker Lendecke
172
173    And:
174    Programmer's Guide to the NetWare Core Protocol
175    Steve Conner & Diane Conner
176    (c) 1996 by Steve Conner & Diane Conner
177    Published by Annabooks, San Diego, California
178    ISBN: 0-929392-31-0
179
180    And:
181    http:developer.novell.com
182    NCP documentation
183
184 */
185
186 static const value_string ncp_type_vals[] = {
187     { NCP_ALLOCATE_SLOT,    "Create a service connection" },
188     { NCP_SERVICE_REQUEST,  "Service request" },
189     { NCP_SERVICE_REPLY,    "Service reply" },
190     { NCP_WATCHDOG,         "Watchdog" },
191     { NCP_DEALLOCATE_SLOT,  "Destroy service connection" },
192     { NCP_BROADCAST_SLOT,   "Server Broadcast" },
193     { NCP_BURST_MODE_XFER,  "Burst mode transfer" },
194     { NCP_POSITIVE_ACK,     "Request being processed" },
195     { NCP_LIP_ECHO,         "Large Internet Packet Echo" },
196     { 0,                    NULL }
197 };
198
199 static const value_string ncp_oplock_vals[] = {
200     { 0x21, "Message Waiting" },
201     { 0x24, "Clear Op-lock" },
202     { 0, NULL }
203 };
204
205 /* Conversation Struct so we can detect NCP server sessions */
206
207 typedef struct {
208     conversation_t *conversation;
209     guint32         nwconnection;
210     guint8          nwtask;
211 } mncp_rhash_key;
212
213 /* Store the packet number for the start of the NCP session.
214  * Note sessions are defined as
215  * NCP Connection + NCP Task == Unique NCP server session
216  * It is normal for multiple sessions per connection to exist
217  * These are normally different applications running on multi-tasking
218  * Operating Systems.
219  */
220 typedef struct {
221     guint32  session_start_packet_num;
222 } mncp_rhash_value;
223
224 static GHashTable *mncp_rhash = NULL;
225
226 /* Hash Functions */
227 static gint
228 mncp_equal(gconstpointer v, gconstpointer v2)
229 {
230     const mncp_rhash_key *val1 = (const mncp_rhash_key*)v;
231     const mncp_rhash_key *val2 = (const mncp_rhash_key*)v2;
232
233     if (val1->conversation == val2->conversation && val1->nwconnection == val2->nwconnection && val1->nwtask == val2->nwtask) {
234         return 1;
235     }
236     return 0;
237 }
238
239 static guint
240 mncp_hash(gconstpointer v)
241 {
242     const mncp_rhash_key *mncp_key = (const mncp_rhash_key*)v;
243     return GPOINTER_TO_UINT(mncp_key->conversation)+mncp_key->nwconnection+mncp_key->nwtask;
244 }
245
246 /* Initializes the hash table each time a new
247  * file is loaded or re-loaded in wireshark */
248 static void
249 mncp_init_protocol(void)
250 {
251     if (mncp_rhash)
252         g_hash_table_destroy(mncp_rhash);
253
254     mncp_rhash = g_hash_table_new(mncp_hash, mncp_equal);
255 }
256
257 /* After the sequential run, we don't need the ncp_request hash and keys
258  * anymore; the lookups have already been done and the vital info
259  * saved in the reply-packets' private_data in the frame_data struct. */
260 static void
261 mncp_postseq_cleanup(void)
262 {
263 }
264
265 static mncp_rhash_value*
266 mncp_hash_insert(conversation_t *conversation, guint32 nwconnection, guint8 nwtask, packet_info *pinfo)
267 {
268     mncp_rhash_key      *key;
269     mncp_rhash_value    *value;
270
271     /* Now remember the request, so we can find it if we later
272        a reply to it. Track by conversation, connection, and task number.
273        in NetWare these values determine each unique session */
274     key = se_alloc(sizeof(mncp_rhash_key));
275     key->conversation = conversation;
276     key->nwconnection = nwconnection;
277     key->nwtask = nwtask;
278
279     value = se_alloc(sizeof(mncp_rhash_value));
280
281     g_hash_table_insert(mncp_rhash, key, value);
282
283     if (ncp_echo_conn && nwconnection != 65535) {
284         expert_add_info_format(pinfo, NULL, PI_RESPONSE_CODE, PI_CHAT, "Detected New Server Session. Connection %d, Task %d", nwconnection, nwtask);
285         value->session_start_packet_num = pinfo->fd->num;
286     }
287
288     return value;
289 }
290
291 /* Returns the ncp_rec*, or NULL if not found. */
292 static mncp_rhash_value*
293 mncp_hash_lookup(conversation_t *conversation, guint32 nwconnection, guint8 nwtask)
294 {
295     mncp_rhash_key        key;
296
297     key.conversation = conversation;
298     key.nwconnection = nwconnection;
299     key.nwtask = nwtask;
300
301     return g_hash_table_lookup(mncp_rhash, &key);
302 }
303
304 /*
305  * Burst packet system flags.
306  */
307 #define ABT 0x04        /* Abort request */
308 #define BSY 0x08        /* Server Busy */
309 #define EOB 0x10        /* End of burst */
310 #define LST 0x40        /* Include Fragment List */
311 #define SYS 0x80        /* System packet */
312
313 static void
314 dissect_ncp_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
315     gboolean is_tcp)
316 {
317     proto_tree            *ncp_tree = NULL;
318     proto_item            *ti;
319     struct ncp_ip_header  ncpiph;
320     struct ncp_ip_rqhdr   ncpiphrq;
321     guint16               ncp_burst_seqno, ncp_ack_seqno;
322     guint16               flags = 0;
323     proto_tree            *flags_tree = NULL;
324     int                   hdr_offset = 0;
325     int                   commhdr = 0;
326     int                   offset = 0;
327     gint                  length_remaining;
328     tvbuff_t              *next_tvb;
329     guint32               testvar = 0, ncp_burst_command, burst_len, burst_off, burst_file;
330     guint8                subfunction;
331     guint32               nw_connection = 0, data_offset;
332     guint16               data_len = 0;
333     guint16               missing_fraglist_count = 0;
334     mncp_rhash_value      *request_value = NULL;
335     conversation_t        *conversation;
336     proto_item            *expert_item;
337
338     col_set_str(pinfo->cinfo, COL_PROTOCOL, "NCP");
339     col_clear(pinfo->cinfo, COL_INFO);
340
341     ncp_hdr = &header;
342
343     ti = proto_tree_add_item(tree, proto_ncp, tvb, 0, -1, ENC_NA);
344     ncp_tree = proto_item_add_subtree(ti, ett_ncp);
345     if (is_tcp) {
346         if (tvb_get_ntohl(tvb, hdr_offset) != NCPIP_RQST && tvb_get_ntohl(tvb, hdr_offset) != NCPIP_RPLY)
347             commhdr += 1;
348         /* Get NCPIP Header data */
349         ncpiph.signature = tvb_get_ntohl(tvb, commhdr);
350         proto_tree_add_uint(ncp_tree, hf_ncp_ip_sig, tvb, commhdr, 4, ncpiph.signature);
351         ncpiph.length = (0x7fffffff & tvb_get_ntohl(tvb, commhdr+4));
352         proto_tree_add_uint(ncp_tree, hf_ncp_ip_length, tvb, commhdr+4, 4, ncpiph.length);
353         commhdr += 8;
354         if (ncpiph.signature == NCPIP_RQST) {
355             ncpiphrq.version = tvb_get_ntohl(tvb, commhdr);
356             proto_tree_add_uint(ncp_tree, hf_ncp_ip_ver, tvb, commhdr, 4, ncpiphrq.version);
357             commhdr += 4;
358             ncpiphrq.rplybufsize = tvb_get_ntohl(tvb, commhdr);
359             proto_tree_add_uint(ncp_tree, hf_ncp_ip_rplybufsize, tvb, commhdr, 4, ncpiphrq.rplybufsize);
360             commhdr += 4;
361         }
362         /* Check to see if this is a valid offset, otherwise increment for packet signature */
363         if (match_strval(tvb_get_ntohs(tvb, commhdr), ncp_type_vals)==NULL) {
364             /* Check to see if we have a valid type after packet signature length */
365             if (match_strval(tvb_get_ntohs(tvb, commhdr+8), ncp_type_vals)!=NULL) {
366                 proto_tree_add_item(ncp_tree, hf_ncp_ip_packetsig, tvb, commhdr, 8, ENC_NA);
367                 commhdr += 8;
368             }
369         }
370     } else {
371         /* Initialize this structure, we use it below */
372         memset(&ncpiph, 0, sizeof(ncpiph));
373     }
374
375     header.type         = tvb_get_ntohs(tvb, commhdr);
376     header.sequence     = tvb_get_guint8(tvb, commhdr+2);
377     header.conn_low     = tvb_get_guint8(tvb, commhdr+3);
378     header.task         = tvb_get_guint8(tvb, commhdr+4);
379     header.conn_high    = tvb_get_guint8(tvb, commhdr+5);
380     proto_tree_add_uint(ncp_tree, hf_ncp_type, tvb, commhdr, 2, header.type);
381     nw_connection = (header.conn_high*256)+header.conn_low;
382
383     /* Ok, we need to track the conversation so that we can
384      * determine if a new server session is occuring for this
385      * connection.
386      */
387     conversation = find_conversation(pinfo->fd->num, &pinfo->src, &pinfo->dst,
388         PT_NCP, (guint32) pinfo->srcport, (guint32) pinfo->destport,
389         0);
390     if ((ncpiph.length & 0x80000000) || ncpiph.signature == NCPIP_RPLY) {
391         /* First time through we will record the initial connection and task
392          * values
393          */
394         if (!pinfo->fd->flags.visited) {
395             if (conversation != NULL) {
396                 /* find the record telling us the
397                  * request made that caused this
398                  * reply
399                  */
400                 request_value = mncp_hash_lookup(conversation, nw_connection, header.task);
401                 /* if for some reason we have no
402                  * conversation in our hash, create
403                  * one */
404                 if (request_value == NULL) {
405                     request_value = mncp_hash_insert(conversation, nw_connection, header.task, pinfo);
406                 }
407             } else {
408                 /* It's not part of any conversation
409                  * - create a new one.
410                  */
411                 conversation = conversation_new(pinfo->fd->num, &pinfo->src,
412                     &pinfo->dst, PT_NCP, (guint32) pinfo->srcport, (guint32) pinfo->destport, 0);
413                 request_value = mncp_hash_insert(conversation, nw_connection, header.task, pinfo);
414             }
415             /* If this is a request packet then we
416              * might have a new task
417              */
418             if (ncpiph.signature == NCPIP_RPLY) {
419                 /* Now on reply packets we have to
420                  * use the state of the original
421                  * request packet, so look up the
422                  * request value and check the task number
423                  */
424                 request_value = mncp_hash_lookup(conversation, nw_connection, header.task);
425             }
426         } else {
427             /* Get request value data */
428             request_value = mncp_hash_lookup(conversation, nw_connection, header.task);
429             if (request_value) {
430                 if ((request_value->session_start_packet_num == pinfo->fd->num) && ncp_echo_conn)
431                 {
432                     expert_add_info_format(pinfo, NULL, PI_RESPONSE_CODE, PI_CHAT, "Detected New Server Session. Connection %d, Task %d", nw_connection, header.task);
433                 }
434             }
435         }
436     } else {
437         if (!pinfo->fd->flags.visited) {
438             if (conversation != NULL) {
439                 /* find the record telling us the
440                  * request made that caused this
441                  * reply
442                  */
443                 request_value = mncp_hash_lookup(conversation, nw_connection, header.task);
444                 /* if for some reason we have no
445                  * conversation in our hash, create
446                  * one */
447                 if (request_value == NULL) {
448                     request_value = mncp_hash_insert(conversation, nw_connection, header.task, pinfo);
449                 }
450             } else {
451                 /* It's not part of any conversation
452                  * - create a new one.
453                  */
454                 conversation = conversation_new(pinfo->fd->num, &pinfo->src,
455                     &pinfo->dst, PT_NCP, (guint32) pinfo->srcport, (guint32) pinfo->destport, 0);
456                 request_value = mncp_hash_insert(conversation, nw_connection, header.task, pinfo);
457             }
458             /* find the record telling us the request
459              * made that caused this reply
460              */
461         } else {
462             request_value = mncp_hash_lookup(conversation, nw_connection, header.task);
463             if (request_value) {
464                 if ((request_value->session_start_packet_num == pinfo->fd->num) && ncp_echo_conn)
465                 {
466                     expert_add_info_format(pinfo, NULL, PI_RESPONSE_CODE, PI_CHAT, "Detected New Server Session. Connection %d, Task %d", nw_connection, header.task);
467                 }
468             }
469         }
470     }
471
472     tap_queue_packet(ncp_tap.hdr, pinfo, ncp_hdr);
473
474     if (check_col(pinfo->cinfo, COL_INFO)) {
475         col_add_str(pinfo->cinfo, COL_INFO,
476             val_to_str(header.type, ncp_type_vals, "Unknown type (0x%04x)"));
477     }
478
479     /*
480      * Process the packet-type-specific header.
481      */
482     switch (header.type) {
483
484     case NCP_BROADCAST_SLOT:    /* Server Broadcast */
485         proto_tree_add_uint(ncp_tree, hf_ncp_seq, tvb, commhdr + 2, 1, header.sequence);
486         proto_tree_add_uint(ncp_tree, hf_ncp_connection,tvb, commhdr + 3, 3, nw_connection);
487         proto_tree_add_item(ncp_tree, hf_ncp_task, tvb, commhdr + 4, 1, ENC_BIG_ENDIAN);
488         proto_tree_add_item(ncp_tree, hf_ncp_oplock_flag, tvb, commhdr + 9, 1, tvb_get_guint8(tvb, commhdr+9));
489         proto_tree_add_item(ncp_tree, hf_ncp_oplock_handle, tvb, commhdr + 10, 4, ENC_BIG_ENDIAN);
490         if ((tvb_get_guint8(tvb, commhdr+9)==0x24) && ncp_echo_file)
491         {
492             expert_add_info_format(pinfo, NULL, PI_RESPONSE_CODE, PI_CHAT, "Server requesting station to clear oplock on handle - %08x", tvb_get_ntohl(tvb, commhdr+10));
493         }
494         break;
495
496     case NCP_LIP_ECHO:    /* Lip Echo Packet */
497         proto_tree_add_item(ncp_tree, hf_lip_echo, tvb, commhdr, 13, ENC_ASCII|ENC_NA);
498         break;
499
500     case NCP_BURST_MODE_XFER:    /* Packet Burst Packet */
501         /*
502          * XXX - we should keep track of whether there's a burst
503          * outstanding on a connection and, if not, treat the
504          * beginning of the data as a burst header.
505          *
506          * The burst header contains:
507          *
508          *    4 bytes of little-endian function number:
509          *        1 = read, 2 = write;
510          *
511          *    4 bytes of file handle;
512          *
513          *    8 reserved bytes;
514          *
515          *    4 bytes of big-endian file offset;
516          *
517          *    4 bytes of big-endian byte count.
518          *
519          * The data follows for a burst write operation.
520          *
521          * The first packet of a burst read reply contains:
522          *
523          *    4 bytes of little-endian result code:
524          *       0: No error
525          *       1: Initial error
526          *       2: I/O error
527          *       3: No data read;
528          *
529          *    4 bytes of returned byte count (big-endian?).
530          *
531          * The data follows.
532          *
533          * Each burst of a write request is responded to with a
534          * burst packet with a 2-byte little-endian result code:
535          *
536          *    0: Write successful
537          *    4: Write error
538          */
539         flags = tvb_get_guint8(tvb, commhdr + 2);
540
541         ti = proto_tree_add_uint(ncp_tree, hf_ncp_system_flags,
542             tvb, commhdr + 2, 1, flags);
543         flags_tree = proto_item_add_subtree(ti, ett_ncp_system_flags);
544
545         proto_tree_add_item(flags_tree, hf_ncp_system_flags_abt,
546             tvb, commhdr + 2, 1, ENC_BIG_ENDIAN);
547         if (flags & ABT) {
548             proto_item_append_text(ti, "  ABT");
549         }
550         flags&=(~( ABT ));
551
552         proto_tree_add_item(flags_tree, hf_ncp_system_flags_bsy,
553             tvb, commhdr + 2, 1, ENC_BIG_ENDIAN);
554         if (flags & BSY) {
555             proto_item_append_text(ti, "  BSY");
556         }
557         flags&=(~( BSY ));
558
559         proto_tree_add_item(flags_tree, hf_ncp_system_flags_eob,
560             tvb, commhdr + 2, 1, ENC_BIG_ENDIAN);
561         if (flags & EOB) {
562             proto_item_append_text(ti, "  EOB");
563         }
564         flags&=(~( EOB ));
565
566         proto_tree_add_item(flags_tree, hf_ncp_system_flags_lst,
567             tvb, commhdr + 2, 1, ENC_BIG_ENDIAN);
568         if (flags & LST) {
569             proto_item_append_text(ti, "  LST");
570         }
571         flags&=(~( LST ));
572
573         proto_tree_add_item(flags_tree, hf_ncp_system_flags_sys,
574             tvb, commhdr + 2, 1, ENC_BIG_ENDIAN);
575         if (flags & SYS) {
576             proto_item_append_text(ti, "  SYS");
577         }
578         flags&=(~( SYS ));
579
580
581         proto_tree_add_item(ncp_tree, hf_ncp_stream_type,
582             tvb, commhdr + 3, 1, ENC_BIG_ENDIAN);
583         proto_tree_add_item(ncp_tree, hf_ncp_src_connection,
584             tvb, commhdr + 4, 4, ENC_BIG_ENDIAN);
585         proto_tree_add_item(ncp_tree, hf_ncp_dst_connection,
586             tvb, commhdr + 8, 4, ENC_BIG_ENDIAN);
587         proto_tree_add_item(ncp_tree, hf_ncp_packet_seqno,
588             tvb, commhdr + 12, 4, ENC_BIG_ENDIAN);
589         proto_tree_add_item(ncp_tree, hf_ncp_delay_time,
590             tvb, commhdr + 16, 4, ENC_BIG_ENDIAN);
591         ncp_burst_seqno = tvb_get_ntohs(tvb, commhdr+20);
592         proto_tree_add_item(ncp_tree, hf_ncp_burst_seqno,
593             tvb, commhdr + 20, 2, ENC_BIG_ENDIAN);
594         ncp_ack_seqno = tvb_get_ntohs(tvb, commhdr+22);
595         proto_tree_add_item(ncp_tree, hf_ncp_ack_seqno,
596             tvb, commhdr + 22, 2, ENC_BIG_ENDIAN);
597         proto_tree_add_item(ncp_tree, hf_ncp_burst_len,
598             tvb, commhdr + 24, 4, ENC_BIG_ENDIAN);
599         data_offset = tvb_get_ntohl(tvb, commhdr + 28);
600         proto_tree_add_uint(ncp_tree, hf_ncp_data_offset,
601             tvb, commhdr + 28, 4, data_offset);
602         data_len = tvb_get_ntohs(tvb, commhdr + 32);
603         proto_tree_add_uint(ncp_tree, hf_ncp_data_bytes,
604             tvb, commhdr + 32, 2, data_len);
605         missing_fraglist_count = tvb_get_ntohs(tvb, commhdr + 34);
606         proto_tree_add_item(ncp_tree, hf_ncp_missing_fraglist_count,
607             tvb, commhdr + 34, 2, ENC_BIG_ENDIAN);
608         offset = commhdr + 36;
609         if (!(flags & SYS) && ncp_burst_seqno == ncp_ack_seqno &&
610             data_offset == 0) {
611             /*
612              * This is either a Burst Read or Burst Write
613              * command.  The data length includes the burst
614              * mode header, plus any data in the command
615              * (there shouldn't be any in a read, but there
616              * might be some in a write).
617              */
618             if (data_len < 4)
619                 return;
620             ncp_burst_command = tvb_get_ntohl(tvb, offset);
621             proto_tree_add_item(ncp_tree, hf_ncp_burst_command,
622                 tvb, offset, 4, ENC_BIG_ENDIAN);
623             offset += 4;
624             data_len -= 4;
625
626             if (data_len < 4)
627                 return;
628             burst_file = tvb_get_ntohl(tvb, offset);
629             proto_tree_add_item(ncp_tree, hf_ncp_burst_file_handle,
630                 tvb, offset, 4, ENC_BIG_ENDIAN);
631             offset += 4;
632             data_len -= 4;
633
634             if (data_len < 8)
635                 return;
636             proto_tree_add_item(ncp_tree, hf_ncp_burst_reserved,
637                 tvb, offset, 8, ENC_NA);
638             offset += 8;
639             data_len -= 8;
640
641             if (data_len < 4)
642                 return;
643             burst_off = tvb_get_ntohl(tvb, offset);
644             proto_tree_add_uint(ncp_tree, hf_ncp_burst_offset,
645                 tvb, offset, 4, burst_off);
646             offset += 4;
647             data_len -= 4;
648
649             if (data_len < 4)
650                 return;
651             burst_len = tvb_get_ntohl(tvb, offset);
652             proto_tree_add_uint(ncp_tree, hf_ncp_burst_len,
653                 tvb, offset, 4, burst_len);
654             offset += 4;
655             data_len -= 4;
656
657             if (check_col(pinfo->cinfo, COL_INFO)) {
658                 col_add_fstr(pinfo->cinfo, COL_INFO,
659                     "%s %d bytes starting at offset %d in file 0x%08x",
660                     val_to_str(ncp_burst_command,
661                       burst_command, "Unknown (0x%08x)"),
662                      burst_len, burst_off, burst_file);
663             }
664             break;
665         } else {
666             if (tvb_get_guint8(tvb, commhdr + 2) & 0x10) {
667                 col_set_str(pinfo->cinfo, COL_INFO, "End of Burst");
668             }
669         }
670         break;
671
672     case NCP_ALLOCATE_SLOT:        /* Allocate Slot Request */
673         length_remaining = tvb_length_remaining(tvb, commhdr + 4);
674         if (length_remaining > 4) {
675             testvar = tvb_get_ntohl(tvb, commhdr+4);
676             if (testvar == 0x4c495020) {
677                 proto_tree_add_item(ncp_tree, hf_lip_echo, tvb, commhdr+4, 13, ENC_ASCII|ENC_NA);
678                 break;
679             }
680         }
681         /* otherwise fall through */
682
683     case NCP_POSITIVE_ACK:        /* Positive Acknowledgement */
684     case NCP_SERVICE_REQUEST:    /* Server NCP Request */
685     case NCP_SERVICE_REPLY:        /* Server NCP Reply */
686     case NCP_WATCHDOG:        /* Watchdog Packet */
687     case NCP_DEALLOCATE_SLOT:    /* Deallocate Slot Request */
688     default:
689         proto_tree_add_uint(ncp_tree, hf_ncp_seq, tvb, commhdr + 2, 1, header.sequence);
690         proto_tree_add_uint(ncp_tree, hf_ncp_connection,tvb, commhdr + 3, 3, nw_connection);
691         proto_tree_add_item(ncp_tree, hf_ncp_task, tvb, commhdr + 4, 1, ENC_BIG_ENDIAN);
692         break;
693     }
694
695     /*
696      * Process the packet body.
697      */
698     switch (header.type) {
699
700     case NCP_ALLOCATE_SLOT:        /* Allocate Slot Request */
701         length_remaining = tvb_length_remaining(tvb, commhdr + 4);
702         if (length_remaining > 4) {
703             testvar = tvb_get_ntohl(tvb, commhdr+4);
704             if (testvar == 0x4c495020) {
705                 proto_tree_add_text(ncp_tree, tvb, commhdr, -1,
706                     "Lip Echo Packet");
707                 /*break;*/
708             }
709         }
710         next_tvb = tvb_new_subset_remaining(tvb, commhdr);
711         dissect_ncp_request(next_tvb, pinfo, nw_connection,
712             header.sequence, header.type, ncp_tree);
713         break;
714
715     case NCP_DEALLOCATE_SLOT:    /* Deallocate Slot Request */
716         next_tvb = tvb_new_subset_remaining(tvb, commhdr);
717         dissect_ncp_request(next_tvb, pinfo, nw_connection,
718             header.sequence, header.type, ncp_tree);
719         break;
720
721     case NCP_SERVICE_REQUEST:    /* Server NCP Request */
722     case NCP_BROADCAST_SLOT:    /* Server Broadcast Packet */
723         next_tvb = tvb_new_subset_remaining(tvb, commhdr);
724         if (tvb_get_guint8(tvb, commhdr+6) == 0x68) {
725             subfunction = tvb_get_guint8(tvb, commhdr+7);
726             switch (subfunction) {
727
728             case 0x02:    /* NDS Frag Packet to decode */
729                 dissect_nds_request(next_tvb, pinfo,
730                     nw_connection, header.sequence,
731                     header.type, ncp_tree);
732                 break;
733
734             case 0x01:    /* NDS Ping */
735                 dissect_ping_req(next_tvb, pinfo,
736                     nw_connection, header.sequence,
737                     header.type, ncp_tree);
738                 break;
739
740             default:
741                 dissect_ncp_request(next_tvb, pinfo,
742                     nw_connection, header.sequence,
743                     header.type, ncp_tree);
744                 break;
745              }
746         } else {
747             dissect_ncp_request(next_tvb, pinfo, nw_connection,
748                 header.sequence, header.type, ncp_tree);
749         }
750         break;
751
752     case NCP_SERVICE_REPLY:        /* Server NCP Reply */
753         next_tvb = tvb_new_subset_remaining(tvb, commhdr);
754         nds_defrag(next_tvb, pinfo, nw_connection, header.sequence,
755             header.type, ncp_tree, &ncp_tap);
756         break;
757
758     case NCP_POSITIVE_ACK:        /* Positive Acknowledgement */
759         /*
760          * XXX - this used to call "nds_defrag()", which would
761          * clear out "frags".  Was that the right thing to
762          * do?
763          */
764         next_tvb = tvb_new_subset_remaining(tvb, commhdr);
765         dissect_ncp_reply(next_tvb, pinfo, nw_connection,
766             header.sequence, header.type, ncp_tree, &ncp_tap);
767         break;
768
769     case NCP_WATCHDOG:        /* Watchdog Packet */
770         /*
771          * XXX - should the completion code be interpreted as
772          * it is in "packet-ncp2222.inc"?  If so, this
773          * packet should be handled by "dissect_ncp_reply()".
774          */
775         proto_tree_add_item(ncp_tree, hf_ncp_completion_code,
776             tvb, commhdr + 6, 1, ENC_LITTLE_ENDIAN);
777         proto_tree_add_item(ncp_tree, hf_ncp_connection_status,
778             tvb, commhdr + 7, 1, ENC_LITTLE_ENDIAN);
779         proto_tree_add_item(ncp_tree, hf_ncp_slot,
780             tvb, commhdr + 8, 1, ENC_LITTLE_ENDIAN);
781         proto_tree_add_item(ncp_tree, hf_ncp_control_code,
782             tvb, commhdr + 9, 1, ENC_LITTLE_ENDIAN);
783         /*
784          * Display the rest of the packet as data.
785          */
786         if (tvb_offset_exists(tvb, commhdr + 10)) {
787             call_dissector(data_handle,
788                 tvb_new_subset_remaining(tvb, commhdr + 10),
789                 pinfo, ncp_tree);
790         }
791         break;
792
793     case NCP_BURST_MODE_XFER:    /* Packet Burst Packet */
794         if (flags & SYS) {
795             /*
796              * System packet; show missing fragments if there
797              * are any.
798              */
799             while (missing_fraglist_count != 0) {
800                 proto_tree_add_item(ncp_tree, hf_ncp_missing_data_offset,
801                     tvb, offset, 4, ENC_BIG_ENDIAN);
802                 offset += 4;
803                 proto_tree_add_item(ncp_tree, hf_ncp_missing_data_count,
804                     tvb, offset, 2, ENC_BIG_ENDIAN);
805                 offset += 2;
806                 missing_fraglist_count--;
807             }
808         } else {
809             /*
810              * XXX - do this by using -1 and -1 as the length
811              * arguments to "tvb_new_subset()" and then calling
812              * "tvb_set_reported_length()"?  That'll throw an
813              * exception if "data_len" goes past the reported
814              * length of the packet, but that's arguably a
815              * feature in this case.
816              */
817             length_remaining = tvb_length_remaining(tvb, offset);
818             if (length_remaining > data_len)
819                 length_remaining = data_len;
820             if (data_len != 0) {
821                 call_dissector(data_handle,
822                     tvb_new_subset(tvb, offset,
823                     length_remaining, data_len),
824                     pinfo, ncp_tree);
825             }
826         }
827         break;
828
829     case NCP_LIP_ECHO:        /* LIP Echo Packet */
830         proto_tree_add_text(ncp_tree, tvb, commhdr, -1,
831             "Lip Echo Packet");
832         break;
833
834     default:
835         expert_item = proto_tree_add_text(ncp_tree, tvb, commhdr + 6, -1,
836             "%s packets not supported yet",
837             val_to_str(header.type, ncp_type_vals,
838                 "Unknown type (0x%04x)"));
839         if (ncp_echo_err) {
840             expert_add_info_format(pinfo, expert_item, PI_UNDECODED, PI_NOTE, "%s packets not supported yet", val_to_str(header.type, ncp_type_vals,
841                 "Unknown type (0x%04x)"));
842         }
843         break;
844     }
845 }
846
847 static void
848 dissect_ncp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
849 {
850     dissect_ncp_common(tvb, pinfo, tree, FALSE);
851 }
852
853 static guint
854 get_ncp_pdu_len(packet_info *pinfo _U_, tvbuff_t *tvb, int offset)
855 {
856   guint32 signature;
857
858   /*
859    * Check the NCP-over-TCP header signature, to make sure it's there.
860    * If it's not there, we cannot trust the next 4 bytes to be a
861    * packet length+"has signature" flag, so we just say the length is
862    * "what remains in the packet".
863    */
864   signature = tvb_get_ntohl(tvb, offset);
865   if (signature != NCPIP_RQST && signature != NCPIP_RPLY)
866     return tvb_length_remaining(tvb, offset);
867
868   /*
869    * Get the length of the NCP-over-TCP packet.  Strip off the "has
870    * signature" flag.
871    */
872
873   return tvb_get_ntohl(tvb, offset + 4) & 0x7fffffff;
874 }
875
876 static void
877 dissect_ncp_tcp_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
878 {
879     dissect_ncp_common(tvb, pinfo, tree, TRUE);
880 }
881
882 static void
883 dissect_ncp_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
884 {
885     tcp_dissect_pdus(tvb, pinfo, tree, ncp_desegment, 8, get_ncp_pdu_len,
886                      dissect_ncp_tcp_pdu);
887 }
888
889 void
890 proto_register_ncp(void)
891 {
892     static hf_register_info hf[] = {
893         { &hf_ncp_ip_sig,
894           { "NCP over IP signature",            "ncp.ip.signature",
895             FT_UINT32, BASE_HEX, VALS(ncp_ip_signature), 0x0,
896             NULL, HFILL }},
897         { &hf_ncp_ip_length,
898           { "NCP over IP length",               "ncp.ip.length",
899             FT_UINT32, BASE_DEC, NULL, 0x0,
900             NULL, HFILL }},
901         { &hf_ncp_ip_ver,
902           { "NCP over IP Version",              "ncp.ip.version",
903             FT_UINT32, BASE_DEC, NULL, 0x0,
904             NULL, HFILL }},
905         { &hf_ncp_ip_rplybufsize,
906           { "NCP over IP Reply Buffer Size",    "ncp.ip.replybufsize",
907             FT_UINT32, BASE_DEC, NULL, 0x0,
908             NULL, HFILL }},
909         { &hf_ncp_ip_packetsig,
910           { "NCP over IP Packet Signature",     "ncp.ip.packetsig",
911             FT_BYTES, BASE_NONE, NULL, 0x0,
912             NULL, HFILL }},
913         { &hf_ncp_type,
914           { "Type",                             "ncp.type",
915             FT_UINT16, BASE_HEX, VALS(ncp_type_vals), 0x0,
916             "NCP message type", HFILL }},
917         { &hf_ncp_seq,
918           { "Sequence Number",                  "ncp.seq",
919             FT_UINT8, BASE_DEC, NULL, 0x0,
920             NULL, HFILL }},
921         { &hf_ncp_connection,
922           { "Connection Number",                "ncp.connection",
923             FT_UINT16, BASE_DEC, NULL, 0x0,
924             NULL, HFILL }},
925         { &hf_ncp_task,
926           { "Task Number",                      "ncp.task",
927             FT_UINT8, BASE_DEC, NULL, 0x0,
928             NULL, HFILL }},
929         { &hf_ncp_oplock_flag,
930           { "Broadcast Message Flag",           "ncp.msg_flag",
931             FT_UINT8, BASE_HEX, VALS(ncp_oplock_vals), 0x0,
932             NULL, HFILL }},
933         { &hf_ncp_oplock_handle,
934           { "File Handle",                      "ncp.oplock_handle",
935             FT_UINT16, BASE_HEX, NULL, 0x0,
936             NULL, HFILL }},
937         { &hf_ncp_stream_type,
938           { "Stream Type",                      "ncp.stream_type",
939             FT_UINT8, BASE_HEX, NULL, 0x0,
940             "Type of burst", HFILL }},
941         { &hf_ncp_system_flags,
942           { "System Flags",                     "ncp.system_flags",
943             FT_UINT8, BASE_HEX, NULL, 0x0,
944             NULL, HFILL }},
945         { &hf_ncp_system_flags_abt,
946           { "ABT",                              "ncp.system_flags.abt",
947             FT_BOOLEAN, 8, NULL, ABT,
948             "Is this an abort request?", HFILL }},
949         { &hf_ncp_system_flags_eob,
950           { "EOB",                              "ncp.system_flags.eob",
951             FT_BOOLEAN, 8, NULL, EOB,
952             "Is this the last packet of the burst?", HFILL }},
953         { &hf_ncp_system_flags_sys,
954           { "SYS",                              "ncp.system_flags.sys",
955             FT_BOOLEAN, 8, NULL, SYS,
956             "Is this a system packet?", HFILL }},
957         { &hf_ncp_system_flags_bsy,
958           { "BSY",                              "ncp.system_flags.bsy",
959             FT_BOOLEAN, 8, NULL, BSY,
960             "Is the server busy?", HFILL }},
961         { &hf_ncp_system_flags_lst,
962           { "LST",                              "ncp.system_flags.lst",
963             FT_BOOLEAN, 8, NULL, LST,
964             "Return Fragment List?", HFILL }},
965         { &hf_ncp_src_connection,
966           { "Source Connection ID",             "ncp.src_connection",
967             FT_UINT32, BASE_DEC, NULL, 0x0,
968             "The workstation's connection identification number", HFILL }},
969         { &hf_ncp_dst_connection,
970           { "Destination Connection ID",        "ncp.dst_connection",
971             FT_UINT32, BASE_DEC, NULL, 0x0,
972             "The server's connection identification number", HFILL }},
973         { &hf_ncp_packet_seqno,
974           { "Packet Sequence Number",           "ncp.packet_seqno",
975             FT_UINT32, BASE_DEC, NULL, 0x0,
976             "Sequence number of this packet in a burst", HFILL }},
977         { &hf_ncp_delay_time,
978           { "Delay Time",                       "ncp.delay_time",       /* in 100 us increments */
979             FT_UINT32, BASE_DEC, NULL, 0x0,
980             "Delay time between consecutive packet sends (100 us increments)", HFILL }},
981         { &hf_ncp_burst_seqno,
982           { "Burst Sequence Number",            "ncp.burst_seqno",
983             FT_UINT16, BASE_DEC, NULL, 0x0,
984             "Sequence number of this packet in the burst", HFILL }},
985         { &hf_ncp_ack_seqno,
986           { "ACK Sequence Number",              "ncp.ack_seqno",
987             FT_UINT16, BASE_DEC, NULL, 0x0,
988             "Next expected burst sequence number", HFILL }},
989         { &hf_ncp_burst_len,
990           { "Burst Length",                     "ncp.burst_len",
991             FT_UINT32, BASE_DEC, NULL, 0x0,
992             "Total length of data in this burst", HFILL }},
993         { &hf_ncp_burst_offset,
994           { "Burst Offset",                     "ncp.burst_offset",
995             FT_UINT32, BASE_DEC, NULL, 0x0,
996             "Offset of data in the burst", HFILL }},
997         { &hf_ncp_data_offset,
998           { "Data Offset",                      "ncp.data_offset",
999             FT_UINT32, BASE_DEC, NULL, 0x0,
1000             "Offset of this packet", HFILL }},
1001         { &hf_ncp_data_bytes,
1002           { "Data Bytes",                       "ncp.data_bytes",
1003             FT_UINT16, BASE_DEC, NULL, 0x0,
1004             "Number of data bytes in this packet", HFILL }},
1005         { &hf_ncp_missing_fraglist_count,
1006           { "Missing Fragment List Count",      "ncp.missing_fraglist_count",
1007             FT_UINT16, BASE_DEC, NULL, 0x0,
1008             "Number of missing fragments reported", HFILL }},
1009         { &hf_ncp_missing_data_offset,
1010           { "Missing Data Offset",              "ncp.missing_data_offset",
1011             FT_UINT32, BASE_DEC, NULL, 0x0,
1012             "Offset of beginning of missing data", HFILL }},
1013         { &hf_ncp_missing_data_count,
1014           { "Missing Data Count",               "ncp.missing_data_count",
1015             FT_UINT16, BASE_DEC, NULL, 0x0,
1016             "Number of bytes of missing data", HFILL }},
1017         { &hf_ncp_completion_code,
1018           { "Completion Code",                  "ncp.completion_code",
1019             FT_UINT8, BASE_DEC, NULL, 0x0,
1020             NULL, HFILL }},
1021         { &hf_ncp_connection_status,
1022           { "Connection Status",                "ncp.connection_status",
1023             FT_UINT8, BASE_DEC, NULL, 0x0,
1024             NULL, HFILL }},
1025         { &hf_ncp_slot,
1026           { "Slot",                             "ncp.slot",
1027             FT_UINT8, BASE_DEC, NULL, 0x0,
1028             NULL, HFILL }},
1029         { &hf_ncp_control_code,
1030           { "Control Code",                     "ncp.control_code",
1031             FT_UINT8, BASE_DEC, NULL, 0x0,
1032             NULL, HFILL }},
1033         { &hf_ncp_fragment_handle,
1034           { "Fragment Handle",                  "ncp.fragger_hndl",
1035             FT_UINT16, BASE_HEX, NULL, 0x0,
1036             NULL, HFILL }},
1037         { &hf_lip_echo,
1038           { "Large Internet Packet Echo",       "ncp.lip_echo",
1039             FT_STRING, BASE_NONE, NULL, 0x0,
1040             NULL, HFILL }},
1041         { &hf_ncp_burst_command,
1042           { "Burst Command",                    "ncp.burst_command",
1043             FT_UINT32, BASE_HEX, VALS(burst_command), 0x0,
1044             "Packet Burst Command", HFILL }},
1045         { &hf_ncp_burst_file_handle,
1046           { "Burst File Handle",                "ncp.file_handle",
1047             FT_UINT32, BASE_HEX, NULL, 0x0,
1048             "Packet Burst File Handle", HFILL }},
1049         { &hf_ncp_burst_reserved,
1050           { "Reserved",                         "ncp.burst_reserved",
1051             FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }},
1052
1053     };
1054     static gint *ett[] = {
1055         &ett_ncp,
1056         &ett_ncp_system_flags,
1057         &ett_nds,
1058         &ett_nds_segments,
1059         &ett_nds_segment,
1060     };
1061     module_t *ncp_module;
1062
1063     proto_ncp = proto_register_protocol("NetWare Core Protocol", "NCP", "ncp");
1064     proto_register_field_array(proto_ncp, hf, array_length(hf));
1065     proto_register_subtree_array(ett, array_length(ett));
1066
1067     ncp_module = prefs_register_protocol(proto_ncp, NULL);
1068     prefs_register_obsolete_preference(ncp_module, "initial_hash_size");
1069     prefs_register_bool_preference(ncp_module, "desegment",
1070                                    "Reassemble NCP-over-TCP messages spanning multiple TCP segments",
1071                                    "Whether the NCP dissector should reassemble messages spanning multiple TCP segments."
1072                                    " To use this option, you must also enable \"Allow subdissectors to reassemble TCP streams\" in the TCP protocol settings.",
1073                                    &ncp_desegment);
1074     prefs_register_bool_preference(ncp_module, "defragment_nds",
1075                                    "Reassemble fragmented NDS messages spanning multiple reply packets",
1076                                    "Whether the NCP dissector should defragment NDS messages spanning multiple reply packets.",
1077                                    &nds_defragment);
1078     prefs_register_bool_preference(ncp_module, "newstyle",
1079                                    "Dissect New Netware Information Structure",
1080                                    "Dissect the NetWare Information Structure as NetWare 5.x or higher or as older NetWare 3.x.",
1081                                    &ncp_newstyle);
1082     prefs_register_bool_preference(ncp_module, "eid_2_expert",
1083                                    "Expert: EID to Name lookups?",
1084                                    "Whether the NCP dissector should echo the NDS Entry ID to name resolves to the expert table.",
1085                                    &nds_echo_eid);
1086     prefs_register_bool_preference(ncp_module, "connection_2_expert",
1087                                    "Expert: NCP Connections?",
1088                                    "Whether the NCP dissector should echo NCP connection information to the expert table.",
1089                                    &ncp_echo_conn);
1090     prefs_register_bool_preference(ncp_module, "error_2_expert",
1091                                    "Expert: NCP Errors?",
1092                                    "Whether the NCP dissector should echo protocol errors to the expert table.",
1093                                    &ncp_echo_err);
1094     prefs_register_bool_preference(ncp_module, "server_2_expert",
1095                                    "Expert: Server Information?",
1096                                    "Whether the NCP dissector should echo server information to the expert table.",
1097                                    &ncp_echo_server);
1098     prefs_register_bool_preference(ncp_module, "file_2_expert",
1099                                    "Expert: File Information?",
1100                                    "Whether the NCP dissector should echo file open/close/oplock information to the expert table.",
1101                                    &ncp_echo_file);
1102     register_init_routine(&mncp_init_protocol);
1103     ncp_tap.stat=register_tap("ncp_srt");
1104     ncp_tap.hdr=register_tap("ncp_hdr");
1105     register_postseq_cleanup_routine(&mncp_postseq_cleanup);
1106 }
1107
1108 void
1109 proto_reg_handoff_ncp(void)
1110 {
1111     dissector_handle_t ncp_handle;
1112     dissector_handle_t ncp_tcp_handle;
1113
1114     ncp_handle = create_dissector_handle(dissect_ncp, proto_ncp);
1115     ncp_tcp_handle = create_dissector_handle(dissect_ncp_tcp, proto_ncp);
1116     dissector_add_uint("tcp.port", TCP_PORT_NCP, ncp_tcp_handle);
1117     dissector_add_uint("udp.port", UDP_PORT_NCP, ncp_handle);
1118     dissector_add_uint("ipx.packet_type", IPX_PACKET_TYPE_NCP, ncp_handle);
1119     dissector_add_uint("ipx.socket", IPX_SOCKET_NCP, ncp_handle);
1120
1121     data_handle = find_dissector("data");
1122 }