2 * Routines for NetWare Core Protocol
3 * Gilbert Ramirez <gram@verdict.uthscsa.edu>
5 * $Id: packet-ncp.c,v 1.12 1999/05/13 16:42:43 gram Exp $
7 * Ethereal - Network traffic analyzer
8 * By Gerald Combs <gerald@unicom.net>
9 * Copyright 1998 Gerald Combs
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
33 #ifdef HAVE_SYS_TYPES_H
34 # include <sys/types.h>
37 #ifdef HAVE_NETINET_IN_H
38 # include <netinet/in.h>
43 #include "packet-ipx.h"
44 #include "packet-ncp.h"
49 dissect_ncp_request(const u_char *pd, int offset, frame_data *fd, proto_tree *ncp_tree, proto_tree *tree);
52 dissect_ncp_reply(const u_char *pd, int offset, frame_data *fd, proto_tree *ncp_tree, proto_tree *tree);
54 static struct ncp2222_record *
55 ncp2222_find(guint8 func, guint8 subfunc);
58 parse_ncp_svc_fields(const u_char *pd, proto_tree *ncp_tree, int offset,
59 struct svc_record *svc);
62 svc_record_byte_count(struct svc_record *sr);
65 gint ncp_equal (const gpointer v, const gpointer v2);
66 guint ncp_hash (const gpointer v);
69 /* The information in this module comes from:
70 NetWare LAN Analysis, Second Edition
71 Laura A. Chappell and Dan E. Hakes
73 Novell Press, San Jose.
76 And from the ncpfs source code by Volker Lendecke
79 Programmer's Guide to the NetWare Core Protocol
80 Steve Conner & Diane Conner
81 (c) 1996 by Steve Conner & Diane Conner
82 Published by Annabooks, San Diego, California
87 /* Every NCP packet has this common header */
88 struct ncp_common_header {
96 /* NCP request packets */
97 struct ncp_request_header {
108 /* NCP reply packets */
109 struct ncp_reply_header {
115 guint8 completion_code;
116 guint8 connection_state;
120 static value_string request_reply_values[] = {
121 { 0x1111, "Create a service connection" },
122 { 0x2222, "Service request" },
123 { 0x3333, "Service reply" },
124 { 0x5555, "Destroy service connection" },
125 { 0x7777, "Burst mode transfer" },
126 { 0x9999, "Request being processed" },
130 /* These are the field types in an NCP packet */
132 nend, /* end of the NCP field list */
133 nbyte, /* one byte of data */
135 nbelong, /* 4-byte big-endian long int */
136 nbeshort, /* 2-byte big-endian short int */
137 ndata, /* unstructured data */
138 nbytevar, /* a variable number of bytes */
139 ndatetime, /* date-time stamp */
140 nasciiz /* null-terminated string of ASCII characters */
143 /* Information on the NCP field */
144 typedef struct svc_record {
150 typedef struct ncp2222_record {
158 void* special_handler;
162 /* Service Queue Job REQUEST */
163 static svc_record ncp_17_7C_C[] = {
164 { nbelong, 4, "The queue the job resides in" },
165 { nbeshort, 2, "Job Type" },
168 /* Service Queue Job REPLY */
169 static svc_record ncp_17_7C_R[] = {
170 { nbelong, 4, "Client station number" },
171 { nbelong, 4, "Task Number" },
172 { nbelong, 4, "User" },
173 { nbelong, 4, "Server specifed to service queue entry" },
174 { ndatetime, 6, "Earliest time to execute" },
175 { ndatetime, 6, "When job entered queue" },
176 { nbelong, 4, "Job Number" },
177 { nbeshort, 2, "Job Type" },
178 { nbeshort, 2, "Job Position" },
179 { nbeshort, 2, "Current status of job" },
180 { nasciiz, 14, "Name of file" },
181 { nbelong, 4, "File handle" },
182 { nbelong, 4, "Client station number" },
183 { nbelong, 4, "Task number" },
184 { nbelong, 4, "Job server" },
188 /* Negotiate Buffer Size REQUEST */
189 static svc_record ncp_21_00_C[] = {
190 { nbeshort, 2, "Caller's maximum packet size: %d bytes" },
194 static svc_record ncp_21_00_R[] = {
195 { nbeshort, 2, "Packet size decided upon by file server: %d" },
199 /* Read from a file REQUEST */
200 static svc_record ncp_48_00_C[] = {
201 { nbyte, 1, "Unknown" },
202 { nhex, 6, "File Handle" },
203 { nbelong, 4, "Byte offset within file" },
204 { nbeshort, 2, "Maximum data bytes to return" },
208 static svc_record ncp_48_00_R[] = {
209 { nbeshort, 2, "Data bytes returned" },
210 { nbytevar, 1, "Padding" },
217 static ncp2222_record ncp2222[] = {
219 { 0x17, 0x7C, SUBFUNC, "Service Queue Job",
220 ncp_17_7C_C, ncp_17_7C_R, NULL
223 { 0x21, 0x00, NOSUB, "Negotiate Buffer Size",
224 ncp_21_00_C, ncp_21_00_R, NULL
227 { 0x48, 0x00, NOSUB, "Read from a file",
228 ncp_48_00_C, ncp_48_00_R, NULL
231 { 0x00, 0x00, NOSUB, NULL,
237 /* NCP packets come in request/reply pairs. The request packets tell the type
238 * of NCP request and give a sequence ID. The response, unfortunately, only
239 * identifies itself via the sequence ID; you have to know what type of NCP
240 * request the request packet contained in order to successfully parse the NCP
241 * response. A global method for doing this does not exist in ethereal yet
242 * (NFS also requires it), so for now the NCP section will keep its own hash
243 * table keeping track of NCP packet types.
245 * The key representing the unique NCP request is composed of 3 variables:
247 * ServerIPXNetwork.Connection.SequenceNumber
248 * 4 bytes 2 bytes 1 byte
249 * guint32 guint16 guint8 (all are host order)
251 * This assumes that all NCP connection is between a client and server.
252 * Servers can be identified by having a 00:00:00:00:00:01 IPX Node address.
253 * We have to let the IPX layer pass us the ServerIPXNetwork via a global
254 * variable (nw_server_address). In the future, if we decode NCP over TCP/UDP,
255 * then nw_server_address will represent the IP address of the server, which
256 * conveniently, is also 4 bytes long.
258 * The value stored in the hash table is the ncp_request_val pointer. This
259 * struct tells us the NCP type and gives the ncp2222_record pointer, if
260 * ncp_type == 0x2222.
262 guint32 nw_server_address = 0; /* set by IPX layer */
263 guint16 nw_connection = 0; /* set by dissect_ncp */
264 guint8 nw_sequence = 0; /* set by dissect_ncp */
265 guint16 nw_ncp_type = 0; /* set by dissect_ncp */
267 struct ncp_request_key {
268 guint32 nw_server_address;
269 guint16 nw_connection;
273 struct ncp_request_val {
275 struct ncp2222_record* ncp_record;
278 GHashTable *ncp_request_hash = NULL;
279 GMemChunk *ncp_request_keys = NULL;
280 GMemChunk *ncp_request_records = NULL;
283 gint ncp_equal (const gpointer v, const gpointer v2)
285 struct ncp_request_key *val1 = (struct ncp_request_key*)v;
286 struct ncp_request_key *val2 = (struct ncp_request_key*)v2;
288 #if defined(DEBUG_NCP_HASH)
289 printf("Comparing %08X:%d:%d and %08X:%d:%d\n",
290 val1->nw_server_address, val1->nw_connection, val1->nw_sequence,
291 val2->nw_server_address, val2->nw_connection, val2->nw_sequence);
294 if (val1->nw_server_address == val2->nw_server_address &&
295 val1->nw_connection == val2->nw_connection &&
296 val1->nw_sequence == val2->nw_sequence ) {
302 guint ncp_hash (const gpointer v)
304 struct ncp_request_key *ncp_key = (struct ncp_request_key*)v;
305 #if defined(DEBUG_NCP_HASH)
306 printf("hash calculated as %d\n", ncp_key->nw_server_address +
307 ((guint32) ncp_key->nw_connection << 16) +
308 ncp_key->nw_sequence);
310 return ncp_key->nw_server_address +
311 ((guint32) ncp_key->nw_connection << 16) +
312 ncp_key->nw_sequence;
315 /* Initializes the hash table and the mem_chunk area each time a new
316 * file is loaded or re-loaded in ethereal */
318 ncp_init_protocol(void)
320 #if defined(DEBUG_NCP_HASH)
321 printf("Initializing NCP hashtable and mem_chunk area\n");
323 if (ncp_request_hash)
324 g_hash_table_destroy(ncp_request_hash);
325 if (ncp_request_keys)
326 g_mem_chunk_destroy(ncp_request_keys);
327 if (ncp_request_records)
328 g_mem_chunk_destroy(ncp_request_records);
330 ncp_request_hash = g_hash_table_new(ncp_hash, ncp_equal);
331 ncp_request_keys = g_mem_chunk_new("ncp_request_keys",
332 sizeof(struct ncp_request_key),
333 100 * sizeof(struct ncp_request_key), G_ALLOC_AND_FREE);
334 ncp_request_records = g_mem_chunk_new("ncp_request_records",
335 sizeof(struct ncp_request_val),
336 100 * sizeof(struct ncp_request_val), G_ALLOC_AND_FREE);
339 static struct ncp2222_record *
340 ncp2222_find(guint8 func, guint8 subfunc)
342 struct ncp2222_record *ncp_record, *retval = NULL;
344 ncp_record = ncp2222;
346 while(ncp_record->func != 0) {
347 if (ncp_record->func == func &&
348 ncp_record->subfunc == (subfunc & ncp_record->submask)) {
358 /* How many bytes of NCP data to expect in the packet */
360 svc_record_byte_count(svc_record *sr)
362 svc_record *rec = sr;
365 while (rec->type != nend && rec->type != ndata) {
366 byte_count += rec->length;
374 dissect_ncp(const u_char *pd, int offset, frame_data *fd, proto_tree *tree,
377 proto_tree *ncp_tree = NULL;
379 int ncp_hdr_length = 0;
380 struct ncp_common_header header;
382 memcpy(&header, &pd[offset], sizeof(header));
383 header.type = ntohs(header.type);
385 if (header.type == 0x1111 ||
386 header.type == 0x2222 ||
387 header.type == 0x5555 ||
388 header.type == 0x7777) {
391 else if (header.type == 0x3333 || header.type == 0x9999) {
395 if (check_col(fd, COL_PROTOCOL))
396 col_add_str(fd, COL_PROTOCOL, "NCP");
398 nw_connection = (header.conn_high << 16) + header.conn_low;
399 nw_sequence = header.sequence;
400 nw_ncp_type = header.type;
403 ti = proto_tree_add_item(tree, offset, END_OF_FRAME,
404 "NetWare Core Protocol");
405 ncp_tree = proto_tree_new();
406 proto_item_add_subtree(ti, ncp_tree, ETT_NCP);
408 proto_tree_add_item(ncp_tree, offset, 2,
409 "Type: %s", val_to_str( header.type,
410 request_reply_values, "Unknown (%04X)"));
412 proto_tree_add_item(ncp_tree, offset+2, 1,
413 "Sequence Number: %d", header.sequence);
415 proto_tree_add_item(ncp_tree, offset+3, 3,
416 "Connection Number: %d", nw_connection);
418 proto_tree_add_item(ncp_tree, offset+4, 1,
419 "Task Number: %d", header.task);
422 /* Note how I use ncp_tree *and* tree in my args for ncp request/reply */
423 if (ncp_hdr_length == 7)
424 dissect_ncp_request(pd, offset, fd, ncp_tree, tree);
425 else if (ncp_hdr_length == 8)
426 dissect_ncp_reply(pd, offset, fd, ncp_tree, tree);
428 dissect_data(pd, offset, fd, tree);
432 dissect_ncp_request(const u_char *pd, int offset, frame_data *fd,
433 proto_tree *ncp_tree, proto_tree *tree) {
435 struct ncp_request_header request;
436 struct ncp2222_record *ncp_request;
437 gchar *description = "";
438 struct ncp_request_val *request_val;
439 struct ncp_request_key *request_key;
440 proto_tree *field_tree = NULL;
441 proto_item *ti = NULL;
444 /*memcpy(&request, &pd[offset], sizeof(request));*/
445 request.function = pd[offset+6];
446 request.subfunc = pd[offset+9];
448 ncp_request = ncp2222_find(request.function, request.subfunc);
451 description = ncp_request->funcname;
453 if (check_col(fd, COL_INFO)) {
454 if (description[0]) {
455 col_add_fstr(fd, COL_INFO, "C %s", description);
458 col_add_fstr(fd, COL_INFO, "C 2222/%02X%02X",
459 request.function, request.subfunc);
464 proto_tree_add_item(ncp_tree, offset+6, 1,
465 "Function Code: 0x%02X (%s)",
466 request.function, description);
469 max_data = svc_record_byte_count(ncp_request->req);
472 ti = proto_tree_add_item(ncp_tree, offset+7, END_OF_FRAME,
473 "NCP Request Packet");
474 field_tree = proto_tree_new();
475 proto_item_add_subtree(ti, field_tree, ETT_NCP_REQUEST_FIELDS);
477 parse_ncp_svc_fields(pd, field_tree, offset+7, ncp_request->req);
482 request_key = g_mem_chunk_alloc(ncp_request_keys);
483 request_key->nw_server_address = nw_server_address;
484 request_key->nw_connection = nw_connection;
485 request_key->nw_sequence = nw_sequence;
487 request_val = g_mem_chunk_alloc(ncp_request_records);
488 request_val->ncp_type = nw_ncp_type;
489 request_val->ncp_record = ncp2222_find(request.function, request.subfunc);
491 g_hash_table_insert(ncp_request_hash, request_key, request_val);
492 #if defined(DEBUG_NCP_HASH)
493 printf("Inserted server %08X connection %d sequence %d (val=%08X)\n",
494 nw_server_address, nw_connection, nw_sequence, request_val);
501 dissect_ncp_reply(const u_char *pd, int offset, frame_data *fd,
502 proto_tree *ncp_tree, proto_tree *tree) {
504 struct ncp_reply_header reply;
505 struct ncp2222_record *ncp_request = NULL;
506 struct ncp_request_val *request_val;
507 struct ncp_request_key request_key;
508 gchar *description = "Unknown";
509 proto_tree *field_tree = NULL;
510 proto_item *ti = NULL;
513 memcpy(&reply, &pd[offset], sizeof(reply));
515 /* find the record telling us the request made that caused this reply */
516 request_key.nw_server_address = nw_server_address;
517 request_key.nw_connection = nw_connection;
518 request_key.nw_sequence = nw_sequence;
520 request_val = (struct ncp_request_val*)
521 g_hash_table_lookup(ncp_request_hash, &request_key);
523 #if defined(DEBUG_NCP_HASH)
524 printf("Looking for server %08X connection %d sequence %d (retval=%08X)\n",
525 nw_server_address, nw_connection, nw_sequence, request_val);
529 ncp_request = request_val->ncp_record;
532 description = ncp_request->funcname;
534 if (check_col(fd, COL_INFO))
535 col_add_fstr(fd, COL_INFO, "R %s", description);
538 proto_tree_add_item(ncp_tree, offset+6, 1,
539 "Completion Code: %d", reply.completion_code);
541 proto_tree_add_item(ncp_tree, offset+7, 1,
542 "Connection Status: %d", reply.connection_state);
545 max_data = svc_record_byte_count(ncp_request->req);
548 ti = proto_tree_add_item(ncp_tree, offset+8, END_OF_FRAME,
550 field_tree = proto_tree_new();
551 proto_item_add_subtree(ti, field_tree, ETT_NCP_REPLY_FIELDS);
553 parse_ncp_svc_fields(pd, field_tree, offset+8, ncp_request->req);
560 /* Populates the protocol tree with information about the svc_record fields */
562 parse_ncp_svc_fields(const u_char *pd, proto_tree *ncp_tree, int offset,
563 struct svc_record *svc)
565 struct svc_record *rec = svc;
566 int field_offset = offset;
568 while (rec->type != nend) {
571 proto_tree_add_item(ncp_tree, field_offset,
572 rec->length, rec->description, pntohs(&pd[field_offset]));
578 field_offset += rec->length;