forked from osmocom/wireshark
453 lines
12 KiB
C
453 lines
12 KiB
C
/* packet-ncp.c
|
|
* Routines for NetWare Core Protocol
|
|
* Gilbert Ramirez <gram@verdict.uthscsa.edu>
|
|
*
|
|
* $Id: packet-ncp.c,v 1.10 1999/03/23 03:14:40 gram Exp $
|
|
*
|
|
* Ethereal - Network traffic analyzer
|
|
* By Gerald Combs <gerald@unicom.net>
|
|
* Copyright 1998 Gerald Combs
|
|
*
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
# include "config.h"
|
|
#endif
|
|
|
|
#ifdef HAVE_SYS_TYPES_H
|
|
# include <sys/types.h>
|
|
#endif
|
|
|
|
#ifdef HAVE_NETINET_IN_H
|
|
# include <netinet/in.h>
|
|
#endif
|
|
|
|
#include <glib.h>
|
|
#include "packet.h"
|
|
#include "packet-ipx.h"
|
|
#include "packet-ncp.h"
|
|
|
|
static void
|
|
dissect_ncp_request(const u_char *pd, int offset, frame_data *fd, proto_tree *ncp_tree, proto_tree *tree);
|
|
|
|
static void
|
|
dissect_ncp_reply(const u_char *pd, int offset, frame_data *fd, proto_tree *ncp_tree, proto_tree *tree);
|
|
|
|
static struct ncp2222_record *
|
|
ncp2222_find(guint8 func, guint8 subfunc);
|
|
|
|
struct svc_record;
|
|
|
|
static int
|
|
svc_record_byte_count(struct svc_record *sr);
|
|
|
|
/* Hash functions */
|
|
gint ncp_equal (const gpointer v, const gpointer v2);
|
|
guint ncp_hash (const gpointer v);
|
|
|
|
|
|
/* The information in this module comes from:
|
|
NetWare LAN Analysis, Second Edition
|
|
Laura A. Chappell and Dan E. Hakes
|
|
(c) 1994 Novell, Inc.
|
|
Novell Press, San Jose.
|
|
ISBN: 0-7821-1362-1
|
|
|
|
And from the ncpfs source code by Volker Lendecke
|
|
|
|
And:
|
|
Programmer's Guide to the NetWare Core Protocol
|
|
Steve Conner & Diane Conner
|
|
(c) 1996 by Steve Conner & Diane Conner
|
|
Published by Annabooks, San Diego, California
|
|
ISBN: 0-929392-31-0
|
|
|
|
*/
|
|
|
|
struct ncp_common_header {
|
|
guint16 type;
|
|
guint8 sequence;
|
|
guint8 conn_low;
|
|
guint8 task;
|
|
guint8 conn_high;
|
|
};
|
|
struct ncp_request_header {
|
|
guint16 type;
|
|
guint8 sequence;
|
|
guint8 conn_low;
|
|
guint8 task;
|
|
guint8 conn_high;
|
|
guint8 function;
|
|
guint16 length;
|
|
guint8 subfunc;
|
|
};
|
|
|
|
struct ncp_reply_header {
|
|
guint16 type;
|
|
guint8 sequence;
|
|
guint8 conn_low;
|
|
guint8 task;
|
|
guint8 conn_high;
|
|
guint8 completion_code;
|
|
guint8 connection_state;
|
|
};
|
|
|
|
|
|
static value_string request_reply_values[] = {
|
|
{ 0x1111, "Create a service connection" },
|
|
{ 0x2222, "Service request" },
|
|
{ 0x3333, "Service reply" },
|
|
{ 0x5555, "Destroy service connection" },
|
|
{ 0x7777, "Burst mode transfer" },
|
|
{ 0x9999, "Request being processed" },
|
|
{ 0x0000, NULL }
|
|
};
|
|
|
|
enum ntype { nend, nbyte, nhex, nbelong, nbeshort, ndata, nbytevar,
|
|
ndatetime, nasciiz };
|
|
|
|
typedef struct svc_record {
|
|
enum ntype type;
|
|
guint8 length;
|
|
gchar *description;
|
|
} svc_record;
|
|
|
|
typedef struct ncp2222_record {
|
|
guint8 func;
|
|
guint8 subfunc;
|
|
guint8 submask;
|
|
gchar *funcname;
|
|
|
|
svc_record *req;
|
|
gchar *req_summ;
|
|
guint8 req_summ_var1;
|
|
guint8 req_summ_var2;
|
|
guint8 req_summ_var3;
|
|
|
|
svc_record *rep;
|
|
gchar *rep_summ;
|
|
guint8 rep_summ_var1;
|
|
guint8 rep_summ_var2;
|
|
guint8 rep_summ_var3;
|
|
} ncp2222_record;
|
|
|
|
/* Service Queue Job REQUEST */
|
|
static svc_record ncp_17_7C_C[] = {
|
|
{ nbelong, 4, "The queue the job resides in" },
|
|
{ nbeshort, 2, "Job Type" },
|
|
{ nend, 0, NULL }
|
|
};
|
|
/* Service Queue Job REPLY */
|
|
static svc_record ncp_17_7C_R[] = {
|
|
{ nbelong, 4, "Client station number" },
|
|
{ nbelong, 4, "Task Number" },
|
|
{ nbelong, 4, "User" },
|
|
{ nbelong, 4, "Server specifed to service queue entry" },
|
|
{ ndatetime,6, "Earliest time to execute" },
|
|
{ ndatetime,6, "When job entered queue" },
|
|
{ nbelong, 4, "Job Number" },
|
|
{ nbeshort, 2, "Job Type" },
|
|
{ nbeshort, 2, "Job Position" },
|
|
{ nbeshort, 2, "Current status of job" },
|
|
{ nasciiz, 14, "Name of file" },
|
|
{ nbelong, 4, "File handle" },
|
|
{ nbelong, 4, "Client station number" },
|
|
{ nbelong, 4, "Task number" },
|
|
{ nbelong, 4, "Job server" },
|
|
{ nend, 0, NULL }
|
|
};
|
|
|
|
/* Read from a file REQUEST */
|
|
static svc_record ncp_48_00_C[] = {
|
|
{ nbyte, 1, "Unknown" },
|
|
{ nhex, 6, "File Handle" },
|
|
{ nbelong, 4, "Byte offset within file" },
|
|
{ nbeshort, 2, "Maximum data bytes to return" },
|
|
{ nend, 0, NULL }
|
|
};
|
|
/* RESPONSE */
|
|
static svc_record ncp_48_00_R[] = {
|
|
{ nbeshort, 2, "Data bytes returned" },
|
|
{ nbytevar, 1, "Padding" },
|
|
{ ndata, 0, NULL }
|
|
};
|
|
|
|
#define SUBFUNC 0xff
|
|
#define NOSUB 0x00
|
|
|
|
static ncp2222_record ncp2222[] = {
|
|
|
|
{ 0x17, 0x7C, SUBFUNC, "Service Queue Job",
|
|
ncp_17_7C_C, "", -1, -1, -1,
|
|
ncp_17_7C_R, "", -1, -1, -1
|
|
},
|
|
|
|
{ 0x48, 0x00, NOSUB, "Read from a file",
|
|
ncp_48_00_C, "F=%s Read %d at %d", 1, 2, 3,
|
|
ncp_48_00_R, "%d bytes read", 0, -1, -1
|
|
},
|
|
|
|
{ 0x00, 0x00, NOSUB, NULL,
|
|
NULL, NULL, -1, -1, -1,
|
|
NULL, NULL, -1, -1, -1
|
|
}
|
|
|
|
};
|
|
|
|
/* NCP packets come in request/reply pairs. The request packets tell the type
|
|
* of NCP request and give a sequence ID. The response, unfortunately, only
|
|
* identifies itself via the sequence ID; you have to know what type of NCP
|
|
* request the request packet contained in order to successfully parse the NCP
|
|
* response. A global method for doing this does not exist in ethereal yet
|
|
* (NFS also requires it), so for now the NCP section will keep its own hash
|
|
* table keeping track of NCP packet types.
|
|
*
|
|
* The key representing the unique NCP request is composed of 3 variables:
|
|
*
|
|
* ServerIPXNetwork.Connection.SequenceNumber
|
|
* 4 bytes 2 bytes 1 byte
|
|
* guint32 guint16 guint8 (all are host order)
|
|
*
|
|
* This assumes that all NCP connection is between a client and server.
|
|
* Servers can be identified by having a 00:00:00:00:00:01 IPX Node address.
|
|
* We have to let the IPX layer pass us the ServerIPXNetwork via a global
|
|
* variable (nw_server_address). In the future, if we decode NCP over TCP/UDP,
|
|
* then nw_server_address will represent the IP address of the server, which
|
|
* conveniently, is also 4 bytes long.
|
|
*
|
|
* The value stored in the hash table is the ncp_request_val pointer. This
|
|
* struct tells us the NCP type and gives the ncp2222_record pointer, if
|
|
* ncp_type == 0x2222.
|
|
*/
|
|
guint32 nw_server_address = 0; /* set by IPX layer */
|
|
guint16 nw_connection = 0; /* set by dissect_ncp */
|
|
guint8 nw_sequence = 0; /* set by dissect_ncp */
|
|
guint16 nw_ncp_type = 0; /* set by dissect_ncp */
|
|
|
|
struct ncp_request_key {
|
|
guint32 nw_server_address;
|
|
guint16 nw_connection;
|
|
guint8 nw_sequence;
|
|
};
|
|
|
|
struct ncp_request_val {
|
|
guint32 ncp_type;
|
|
struct ncp2222_record* ncp_record;
|
|
};
|
|
|
|
GHashTable *ncp_request_hash = NULL;
|
|
GMemChunk *ncp_request_records = NULL;
|
|
|
|
/* Hash Functions */
|
|
gint ncp_equal (const gpointer v, const gpointer v2)
|
|
{
|
|
return memcmp(v, v2, 7);
|
|
}
|
|
|
|
guint ncp_hash (const gpointer v)
|
|
{
|
|
struct ncp_request_key *ncp_key = (struct ncp_request_key*)v;
|
|
|
|
return ncp_key->nw_server_address +
|
|
((guint32) ncp_key->nw_connection << 16) +
|
|
ncp_key->nw_sequence;
|
|
}
|
|
|
|
void
|
|
ncp_init_protocol(void)
|
|
{
|
|
if (ncp_request_hash)
|
|
g_hash_table_destroy(ncp_request_hash);
|
|
if (ncp_request_records)
|
|
g_mem_chunk_destroy(ncp_request_records);
|
|
|
|
ncp_request_hash = g_hash_table_new(ncp_hash, ncp_equal);
|
|
ncp_request_records = g_mem_chunk_new("ncp_request_records",
|
|
sizeof(struct ncp_request_val), 50 * sizeof(struct ncp_request_val),
|
|
G_ALLOC_AND_FREE);
|
|
}
|
|
|
|
static struct ncp2222_record *
|
|
ncp2222_find(guint8 func, guint8 subfunc)
|
|
{
|
|
struct ncp2222_record *ncp_record, *retval = NULL;
|
|
|
|
ncp_record = ncp2222;
|
|
|
|
while(ncp_record->func != 0) {
|
|
if (ncp_record->func == func &&
|
|
ncp_record->subfunc == (subfunc & ncp_record->submask)) {
|
|
retval = ncp_record;
|
|
break;
|
|
}
|
|
ncp_record++;
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
static int
|
|
svc_record_byte_count(svc_record *sr)
|
|
{
|
|
svc_record *rec = sr;
|
|
int byte_count = 0;
|
|
|
|
while (rec->type != nend && rec->type != ndata) {
|
|
byte_count += rec->length;
|
|
rec++;
|
|
}
|
|
|
|
return byte_count;
|
|
}
|
|
|
|
void
|
|
dissect_ncp(const u_char *pd, int offset, frame_data *fd, proto_tree *tree) {
|
|
|
|
proto_tree *ncp_tree = NULL;
|
|
proto_item *ti;
|
|
int ncp_hdr_length = 0;
|
|
struct ncp_common_header header;
|
|
|
|
memcpy(&header, &pd[offset], sizeof(header));
|
|
header.type = ntohs(header.type);
|
|
|
|
if (header.type == 0x1111 ||
|
|
header.type == 0x2222 ||
|
|
header.type == 0x5555 ||
|
|
header.type == 0x7777) {
|
|
ncp_hdr_length = 7;
|
|
}
|
|
else if (header.type == 0x3333 || header.type == 0x9999) {
|
|
ncp_hdr_length = 8;
|
|
}
|
|
|
|
if (check_col(fd, COL_PROTOCOL))
|
|
col_add_str(fd, COL_PROTOCOL, "NCP");
|
|
nw_connection = (header.conn_high << 16) + header.conn_low;
|
|
nw_sequence = header.sequence;
|
|
nw_ncp_type = header.type;
|
|
|
|
if (tree) {
|
|
ti = proto_tree_add_item(tree, offset, END_OF_FRAME,
|
|
"NetWare Core Protocol");
|
|
ncp_tree = proto_tree_new();
|
|
proto_item_add_subtree(ti, ncp_tree, ETT_NCP);
|
|
|
|
proto_tree_add_item(ncp_tree, offset, 2,
|
|
"Type: %s", val_to_str( header.type,
|
|
request_reply_values, "Unknown (%04X)"));
|
|
|
|
proto_tree_add_item(ncp_tree, offset+2, 1,
|
|
"Sequence Number: %d", header.sequence);
|
|
|
|
proto_tree_add_item(ncp_tree, offset+3, 3,
|
|
"Connection Number: %d", nw_connection);
|
|
|
|
proto_tree_add_item(ncp_tree, offset+4, 1,
|
|
"Task Number: %d", header.task);
|
|
}
|
|
|
|
/* Note how I use ncp_tree *and* tree in my args for ncp request/reply */
|
|
if (ncp_hdr_length == 7)
|
|
dissect_ncp_request(pd, offset, fd, ncp_tree, tree);
|
|
else if (ncp_hdr_length == 8)
|
|
dissect_ncp_reply(pd, offset, fd, ncp_tree, tree);
|
|
else
|
|
dissect_data(pd, offset, fd, tree);
|
|
}
|
|
|
|
void
|
|
dissect_ncp_request(const u_char *pd, int offset, frame_data *fd, proto_tree *ncp_tree, proto_tree *tree) {
|
|
|
|
struct ncp_request_header request;
|
|
struct ncp2222_record *ncp_request;
|
|
gchar *description = "Unknown";
|
|
struct ncp_request_val *request_val;
|
|
struct ncp_request_key request_key;
|
|
|
|
/*memcpy(&request, &pd[offset], sizeof(request));*/
|
|
request.function = pd[offset+6];
|
|
request.subfunc = pd[offset+9];
|
|
|
|
ncp_request = ncp2222_find(request.function, request.subfunc);
|
|
|
|
if (ncp_request)
|
|
description = ncp_request->funcname;
|
|
|
|
if (check_col(fd, COL_INFO))
|
|
col_add_fstr(fd, COL_INFO, "C %s", description);
|
|
|
|
if (ncp_tree) {
|
|
proto_tree_add_item(ncp_tree, offset+6, 1, "Function Code: 0x%02X (%s)",
|
|
request.function, description);
|
|
if (ncp_request) {
|
|
offset += 10 + svc_record_byte_count(ncp_request->req);
|
|
dissect_data(pd, offset, fd, tree);
|
|
}
|
|
}
|
|
else { /* ! tree */
|
|
request_val = g_mem_chunk_alloc(ncp_request_records);
|
|
request_val->ncp_type = nw_ncp_type;
|
|
request_val->ncp_record = ncp2222_find(request.function, request.subfunc);
|
|
request_key.nw_server_address = nw_server_address;
|
|
request_key.nw_connection = nw_connection;
|
|
request_key.nw_sequence = nw_sequence;
|
|
|
|
g_hash_table_insert(ncp_request_hash, &request_key, request_val);
|
|
}
|
|
|
|
}
|
|
|
|
void
|
|
dissect_ncp_reply(const u_char *pd, int offset, frame_data *fd, proto_tree *ncp_tree, proto_tree *tree) {
|
|
struct ncp_reply_header reply;
|
|
struct ncp2222_record *ncp_request = NULL;
|
|
struct ncp_request_val *request_val;
|
|
struct ncp_request_key request_key;
|
|
gchar *description = "Unknown";
|
|
|
|
memcpy(&reply, &pd[offset], sizeof(reply));
|
|
|
|
/* find the record telling us the request made that caused this reply */
|
|
request_key.nw_server_address = nw_server_address;
|
|
request_key.nw_connection = nw_connection;
|
|
request_key.nw_sequence = nw_sequence;
|
|
|
|
request_val = (struct ncp_request_val*)
|
|
g_hash_table_lookup(ncp_request_hash, &request_key);
|
|
|
|
if (request_val)
|
|
ncp_request = request_val->ncp_record;
|
|
|
|
if (ncp_request)
|
|
description = ncp_request->funcname;
|
|
|
|
if (check_col(fd, COL_INFO))
|
|
col_add_fstr(fd, COL_INFO, "R %s", description);
|
|
|
|
if (ncp_tree) {
|
|
proto_tree_add_item(ncp_tree, offset+6, 1,
|
|
"Completion Code: %d", reply.completion_code);
|
|
|
|
proto_tree_add_item(ncp_tree, offset+7, 1,
|
|
"Connection Status: %d", reply.connection_state);
|
|
offset += 8;
|
|
dissect_data(pd, offset, fd, tree);
|
|
}
|
|
|
|
}
|