wireshark/packet-smb-pipe.c

3070 lines
83 KiB
C

/*
XXX Fixme : shouldnt show [malformed frame] for long packets
*/
/* packet-smb-pipe.c
* Routines for SMB named pipe packet dissection
* Copyright 1999, Richard Sharpe <rsharpe@ns.aus.com>
* significant rewrite to tvbuffify the dissector, Ronnie Sahlberg and
* Guy Harris 2001
*
* $Id: packet-smb-pipe.c,v 1.65 2002/01/20 22:12:28 guy Exp $
*
* Ethereal - Network traffic analyzer
* By Gerald Combs <gerald@ethereal.com>
* Copyright 1998 Gerald Combs
*
* Copied from packet-pop.c
*
* 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
#include <stdio.h>
#ifdef HAVE_SYS_TYPES_H
# include <sys/types.h>
#endif
#ifdef HAVE_NETINET_IN_H
# include <netinet/in.h>
#endif
#include <time.h>
#include <string.h>
#include <glib.h>
#include <ctype.h>
#include "packet.h"
#include "conversation.h"
#include "smb.h"
#include "packet-smb-pipe.h"
#include "packet-smb-browse.h"
#include "packet-dcerpc.h"
#include "reassemble.h"
static int proto_smb_pipe = -1;
static int hf_pipe_function = -1;
static int hf_pipe_priority = -1;
static int hf_pipe_peek_available = -1;
static int hf_pipe_peek_remaining = -1;
static int hf_pipe_peek_status = -1;
static int hf_pipe_getinfo_info_level = -1;
static int hf_pipe_getinfo_output_buffer_size = -1;
static int hf_pipe_getinfo_input_buffer_size = -1;
static int hf_pipe_getinfo_maximum_instances = -1;
static int hf_pipe_getinfo_current_instances = -1;
static int hf_pipe_getinfo_pipe_name_length = -1;
static int hf_pipe_getinfo_pipe_name = -1;
static int hf_pipe_write_raw_bytes_written = -1;
static gint ett_smb_pipe = -1;
static gint ett_smb_pipe_fragments = -1;
static int proto_smb_lanman = -1;
static int hf_function_code = -1;
static int hf_param_desc = -1;
static int hf_return_desc = -1;
static int hf_aux_data_desc = -1;
static int hf_detail_level = -1;
static int hf_recv_buf_len = -1;
static int hf_send_buf_len = -1;
static int hf_continuation_from = -1;
static int hf_status = -1;
static int hf_convert = -1;
static int hf_ecount = -1;
static int hf_acount = -1;
static int hf_share_name = -1;
static int hf_share_type = -1;
static int hf_share_comment = -1;
static int hf_share_permissions = -1;
static int hf_share_max_uses = -1;
static int hf_share_current_uses = -1;
static int hf_share_path = -1;
static int hf_share_password = -1;
static int hf_server_name = -1;
static int hf_server_major = -1;
static int hf_server_minor = -1;
static int hf_server_comment = -1;
static int hf_abytes = -1;
static int hf_current_time = -1;
static int hf_msecs = -1;
static int hf_hour = -1;
static int hf_minute = -1;
static int hf_second = -1;
static int hf_hundredths = -1;
static int hf_tzoffset = -1;
static int hf_timeinterval = -1;
static int hf_day = -1;
static int hf_month = -1;
static int hf_year = -1;
static int hf_weekday = -1;
static int hf_enumeration_domain = -1;
static int hf_computer_name = -1;
static int hf_user_name = -1;
static int hf_workstation_domain = -1;
static int hf_workstation_major = -1;
static int hf_workstation_minor = -1;
static int hf_logon_domain = -1;
static int hf_other_domains = -1;
static int hf_password = -1;
static int hf_workstation_name = -1;
static int hf_ustruct_size = -1;
static int hf_logon_code = -1;
static int hf_privilege_level = -1;
static int hf_operator_privileges = -1;
static int hf_num_logons = -1;
static int hf_bad_pw_count = -1;
static int hf_last_logon = -1;
static int hf_last_logoff = -1;
static int hf_logoff_time = -1;
static int hf_kickoff_time = -1;
static int hf_password_age = -1;
static int hf_password_can_change = -1;
static int hf_password_must_change = -1;
static int hf_script_path = -1;
static int hf_logoff_code = -1;
static int hf_duration = -1;
static int hf_user_comment = -1;
static int hf_full_name = -1;
static int hf_homedir = -1;
static int hf_parameters = -1;
static int hf_logon_server = -1;
static int hf_country_code = -1;
static int hf_workstations = -1;
static int hf_max_storage = -1;
static int hf_units_per_week = -1;
static int hf_logon_hours = -1;
static int hf_code_page = -1;
static int hf_new_password = -1;
static int hf_old_password = -1;
static int hf_reserved = -1;
static gint ett_lanman = -1;
static gint ett_lanman_shares = -1;
static gint ett_lanman_share = -1;
static gint ett_lanman_servers = -1;
static gint ett_lanman_server = -1;
static dissector_handle_t data_handle;
/*
* See
*
* ftp://ftp.microsoft.com/developr/drg/CIFS/cifsrap2.txt
*
* among other documents.
*/
static const value_string status_vals[] = {
{0, "Success"},
{5, "User has insufficient privilege"},
{65, "Network access is denied"},
{86, "The specified password is invalid"},
{SMBE_moredata, "Additional data is available"},
{2114, "Service is not running on the remote computer"},
{2123, "Supplied buffer is too small"},
{2141, "Server is not configured for transactions (IPC$ not shared)"},
{2212, "An error occurred while loading or running the logon script"},
{2214, "The logon was not validated by any server"},
{2217, "The logon server is running an older software version"},
{2221, "The user name was not found"},
{2240, "The user is not allowed to logon from this computer"},
{2241, "The user is not allowed to logon at this time"},
{2242, "The user password has expired"},
{2243, "The password cannot be changed"},
{2246, "The password is too short"},
{0, NULL}
};
static const value_string share_type_vals[] = {
{0, "Directory tree"},
{1, "Printer queue"},
{2, "Communications device"},
{3, "IPC"},
{0, NULL}
};
static const value_string privilege_vals[] = {
{0, "Guest"},
{1, "User"},
{2, "Administrator"},
{0, NULL}
};
static const value_string op_privilege_vals[] = {
{0, "Print operator"},
{1, "Communications operator"},
{2, "Server operator"},
{3, "Accounts operator"},
{0, NULL}
};
static const value_string weekday_vals[] = {
{0, "Sunday"},
{1, "Monday"},
{2, "Tuesday"},
{3, "Wednesday"},
{4, "Thursday"},
{5, "Friday"},
{6, "Saturday"},
{0, NULL}
};
static int
add_word_param(tvbuff_t *tvb, int offset, int count, packet_info *pinfo,
proto_tree *tree, int convert, int hf_index)
{
guint16 WParam;
if (hf_index != -1)
proto_tree_add_item(tree, hf_index, tvb, offset, 2, TRUE);
else {
WParam = tvb_get_letohs(tvb, offset);
proto_tree_add_text(tree, tvb, offset, 2,
"Word Param: %u (0x%04X)", WParam, WParam);
}
offset += 2;
return offset;
}
static int
add_dword_param(tvbuff_t *tvb, int offset, int count, packet_info *pinfo,
proto_tree *tree, int convert, int hf_index)
{
guint32 LParam;
if (hf_index != -1)
proto_tree_add_item(tree, hf_index, tvb, offset, 4, TRUE);
else {
LParam = tvb_get_letohl(tvb, offset);
proto_tree_add_text(tree, tvb, offset, 4,
"Doubleword Param: %u (0x%08X)", LParam, LParam);
}
offset += 4;
return offset;
}
static int
add_byte_param(tvbuff_t *tvb, int offset, int count, packet_info *pinfo,
proto_tree *tree, int convert, int hf_index)
{
guint8 BParam;
if (hf_index != -1)
proto_tree_add_item(tree, hf_index, tvb, offset, count, TRUE);
else {
if (count == 1) {
BParam = tvb_get_guint8(tvb, offset);
proto_tree_add_text(tree, tvb, offset, count,
"Byte Param: %u (0x%02X)",
BParam, BParam);
} else {
proto_tree_add_text(tree, tvb, offset, count,
"Bytes Param: %s, type is wrong",
tvb_bytes_to_str(tvb, offset, count));
}
}
offset += count;
return offset;
}
static int
add_pad_param(tvbuff_t *tvb, int offset, int count, packet_info *pinfo,
proto_tree *tree, int convert, int hf_index)
{
/*
* This is for parameters that have descriptor entries but that
* are, in practice, just padding.
*/
offset += count;
return offset;
}
static void
add_null_pointer_param(tvbuff_t *tvb, int offset, int count,
packet_info *pinfo, proto_tree *tree, int convert, int hf_index)
{
if (hf_index != -1) {
proto_tree_add_text(tree, tvb, offset, 0,
"%s (Null pointer)",
proto_registrar_get_name(hf_index));
} else {
proto_tree_add_text(tree, tvb, offset, 0,
"String Param (Null pointer)");
}
}
static int
add_string_param(tvbuff_t *tvb, int offset, int count, packet_info *pinfo,
proto_tree *tree, int convert, int hf_index)
{
guint string_len;
string_len = tvb_strsize(tvb, offset);
if (hf_index != -1) {
proto_tree_add_item(tree, hf_index, tvb, offset, string_len,
TRUE);
} else {
proto_tree_add_text(tree, tvb, offset, string_len,
"String Param: %s",
tvb_format_text(tvb, offset, string_len));
}
offset += string_len;
return offset;
}
static const char *
get_pointer_value(tvbuff_t *tvb, int offset, int convert, int *cptrp, int *lenp)
{
int cptr;
gint string_len;
/* pointer to string */
cptr = (tvb_get_letohl(tvb, offset)&0xffff)-convert;
*cptrp = cptr;
/* string */
if (tvb_offset_exists(tvb, cptr) &&
(string_len = tvb_strnlen(tvb, cptr, -1)) != -1) {
string_len++; /* include the terminating '\0' */
*lenp = string_len;
return tvb_format_text(tvb, cptr, string_len - 1);
} else
return NULL;
}
static int
add_pointer_param(tvbuff_t *tvb, int offset, int count, packet_info *pinfo,
proto_tree *tree, int convert, int hf_index)
{
int cptr;
const char *string;
gint string_len;
string = get_pointer_value(tvb, offset, convert, &cptr, &string_len);
offset += 4;
/* string */
if (string != NULL) {
if (hf_index != -1) {
proto_tree_add_item(tree, hf_index, tvb, cptr,
string_len, TRUE);
} else {
proto_tree_add_text(tree, tvb, cptr, string_len,
"String Param: %s", string);
}
} else {
if (hf_index != -1) {
proto_tree_add_text(tree, tvb, 0, 0,
"%s: <String goes past end of frame>",
proto_registrar_get_name(hf_index));
} else {
proto_tree_add_text(tree, tvb, 0, 0,
"String Param: <String goes past end of frame>");
}
}
return offset;
}
static int
add_detail_level(tvbuff_t *tvb, int offset, int count, packet_info *pinfo,
proto_tree *tree, int convert, int hf_index)
{
struct smb_info *smb_info = pinfo->private_data;
smb_transact_info_t *trp = smb_info->sip->extra_info;
guint16 level;
level = tvb_get_letohs(tvb, offset);
if (!pinfo->fd->flags.visited)
trp->info_level = level; /* remember this for the response */
proto_tree_add_uint(tree, hf_index, tvb, offset, 2, level);
offset += 2;
return offset;
}
static int
add_max_uses(tvbuff_t *tvb, int offset, int count, packet_info *pinfo,
proto_tree *tree, int convert, int hf_index)
{
guint16 WParam;
WParam = tvb_get_letohs(tvb, offset);
if (WParam == 0xffff) { /* -1 */
proto_tree_add_uint_format(tree, hf_index, tvb,
offset, 2, WParam,
"%s: No limit",
proto_registrar_get_name(hf_index));
} else {
proto_tree_add_uint(tree, hf_index, tvb,
offset, 2, WParam);
}
offset += 2;
return offset;
}
static int
add_server_type(tvbuff_t *tvb, int offset, int count,
packet_info *pinfo, proto_tree *tree, int convert, int hf_index)
{
dissect_smb_server_type_flags(tvb, pinfo, tree, offset, FALSE);
offset += 4;
return offset;
}
static int
add_server_type_info(tvbuff_t *tvb, int offset, int count,
packet_info *pinfo, proto_tree *tree, int convert, int hf_index)
{
dissect_smb_server_type_flags(tvb, pinfo, tree, offset, TRUE);
offset += 4;
return offset;
}
static int
add_reltime(tvbuff_t *tvb, int offset, int count, packet_info *pinfo,
proto_tree *tree, int convert, int hf_index)
{
nstime_t nstime;
nstime.secs = tvb_get_letohl(tvb, offset);
nstime.nsecs = 0;
proto_tree_add_time_format(tree, hf_index, tvb, offset, 4,
&nstime, "%s: %s", proto_registrar_get_name(hf_index),
time_secs_to_str(nstime.secs));
offset += 4;
return offset;
}
/*
* Sigh. These are for handling Microsoft's annoying almost-UNIX-time-but-
* it's-local-time-not-UTC time.
*/
static int
add_abstime_common(tvbuff_t *tvb, int offset, int count,
packet_info *pinfo, proto_tree *tree, int convert, int hf_index,
const char *absent_name)
{
nstime_t nstime;
struct tm *tmp;
nstime.secs = tvb_get_letohl(tvb, offset);
nstime.nsecs = 0;
if (nstime.secs == -1) {
proto_tree_add_time_format(tree, hf_index, tvb, offset, 4,
&nstime, "%s: %s", proto_registrar_get_name(hf_index),
absent_name);
} else {
/*
* Run it through "gmtime()" to break it down, and then
* run it through "mktime()" to put it back together
* as UTC.
*/
tmp = gmtime(&nstime.secs);
tmp->tm_isdst = -1; /* we don't know if it's DST or not */
nstime.secs = mktime(tmp);
proto_tree_add_time(tree, hf_index, tvb, offset, 4,
&nstime);
}
offset += 4;
return offset;
}
static int
add_abstime_absent_never(tvbuff_t *tvb, int offset, int count,
packet_info *pinfo, proto_tree *tree, int convert, int hf_index)
{
return add_abstime_common(tvb, offset, count, pinfo, tree,
convert, hf_index, "Never");
}
static int
add_abstime_absent_unknown(tvbuff_t *tvb, int offset, int count,
packet_info *pinfo, proto_tree *tree, int convert, int hf_index)
{
return add_abstime_common(tvb, offset, count, pinfo, tree,
convert, hf_index, "Unknown");
}
static int
add_nlogons(tvbuff_t *tvb, int offset, int count, packet_info *pinfo,
proto_tree *tree, int convert, int hf_index)
{
guint16 nlogons;
nlogons = tvb_get_letohs(tvb, offset);
if (nlogons == 0xffff) /* -1 */
proto_tree_add_uint_format(tree, hf_index, tvb, offset, 2,
nlogons, "%s: Unknown",
proto_registrar_get_name(hf_index));
else
proto_tree_add_uint(tree, hf_index, tvb, offset, 2,
nlogons);
offset += 2;
return offset;
}
static int
add_max_storage(tvbuff_t *tvb, int offset, int count, packet_info *pinfo,
proto_tree *tree, int convert, int hf_index)
{
guint32 max_storage;
max_storage = tvb_get_letohl(tvb, offset);
if (max_storage == 0xffffffff)
proto_tree_add_uint_format(tree, hf_index, tvb, offset, 4,
max_storage, "%s: No limit",
proto_registrar_get_name(hf_index));
else
proto_tree_add_uint(tree, hf_index, tvb, offset, 4,
max_storage);
offset += 4;
return offset;
}
static int
add_logon_hours(tvbuff_t *tvb, int offset, int count, packet_info *pinfo,
proto_tree *tree, int convert, int hf_index)
{
int cptr;
/* pointer to string */
cptr = (tvb_get_letohl(tvb, offset)&0xffff)-convert;
offset += 4;
/* string */
/* XXX - should actually carve up the bits */
proto_tree_add_item(tree, hf_index, tvb, cptr, 21, TRUE);
return offset;
}
static int
add_tzoffset(tvbuff_t *tvb, int offset, int count, packet_info *pinfo,
proto_tree *tree, int convert, int hf_index)
{
gint16 tzoffset;
tzoffset = tvb_get_letohs(tvb, offset);
if (tzoffset < 0) {
proto_tree_add_int_format(tree, hf_tzoffset, tvb, offset, 2,
tzoffset, "%s: %s east of UTC",
proto_registrar_get_name(hf_index),
time_secs_to_str(-tzoffset*60));
} else if (tzoffset > 0) {
proto_tree_add_int_format(tree, hf_tzoffset, tvb, offset, 2,
tzoffset, "%s: %s west of UTC",
proto_registrar_get_name(hf_index),
time_secs_to_str(tzoffset*60));
} else {
proto_tree_add_int_format(tree, hf_tzoffset, tvb, offset, 2,
tzoffset, "%s: at UTC",
proto_registrar_get_name(hf_index));
}
offset += 2;
return offset;
}
static int
add_timeinterval(tvbuff_t *tvb, int offset, int count, packet_info *pinfo,
proto_tree *tree, int convert, int hf_index)
{
guint16 timeinterval;
timeinterval = tvb_get_letohs(tvb, offset);
proto_tree_add_uint_format(tree, hf_timeinterval, tvb, offset, 2,
timeinterval, "%s: %f seconds", proto_registrar_get_name(hf_index),
timeinterval*.0001);
offset += 2;
return offset;
}
static int
add_logon_args(tvbuff_t *tvb, int offset, int count, packet_info *pinfo,
proto_tree *tree, int convert, int hf_index)
{
if (count != 54) {
proto_tree_add_text(tree, tvb, offset, count,
"Bogus NetWkstaUserLogon parameters: length is %d, should be 54",
count);
offset += count;
return offset;
}
/* user name */
proto_tree_add_item(tree, hf_user_name, tvb, offset, 21, TRUE);
offset += 21;
/* pad1 */
offset += 1;
/* password */
proto_tree_add_item(tree, hf_password, tvb, offset, 15, TRUE);
offset += 15;
/* pad2 */
offset += 1;
/* workstation name */
proto_tree_add_item(tree, hf_workstation_name, tvb, offset, 16, TRUE);
offset += 16;
return offset;
}
/*
* The following data structure describes the Remote API requests we
* understand.
*
* Simply fill in the number and parameter information.
* Try to keep them in order.
*
* We will extend this data structure as we try to decode more.
*/
/*
* This is a pointer to a function to process an item.
*/
typedef int (*item_func)(tvbuff_t *, int, int, packet_info *, proto_tree *,
int, int);
/*
* Type of an item; determines what parameter strings are valid for
* the item.
*/
typedef enum {
PARAM_NONE, /* for the end-of-list stopper */
PARAM_WORD, /* 'W' or 'h' - 16-bit word */
PARAM_DWORD, /* 'D' or 'i' - 32-bit word */
PARAM_BYTES, /* 'B' or 'b' or 'g' or 'O' - one or more bytes */
PARAM_STRINGZ, /* 'z' or 'O' - null-terminated string */
} param_type_t;
/*
* This structure describes an item; "hf_index" points to the index
* for the field corresponding to that item, "func" points to the
* function to use to add that item to the tree, and "type" is the
* type that the item is supposed to have.
*/
typedef struct {
int *hf_index;
item_func func;
param_type_t type;
} item_t;
/*
* This structure describes a list of items; each list of items
* has a corresponding detail level.
*/
typedef struct {
int level;
const item_t *item_list;
} item_list_t;
struct lanman_desc {
int lanman_num;
const item_t *req;
proto_item *(*req_data_item)(tvbuff_t *, packet_info *,
proto_tree *, int);
gint *ett_req_data;
const item_t *req_data;
const item_t *req_aux_data;
const item_t *resp;
proto_item *(*resp_data_item)(tvbuff_t *, packet_info *,
proto_tree *, int);
gint *ett_resp_data;
proto_item *(*resp_data_element_item)(tvbuff_t *, packet_info *,
proto_tree *, int);
gint *ett_resp_data_element_item;
const item_list_t *resp_data_list;
const item_t *resp_aux_data;
};
static int no_hf = -1; /* for padding crap */
static const item_t lm_params_req_netshareenum[] = {
{ &hf_detail_level, add_detail_level, PARAM_WORD },
{ &hf_recv_buf_len, add_word_param, PARAM_WORD },
{ NULL, NULL, PARAM_NONE }
};
static const item_t lm_params_resp_netshareenum[] = {
{ &hf_acount, add_word_param, PARAM_WORD },
{ NULL, NULL, PARAM_NONE }
};
/*
* Create a subtree for all available shares.
*/
static proto_item *
netshareenum_shares_list(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
int offset)
{
if (tree) {
return proto_tree_add_text(tree, tvb, offset, -1,
"Available Shares");
} else
return NULL;
}
/*
* Create a subtree for a share.
*/
static proto_item *
netshareenum_share_entry(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
int offset)
{
if (tree) {
return proto_tree_add_text(tree, tvb, offset, -1,
"Share %.13s", tvb_get_ptr(tvb, offset, 13));
} else
return NULL;
}
static const item_t lm_null[] = {
{ NULL, NULL, PARAM_NONE }
};
static const item_list_t lm_null_list[] = {
{ 0, lm_null }
};
static const item_t lm_data_resp_netshareenum_1[] = {
{ &hf_share_name, add_byte_param, PARAM_BYTES },
{ &no_hf, add_pad_param, PARAM_BYTES },
{ &hf_share_type, add_word_param, PARAM_WORD },
{ &hf_share_comment, add_pointer_param, PARAM_STRINGZ },
{ NULL, NULL, PARAM_NONE }
};
static const item_list_t lm_data_resp_netshareenum[] = {
{ 1, lm_data_resp_netshareenum_1 },
{ -1, lm_null }
};
static const item_t lm_params_req_netsharegetinfo[] = {
{ &hf_share_name, add_string_param, PARAM_STRINGZ },
{ &hf_detail_level, add_detail_level, PARAM_WORD },
{ NULL, NULL, PARAM_NONE }
};
static const item_t lm_params_resp_netsharegetinfo[] = {
{ &hf_abytes, add_word_param, PARAM_WORD },
{ NULL, NULL, PARAM_NONE }
};
static const item_t lm_data_resp_netsharegetinfo_0[] = {
{ &hf_share_name, add_byte_param, PARAM_BYTES },
{ NULL, NULL, PARAM_NONE }
};
static const item_t lm_data_resp_netsharegetinfo_1[] = {
{ &hf_share_name, add_byte_param, PARAM_BYTES },
{ &no_hf, add_pad_param, PARAM_BYTES },
{ &hf_share_type, add_word_param, PARAM_WORD },
{ &hf_share_comment, add_pointer_param, PARAM_STRINGZ },
{ NULL, NULL, PARAM_NONE }
};
static const item_t lm_data_resp_netsharegetinfo_2[] = {
{ &hf_share_name, add_byte_param, PARAM_BYTES },
{ &no_hf, add_pad_param, PARAM_BYTES },
{ &hf_share_type, add_word_param, PARAM_WORD },
{ &hf_share_comment, add_pointer_param, PARAM_STRINGZ },
{ &hf_share_permissions, add_word_param, PARAM_WORD }, /* XXX - do as bit fields */
{ &hf_share_max_uses, add_max_uses, PARAM_WORD },
{ &hf_share_current_uses, add_word_param, PARAM_WORD },
{ &hf_share_path, add_pointer_param, PARAM_STRINGZ },
{ &hf_share_password, add_byte_param, PARAM_BYTES },
{ NULL, NULL, PARAM_NONE }
};
static const item_list_t lm_data_resp_netsharegetinfo[] = {
{ 0, lm_data_resp_netsharegetinfo_0 },
{ 1, lm_data_resp_netsharegetinfo_1 },
{ 2, lm_data_resp_netsharegetinfo_2 },
{ -1, lm_null }
};
static const item_t lm_params_req_netservergetinfo[] = {
{ &hf_detail_level, add_detail_level, PARAM_WORD },
{ NULL, NULL, PARAM_NONE }
};
static const item_t lm_params_resp_netservergetinfo[] = {
{ &hf_abytes, add_word_param, PARAM_WORD },
{ NULL, NULL, PARAM_NONE }
};
static const item_t lm_data_serverinfo_0[] = {
{ &hf_server_name, add_byte_param, PARAM_BYTES },
{ NULL, NULL, PARAM_NONE }
};
static const item_t lm_data_serverinfo_1[] = {
{ &hf_server_name, add_byte_param, PARAM_BYTES },
{ &hf_server_major, add_byte_param, PARAM_BYTES },
{ &hf_server_minor, add_byte_param, PARAM_BYTES },
{ &no_hf, add_server_type, PARAM_DWORD },
{ &hf_server_comment, add_pointer_param, PARAM_STRINGZ },
{ NULL, NULL, PARAM_NONE }
};
static const item_list_t lm_data_serverinfo[] = {
{ 0, lm_data_serverinfo_0 },
{ 1, lm_data_serverinfo_1 },
{ -1, lm_null }
};
static const item_t lm_params_req_netusergetinfo[] = {
{ &hf_detail_level, add_detail_level, PARAM_WORD },
{ NULL, NULL, PARAM_NONE }
};
static const item_t lm_params_resp_netusergetinfo[] = {
{ &hf_abytes, add_word_param, PARAM_WORD },
{ NULL, NULL, PARAM_NONE }
};
static const item_t lm_data_resp_netusergetinfo_11[] = {
{ &hf_user_name, add_byte_param, PARAM_BYTES },
{ &no_hf, add_pad_param, PARAM_BYTES },
{ &hf_user_comment, add_pointer_param, PARAM_STRINGZ },
{ &hf_full_name, add_pointer_param, PARAM_STRINGZ },
{ &hf_privilege_level, add_word_param, PARAM_WORD },
{ &hf_operator_privileges, add_dword_param, PARAM_DWORD },
{ &hf_password_age, add_reltime, PARAM_DWORD },
{ &hf_homedir, add_pointer_param, PARAM_STRINGZ },
{ &hf_parameters, add_pointer_param, PARAM_STRINGZ },
{ &hf_last_logon, add_abstime_absent_unknown, PARAM_DWORD },
{ &hf_last_logoff, add_abstime_absent_unknown, PARAM_DWORD },
{ &hf_bad_pw_count, add_word_param, PARAM_WORD },
{ &hf_num_logons, add_nlogons, PARAM_WORD },
{ &hf_logon_server, add_pointer_param, PARAM_STRINGZ },
{ &hf_country_code, add_word_param, PARAM_WORD },
{ &hf_workstations, add_pointer_param, PARAM_STRINGZ },
{ &hf_max_storage, add_max_storage, PARAM_DWORD },
{ &hf_logon_hours, add_logon_hours, PARAM_DWORD },
{ &hf_code_page, add_word_param, PARAM_WORD },
{ NULL, NULL, PARAM_NONE }
};
static const item_list_t lm_data_resp_netusergetinfo[] = {
{ 11, lm_data_resp_netusergetinfo_11 },
{ -1, lm_null }
};
/*
* Has no detail level; make it the default.
*/
static const item_t lm_data_resp_netremotetod_nolevel[] = {
{ &hf_current_time, add_abstime_absent_unknown, PARAM_DWORD },
{ &hf_msecs, add_dword_param, PARAM_DWORD },
{ &hf_hour, add_byte_param, PARAM_BYTES },
{ &hf_minute, add_byte_param, PARAM_BYTES },
{ &hf_second, add_byte_param, PARAM_BYTES },
{ &hf_hundredths, add_byte_param, PARAM_BYTES },
{ &hf_tzoffset, add_tzoffset, PARAM_WORD },
{ &hf_timeinterval, add_timeinterval, PARAM_WORD },
{ &hf_day, add_byte_param, PARAM_BYTES },
{ &hf_month, add_byte_param, PARAM_BYTES },
{ &hf_year, add_word_param, PARAM_WORD },
{ &hf_weekday, add_byte_param, PARAM_BYTES },
{ NULL, NULL, PARAM_NONE }
};
static const item_list_t lm_data_resp_netremotetod[] = {
{ -1, lm_data_resp_netremotetod_nolevel },
};
static const item_t lm_params_req_netserverenum2[] = {
{ &hf_detail_level, add_detail_level, PARAM_WORD },
{ &no_hf, add_server_type_info, PARAM_DWORD },
{ &hf_enumeration_domain, add_string_param, PARAM_STRINGZ },
{ NULL, NULL, PARAM_NONE }
};
/*
* Create a subtree for all servers.
*/
static proto_item *
netserverenum2_servers_list(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
int offset)
{
if (tree) {
return proto_tree_add_text(tree, tvb, offset, -1,
"Servers");
} else
return NULL;
}
/*
* Create a subtree for a share.
*/
static proto_item *
netserverenum2_server_entry(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
int offset)
{
if (tree) {
return proto_tree_add_text(tree, tvb, offset, -1,
"Server %.16s", tvb_get_ptr(tvb, offset, 16));
} else
return NULL;
}
static const item_t lm_params_resp_netserverenum2[] = {
{ &hf_acount, add_word_param, PARAM_WORD },
{ NULL, NULL, PARAM_NONE }
};
static const item_t lm_params_req_netwkstagetinfo[] = {
{ &hf_detail_level, add_detail_level, PARAM_WORD },
{ NULL, NULL, PARAM_NONE }
};
static const item_t lm_params_resp_netwkstagetinfo[] = {
{ &hf_abytes, add_word_param, PARAM_WORD },
{ NULL, NULL, PARAM_NONE }
};
static const item_t lm_data_resp_netwkstagetinfo_10[] = {
{ &hf_computer_name, add_pointer_param, PARAM_STRINGZ },
{ &hf_user_name, add_pointer_param, PARAM_STRINGZ },
{ &hf_workstation_domain, add_pointer_param, PARAM_STRINGZ },
{ &hf_workstation_major, add_byte_param, PARAM_BYTES },
{ &hf_workstation_minor, add_byte_param, PARAM_BYTES },
{ &hf_logon_domain, add_pointer_param, PARAM_STRINGZ },
{ &hf_other_domains, add_pointer_param, PARAM_STRINGZ },
{ NULL, NULL, PARAM_NONE }
};
static const item_list_t lm_data_resp_netwkstagetinfo[] = {
{ 10, lm_data_resp_netwkstagetinfo_10 },
{ -1, lm_null }
};
static const item_t lm_params_req_netwkstauserlogon[] = {
{ &no_hf, add_pointer_param, PARAM_STRINGZ },
{ &no_hf, add_pointer_param, PARAM_STRINGZ },
{ &hf_detail_level, add_detail_level, PARAM_WORD },
{ &no_hf, add_logon_args, PARAM_BYTES },
{ &hf_ustruct_size, add_word_param, PARAM_WORD },
{ NULL, NULL, PARAM_NONE }
};
static const item_t lm_params_resp_netwkstauserlogon[] = {
{ &hf_abytes, add_word_param, PARAM_WORD },
{ NULL, NULL, PARAM_NONE }
};
static const item_t lm_data_resp_netwkstauserlogon_1[] = {
{ &hf_logon_code, add_word_param, PARAM_WORD },
{ &hf_user_name, add_byte_param, PARAM_BYTES },
{ &no_hf, add_pad_param, PARAM_BYTES },
{ &hf_privilege_level, add_word_param, PARAM_WORD },
{ &hf_operator_privileges, add_dword_param, PARAM_DWORD },
{ &hf_num_logons, add_nlogons, PARAM_WORD },
{ &hf_bad_pw_count, add_word_param, PARAM_WORD },
{ &hf_last_logon, add_abstime_absent_unknown, PARAM_DWORD },
{ &hf_last_logoff, add_abstime_absent_unknown, PARAM_DWORD },
{ &hf_logoff_time, add_abstime_absent_never, PARAM_DWORD },
{ &hf_kickoff_time, add_abstime_absent_never, PARAM_DWORD },
{ &hf_password_age, add_reltime, PARAM_DWORD },
{ &hf_password_can_change, add_abstime_absent_never, PARAM_DWORD },
{ &hf_password_must_change, add_abstime_absent_never, PARAM_DWORD },
{ &hf_server_name, add_pointer_param, PARAM_STRINGZ },
{ &hf_logon_domain, add_pointer_param, PARAM_STRINGZ },
{ &hf_script_path, add_pointer_param, PARAM_STRINGZ },
{ &hf_reserved, add_dword_param, PARAM_DWORD },
{ NULL, NULL, PARAM_NONE }
};
static const item_list_t lm_data_resp_netwkstauserlogon[] = {
{ 1, lm_data_resp_netwkstauserlogon_1 },
{ -1, lm_null }
};
static const item_t lm_params_req_netwkstauserlogoff[] = {
{ &hf_user_name, add_byte_param, PARAM_BYTES },
{ &no_hf, add_pad_param, PARAM_BYTES },
{ &hf_workstation_name, add_byte_param, PARAM_BYTES },
{ NULL, NULL, PARAM_NONE }
};
static const item_t lm_params_resp_netwkstauserlogoff[] = {
{ &hf_abytes, add_word_param, PARAM_WORD },
{ NULL, NULL, PARAM_NONE }
};
static const item_t lm_data_resp_netwkstauserlogoff_1[] = {
{ &hf_logoff_code, add_word_param, PARAM_WORD },
{ &hf_duration, add_reltime, PARAM_DWORD },
{ &hf_num_logons, add_nlogons, PARAM_WORD },
{ NULL, NULL, PARAM_NONE }
};
static const item_list_t lm_data_resp_netwkstauserlogoff[] = {
{ 1, lm_data_resp_netwkstauserlogoff_1 },
{ -1, lm_null }
};
static const item_t lm_params_req_samoemchangepassword[] = {
{ &hf_user_name, add_string_param, PARAM_STRINGZ },
{ NULL, NULL, PARAM_NONE }
};
static const item_t lm_data_req_samoemchangepassword[] = {
{ &hf_new_password, add_byte_param, PARAM_BYTES },
{ &hf_old_password, add_byte_param, PARAM_BYTES },
{ NULL, NULL, PARAM_NONE }
};
#define LANMAN_NETSHAREENUM 0
#define LANMAN_NETSHAREGETINFO 1
#define LANMAN_NETSERVERGETINFO 13
#define LANMAN_NETGROUPGETUSERS 52
#define LANMAN_NETUSERGETINFO 56
#define LANMAN_NETUSERGETGROUPS 59
#define LANMAN_NETWKSTAGETINFO 63
#define LANMAN_DOSPRINTQENUM 69
#define LANMAN_DOSPRINTQGETINFO 70
#define LANMAN_WPRINTQUEUEPAUSE 74
#define LANMAN_WPRINTQUEUERESUME 75
#define LANMAN_WPRINTJOBENUMERATE 76
#define LANMAN_WPRINTJOBGETINFO 77
#define LANMAN_RDOSPRINTJOBDEL 81
#define LANMAN_RDOSPRINTJOBPAUSE 82
#define LANMAN_RDOSPRINTJOBRESUME 83
#define LANMAN_WPRINTDESTENUM 84
#define LANMAN_WPRINTDESTGETINFO 85
#define LANMAN_NETREMOTETOD 91
#define LANMAN_WPRINTQUEUEPURGE 103
#define LANMAN_NETSERVERENUM2 104
#define LANMAN_WACCESSGETUSERPERMS 105
#define LANMAN_SETUSERPASSWORD 115
#define LANMAN_NETWKSTAUSERLOGON 132
#define LANMAN_NETWKSTAUSERLOGOFF 133
#define LANMAN_PRINTJOBINFO 147
#define LANMAN_WPRINTDRIVERENUM 205
#define LANMAN_WPRINTQPROCENUM 206
#define LANMAN_WPRINTPORTENUM 207
#define LANMAN_SAMOEMCHANGEPASSWORD 214
static const struct lanman_desc lmd[] = {
{ LANMAN_NETSHAREENUM,
lm_params_req_netshareenum,
NULL,
NULL,
lm_null,
lm_null,
lm_params_resp_netshareenum,
netshareenum_shares_list,
&ett_lanman_shares,
netshareenum_share_entry,
&ett_lanman_share,
lm_data_resp_netshareenum,
lm_null },
{ LANMAN_NETSHAREGETINFO,
lm_params_req_netsharegetinfo,
NULL,
NULL,
lm_null,
lm_null,
lm_params_resp_netsharegetinfo,
NULL,
NULL,
NULL,
NULL,
lm_data_resp_netsharegetinfo,
lm_null },
{ LANMAN_NETSERVERGETINFO,
lm_params_req_netservergetinfo,
NULL,
NULL,
lm_null,
lm_null,
lm_params_resp_netservergetinfo,
NULL,
NULL,
NULL,
NULL,
lm_data_serverinfo,
lm_null },
{ LANMAN_NETUSERGETINFO,
lm_params_req_netusergetinfo,
NULL,
NULL,
lm_null,
lm_null,
lm_params_resp_netusergetinfo,
NULL,
NULL,
NULL,
NULL,
lm_data_resp_netusergetinfo,
lm_null },
{ LANMAN_NETREMOTETOD,
lm_null,
NULL,
NULL,
lm_null,
lm_null,
lm_null,
NULL,
NULL,
NULL,
NULL,
lm_data_resp_netremotetod,
lm_null },
{ LANMAN_NETSERVERENUM2,
lm_params_req_netserverenum2,
NULL,
NULL,
lm_null,
lm_null,
lm_params_resp_netserverenum2,
netserverenum2_servers_list,
&ett_lanman_servers,
netserverenum2_server_entry,
&ett_lanman_server,
lm_data_serverinfo,
lm_null },
{ LANMAN_NETWKSTAGETINFO,
lm_params_req_netwkstagetinfo,
NULL,
NULL,
lm_null,
lm_null,
lm_params_resp_netwkstagetinfo,
NULL,
NULL,
NULL,
NULL,
lm_data_resp_netwkstagetinfo,
lm_null },
{ LANMAN_NETWKSTAUSERLOGON,
lm_params_req_netwkstauserlogon,
NULL,
NULL,
lm_null,
lm_null,
lm_params_resp_netwkstauserlogon,
NULL,
NULL,
NULL,
NULL,
lm_data_resp_netwkstauserlogon,
lm_null },
{ LANMAN_NETWKSTAUSERLOGOFF,
lm_params_req_netwkstauserlogoff,
NULL,
NULL,
lm_null,
lm_null,
lm_params_resp_netwkstauserlogoff,
NULL,
NULL,
NULL,
NULL,
lm_data_resp_netwkstauserlogoff,
lm_null },
{ LANMAN_SAMOEMCHANGEPASSWORD,
lm_params_req_samoemchangepassword,
NULL,
NULL,
lm_data_req_samoemchangepassword,
lm_null,
lm_null,
NULL,
NULL,
NULL,
NULL,
lm_null_list,
lm_null },
{ -1,
lm_null,
NULL,
NULL,
lm_null,
lm_null,
lm_null,
NULL,
NULL,
NULL,
NULL,
lm_null_list,
lm_null }
};
static const struct lanman_desc *
find_lanman(int lanman_num)
{
int i;
for (i = 0; lmd[i].lanman_num != -1; i++) {
if (lmd[i].lanman_num == lanman_num)
break;
}
return &lmd[i];
}
static const guchar *
get_count(const guchar *desc, int *countp)
{
int count = 0;
guchar c;
if (!isdigit(*desc)) {
*countp = 1; /* no count was supplied */
return desc;
}
while ((c = *desc) != '\0' && isdigit(c)) {
count = (count * 10) + c - '0';
desc++;
}
*countp = count; /* XXX - what if it's 0? */
return desc;
}
static int
dissect_request_parameters(tvbuff_t *tvb, int offset, packet_info *pinfo,
proto_tree *tree, const guchar *desc, const item_t *items,
gboolean *has_data_p)
{
guint c;
guint16 WParam;
guint32 LParam;
guint string_len;
int count;
*has_data_p = FALSE;
while ((c = *desc++) != '\0') {
switch (c) {
case 'W':
/*
* A 16-bit word value in the request.
*/
if (items->func == NULL) {
/*
* We've run out of items in the table;
* fall back on the default.
*/
offset = add_word_param(tvb, offset, 0, pinfo,
tree, 0, -1);
} else if (items->type != PARAM_WORD) {
/*
* Descriptor character is 'W', but this
* isn't a word parameter.
*/
WParam = tvb_get_letohs(tvb, offset);
proto_tree_add_text(tree, tvb, offset, 2,
"%s: Value is %u (0x%04X), type is wrong (W)",
(*items->hf_index == -1) ?
"Word Param" :
proto_registrar_get_name(*items->hf_index),
WParam, WParam);
offset += 2;
items++;
} else {
offset = (*items->func)(tvb, offset, 0, pinfo,
tree, 0, *items->hf_index);
items++;
}
break;
case 'D':
/*
* A 32-bit doubleword value in the request.
*/
if (items->func == NULL) {
/*
* We've run out of items in the table;
* fall back on the default.
*/
offset = add_dword_param(tvb, offset, 0, pinfo,
tree, 0, -1);
} else if (items->type != PARAM_DWORD) {
/*
* Descriptor character is 'D', but this
* isn't a doubleword parameter.
*/
LParam = tvb_get_letohl(tvb, offset);
proto_tree_add_text(tree, tvb, offset, 2,
"%s: Value is %u (0x%08X), type is wrong (D)",
(*items->hf_index == -1) ?
"Doubleword Param" :
proto_registrar_get_name(*items->hf_index),
LParam, LParam);
offset += 4;
items++;
} else {
offset = (*items->func)(tvb, offset, 0, pinfo,
tree, 0, *items->hf_index);
items++;
}
break;
case 'b':
/*
* A byte or multi-byte value in the request.
*/
desc = get_count(desc, &count);
if (items->func == NULL) {
/*
* We've run out of items in the table;
* fall back on the default.
*/
offset = add_byte_param(tvb, offset, count,
pinfo, tree, 0, -1);
} else if (items->type != PARAM_BYTES) {
/*
* Descriptor character is 'b', but this
* isn't a byte/bytes parameter.
*/
proto_tree_add_text(tree, tvb, offset, count,
"%s: Value is %s, type is wrong (b)",
(*items->hf_index == -1) ?
"Byte Param" :
proto_registrar_get_name(*items->hf_index),
tvb_bytes_to_str(tvb, offset, count));
offset += count;
items++;
} else {
offset = (*items->func)(tvb, offset, count,
pinfo, tree, 0, *items->hf_index);
items++;
}
break;
case 'O':
/*
* A null pointer.
*/
if (items->func == NULL) {
/*
* We've run out of items in the table;
* fall back on the default.
*/
add_null_pointer_param(tvb, offset, 0,
pinfo, tree, 0, -1);
} else {
/*
* If "*items->hf_index" is -1, this is
* a reserved must-be-null field; don't
* clutter the protocol tree by putting
* it in.
*/
if (*items->hf_index != -1) {
add_null_pointer_param(tvb,
offset, 0, pinfo, tree, 0,
*items->hf_index);
}
items++;
}
break;
case 'z':
/*
* A null-terminated ASCII string.
*/
if (items->func == NULL) {
/*
* We've run out of items in the table;
* fall back on the default.
*/
offset = add_string_param(tvb, offset, 0,
pinfo, tree, 0, -1);
} else if (items->type != PARAM_STRINGZ) {
/*
* Descriptor character is 'z', but this
* isn't a string parameter.
*/
string_len = tvb_strsize(tvb, offset);
proto_tree_add_text(tree, tvb, offset, string_len,
"%s: Value is %s, type is wrong (z)",
(*items->hf_index == -1) ?
"String Param" :
proto_registrar_get_name(*items->hf_index),
tvb_format_text(tvb, offset, string_len));
offset += string_len;
items++;
} else {
offset = (*items->func)(tvb, offset, 0,
pinfo, tree, 0, *items->hf_index);
items++;
}
break;
case 'F':
/*
* One or more pad bytes.
*/
desc = get_count(desc, &count);
proto_tree_add_text(tree, tvb, offset, count,
"%s", "Padding");
offset += count;
break;
case 'L':
/*
* 16-bit receive buffer length.
*/
proto_tree_add_item(tree, hf_recv_buf_len, tvb,
offset, 2, TRUE);
offset += 2;
break;
case 's':
/*
* 32-bit send buffer offset.
* This appears not to be sent over the wire.
*/
*has_data_p = TRUE;
break;
case 'T':
/*
* 16-bit send buffer length.
* This also appears not to be sent over the wire.
*/
break;
default:
break;
}
}
return offset;
}
static int
dissect_response_parameters(tvbuff_t *tvb, int offset, packet_info *pinfo,
proto_tree *tree, const guchar *desc, const item_t *items,
gboolean *has_data_p, gboolean *has_ent_count_p, guint16 *ent_count_p)
{
guint c;
guint16 WParam;
guint32 LParam;
int count;
*has_data_p = FALSE;
*has_ent_count_p = FALSE;
while ((c = *desc++) != '\0') {
switch (c) {
case 'r':
/*
* 32-bit receive buffer offset.
*/
*has_data_p = TRUE;
break;
case 'g':
/*
* A byte or series of bytes is returned.
*/
desc = get_count(desc, &count);
if (items->func == NULL) {
/*
* We've run out of items in the table;
* fall back on the default.
*/
offset = add_byte_param(tvb, offset, count,
pinfo, tree, 0, -1);
} else if (items->type != PARAM_BYTES) {
/*
* Descriptor character is 'b', but this
* isn't a byte/bytes parameter.
*/
proto_tree_add_text(tree, tvb, offset, count,
"%s: Value is %s, type is wrong (g)",
(*items->hf_index == -1) ?
"Byte Param" :
proto_registrar_get_name(*items->hf_index),
tvb_bytes_to_str(tvb, offset, count));
offset += count;
items++;
} else {
offset = (*items->func)(tvb, offset, count,
pinfo, tree, 0, *items->hf_index);
items++;
}
break;
case 'h':
/*
* A 16-bit word is received.
*/
if (items->func == NULL) {
/*
* We've run out of items in the table;
* fall back on the default.
*/
offset = add_word_param(tvb, offset, 0, pinfo,
tree, 0, -1);
} else if (items->type != PARAM_WORD) {
/*
* Descriptor character is 'h', but this
* isn't a word parameter.
*/
WParam = tvb_get_letohs(tvb, offset);
proto_tree_add_text(tree, tvb, offset, 2,
"%s: Value is %u (0x%04X), type is wrong (W)",
(*items->hf_index == -1) ?
"Word Param" :
proto_registrar_get_name(*items->hf_index),
WParam, WParam);
offset += 2;
items++;
} else {
offset = (*items->func)(tvb, offset, 0, pinfo,
tree, 0, *items->hf_index);
items++;
}
break;
case 'i':
/*
* A 32-bit doubleword is received.
*/
if (items->func == NULL) {
/*
* We've run out of items in the table;
* fall back on the default.
*/
offset = add_dword_param(tvb, offset, 0, pinfo,
tree, 0, -1);
} else if (items->type != PARAM_DWORD) {
/*
* Descriptor character is 'i', but this
* isn't a doubleword parameter.
*/
LParam = tvb_get_letohl(tvb, offset);
proto_tree_add_text(tree, tvb, offset, 2,
"%s: Value is %u (0x%08X), type is wrong (i)",
(*items->hf_index == -1) ?
"Doubleword Param" :
proto_registrar_get_name(*items->hf_index),
LParam, LParam);
offset += 4;
items++;
} else {
offset = (*items->func)(tvb, offset, 0, pinfo,
tree, 0, *items->hf_index);
items++;
}
break;
case 'e':
/*
* A 16-bit entry count is returned.
*/
WParam = tvb_get_letohs(tvb, offset);
proto_tree_add_uint(tree, hf_ecount, tvb, offset, 2,
WParam);
offset += 2;
*has_ent_count_p = TRUE;
*ent_count_p = WParam; /* Save this for later retrieval */
break;
default:
break;
}
}
return offset;
}
static int
dissect_transact_data(tvbuff_t *tvb, int offset, int convert,
packet_info *pinfo, proto_tree *tree, const guchar *desc,
const item_t *items, guint16 *aux_count_p)
{
guint c;
guint16 WParam;
guint32 LParam;
int count;
int cptr;
const char *string;
gint string_len;
if (aux_count_p != NULL)
*aux_count_p = 0;
while ((c = *desc++) != '\0') {
switch (c) {
case 'W':
/*
* A 16-bit word value.
* XXX - handle the count?
*/
desc = get_count(desc, &count);
if (items->func == NULL) {
/*
* We've run out of items in the table;
* fall back on the default.
*/
offset = add_word_param(tvb, offset, 0, pinfo,
tree, convert, -1);
} else if (items->type != PARAM_WORD) {
/*
* Descriptor character is 'W', but this
* isn't a word parameter.
*/
WParam = tvb_get_letohs(tvb, offset);
proto_tree_add_text(tree, tvb, offset, 2,
"%s: Value is %u (0x%04X), type is wrong (W)",
(*items->hf_index == -1) ?
"Word Param" :
proto_registrar_get_name(*items->hf_index),
WParam, WParam);
offset += 2;
items++;
} else {
offset = (*items->func)(tvb, offset, 0, pinfo,
tree, convert, *items->hf_index);
items++;
}
break;
case 'D':
/*
* A 32-bit doubleword value.
* XXX - handle the count?
*/
desc = get_count(desc, &count);
if (items->func == NULL) {
/*
* We've run out of items in the table;
* fall back on the default.
*/
offset = add_dword_param(tvb, offset, 0, pinfo,
tree, convert, -1);
} else if (items->type != PARAM_DWORD) {
/*
* Descriptor character is 'D', but this
* isn't a doubleword parameter.
*/
LParam = tvb_get_letohl(tvb, offset);
proto_tree_add_text(tree, tvb, offset, 2,
"%s: Value is %u (0x%08X), type is wrong (D)",
(*items->hf_index == -1) ?
"Doubleword Param" :
proto_registrar_get_name(*items->hf_index),
LParam, LParam);
offset += 4;
items++;
} else {
offset = (*items->func)(tvb, offset, 0, pinfo,
tree, convert, *items->hf_index);
items++;
}
break;
case 'B':
/*
* A byte or multi-byte value.
*/
desc = get_count(desc, &count);
if (items->func == NULL) {
/*
* We've run out of items in the table;
* fall back on the default.
*/
offset = add_byte_param(tvb, offset, count,
pinfo, tree, convert, -1);
} else if (items->type != PARAM_BYTES) {
/*
* Descriptor character is 'B', but this
* isn't a byte/bytes parameter.
*/
proto_tree_add_text(tree, tvb, offset, count,
"%s: Value is %s, type is wrong (B)",
(*items->hf_index == -1) ?
"Byte Param" :
proto_registrar_get_name(*items->hf_index),
tvb_bytes_to_str(tvb, offset, count));
offset += count;
items++;
} else {
offset = (*items->func)(tvb, offset, count,
pinfo, tree, convert, *items->hf_index);
items++;
}
break;
case 'O':
/*
* A null pointer.
*/
if (items->func == NULL) {
/*
* We've run out of items in the table;
* fall back on the default.
*/
add_null_pointer_param(tvb, offset, 0,
pinfo, tree, convert, -1);
} else {
/*
* If "*items->hf_index" is -1, this is
* a reserved must-be-null field; don't
* clutter the protocol tree by putting
* it in.
*/
if (*items->hf_index != -1) {
add_null_pointer_param(tvb,
offset, 0, pinfo, tree, convert,
*items->hf_index);
}
items++;
}
break;
case 'z':
/*
* A pointer to a null-terminated ASCII string.
*/
if (items->func == NULL) {
/*
* We've run out of items in the table;
* fall back on the default.
*/
offset = add_pointer_param(tvb, offset, 0,
pinfo, tree, convert, -1);
} else if (items->type != PARAM_STRINGZ) {
/*
* Descriptor character is 'z', but this
* isn't a string parameter.
*/
string = get_pointer_value(tvb, offset,
convert, &cptr, &string_len);
offset += 4;
proto_tree_add_text(tree, tvb, cptr, string_len,
"%s: Value is %s, type is wrong (z)",
(*items->hf_index == -1) ?
"String Param" :
proto_registrar_get_name(*items->hf_index),
string);
items++;
} else {
offset = (*items->func)(tvb, offset, 0,
pinfo, tree, convert, *items->hf_index);
items++;
}
break;
case 'N':
/*
* 16-bit auxiliary data structure count.
* XXX - hf_acount?
*/
WParam = tvb_get_letohs(tvb, offset);
proto_tree_add_text(tree, tvb, offset, 2,
"%s: %u (0x%04X)",
"Auxiliary data structure count",
WParam, WParam);
offset += 2;
if (aux_count_p != NULL)
*aux_count_p = WParam; /* Save this for later retrieval */
break;
default:
break;
}
}
return offset;
}
static const value_string commands[] = {
{LANMAN_NETSHAREENUM, "NetShareEnum"},
{LANMAN_NETSHAREGETINFO, "NetShareGetInfo"},
{LANMAN_NETSERVERGETINFO, "NetServerGetInfo"},
{LANMAN_NETGROUPGETUSERS, "NetGroupGetUsers"},
{LANMAN_NETUSERGETINFO, "NetUserGetInfo"},
{LANMAN_NETUSERGETGROUPS, "NetUserGetGroups"},
{LANMAN_NETWKSTAGETINFO, "NetWkstaGetInfo"},
{LANMAN_DOSPRINTQENUM, "DOSPrintQEnum"},
{LANMAN_DOSPRINTQGETINFO, "DOSPrintQGetInfo"},
{LANMAN_WPRINTQUEUEPAUSE, "WPrintQueuePause"},
{LANMAN_WPRINTQUEUERESUME, "WPrintQueueResume"},
{LANMAN_WPRINTJOBENUMERATE, "WPrintJobEnumerate"},
{LANMAN_WPRINTJOBGETINFO, "WPrintJobGetInfo"},
{LANMAN_RDOSPRINTJOBDEL, "RDOSPrintJobDel"},
{LANMAN_RDOSPRINTJOBPAUSE, "RDOSPrintJobPause"},
{LANMAN_RDOSPRINTJOBRESUME, "RDOSPrintJobResume"},
{LANMAN_WPRINTDESTENUM, "WPrintDestEnum"},
{LANMAN_WPRINTDESTGETINFO, "WPrintDestGetInfo"},
{LANMAN_NETREMOTETOD, "NetRemoteTOD"},
{LANMAN_WPRINTQUEUEPURGE, "WPrintQueuePurge"},
{LANMAN_NETSERVERENUM2, "NetServerEnum2"},
{LANMAN_WACCESSGETUSERPERMS, "WAccessGetUserPerms"},
{LANMAN_SETUSERPASSWORD, "SetUserPassword"},
{LANMAN_NETWKSTAUSERLOGON, "NetWkstaUserLogon"},
{LANMAN_NETWKSTAUSERLOGOFF, "NetWkstaUserLogoff"},
{LANMAN_PRINTJOBINFO, "PrintJobInfo"},
{LANMAN_WPRINTDRIVERENUM, "WPrintDriverEnum"},
{LANMAN_WPRINTQPROCENUM, "WPrintQProcEnum"},
{LANMAN_WPRINTPORTENUM, "WPrintPortEnum"},
{LANMAN_SAMOEMCHANGEPASSWORD, "SamOEMChangePassword"},
{0, NULL}
};
static void
dissect_response_data(tvbuff_t *tvb, packet_info *pinfo, int convert,
proto_tree *tree, struct smb_info *smb_info,
const struct lanman_desc *lanman, gboolean has_ent_count,
guint16 ent_count)
{
smb_transact_info_t *trp = smb_info->sip->extra_info;
const item_list_t *resp_data_list;
int offset, start_offset;
const item_t *resp_data;
proto_item *data_item;
proto_tree *data_tree;
proto_item *entry_item;
proto_tree *entry_tree;
guint i, j;
guint16 aux_count;
/*
* Find the item table for the matching request's detail level.
*/
for (resp_data_list = lanman->resp_data_list;
resp_data_list->level != -1; resp_data_list++) {
if (resp_data_list->level == trp->info_level)
break;
}
resp_data = resp_data_list->item_list;
offset = 0;
if (lanman->resp_data_item != NULL) {
/*
* Create a protocol tree item for the data.
*/
data_item = (*lanman->resp_data_item)(tvb,
pinfo, tree, offset);
data_tree = proto_item_add_subtree(data_item,
*lanman->ett_resp_data);
} else {
/*
* Just leave it at the top level.
*/
data_item = NULL;
data_tree = tree;
}
if (trp->data_descrip == NULL) {
/*
* This could happen if we only dissected
* part of the request to which this is a
* reply, e.g. if the request was split
* across TCP segments and we weren't doing
* TCP desegmentation, or if we had a snapshot
* length that was too short.
*
* We can't dissect the data; just show it
* as raw data.
*/
proto_tree_add_text(tree, tvb, offset, -1,
"Data (no descriptor available)");
offset += tvb_length_remaining(tvb, offset);
} else {
/*
* If we have an entry count, show all the entries,
* with each one having a protocol tree item.
*
* Otherwise, we just show one returned item, with
* no protocol tree item.
*/
if (!has_ent_count)
ent_count = 1;
for (i = 0; i < ent_count; i++) {
start_offset = offset;
if (has_ent_count) {
/*
* Create a protocol tree item for the
* entry.
*/
entry_item =
(*lanman->resp_data_element_item)
(tvb, pinfo, data_tree, offset);
entry_tree = proto_item_add_subtree(
entry_item,
*lanman->ett_resp_data_element_item);
} else {
/*
* Just leave it at the current
* level.
*/
entry_item = NULL;
entry_tree = data_tree;
}
offset = dissect_transact_data(tvb, offset,
convert, pinfo, entry_tree,
trp->data_descrip, resp_data, &aux_count);
/* auxiliary data */
if (trp->aux_data_descrip != NULL) {
for (j = 0; j < aux_count; j++) {
offset = dissect_transact_data(
tvb, offset, convert,
pinfo, entry_tree,
trp->data_descrip,
lanman->resp_aux_data, NULL);
}
}
if (entry_item != NULL) {
/*
* Set the length of the protocol tree
* item for the entry.
*/
proto_item_set_len(entry_item,
offset - start_offset);
}
}
}
if (data_item != NULL) {
/*
* Set the length of the protocol tree item
* for the data.
*/
proto_item_set_len(data_item, offset);
}
}
static gboolean
dissect_pipe_lanman(tvbuff_t *pd_tvb, tvbuff_t *p_tvb, tvbuff_t *d_tvb,
packet_info *pinfo, proto_tree *parent_tree)
{
smb_info_t *smb_info = pinfo->private_data;
smb_transact_info_t *trp = smb_info->sip->extra_info;
int offset = 0, start_offset;
guint16 cmd;
guint16 status;
int convert;
const struct lanman_desc *lanman;
proto_item *item = NULL;
proto_tree *tree = NULL;
guint descriptor_len;
const gchar *param_descrip, *data_descrip, *aux_data_descrip = NULL;
gboolean has_data;
gboolean has_ent_count;
guint16 ent_count, aux_count;
guint i;
proto_item *data_item;
proto_tree *data_tree;
if (!proto_is_protocol_enabled(proto_smb_lanman))
return FALSE;
if (smb_info->request && p_tvb == NULL) {
/*
* Requests must have parameters.
*/
return FALSE;
}
pinfo->current_proto = "LANMAN";
if (check_col(pinfo->cinfo, COL_PROTOCOL)) {
col_set_str(pinfo->cinfo, COL_PROTOCOL, "LANMAN");
}
if (parent_tree) {
item = proto_tree_add_item(parent_tree, proto_smb_lanman,
pd_tvb, 0, -1, FALSE);
tree = proto_item_add_subtree(item, ett_lanman);
}
if (smb_info->request) { /* this is a request */
/* function code */
cmd = tvb_get_letohs(p_tvb, offset);
if (check_col(pinfo->cinfo, COL_INFO)) {
col_add_fstr(pinfo->cinfo, COL_INFO, "%s Request", val_to_str(cmd, commands, "Unknown Command:0x%02x"));
}
proto_tree_add_uint(tree, hf_function_code, p_tvb, offset, 2,
cmd);
offset += 2;
/*
* If we haven't already done so, save the function code in
* the structure we were handed, so that it's available to
* the code parsing the reply, and initialize the detail
* level to -1, meaning "unknown".
*/
if (!pinfo->fd->flags.visited) {
trp->lanman_cmd = cmd;
trp->info_level = -1;
trp->param_descrip=NULL;
trp->data_descrip=NULL;
trp->aux_data_descrip=NULL;
}
/* parameter descriptor */
descriptor_len = tvb_strsize(p_tvb, offset);
proto_tree_add_item(tree, hf_param_desc, p_tvb, offset,
descriptor_len, TRUE);
param_descrip = tvb_get_ptr(p_tvb, offset, descriptor_len);
if (!pinfo->fd->flags.visited) {
/*
* Save the parameter descriptor for future use.
*/
g_assert(trp->param_descrip == NULL);
trp->param_descrip = g_strdup(param_descrip);
}
offset += descriptor_len;
/* return descriptor */
descriptor_len = tvb_strsize(p_tvb, offset);
proto_tree_add_item(tree, hf_return_desc, p_tvb, offset,
descriptor_len, TRUE);
data_descrip = tvb_get_ptr(p_tvb, offset, descriptor_len);
if (!pinfo->fd->flags.visited) {
/*
* Save the return descriptor for future use.
*/
g_assert(trp->data_descrip == NULL);
trp->data_descrip = g_strdup(data_descrip);
}
offset += descriptor_len;
lanman = find_lanman(cmd);
/* request parameters */
start_offset = offset;
offset = dissect_request_parameters(p_tvb, offset, pinfo, tree,
param_descrip, lanman->req, &has_data);
/* auxiliary data descriptor */
if (tvb_reported_length_remaining(p_tvb, offset) > 0){
/*
* There are more parameters left, so the next
* item is the auxiliary data descriptor.
*/
descriptor_len = tvb_strsize(p_tvb, offset);
proto_tree_add_item(tree, hf_return_desc, p_tvb, offset,
descriptor_len, TRUE);
aux_data_descrip = tvb_get_ptr(p_tvb, offset, descriptor_len);
if (!pinfo->fd->flags.visited) {
/*
* Save the auxiliary data descriptor for
* future use.
*/
g_assert(trp->aux_data_descrip == NULL);
trp->aux_data_descrip =
g_strdup(aux_data_descrip);
}
offset += descriptor_len;
}
/* reset offset, we now start dissecting the data area */
offset = 0;
if (has_data && d_tvb && tvb_reported_length(d_tvb) != 0) {
/*
* There's a send buffer item in the descriptor
* string, and the data count in the transaction
* is non-zero, so there's data to dissect.
*/
if (lanman->req_data_item != NULL) {
/*
* Create a protocol tree item for the data.
*/
data_item = (*lanman->req_data_item)(d_tvb,
pinfo, tree, offset);
data_tree = proto_item_add_subtree(data_item,
*lanman->ett_req_data);
} else {
/*
* Just leave it at the top level.
*/
data_item = NULL;
data_tree = tree;
}
/* data */
offset = dissect_transact_data(d_tvb, offset, -1,
pinfo, data_tree, data_descrip, lanman->req_data,
&aux_count); /* XXX - what about strings? */
/* auxiliary data */
if (aux_data_descrip != NULL) {
for (i = 0; i < aux_count; i++) {
offset = dissect_transact_data(d_tvb,
offset, -1, pinfo, data_tree,
aux_data_descrip,
lanman->req_aux_data, NULL);
}
}
if (data_item != NULL) {
/*
* Set the length of the protocol tree item
* for the data.
*/
proto_item_set_len(data_item, offset);
}
}
} else {
/*
* This is a response.
* Have we seen the request to which it's a response?
*/
if (trp == NULL)
return FALSE; /* no - can't dissect it */
/* ok we have seen this one before */
/* if it looks like an interim response, update COL_INFO and return */
if( ( (p_tvb==NULL) || (tvb_reported_length(p_tvb)==0) )
&& ( (d_tvb==NULL) || (tvb_reported_length(d_tvb)==0) ) ){
/* command */
if (check_col(pinfo->cinfo, COL_INFO)) {
col_add_fstr(pinfo->cinfo, COL_INFO, "%s Interim Response",
val_to_str(trp->lanman_cmd, commands, "Unknown Command (0x%02x)"));
}
proto_tree_add_uint(tree, hf_function_code, p_tvb, 0, 0, trp->lanman_cmd);
return TRUE;
}
/* command */
if (check_col(pinfo->cinfo, COL_INFO)) {
col_add_fstr(pinfo->cinfo, COL_INFO, "%s Response",
val_to_str(trp->lanman_cmd, commands, "Unknown Command (0x%02x)"));
}
proto_tree_add_uint(tree, hf_function_code, p_tvb, 0, 0,
trp->lanman_cmd);
lanman = find_lanman(trp->lanman_cmd);
/* response parameters */
/* status */
status = tvb_get_letohs(p_tvb, offset);
proto_tree_add_uint(tree, hf_status, p_tvb, offset, 2, status);
offset += 2;
/* convert */
convert = tvb_get_letohs(p_tvb, offset);
proto_tree_add_uint(tree, hf_convert, p_tvb, offset, 2, convert);
offset += 2;
/* rest of the parameters */
offset = dissect_response_parameters(p_tvb, offset, pinfo, tree,
trp->param_descrip, lanman->resp, &has_data,
&has_ent_count, &ent_count);
/* reset offset, we now start dissecting the data area */
offset = 0;
/* data */
if (has_data && d_tvb && tvb_reported_length(d_tvb) > 0) {
/*
* There's a receive buffer item in the descriptor
* string, and the data count in the transaction
* is non-zero, so there's data to dissect.
*/
dissect_response_data(d_tvb, pinfo, convert, tree,
smb_info, lanman, has_ent_count, ent_count);
}
}
return TRUE;
}
void
proto_register_pipe_lanman(void)
{
static hf_register_info hf[] = {
{ &hf_function_code,
{ "Function Code", "lanman.function_code", FT_UINT16, BASE_DEC,
VALS(commands), 0, "LANMAN Function Code/Command", HFILL }},
{ &hf_param_desc,
{ "Parameter Descriptor", "lanman.param_desc", FT_STRING, BASE_NONE,
NULL, 0, "LANMAN Parameter Descriptor", HFILL }},
{ &hf_return_desc,
{ "Return Descriptor", "lanman.ret_desc", FT_STRING, BASE_NONE,
NULL, 0, "LANMAN Return Descriptor", HFILL }},
{ &hf_aux_data_desc,
{ "Auxiliary Data Descriptor", "lanman.aux_data_desc", FT_STRING, BASE_NONE,
NULL, 0, "LANMAN Auxiliary Data Descriptor", HFILL }},
{ &hf_detail_level,
{ "Detail Level", "lanman.level", FT_UINT16, BASE_DEC,
NULL, 0, "LANMAN Detail Level", HFILL }},
{ &hf_recv_buf_len,
{ "Receive Buffer Length", "lanman.recv_buf_len", FT_UINT16, BASE_DEC,
NULL, 0, "LANMAN Receive Buffer Length", HFILL }},
{ &hf_send_buf_len,
{ "Send Buffer Length", "lanman.send_buf_len", FT_UINT16, BASE_DEC,
NULL, 0, "LANMAN Send Buffer Length", HFILL }},
{ &hf_continuation_from,
{ "Continuation from message in frame", "lanman.continuation_from", FT_UINT32, BASE_DEC,
NULL, 0, "This is a LANMAN continuation from the message in the frame in question", HFILL }},
{ &hf_status,
{ "Status", "lanman.status", FT_UINT16, BASE_DEC,
VALS(status_vals), 0, "LANMAN Return status", HFILL }},
{ &hf_convert,
{ "Convert", "lanman.convert", FT_UINT16, BASE_DEC,
NULL, 0, "LANMAN Convert", HFILL }},
{ &hf_ecount,
{ "Entry Count", "lanman.entry_count", FT_UINT16, BASE_DEC,
NULL, 0, "LANMAN Number of Entries", HFILL }},
{ &hf_acount,
{ "Available Entries", "lanman.available_count", FT_UINT16, BASE_DEC,
NULL, 0, "LANMAN Number of Available Entries", HFILL }},
{ &hf_share_name,
{ "Share Name", "lanman.share.name", FT_STRING, BASE_NONE,
NULL, 0, "LANMAN Name of Share", HFILL }},
{ &hf_share_type,
{ "Share Type", "lanman.share.type", FT_UINT16, BASE_DEC,
VALS(share_type_vals), 0, "LANMAN Type of Share", HFILL }},
{ &hf_share_comment,
{ "Share Comment", "lanman.share.comment", FT_STRING, BASE_NONE,
NULL, 0, "LANMAN Share Comment", HFILL }},
{ &hf_share_permissions,
{ "Share Permissions", "lanman.share.permissions", FT_UINT16, BASE_DEC,
NULL, 0, "LANMAN Permissions on share", HFILL }},
{ &hf_share_max_uses,
{ "Share Max Uses", "lanman.share.max_uses", FT_UINT16, BASE_DEC,
NULL, 0, "LANMAN Max connections allowed to share", HFILL }},
{ &hf_share_current_uses,
{ "Share Current Uses", "lanman.share.current_uses", FT_UINT16, BASE_DEC,
NULL, 0, "LANMAN Current connections to share", HFILL }},
{ &hf_share_path,
{ "Share Path", "lanman.share.path", FT_STRING, BASE_NONE,
NULL, 0, "LANMAN Share Path", HFILL }},
{ &hf_share_password,
{ "Share Password", "lanman.share.password", FT_STRING, BASE_NONE,
NULL, 0, "LANMAN Share Password", HFILL }},
{ &hf_server_name,
{ "Server Name", "lanman.server.name", FT_STRING, BASE_NONE,
NULL, 0, "LANMAN Name of Server", HFILL }},
{ &hf_server_major,
{ "Major Version", "lanman.server.major", FT_UINT8, BASE_DEC,
NULL, 0, "LANMAN Server Major Version", HFILL }},
{ &hf_server_minor,
{ "Minor Version", "lanman.server.minor", FT_UINT8, BASE_DEC,
NULL, 0, "LANMAN Server Minor Version", HFILL }},
{ &hf_server_comment,
{ "Server Comment", "lanman.server.comment", FT_STRING, BASE_NONE,
NULL, 0, "LANMAN Server Comment", HFILL }},
{ &hf_abytes,
{ "Available Bytes", "lanman.available_bytes", FT_UINT16, BASE_DEC,
NULL, 0, "LANMAN Number of Available Bytes", HFILL }},
{ &hf_current_time,
{ "Current Date/Time", "lanman.current_time", FT_ABSOLUTE_TIME, BASE_NONE,
NULL, 0, "LANMAN Current date and time, in seconds since 00:00:00, January 1, 1970", HFILL }},
{ &hf_msecs,
{ "Milliseconds", "lanman.msecs", FT_UINT32, BASE_DEC,
NULL, 0, "LANMAN Milliseconds since arbitrary time in the past (typically boot time)", HFILL }},
{ &hf_hour,
{ "Hour", "lanman.hour", FT_UINT8, BASE_DEC,
NULL, 0, "LANMAN Current hour", HFILL }},
{ &hf_minute,
{ "Minute", "lanman.minute", FT_UINT8, BASE_DEC,
NULL, 0, "LANMAN Current minute", HFILL }},
{ &hf_second,
{ "Second", "lanman.second", FT_UINT8, BASE_DEC,
NULL, 0, "LANMAN Current second", HFILL }},
{ &hf_hundredths,
{ "Hundredths of a second", "lanman.hundredths", FT_UINT8, BASE_DEC,
NULL, 0, "LANMAN Current hundredths of a second", HFILL }},
{ &hf_tzoffset,
{ "Time Zone Offset", "lanman.tzoffset", FT_INT16, BASE_DEC,
NULL, 0, "LANMAN Offset of time zone from GMT, in minutes", HFILL }},
{ &hf_timeinterval,
{ "Time Interval", "lanman.timeinterval", FT_UINT16, BASE_DEC,
NULL, 0, "LANMAN .0001 second units per clock tick", HFILL }},
{ &hf_day,
{ "Day", "lanman.day", FT_UINT8, BASE_DEC,
NULL, 0, "LANMAN Current day", HFILL }},
{ &hf_month,
{ "Month", "lanman.month", FT_UINT8, BASE_DEC,
NULL, 0, "LANMAN Current month", HFILL }},
{ &hf_year,
{ "Year", "lanman.year", FT_UINT16, BASE_DEC,
NULL, 0, "LANMAN Current year", HFILL }},
{ &hf_weekday,
{ "Weekday", "lanman.weekday", FT_UINT8, BASE_DEC,
VALS(weekday_vals), 0, "LANMAN Current day of the week", HFILL }},
{ &hf_enumeration_domain,
{ "Enumeration Domain", "lanman.enumeration_domain", FT_STRING, BASE_NONE,
NULL, 0, "LANMAN Domain in which to enumerate servers", HFILL }},
{ &hf_computer_name,
{ "Computer Name", "lanman.computer_name", FT_STRING, BASE_NONE,
NULL, 0, "LANMAN Computer Name", HFILL }},
{ &hf_user_name,
{ "User Name", "lanman.user_name", FT_STRING, BASE_NONE,
NULL, 0, "LANMAN User Name", HFILL }},
{ &hf_workstation_domain,
{ "Workstation Domain", "lanman.workstation_domain", FT_STRING, BASE_NONE,
NULL, 0, "LANMAN Workstation Domain", HFILL }},
{ &hf_workstation_major,
{ "Workstation Major Version", "lanman.workstation_major", FT_UINT8, BASE_DEC,
NULL, 0, "LANMAN Workstation Major Version", HFILL }},
{ &hf_workstation_minor,
{ "Workstation Minor Version", "lanman.workstation_minor", FT_UINT8, BASE_DEC,
NULL, 0, "LANMAN Workstation Minor Version", HFILL }},
{ &hf_logon_domain,
{ "Logon Domain", "lanman.logon_domain", FT_STRING, BASE_NONE,
NULL, 0, "LANMAN Logon Domain", HFILL }},
{ &hf_other_domains,
{ "Other Domains", "lanman.other_domains", FT_STRING, BASE_NONE,
NULL, 0, "LANMAN Other Domains", HFILL }},
{ &hf_password,
{ "Password", "lanman.password", FT_STRING, BASE_NONE,
NULL, 0, "LANMAN Password", HFILL }},
{ &hf_workstation_name,
{ "Workstation Name", "lanman.workstation_name", FT_STRING, BASE_NONE,
NULL, 0, "LANMAN Workstation Name", HFILL }},
{ &hf_ustruct_size,
{ "Length of UStruct", "lanman.ustruct_size", FT_UINT16, BASE_DEC,
NULL, 0, "LANMAN UStruct Length", HFILL }},
{ &hf_logon_code,
{ "Logon Code", "lanman.logon_code", FT_UINT16, BASE_DEC,
VALS(status_vals), 0, "LANMAN Logon Code", HFILL }},
{ &hf_privilege_level,
{ "Privilege Level", "lanman.privilege_level", FT_UINT16, BASE_DEC,
VALS(privilege_vals), 0, "LANMAN Privilege Level", HFILL }},
{ &hf_operator_privileges,
{ "Operator Privileges", "lanman.operator_privileges", FT_UINT32, BASE_DEC,
VALS(op_privilege_vals), 0, "LANMAN Operator Privileges", HFILL }},
{ &hf_num_logons,
{ "Number of Logons", "lanman.num_logons", FT_UINT16, BASE_DEC,
NULL, 0, "LANMAN Number of Logons", HFILL }},
{ &hf_bad_pw_count,
{ "Bad Password Count", "lanman.bad_pw_count", FT_UINT16, BASE_DEC,
NULL, 0, "LANMAN Number of incorrect passwords entered since last successful login", HFILL }},
{ &hf_last_logon,
{ "Last Logon Date/Time", "lanman.last_logon", FT_ABSOLUTE_TIME, BASE_NONE,
NULL, 0, "LANMAN Date and time of last logon", HFILL }},
{ &hf_last_logoff,
{ "Last Logoff Date/Time", "lanman.last_logoff", FT_ABSOLUTE_TIME, BASE_NONE,
NULL, 0, "LANMAN Date and time of last logoff", HFILL }},
{ &hf_logoff_time,
{ "Logoff Date/Time", "lanman.logoff_time", FT_ABSOLUTE_TIME, BASE_NONE,
NULL, 0, "LANMAN Date and time when user should log off", HFILL }},
{ &hf_kickoff_time,
{ "Kickoff Date/Time", "lanman.kickoff_time", FT_ABSOLUTE_TIME, BASE_NONE,
NULL, 0, "LANMAN Date and time when user will be logged off", HFILL }},
{ &hf_password_age,
{ "Password Age", "lanman.password_age", FT_RELATIVE_TIME, BASE_NONE,
NULL, 0, "LANMAN Time since user last changed his/her password", HFILL }},
{ &hf_password_can_change,
{ "Password Can Change", "lanman.password_can_change", FT_ABSOLUTE_TIME, BASE_NONE,
NULL, 0, "LANMAN Date and time when user can change their password", HFILL }},
{ &hf_password_must_change,
{ "Password Must Change", "lanman.password_must_change", FT_ABSOLUTE_TIME, BASE_NONE,
NULL, 0, "LANMAN Date and time when user must change their password", HFILL }},
{ &hf_script_path,
{ "Script Path", "lanman.script_path", FT_STRING, BASE_NONE,
NULL, 0, "LANMAN Pathname of user's logon script", HFILL }},
{ &hf_logoff_code,
{ "Logoff Code", "lanman.logoff_code", FT_UINT16, BASE_DEC,
VALS(status_vals), 0, "LANMAN Logoff Code", HFILL }},
{ &hf_duration,
{ "Duration of Session", "lanman.duration", FT_RELATIVE_TIME, BASE_NONE,
NULL, 0, "LANMAN Number of seconds the user was logged on", HFILL }},
{ &hf_user_comment,
{ "User Comment", "lanman.user_comment", FT_STRING, BASE_NONE,
NULL, 0, "LANMAN User Comment", HFILL }},
{ &hf_full_name,
{ "Full Name", "lanman.full_name", FT_STRING, BASE_NONE,
NULL, 0, "LANMAN Full Name", HFILL }},
{ &hf_homedir,
{ "Home Directory", "lanman.homedir", FT_STRING, BASE_NONE,
NULL, 0, "LANMAN Home Directory", HFILL }},
{ &hf_parameters,
{ "Parameters", "lanman.parameters", FT_STRING, BASE_NONE,
NULL, 0, "LANMAN Parameters", HFILL }},
{ &hf_logon_server,
{ "Logon Server", "lanman.logon_server", FT_STRING, BASE_NONE,
NULL, 0, "LANMAN Logon Server", HFILL }},
/* XXX - we should have a value_string table for this */
{ &hf_country_code,
{ "Country Code", "lanman.country_code", FT_UINT16, BASE_DEC,
NULL, 0, "LANMAN Country Code", HFILL }},
{ &hf_workstations,
{ "Workstations", "lanman.workstations", FT_STRING, BASE_NONE,
NULL, 0, "LANMAN Workstations", HFILL }},
{ &hf_max_storage,
{ "Max Storage", "lanman.max_storage", FT_UINT32, BASE_DEC,
NULL, 0, "LANMAN Max Storage", HFILL }},
{ &hf_units_per_week,
{ "Units Per Week", "lanman.units_per_week", FT_UINT16, BASE_DEC,
NULL, 0, "LANMAN Units Per Week", HFILL }},
{ &hf_logon_hours,
{ "Logon Hours", "lanman.logon_hours", FT_BYTES, BASE_NONE,
NULL, 0, "LANMAN Logon Hours", HFILL }},
/* XXX - we should have a value_string table for this */
{ &hf_code_page,
{ "Code Page", "lanman.code_page", FT_UINT16, BASE_DEC,
NULL, 0, "LANMAN Code Page", HFILL }},
{ &hf_new_password,
{ "New Password", "lanman.new_password", FT_BYTES, BASE_HEX,
NULL, 0, "LANMAN New Password (encrypted)", HFILL }},
{ &hf_old_password,
{ "Old Password", "lanman.old_password", FT_BYTES, BASE_HEX,
NULL, 0, "LANMAN Old Password (encrypted)", HFILL }},
{ &hf_reserved,
{ "Reserved", "lanman.reserved", FT_UINT32, BASE_HEX,
NULL, 0, "LANMAN Reserved", HFILL }},
};
static gint *ett[] = {
&ett_lanman,
&ett_lanman_servers,
&ett_lanman_server,
&ett_lanman_shares,
&ett_lanman_share,
};
proto_smb_lanman = proto_register_protocol(
"Microsoft Windows Lanman Remote API Protocol", "LANMAN", "lanman");
proto_register_field_array(proto_smb_lanman, hf, array_length(hf));
proto_register_subtree_array(ett, array_length(ett));
}
static heur_dissector_list_t smb_transact_heur_subdissector_list;
static gboolean
dissect_pipe_dcerpc(tvbuff_t *d_tvb, packet_info *pinfo, proto_tree *parent_tree,
proto_tree *tree, guint32 fid)
{
dcerpc_private_info dcerpc_priv;
smb_info_t *smb_priv = (smb_info_t *)pinfo->private_data;
gboolean result;
gboolean save_fragmented;
dcerpc_priv.transport_type = DCERPC_TRANSPORT_SMB;
dcerpc_priv.data.smb.fid = fid;
pinfo->private_data = &dcerpc_priv;
/* offer desegmentation service to DCERPC */
pinfo->can_desegment=0;
pinfo->desegment_offset = 0;
pinfo->desegment_len = 0;
if(smb_dcerpc_reassembly){
pinfo->can_desegment=2;
}
save_fragmented = pinfo->fragmented;
/* see if this packet is already desegmented */
if(smb_dcerpc_reassembly && pinfo->fd->flags.visited){
fragment_data *fd_head;
tvbuff_t *new_tvb;
fd_head=fragment_get(pinfo, pinfo->fd->num ,
dcerpc_fragment_table);
if(fd_head && fd_head->flags&FD_DEFRAGMENTED){
proto_tree *tr;
proto_item *it;
fragment_data *fd;
new_tvb = tvb_new_real_data(fd_head->data,
fd_head->datalen, fd_head->datalen,
"DCERPC over SMB");
tvb_set_child_real_data_tvbuff(d_tvb, new_tvb);
pinfo->fd->data_src=g_slist_append(pinfo->fd->data_src,
new_tvb);
pinfo->fragmented=FALSE;
d_tvb=new_tvb;
/* list what segments we have */
it = proto_tree_add_text(tree, d_tvb, 0, 0, "Fragments");
tr = proto_item_add_subtree(it, ett_smb_pipe_fragments);
for(fd=fd_head->next;fd;fd=fd->next){
proto_tree_add_text(tr, d_tvb, 0, 0, "Frame:%u Data:%u-%u",
fd->frame, fd->offset, fd->offset+fd->len-1);
}
}
}
result = dissector_try_heuristic(smb_transact_heur_subdissector_list, d_tvb,
pinfo, parent_tree);
pinfo->private_data = smb_priv;
/* check if dissector wanted us to desegment the data */
if(smb_dcerpc_reassembly && !pinfo->fd->flags.visited && pinfo->desegment_len){
fragment_add(d_tvb, 0, pinfo, pinfo->fd->num,
dcerpc_fragment_table,
0, tvb_length(d_tvb), TRUE);
fragment_set_tot_len(pinfo, pinfo->fd->num,
dcerpc_fragment_table,
pinfo->desegment_len+tvb_length(d_tvb));
/* since the other fragments are in normal ReadAndX and WriteAndX calls
we must make sure we can map FID values to this defragmentation
session */
/* first remove any old mappings */
if(g_hash_table_lookup(smb_priv->ct->dcerpc_fid_to_frame, (void *)fid)){
g_hash_table_remove(smb_priv->ct->dcerpc_fid_to_frame, (void *)fid);
}
g_hash_table_insert(smb_priv->ct->dcerpc_fid_to_frame, (void *)fid,
(void *)pinfo->fd->num);
}
/* clear out the variables */
pinfo->can_desegment=0;
pinfo->desegment_offset = 0;
pinfo->desegment_len = 0;
if (!result)
call_dissector(data_handle, d_tvb, pinfo, parent_tree);
pinfo->fragmented = save_fragmented;
return TRUE;
}
void
proto_register_pipe_dcerpc(void)
{
register_heur_dissector_list("smb_transact", &smb_transact_heur_subdissector_list);
}
#define CALL_NAMED_PIPE 0x54
#define WAIT_NAMED_PIPE 0x53
#define PEEK_NAMED_PIPE 0x23
#define Q_NM_P_HAND_STATE 0x21
#define SET_NM_P_HAND_STATE 0x01
#define Q_NM_PIPE_INFO 0x22
#define TRANSACT_NM_PIPE 0x26
#define RAW_READ_NM_PIPE 0x11
#define RAW_WRITE_NM_PIPE 0x31
static const value_string functions[] = {
{CALL_NAMED_PIPE, "CallNamedPipe"},
{WAIT_NAMED_PIPE, "WaitNamedPipe"},
{PEEK_NAMED_PIPE, "PeekNamedPipe"},
{Q_NM_P_HAND_STATE, "QNmPHandState"},
{SET_NM_P_HAND_STATE, "SetNmPHandState"},
{Q_NM_PIPE_INFO, "QNmPipeInfo"},
{TRANSACT_NM_PIPE, "TransactNmPipe"},
{RAW_READ_NM_PIPE, "RawReadNmPipe"},
{RAW_WRITE_NM_PIPE, "RawWriteNmPipe"},
{0, NULL}
};
static const value_string pipe_status[] = {
{1, "Disconnected by server"},
{2, "Listening"},
{3, "Connection to server is OK"},
{4, "Server end of pipe is closed"},
{0, NULL}
};
#define PIPE_LANMAN 1
#define PIPE_DCERPC 2
/* decode the SMB pipe protocol
for requests
pipe is the name of the pipe, e.g. LANMAN
smb_info->trans_subcmd is set to the symbolic constant matching the mailslot name
for responses
pipe is NULL
smb_info->trans_subcmd gives us which pipe this response is for
*/
gboolean
dissect_pipe_smb(tvbuff_t *sp_tvb, tvbuff_t *s_tvb, tvbuff_t *pd_tvb,
tvbuff_t *p_tvb, tvbuff_t *d_tvb, const char *pipe,
packet_info *pinfo, proto_tree *tree)
{
smb_info_t *smb_info;
smb_transact_info_t *tri;
guint sp_len;
proto_item *pipe_item = NULL;
proto_tree *pipe_tree = NULL;
int offset;
int trans_subcmd;
int function;
int fid = -1;
guint16 info_level;
if (!proto_is_protocol_enabled(proto_smb_pipe))
return FALSE;
pinfo->current_proto = "SMB Pipe";
smb_info = pinfo->private_data;
/*
* Set the columns.
*/
if (check_col(pinfo->cinfo, COL_PROTOCOL)) {
col_set_str(pinfo->cinfo, COL_PROTOCOL, "SMB Pipe");
}
if (check_col(pinfo->cinfo, COL_INFO)) {
col_set_str(pinfo->cinfo, COL_INFO,
smb_info->request ? "Request" : "Response");
}
if (smb_info->sip != NULL)
tri = smb_info->sip->extra_info;
else
tri = NULL;
/*
* Set up a subtree for the pipe protocol. (It might not contain
* anything.)
*/
if (sp_tvb != NULL)
sp_len = tvb_length(sp_tvb);
else
sp_len = 0;
if (tree) {
pipe_item = proto_tree_add_item(tree, proto_smb_pipe,
sp_tvb, 0, sp_len, FALSE);
pipe_tree = proto_item_add_subtree(pipe_item, ett_smb_pipe);
}
offset = 0;
/*
* Do we have any setup words at all?
*/
if (s_tvb != NULL && tvb_length(s_tvb) != 0) {
/*
* Yes. The first of them is the function.
*/
function = tvb_get_letohs(s_tvb, offset);
proto_tree_add_uint(pipe_tree, hf_pipe_function, s_tvb,
offset, 2, function);
offset += 2;
if (check_col(pinfo->cinfo, COL_INFO)) {
col_add_fstr(pinfo->cinfo, COL_INFO, "%s %s",
val_to_str(function, functions, "Unknown function (0x%04x)"),
smb_info->request ? "Request" : "Response");
}
if (tri != NULL)
tri->function = function;
/*
* The second of them depends on the function.
*/
switch (function) {
case CALL_NAMED_PIPE:
case WAIT_NAMED_PIPE:
/*
* It's a priority.
*/
proto_tree_add_item(pipe_tree, hf_pipe_priority, s_tvb,
offset, 2, TRUE);
break;
case PEEK_NAMED_PIPE:
case Q_NM_P_HAND_STATE:
case SET_NM_P_HAND_STATE:
case Q_NM_PIPE_INFO:
case TRANSACT_NM_PIPE:
case RAW_READ_NM_PIPE:
case RAW_WRITE_NM_PIPE:
/*
* It's a FID.
*/
fid = tvb_get_letohs(s_tvb, 2);
add_fid(s_tvb, pinfo, pipe_tree, offset, 2, fid);
if (tri != NULL)
tri->fid = fid;
break;
default:
/*
* It's something unknown.
* XXX - put it into the tree?
*/
break;
}
offset += 2;
} else {
/*
* This is either a response or a pipe transaction with
* no setup information.
*
* In the former case, we can get that information from
* the matching request, if we saw it.
*
* In the latter case, there is no function or FID.
*/
if (tri != NULL && tri->function != -1) {
function = tri->function;
proto_tree_add_uint(pipe_tree, hf_pipe_function, NULL,
0, 0, function);
if (check_col(pinfo->cinfo, COL_INFO)) {
col_add_fstr(pinfo->cinfo, COL_INFO, "%s %s",
val_to_str(function, functions, "Unknown function (0x%04x)"),
smb_info->request ? "Request" : "Response");
}
fid = tri->fid;
if (fid != -1)
add_fid(NULL, pinfo, pipe_tree, 0, 0, fid);
} else {
function = -1;
fid = -1;
}
}
/*
* XXX - put the byte count and the pipe name into the tree as well;
* that requires us to fetch a possibly-Unicode string.
*/
if(smb_info->request){
if(strncmp(pipe,"LANMAN",6) == 0){
trans_subcmd=PIPE_LANMAN;
} else {
/* assume it is DCERPC */
trans_subcmd=PIPE_DCERPC;
}
if (!pinfo->fd->flags.visited)
tri->trans_subcmd = trans_subcmd;
} else
trans_subcmd = tri->trans_subcmd;
if (tri == NULL) {
/*
* We don't know what type of pipe transaction this
* was, so indicate that we didn't dissect it.
*/
return FALSE;
}
switch (function) {
case CALL_NAMED_PIPE:
case TRANSACT_NM_PIPE:
switch(trans_subcmd){
case PIPE_LANMAN:
return dissect_pipe_lanman(pd_tvb, p_tvb, d_tvb, pinfo,
tree);
break;
case PIPE_DCERPC:
/*
* Only dissect this if we know the FID.
*/
if (fid != -1) {
if (d_tvb == NULL)
return FALSE;
return dissect_pipe_dcerpc(d_tvb, pinfo, tree,
pipe_tree, fid);
}
break;
}
break;
case -1:
/*
* We don't know the function; we dissect only LANMAN
* pipe messages, not RPC pipe messages, in that case.
*/
switch(trans_subcmd){
case PIPE_LANMAN:
return dissect_pipe_lanman(pd_tvb, p_tvb, d_tvb, pinfo,
tree);
break;
}
break;
case WAIT_NAMED_PIPE:
break;
case PEEK_NAMED_PIPE:
/*
* Request contains no parameters or data.
*/
if (!smb_info->request) {
if (p_tvb == NULL)
return FALSE;
offset = 0;
proto_tree_add_item(pipe_tree, hf_pipe_peek_available,
p_tvb, offset, 2, TRUE);
offset += 2;
proto_tree_add_item(pipe_tree, hf_pipe_peek_remaining,
p_tvb, offset, 2, TRUE);
offset += 2;
proto_tree_add_item(pipe_tree, hf_pipe_peek_status,
p_tvb, offset, 2, TRUE);
offset += 2;
}
break;
case Q_NM_P_HAND_STATE:
/*
* Request contains no parameters or data.
*/
if (!smb_info->request) {
if (p_tvb == NULL)
return FALSE;
offset = dissect_ipc_state(p_tvb, pinfo, pipe_tree, 0,
FALSE);
}
break;
case SET_NM_P_HAND_STATE:
/*
* Response contains no parameters or data.
*/
if (smb_info->request) {
if (p_tvb == NULL)
return FALSE;
offset = dissect_ipc_state(p_tvb, pinfo, pipe_tree, 0,
TRUE);
}
break;
case Q_NM_PIPE_INFO:
offset = 0;
if (smb_info->request) {
if (p_tvb == NULL)
return FALSE;
/*
* Request contains an information level.
*/
info_level = tvb_get_letohs(p_tvb, offset);
proto_tree_add_uint(pipe_tree, hf_pipe_getinfo_info_level,
p_tvb, offset, 2, info_level);
offset += 2;
if (!pinfo->fd->flags.visited)
tri->info_level = info_level;
} else {
guint8 pipe_namelen;
if (d_tvb == NULL)
return FALSE;
switch (tri->info_level) {
case 1:
proto_tree_add_item(pipe_tree,
hf_pipe_getinfo_output_buffer_size,
d_tvb, offset, 2, TRUE);
offset += 2;
proto_tree_add_item(pipe_tree,
hf_pipe_getinfo_input_buffer_size,
d_tvb, offset, 2, TRUE);
offset += 2;
proto_tree_add_item(pipe_tree,
hf_pipe_getinfo_maximum_instances,
d_tvb, offset, 1, TRUE);
offset += 1;
proto_tree_add_item(pipe_tree,
hf_pipe_getinfo_current_instances,
d_tvb, offset, 1, TRUE);
offset += 1;
pipe_namelen = tvb_get_guint8(d_tvb, offset);
proto_tree_add_uint(pipe_tree,
hf_pipe_getinfo_pipe_name_length,
d_tvb, offset, 1, pipe_namelen);
offset += 1;
/* XXX - can this be Unicode? */
proto_tree_add_item(pipe_tree,
hf_pipe_getinfo_pipe_name,
d_tvb, offset, pipe_namelen, TRUE);
break;
}
}
break;
case RAW_READ_NM_PIPE:
/*
* Request contains no parameters or data.
*/
if (!smb_info->request) {
if (d_tvb == NULL)
return FALSE;
offset = dissect_file_data(d_tvb, pinfo, pipe_tree, 0,
tvb_reported_length(d_tvb),
tvb_reported_length(d_tvb));
}
break;
case RAW_WRITE_NM_PIPE:
offset = 0;
if (smb_info->request) {
if (d_tvb == NULL)
return FALSE;
offset = dissect_file_data(d_tvb, pinfo, pipe_tree,
offset, tvb_reported_length(d_tvb),
tvb_reported_length(d_tvb));
} else {
if (p_tvb == NULL)
return FALSE;
proto_tree_add_item(pipe_tree,
hf_pipe_write_raw_bytes_written,
p_tvb, offset, 2, TRUE);
offset += 2;
}
break;
}
return TRUE;
}
void
proto_register_smb_pipe(void)
{
static hf_register_info hf[] = {
{ &hf_pipe_function,
{ "Function", "pipe.function", FT_UINT16, BASE_HEX,
VALS(functions), 0, "SMB Pipe Function Code", HFILL }},
{ &hf_pipe_priority,
{ "Priority", "pipe.priority", FT_UINT16, BASE_DEC,
NULL, 0, "SMB Pipe Priority", HFILL }},
{ &hf_pipe_peek_available,
{ "Available Bytes", "pipe.peek.available_bytes", FT_UINT16, BASE_DEC,
NULL, 0, "Total number of bytes available to be read from the pipe", HFILL }},
{ &hf_pipe_peek_remaining,
{ "Bytes Remaining", "pipe.peek.remaining_bytes", FT_UINT16, BASE_DEC,
NULL, 0, "Total number of bytes remaining in the message at the head of the pipe", HFILL }},
{ &hf_pipe_peek_status,
{ "Pipe Status", "pipe.peek.status", FT_UINT16, BASE_DEC,
VALS(pipe_status), 0, "Pipe status", HFILL }},
{ &hf_pipe_getinfo_info_level,
{ "Information Level", "pipe.getinfo.info_level", FT_UINT16, BASE_DEC,
NULL, 0, "Information level of information to return", HFILL }},
{ &hf_pipe_getinfo_output_buffer_size,
{ "Output Buffer Size", "pipe.getinfo.output_buffer_size", FT_UINT16, BASE_DEC,
NULL, 0, "Actual size of buffer for outgoing (server) I/O", HFILL }},
{ &hf_pipe_getinfo_input_buffer_size,
{ "Input Buffer Size", "pipe.getinfo.input_buffer_size", FT_UINT16, BASE_DEC,
NULL, 0, "Actual size of buffer for incoming (client) I/O", HFILL }},
{ &hf_pipe_getinfo_maximum_instances,
{ "Maximum Instances", "pipe.getinfo.maximum_instances", FT_UINT8, BASE_DEC,
NULL, 0, "Maximum allowed number of instances", HFILL }},
{ &hf_pipe_getinfo_current_instances,
{ "Current Instances", "pipe.getinfo.current_instances", FT_UINT8, BASE_DEC,
NULL, 0, "Current number of instances", HFILL }},
{ &hf_pipe_getinfo_pipe_name_length,
{ "Pipe Name Length", "pipe.getinfo.pipe_name_length", FT_UINT8, BASE_DEC,
NULL, 0, "Length of pipe name", HFILL }},
{ &hf_pipe_getinfo_pipe_name,
{ "Pipe Name", "pipe.getinfo.pipe_name", FT_STRING, BASE_NONE,
NULL, 0, "Name of pipe", HFILL }},
{ &hf_pipe_write_raw_bytes_written,
{ "Bytes Written", "pipe.write_raw.bytes_written", FT_UINT16, BASE_DEC,
NULL, 0, "Number of bytes written to the pipe", HFILL }},
};
static gint *ett[] = {
&ett_smb_pipe,
&ett_smb_pipe_fragments,
};
proto_smb_pipe = proto_register_protocol(
"SMB Pipe Protocol", "SMB Pipe", "pipe");
proto_register_field_array(proto_smb_pipe, hf, array_length(hf));
proto_register_subtree_array(ett, array_length(ett));
}
void
proto_reg_handoff_smb_pipe(void)
{
data_handle = find_dissector("data");
}