/* packet-ncp.c * Routines for NetWare Core Protocol * Gilbert Ramirez * Modified to allow NCP over TCP/IP decodes by James Coe * Modified to decode server op-lock, packet signature, * & NDS packets by Greg Morris * * Portions Copyright (c) by Gilbert Ramirez 2000-2002 * Portions Copyright (c) by James Coe 2000-2002 * Portions Copyright (c) Novell, Inc. 2000-2003 * * Wireshark - Network traffic analyzer * By Gerald Combs * Copyright 2000 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ /* XXX: ToDo: Find and fix possible memory leak(s): Example: A 40M capture file with mostly NCP frames results in a 400K-800K memory usage increase each time the file is reloaded. (If the NCP dissection is disabled, there is minimal memory usage increase each time the file is reloaded). */ #include "config.h" #include #include #include #include "packet-ipx.h" #include "packet-tcp.h" #include "packet-ncp-int.h" #include void proto_register_ncp(void); void proto_reg_handoff_ncp(void); int proto_ncp = -1; static int hf_ncp_ip_ver = -1; static int hf_ncp_ip_length = -1; static int hf_ncp_ip_rplybufsize = -1; static int hf_ncp_ip_sig = -1; static int hf_ncp_ip_packetsig = -1; static int hf_ncp_type = -1; static int hf_ncp_seq = -1; static int hf_ncp_connection = -1; static int hf_ncp_task = -1; static int hf_ncp_stream_type = -1; static int hf_ncp_system_flags = -1; static int hf_ncp_system_flags_abt = -1; static int hf_ncp_system_flags_eob = -1; static int hf_ncp_system_flags_sys = -1; static int hf_ncp_system_flags_bsy = -1; static int hf_ncp_system_flags_lst = -1; static int hf_ncp_src_connection = -1; static int hf_ncp_dst_connection = -1; static int hf_ncp_packet_seqno = -1; static int hf_ncp_delay_time = -1; static int hf_ncp_burst_seqno = -1; static int hf_ncp_ack_seqno = -1; static int hf_ncp_burst_len = -1; static int hf_ncp_burst_offset = -1; static int hf_ncp_data_offset = -1; static int hf_ncp_data_bytes = -1; static int hf_ncp_missing_fraglist_count = -1; static int hf_ncp_missing_data_offset = -1; static int hf_ncp_missing_data_count = -1; static int hf_ncp_oplock_flag = -1; static int hf_ncp_oplock_handle = -1; static int hf_ncp_completion_code = -1; static int hf_ncp_connection_status = -1; static int hf_ncp_slot = -1; static int hf_ncp_control_code = -1; /* static int hf_ncp_fragment_handle = -1; */ static int hf_lip_echo = -1; static int hf_ncp_burst_command = -1; static int hf_ncp_burst_file_handle = -1; static int hf_ncp_burst_reserved = -1; gint ett_ncp = -1; gint ett_nds = -1; gint ett_nds_segments = -1; gint ett_nds_segment = -1; static gint ett_ncp_system_flags = -1; static expert_field ei_ncp_oplock_handle = EI_INIT; static expert_field ei_ncp_new_server_session = EI_INIT; static expert_field ei_ncp_type = EI_INIT; static struct novell_tap ncp_tap; static struct ncp_common_header header; static struct ncp_common_header *ncp_hdr; dissector_handle_t nds_data_handle; /* desegmentation of NCP over TCP */ static gboolean ncp_desegment = TRUE; #define TCP_PORT_NCP 524 #define UDP_PORT_NCP 524 #define NCP_RQST_HDR_LENGTH 7 #define NCP_RPLY_HDR_LENGTH 8 /* These are the header structures to handle NCP over IP */ #define NCPIP_RQST 0x446d6454 /* "DmdT" */ #define NCPIP_RPLY 0x744e6350 /* "tNcP" */ struct ncp_ip_header { guint32 signature; guint32 length; }; /* This header only appears on NCP over IP request packets */ struct ncp_ip_rqhdr { guint32 version; guint32 rplybufsize; }; static const value_string ncp_ip_signature[] = { { NCPIP_RQST, "Demand Transport (Request)" }, { NCPIP_RPLY, "Transport is NCP (Reply)" }, { 0, NULL } }; static const value_string burst_command[] = { { 0x01000000, "Burst Read" }, { 0x02000000, "Burst Write" }, { 0, NULL } }; /* 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 And: https://www.novell.com/developer/ndk/netware_core_protocols.html NCP documentation (formerly http:developer.novell.com) */ static const value_string ncp_type_vals[] = { { NCP_ALLOCATE_SLOT, "Create a service connection" }, { NCP_SERVICE_REQUEST, "Service request" }, { NCP_SERVICE_REPLY, "Service reply" }, { NCP_WATCHDOG, "Watchdog" }, { NCP_DEALLOCATE_SLOT, "Destroy service connection" }, { NCP_BROADCAST_SLOT, "Server Broadcast" }, { NCP_BURST_MODE_XFER, "Burst mode transfer" }, { NCP_POSITIVE_ACK, "Request being processed" }, { NCP_LIP_ECHO, "Large Internet Packet Echo" }, { 0, NULL } }; static const value_string ncp_oplock_vals[] = { { 0x21, "Message Waiting" }, { 0x24, "Clear Op-lock" }, { 0, NULL } }; enum ncp_table_values { NCP_NCP_SRT_TABLE_INDEX = 0, NCP_NDS_SRT_TABLE_INDEX, NCP_FUNC_SRT_TABLE_INDEX, NCP_SSS_SRT_TABLE_INDEX, NCP_NMAS_SRT_TABLE_INDEX, NCP_SUB17_SRT_TABLE_INDEX, NCP_SUB21_SRT_TABLE_INDEX, NCP_SUB22_SRT_TABLE_INDEX, NCP_SUB23_SRT_TABLE_INDEX, NCP_SUB32_SRT_TABLE_INDEX, NCP_SUB34_SRT_TABLE_INDEX, NCP_SUB35_SRT_TABLE_INDEX, NCP_SUB36_SRT_TABLE_INDEX, NCP_SUB86_SRT_TABLE_INDEX, NCP_SUB87_SRT_TABLE_INDEX, NCP_SUB89_SRT_TABLE_INDEX, NCP_SUB90_SRT_TABLE_INDEX, NCP_SUB92_SRT_TABLE_INDEX, NCP_SUB94_SRT_TABLE_INDEX, NCP_SUB104_SRT_TABLE_INDEX, NCP_SUB111_SRT_TABLE_INDEX, NCP_SUB114_SRT_TABLE_INDEX, NCP_SUB123_SRT_TABLE_INDEX, NCP_SUB131_SRT_TABLE_INDEX }; #define NCP_NUM_PROCEDURES 0 static const value_string ncp_group_vals[] = { { 0, "Synchronization" }, { 1, "Print" }, { 2, "File System" }, { 3, "Connection" }, { 4, "File Server Environment" }, { 5, "Message" }, { 6, "Bindery" }, { 7, "Queue Management System (QMS)" }, { 8, "Accounting" }, { 9, "Transaction Tracking" }, { 10, "AFP" }, { 11, "NCP Extension" }, { 12, "Extended Attribute" }, { 13, "Auditing" }, { 14, "Enhanced File System" }, { 15, "Migration" }, { 16, "Novell Modular Authentication Services (NMAS)" }, { 17, "Secret Store Services (SSS)" }, { 18, "Packet Burst" }, { 19, "Novell Directory Services (NDS)" }, { 20, "Time Synchronization" }, { 21, "Server Statistics" }, { 22, "Remote" }, { 0, NULL} }; WS_DLL_PUBLIC_DEF const value_string sss_verb_enum[] = { { 0x00000000, "Query Server" }, { 0x00000001, "Read App Secrets" }, { 0x00000002, "Write App Secrets" }, { 0x00000003, "Add Secret ID" }, { 0x00000004, "Remove Secret ID" }, { 0x00000005, "Remove SecretStore" }, { 0x00000006, "Enumerate Secret IDs" }, { 0x00000007, "Unlock Store" }, { 0x00000008, "Set Master Password" }, { 0x00000009, "Get Service Information" }, { 0x000000ff, "Fragment"}, { 0x00000000, NULL} }; WS_DLL_PUBLIC_DEF const value_string nmas_subverb_enum[] = { { 0, "Fragmented Ping" }, { 2, "Client Put Data" }, { 4, "Client Get Data" }, { 6, "Client Get User NDS Credentials" }, { 8, "Login Store Management" }, { 10, "Writable Object Check" }, { 1242, "Message Handler" }, { 0, NULL} }; WS_DLL_PUBLIC_DEF const value_string ncp_nds_verb_vals[] = { { 1, "Resolve Name" }, { 2, "Read Entry Information" }, { 3, "Read" }, { 4, "Compare" }, { 5, "List" }, { 6, "Search Entries" }, { 7, "Add Entry" }, { 8, "Remove Entry" }, { 9, "Modify Entry" }, { 10, "Modify RDN" }, { 11, "Create Attribute" }, { 12, "Read Attribute Definition" }, { 13, "Remove Attribute Definition" }, { 14, "Define Class" }, { 15, "Read Class Definition" }, { 16, "Modify Class Definition" }, { 17, "Remove Class Definition" }, { 18, "List Containable Classes" }, { 19, "Get Effective Rights" }, { 20, "Add Partition" }, { 21, "Remove Partition" }, { 22, "List Partitions" }, { 23, "Split Partition" }, { 24, "Join Partitions" }, { 25, "Add Replica" }, { 26, "Remove Replica" }, { 27, "Open Stream" }, { 28, "Search Filter" }, { 29, "Create Subordinate Reference" }, { 30, "Link Replica" }, { 31, "Change Replica Type" }, { 32, "Start Update Schema" }, { 33, "End Update Schema" }, { 34, "Update Schema" }, { 35, "Start Update Replica" }, { 36, "End Update Replica" }, { 37, "Update Replica" }, { 38, "Synchronize Partition" }, { 39, "Synchronize Schema" }, { 40, "Read Syntaxes" }, { 41, "Get Replica Root ID" }, { 42, "Begin Move Entry" }, { 43, "Finish Move Entry" }, { 44, "Release Moved Entry" }, { 45, "Backup Entry" }, { 46, "Restore Entry" }, { 47, "Save DIB (Obsolete)" }, { 48, "Control" }, { 49, "Remove Backlink" }, { 50, "Close Iteration" }, { 51, "Mutate Entry" }, { 52, "Audit Skulking" }, { 53, "Get Server Address" }, { 54, "Set Keys" }, { 55, "Change Password" }, { 56, "Verify Password" }, { 57, "Begin Login" }, { 58, "Finish Login" }, { 59, "Begin Authentication" }, { 60, "Finish Authentication" }, { 61, "Logout" }, { 62, "Repair Ring (Obsolete)" }, { 63, "Repair Timestamps" }, { 64, "Create Back Link" }, { 65, "Delete External Reference" }, { 66, "Rename External Reference" }, { 67, "Create Queue Entry Directory" }, { 68, "Remove Queue Entry Directory" }, { 69, "Merge Entries" }, { 70, "Change Tree Name" }, { 71, "Partition Entry Count" }, { 72, "Check Login Restrictions" }, { 73, "Start Join" }, { 74, "Low Level Split" }, { 75, "Low Level Join" }, { 76, "Abort Partition Operation" }, { 77, "Get All Servers" }, { 78, "Partition Function" }, { 79, "Read References" }, { 80, "Inspect Entry" }, { 81, "Get Remote Entry ID" }, { 82, "Change Security" }, { 83, "Check Console Operator" }, { 84, "Start Move Tree" }, { 85, "Move Tree" }, { 86, "End Move Tree" }, { 87, "Low Level Abort Join" }, { 88, "Check Security Equivalence" }, { 89, "Merge Tree" }, { 90, "Sync External Reference" }, { 91, "Resend Entry" }, { 92, "New Schema Epoch" }, { 93, "Statistics" }, { 94, "Ping" }, { 95, "Get Bindery Contexts" }, { 96, "Monitor Connection" }, { 97, "Get DS Statistics" }, { 98, "Reset DS Counters" }, { 99, "Console" }, { 100, "Read Stream" }, { 101, "Write Stream" }, { 102, "Create Orphan Partition" }, { 103, "Remove Orphan Partition" }, { 104, "Link Orphan Partition" }, { 105, "Set Distributed Reference Link (DRL)" }, { 106, "Available" }, { 107, "Available" }, { 108, "Verify Distributed Reference Link (DRL)" }, { 109, "Verify Partition" }, { 110, "Iterator" }, { 111, "Available" }, { 112, "Close Stream" }, { 113, "Available" }, { 114, "Read Status" }, { 115, "Partition Sync Status" }, { 116, "Read Reference Data" }, { 117, "Write Reference Data" }, { 118, "Resource Event" }, { 119, "DIB Request (obsolete)" }, { 120, "Set Replication Filter" }, { 121, "Get Replication Filter" }, { 122, "Change Attribute Definition" }, { 123, "Schema in Use" }, { 124, "Remove Keys" }, { 125, "Clone" }, { 126, "Multiple Operations Transaction" }, { 240, "Ping" }, { 255, "EDirectory Call" }, { 0, NULL } }; static void ncpstat_init(struct register_srt* srt _U_, GArray* srt_array, srt_gui_init_cb gui_callback, void* gui_data) { /* Initialize all of the SRT tables with 0 rows. That way we can "filter" the drawing function to only output tables with rows > 0 */ init_srt_table("NCP", "Groups", srt_array, NCP_NUM_PROCEDURES, NULL, "ncp.group", gui_callback, gui_data, NULL); /* NDS Verbs */ init_srt_table("NDS Verbs", "NDS", srt_array, NCP_NUM_PROCEDURES, NULL, "ncp.ndsverb", gui_callback, gui_data, NULL); /* NCP Functions */ init_srt_table("NCP Functions without Subfunctions", "Functions", srt_array, NCP_NUM_PROCEDURES, NULL, "ncp.func", gui_callback, gui_data, NULL); /* Secret Store Verbs */ init_srt_table("Secret Store Verbs", "SSS", srt_array, NCP_NUM_PROCEDURES, NULL, "sss.subverb", gui_callback, gui_data, NULL); /* NMAS Verbs */ init_srt_table("NMAS Verbs", "NMAS", srt_array, NCP_NUM_PROCEDURES, NULL, "nmas.subverb", gui_callback, gui_data, NULL); /* NCP Subfunctions */ init_srt_table("Subfunctions for NCP 17", "17", srt_array, NCP_NUM_PROCEDURES, NULL, "ncp.func==17 && ncp.subfunc", gui_callback, gui_data, NULL); init_srt_table("Subfunctions for NCP 21", "21", srt_array, NCP_NUM_PROCEDURES, NULL, "ncp.func==21 && ncp.subfunc", gui_callback, gui_data, NULL); init_srt_table("Subfunctions for NCP 22", "22", srt_array, NCP_NUM_PROCEDURES, NULL, "ncp.func==22 && ncp.subfunc", gui_callback, gui_data, NULL); init_srt_table("Subfunctions for NCP 23", "23", srt_array, NCP_NUM_PROCEDURES, NULL, "ncp.func==23 && ncp.subfunc", gui_callback, gui_data, NULL); init_srt_table("Subfunctions for NCP 32", "32", srt_array, NCP_NUM_PROCEDURES, NULL, "ncp.func==32 && ncp.subfunc", gui_callback, gui_data, NULL); init_srt_table("Subfunctions for NCP 34", "34", srt_array, NCP_NUM_PROCEDURES, NULL, "ncp.func==34 && ncp.subfunc", gui_callback, gui_data, NULL); init_srt_table("Subfunctions for NCP 35", "35", srt_array, NCP_NUM_PROCEDURES, NULL, "ncp.func==35 && ncp.subfunc", gui_callback, gui_data, NULL); init_srt_table("Subfunctions for NCP 36", "36", srt_array, NCP_NUM_PROCEDURES, NULL, "ncp.func==36 && ncp.subfunc", gui_callback, gui_data, NULL); init_srt_table("Subfunctions for NCP 86", "86", srt_array, NCP_NUM_PROCEDURES, NULL, "ncp.func==86 && ncp.subfunc", gui_callback, gui_data, NULL); init_srt_table("Subfunctions for NCP 87", "87", srt_array, NCP_NUM_PROCEDURES, NULL, "ncp.func==87 && ncp.subfunc", gui_callback, gui_data, NULL); init_srt_table("Subfunctions for NCP 89 (Extended NCP's with UTF8 Support)", "89", srt_array, NCP_NUM_PROCEDURES, NULL, "ncp.func==89 && ncp.subfunc", gui_callback, gui_data, NULL); init_srt_table("Subfunctions for NCP 90", "90", srt_array, NCP_NUM_PROCEDURES, NULL, "ncp.func==90 && ncp.subfunc", gui_callback, gui_data, NULL); init_srt_table("Subfunctions for NCP 92 (Secret Store Services)", "92", srt_array, NCP_NUM_PROCEDURES, NULL, "ncp.func==92 && ncp.subfunc", gui_callback, gui_data, NULL); init_srt_table("Subfunctions for NCP 94 (Novell Modular Authentication Services)", "94", srt_array, NCP_NUM_PROCEDURES, NULL, "ncp.func==94 && ncp.subfunc", gui_callback, gui_data, NULL); init_srt_table("Subfunctions for NCP 104", "104", srt_array, NCP_NUM_PROCEDURES, NULL, "ncp.func==104 && ncp.subfunc", gui_callback, gui_data, NULL); init_srt_table("Subfunctions for NCP 111", "111", srt_array, NCP_NUM_PROCEDURES, NULL, "ncp.func==111 && ncp.subfunc", gui_callback, gui_data, NULL); init_srt_table("Subfunctions for NCP 114", "114", srt_array, NCP_NUM_PROCEDURES, NULL, "ncp.func==114 && ncp.subfunc", gui_callback, gui_data, NULL); init_srt_table("Subfunctions for NCP 123", "123", srt_array, NCP_NUM_PROCEDURES, NULL, "ncp.func==123 && ncp.subfunc", gui_callback, gui_data, NULL); init_srt_table("Subfunctions for NCP 131", "131", srt_array, NCP_NUM_PROCEDURES, NULL, "ncp.func==131 && ncp.subfunc", gui_callback, gui_data, NULL); } static int ncpstat_packet(void *pss, packet_info *pinfo, epan_dissect_t *edt _U_, const void *prv) { guint i = 0; srt_stat_table *ncp_srt_table; srt_data_t *data = (srt_data_t *)pss; const ncp_req_hash_value *request_val=(const ncp_req_hash_value *)prv; gchar* tmp_str; /* if we haven't seen the request, just ignore it */ if(!request_val || request_val->ncp_rec==0){ return 0; } /* By Group */ tmp_str = val_to_str_wmem(NULL, request_val->ncp_rec->group, ncp_group_vals, "Unknown(%u)"); i = NCP_NCP_SRT_TABLE_INDEX; ncp_srt_table = g_array_index(data->srt_array, srt_stat_table*, i); init_srt_table_row(ncp_srt_table, request_val->ncp_rec->group, tmp_str); wmem_free(NULL, tmp_str); add_srt_table_data(ncp_srt_table, request_val->ncp_rec->group, &request_val->req_frame_time, pinfo); /* By NCP number without subfunction*/ if (request_val->ncp_rec->subfunc==0) { i = NCP_FUNC_SRT_TABLE_INDEX; ncp_srt_table = g_array_index(data->srt_array, srt_stat_table*, i); init_srt_table_row(ncp_srt_table, request_val->ncp_rec->func, request_val->ncp_rec->name); add_srt_table_data(ncp_srt_table, request_val->ncp_rec->func, &request_val->req_frame_time, pinfo); } /* By Subfunction number */ if(request_val->ncp_rec->subfunc!=0){ if (request_val->ncp_rec->func==17) { i = NCP_SUB17_SRT_TABLE_INDEX; ncp_srt_table = g_array_index(data->srt_array, srt_stat_table*, i); init_srt_table_row(ncp_srt_table, (request_val->ncp_rec->subfunc), request_val->ncp_rec->name); add_srt_table_data(ncp_srt_table, (request_val->ncp_rec->subfunc), &request_val->req_frame_time, pinfo); } if (request_val->ncp_rec->func==21) { i = NCP_SUB21_SRT_TABLE_INDEX; ncp_srt_table = g_array_index(data->srt_array, srt_stat_table*, i); init_srt_table_row(ncp_srt_table, (request_val->ncp_rec->subfunc), request_val->ncp_rec->name); add_srt_table_data(ncp_srt_table, (request_val->ncp_rec->subfunc), &request_val->req_frame_time, pinfo); } if (request_val->ncp_rec->func==22) { i = NCP_SUB22_SRT_TABLE_INDEX; ncp_srt_table = g_array_index(data->srt_array, srt_stat_table*, i); init_srt_table_row(ncp_srt_table, (request_val->ncp_rec->subfunc), request_val->ncp_rec->name); add_srt_table_data(ncp_srt_table, (request_val->ncp_rec->subfunc), &request_val->req_frame_time, pinfo); } if (request_val->ncp_rec->func==23) { i = NCP_SUB23_SRT_TABLE_INDEX; ncp_srt_table = g_array_index(data->srt_array, srt_stat_table*, i); init_srt_table_row(ncp_srt_table, (request_val->ncp_rec->subfunc), request_val->ncp_rec->name); add_srt_table_data(ncp_srt_table, (request_val->ncp_rec->subfunc), &request_val->req_frame_time, pinfo); } if (request_val->ncp_rec->func==32) { i = NCP_SUB32_SRT_TABLE_INDEX; ncp_srt_table = g_array_index(data->srt_array, srt_stat_table*, i); init_srt_table_row(ncp_srt_table, (request_val->ncp_rec->subfunc), request_val->ncp_rec->name); add_srt_table_data(ncp_srt_table, (request_val->ncp_rec->subfunc), &request_val->req_frame_time, pinfo); } if (request_val->ncp_rec->func==34) { i = NCP_SUB34_SRT_TABLE_INDEX; ncp_srt_table = g_array_index(data->srt_array, srt_stat_table*, i); init_srt_table_row(ncp_srt_table, (request_val->ncp_rec->subfunc), request_val->ncp_rec->name); add_srt_table_data(ncp_srt_table, (request_val->ncp_rec->subfunc), &request_val->req_frame_time, pinfo); } if (request_val->ncp_rec->func==35) { i = NCP_SUB35_SRT_TABLE_INDEX; ncp_srt_table = g_array_index(data->srt_array, srt_stat_table*, i); init_srt_table_row(ncp_srt_table, (request_val->ncp_rec->subfunc), request_val->ncp_rec->name); add_srt_table_data(ncp_srt_table, (request_val->ncp_rec->subfunc), &request_val->req_frame_time, pinfo); } if (request_val->ncp_rec->func==36) { i = NCP_SUB36_SRT_TABLE_INDEX; ncp_srt_table = g_array_index(data->srt_array, srt_stat_table*, i); init_srt_table_row(ncp_srt_table, (request_val->ncp_rec->subfunc), request_val->ncp_rec->name); add_srt_table_data(ncp_srt_table, (request_val->ncp_rec->subfunc), &request_val->req_frame_time, pinfo); } if (request_val->ncp_rec->func==86) { i = NCP_SUB86_SRT_TABLE_INDEX; ncp_srt_table = g_array_index(data->srt_array, srt_stat_table*, i); init_srt_table_row(ncp_srt_table, (request_val->ncp_rec->subfunc), request_val->ncp_rec->name); add_srt_table_data(ncp_srt_table, (request_val->ncp_rec->subfunc), &request_val->req_frame_time, pinfo); } if (request_val->ncp_rec->func==87) { i = NCP_SUB87_SRT_TABLE_INDEX; ncp_srt_table = g_array_index(data->srt_array, srt_stat_table*, i); init_srt_table_row(ncp_srt_table, (request_val->ncp_rec->subfunc), request_val->ncp_rec->name); add_srt_table_data(ncp_srt_table, (request_val->ncp_rec->subfunc), &request_val->req_frame_time, pinfo); } if (request_val->ncp_rec->func==89) { i = NCP_SUB89_SRT_TABLE_INDEX; ncp_srt_table = g_array_index(data->srt_array, srt_stat_table*, i); init_srt_table_row(ncp_srt_table, (request_val->ncp_rec->subfunc), request_val->ncp_rec->name); add_srt_table_data(ncp_srt_table, (request_val->ncp_rec->subfunc), &request_val->req_frame_time, pinfo); } if (request_val->ncp_rec->func==90) { i = NCP_SUB90_SRT_TABLE_INDEX; ncp_srt_table = g_array_index(data->srt_array, srt_stat_table*, i); init_srt_table_row(ncp_srt_table, (request_val->ncp_rec->subfunc), request_val->ncp_rec->name); add_srt_table_data(ncp_srt_table, (request_val->ncp_rec->subfunc), &request_val->req_frame_time, pinfo); } if (request_val->ncp_rec->func==92) { i = NCP_SUB92_SRT_TABLE_INDEX; ncp_srt_table = g_array_index(data->srt_array, srt_stat_table*, i); init_srt_table_row(ncp_srt_table, (request_val->ncp_rec->subfunc), request_val->ncp_rec->name); add_srt_table_data(ncp_srt_table, (request_val->ncp_rec->subfunc), &request_val->req_frame_time, pinfo); } if (request_val->ncp_rec->func==94) { i = NCP_SUB94_SRT_TABLE_INDEX; ncp_srt_table = g_array_index(data->srt_array, srt_stat_table*, i); init_srt_table_row(ncp_srt_table, (request_val->ncp_rec->subfunc), request_val->ncp_rec->name); add_srt_table_data(ncp_srt_table, (request_val->ncp_rec->subfunc), &request_val->req_frame_time, pinfo); } if (request_val->ncp_rec->func==104) { i = NCP_SUB104_SRT_TABLE_INDEX; ncp_srt_table = g_array_index(data->srt_array, srt_stat_table*, i); init_srt_table_row(ncp_srt_table, (request_val->ncp_rec->subfunc), request_val->ncp_rec->name); add_srt_table_data(ncp_srt_table, (request_val->ncp_rec->subfunc), &request_val->req_frame_time, pinfo); } if (request_val->ncp_rec->func==111) { i = NCP_SUB111_SRT_TABLE_INDEX; ncp_srt_table = g_array_index(data->srt_array, srt_stat_table*, i); init_srt_table_row(ncp_srt_table, (request_val->ncp_rec->subfunc), request_val->ncp_rec->name); add_srt_table_data(ncp_srt_table, (request_val->ncp_rec->subfunc), &request_val->req_frame_time, pinfo); } if (request_val->ncp_rec->func==114) { i = NCP_SUB114_SRT_TABLE_INDEX; ncp_srt_table = g_array_index(data->srt_array, srt_stat_table*, i); init_srt_table_row(ncp_srt_table, (request_val->ncp_rec->subfunc), request_val->ncp_rec->name); add_srt_table_data(ncp_srt_table, (request_val->ncp_rec->subfunc), &request_val->req_frame_time, pinfo); } if (request_val->ncp_rec->func==123) { i = NCP_SUB123_SRT_TABLE_INDEX; ncp_srt_table = g_array_index(data->srt_array, srt_stat_table*, i); init_srt_table_row(ncp_srt_table, (request_val->ncp_rec->subfunc), request_val->ncp_rec->name); add_srt_table_data(ncp_srt_table, (request_val->ncp_rec->subfunc), &request_val->req_frame_time, pinfo); } if (request_val->ncp_rec->func==131) { i = NCP_SUB131_SRT_TABLE_INDEX; ncp_srt_table = g_array_index(data->srt_array, srt_stat_table*, i); init_srt_table_row(ncp_srt_table, (request_val->ncp_rec->subfunc), request_val->ncp_rec->name); add_srt_table_data(ncp_srt_table, (request_val->ncp_rec->subfunc), &request_val->req_frame_time, pinfo); } } /* By NDS verb */ if (request_val->ncp_rec->func==0x68) { tmp_str = val_to_str_wmem(NULL, request_val->nds_request_verb, ncp_nds_verb_vals, "Unknown(%u)"); i = NCP_NDS_SRT_TABLE_INDEX; ncp_srt_table = g_array_index(data->srt_array, srt_stat_table*, i); init_srt_table_row(ncp_srt_table, (request_val->nds_request_verb), tmp_str); add_srt_table_data(ncp_srt_table, (request_val->nds_request_verb), &request_val->req_frame_time, pinfo); wmem_free(NULL, tmp_str); } if (request_val->ncp_rec->func==0x5c) { tmp_str = val_to_str_wmem(NULL, request_val->req_nds_flags, sss_verb_enum, "Unknown(%u)"); i = NCP_SSS_SRT_TABLE_INDEX; ncp_srt_table = g_array_index(data->srt_array, srt_stat_table*, i); init_srt_table_row(ncp_srt_table, (request_val->req_nds_flags), tmp_str); add_srt_table_data(ncp_srt_table, (request_val->req_nds_flags), &request_val->req_frame_time, pinfo); wmem_free(NULL, tmp_str); } if (request_val->ncp_rec->func==0x5e) { tmp_str = val_to_str_wmem(NULL, request_val->req_nds_flags, nmas_subverb_enum, "Unknown(%u)"); i = NCP_NMAS_SRT_TABLE_INDEX; ncp_srt_table = g_array_index(data->srt_array, srt_stat_table*, i); init_srt_table_row(ncp_srt_table, (request_val->req_nds_flags), tmp_str); add_srt_table_data(ncp_srt_table, (request_val->req_nds_flags), &request_val->req_frame_time, pinfo); wmem_free(NULL, tmp_str); } return 1; } /* Conversation Struct so we can detect NCP server sessions */ typedef struct { conversation_t *conversation; guint32 nwconnection; guint8 nwtask; } mncp_rhash_key; /* Store the packet number for the start of the NCP session. * Note sessions are defined as * NCP Connection + NCP Task == Unique NCP server session * It is normal for multiple sessions per connection to exist * These are normally different applications running on multi-tasking * Operating Systems. */ typedef struct { guint32 session_start_packet_num; } mncp_rhash_value; static GHashTable *mncp_rhash = NULL; /* Hash Functions */ static gint mncp_equal(gconstpointer v, gconstpointer v2) { const mncp_rhash_key *val1 = (const mncp_rhash_key*)v; const mncp_rhash_key *val2 = (const mncp_rhash_key*)v2; if (val1->conversation == val2->conversation && val1->nwconnection == val2->nwconnection && val1->nwtask == val2->nwtask) { return 1; } return 0; } static guint mncp_hash(gconstpointer v) { const mncp_rhash_key *mncp_key = (const mncp_rhash_key*)v; return GPOINTER_TO_UINT(mncp_key->conversation)+mncp_key->nwconnection+mncp_key->nwtask; } /* Initializes the hash table each time a new * file is loaded or re-loaded in wireshark */ static void mncp_init_protocol(void) { mncp_rhash = g_hash_table_new(mncp_hash, mncp_equal); } static void mncp_cleanup_protocol(void) { g_hash_table_destroy(mncp_rhash); } static mncp_rhash_value* mncp_hash_insert(conversation_t *conversation, guint32 nwconnection, guint8 nwtask, packet_info *pinfo) { mncp_rhash_key *key; mncp_rhash_value *value; /* Now remember the request, so we can find it if we later a reply to it. Track by conversation, connection, and task number. in NetWare these values determine each unique session */ key = wmem_new(wmem_file_scope(), mncp_rhash_key); key->conversation = conversation; key->nwconnection = nwconnection; key->nwtask = nwtask; value = wmem_new(wmem_file_scope(), mncp_rhash_value); g_hash_table_insert(mncp_rhash, key, value); if (ncp_echo_conn && nwconnection != 65535) { expert_add_info_format(pinfo, NULL, &ei_ncp_new_server_session, "Detected New Server Session. Connection %d, Task %d", nwconnection, nwtask); value->session_start_packet_num = pinfo->num; } return value; } /* Returns the ncp_rec*, or NULL if not found. */ static mncp_rhash_value* mncp_hash_lookup(conversation_t *conversation, guint32 nwconnection, guint8 nwtask) { mncp_rhash_key key; key.conversation = conversation; key.nwconnection = nwconnection; key.nwtask = nwtask; return (mncp_rhash_value *)g_hash_table_lookup(mncp_rhash, &key); } static const char* ncp_conv_get_filter_type(conv_item_t* conv _U_, conv_filter_type_e filter) { if ((filter == CONV_FT_SRC_PORT) || (filter == CONV_FT_DST_PORT) || (filter == CONV_FT_ANY_PORT)) return "ncp.connection"; return CONV_FILTER_INVALID; } static ct_dissector_info_t ncp_ct_dissector_info = {&ncp_conv_get_filter_type}; static int ncp_conversation_packet(void *pct, packet_info *pinfo, epan_dissect_t *edt _U_, const void *vip) { conv_hash_t *hash = (conv_hash_t*) pct; const struct ncp_common_header *ncph=(const struct ncp_common_header *)vip; guint32 connection; connection = (ncph->conn_high * 256)+ncph->conn_low; if (connection < 65535) { add_conversation_table_data(hash, &pinfo->src, &pinfo->dst, connection, connection, 1, pinfo->fd->pkt_len, &pinfo->rel_ts, &pinfo->abs_ts, &ncp_ct_dissector_info, PT_NCP); } return 1; } static const char* ncp_host_get_filter_type(hostlist_talker_t* host _U_, conv_filter_type_e filter) { return ncp_conv_get_filter_type(NULL, filter); } static hostlist_dissector_info_t ncp_host_dissector_info = {&ncp_host_get_filter_type}; static int ncp_hostlist_packet(void *pit, packet_info *pinfo, epan_dissect_t *edt _U_, const void *vip _U_) { conv_hash_t *hash = (conv_hash_t*) pit; /*const ncp_common_header *ncphdr=vip;*/ /* Take two "add" passes per packet, adding for each direction, ensures that all packets are counted properly (even if address is sending to itself) XXX - this could probably be done more efficiently inside hostlist_table */ add_hostlist_table_data(hash, &pinfo->src, 0, TRUE, 1, pinfo->fd->pkt_len, &ncp_host_dissector_info, PT_NCP); add_hostlist_table_data(hash, &pinfo->dst, 0, FALSE, 1, pinfo->fd->pkt_len, &ncp_host_dissector_info, PT_NCP); return 1; } /* * Burst packet system flags. */ #define ABT 0x04 /* Abort request */ #define BSY 0x08 /* Server Busy */ #define EOB 0x10 /* End of burst */ #define LST 0x40 /* Include Fragment List */ #define SYS 0x80 /* System packet */ static void dissect_ncp_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, gboolean is_tcp) { proto_tree *ncp_tree = NULL; proto_item *ti; struct ncp_ip_header ncpiph; struct ncp_ip_rqhdr ncpiphrq; guint16 ncp_burst_seqno, ncp_ack_seqno; guint16 flags = 0; proto_tree *flags_tree = NULL; int hdr_offset = 0; int commhdr = 0; int offset = 0; gint length_remaining; tvbuff_t *next_tvb; guint32 testvar = 0, ncp_burst_command, burst_len, burst_off, burst_file; guint8 subfunction; guint32 nw_connection = 0, data_offset; guint16 data_len = 0; guint16 missing_fraglist_count = 0; mncp_rhash_value *request_value = NULL; conversation_t *conversation; col_set_str(pinfo->cinfo, COL_PROTOCOL, "NCP"); col_clear(pinfo->cinfo, COL_INFO); ncp_hdr = &header; ti = proto_tree_add_item(tree, proto_ncp, tvb, 0, -1, ENC_NA); ncp_tree = proto_item_add_subtree(ti, ett_ncp); if (is_tcp) { if (tvb_get_ntohl(tvb, hdr_offset) != NCPIP_RQST && tvb_get_ntohl(tvb, hdr_offset) != NCPIP_RPLY) commhdr += 1; /* Get NCPIP Header data */ ncpiph.signature = tvb_get_ntohl(tvb, commhdr); proto_tree_add_uint(ncp_tree, hf_ncp_ip_sig, tvb, commhdr, 4, ncpiph.signature); ncpiph.length = (0x7fffffff & tvb_get_ntohl(tvb, commhdr+4)); proto_tree_add_uint(ncp_tree, hf_ncp_ip_length, tvb, commhdr+4, 4, ncpiph.length); commhdr += 8; if (ncpiph.signature == NCPIP_RQST) { ncpiphrq.version = tvb_get_ntohl(tvb, commhdr); proto_tree_add_uint(ncp_tree, hf_ncp_ip_ver, tvb, commhdr, 4, ncpiphrq.version); commhdr += 4; ncpiphrq.rplybufsize = tvb_get_ntohl(tvb, commhdr); proto_tree_add_uint(ncp_tree, hf_ncp_ip_rplybufsize, tvb, commhdr, 4, ncpiphrq.rplybufsize); commhdr += 4; } /* Check to see if this is a valid offset, otherwise increment for packet signature */ if (try_val_to_str(tvb_get_ntohs(tvb, commhdr), ncp_type_vals)==NULL) { /* Check to see if we have a valid type after packet signature length */ if (try_val_to_str(tvb_get_ntohs(tvb, commhdr+8), ncp_type_vals)!=NULL) { proto_tree_add_item(ncp_tree, hf_ncp_ip_packetsig, tvb, commhdr, 8, ENC_NA); commhdr += 8; } } } else { /* Initialize this structure, we use it below */ memset(&ncpiph, 0, sizeof(ncpiph)); } header.type = tvb_get_ntohs(tvb, commhdr); header.sequence = tvb_get_guint8(tvb, commhdr+2); header.conn_low = tvb_get_guint8(tvb, commhdr+3); header.task = tvb_get_guint8(tvb, commhdr+4); header.conn_high = tvb_get_guint8(tvb, commhdr+5); proto_tree_add_uint(ncp_tree, hf_ncp_type, tvb, commhdr, 2, header.type); nw_connection = (header.conn_high*256)+header.conn_low; /* Ok, we need to track the conversation so that we can * determine if a new server session is occuring for this * connection. */ conversation = find_conversation(pinfo->num, &pinfo->src, &pinfo->dst, PT_NCP, (guint32) pinfo->srcport, (guint32) pinfo->destport, 0); if ((ncpiph.length & 0x80000000) || ncpiph.signature == NCPIP_RPLY) { /* First time through we will record the initial connection and task * values */ if (!pinfo->fd->flags.visited) { if (conversation != NULL) { /* find the record telling us the * request made that caused this * reply */ request_value = mncp_hash_lookup(conversation, nw_connection, header.task); /* if for some reason we have no * conversation in our hash, create * one */ if (request_value == NULL) { mncp_hash_insert(conversation, nw_connection, header.task, pinfo); } } else { /* It's not part of any conversation * - create a new one. */ conversation = conversation_new(pinfo->num, &pinfo->src, &pinfo->dst, PT_NCP, (guint32) pinfo->srcport, (guint32) pinfo->destport, 0); mncp_hash_insert(conversation, nw_connection, header.task, pinfo); } /* If this is a request packet then we * might have a new task */ if (ncpiph.signature == NCPIP_RPLY) { /* Now on reply packets we have to * use the state of the original * request packet, so look up the * request value and check the task number */ /*request_value = mncp_hash_lookup(conversation, nw_connection, header.task);*/ } } else { /* Get request value data */ request_value = mncp_hash_lookup(conversation, nw_connection, header.task); if (request_value) { if ((request_value->session_start_packet_num == pinfo->num) && ncp_echo_conn) { expert_add_info_format(pinfo, NULL, &ei_ncp_new_server_session, "Detected New Server Session. Connection %d, Task %d", nw_connection, header.task); } } } } else { if (!pinfo->fd->flags.visited) { if (conversation != NULL) { /* find the record telling us the * request made that caused this * reply */ request_value = mncp_hash_lookup(conversation, nw_connection, header.task); /* if for some reason we have no * conversation in our hash, create * one */ if (request_value == NULL) { mncp_hash_insert(conversation, nw_connection, header.task, pinfo); } } else { /* It's not part of any conversation * - create a new one. */ conversation = conversation_new(pinfo->num, &pinfo->src, &pinfo->dst, PT_NCP, (guint32) pinfo->srcport, (guint32) pinfo->destport, 0); mncp_hash_insert(conversation, nw_connection, header.task, pinfo); } /* find the record telling us the request * made that caused this reply */ } else { request_value = mncp_hash_lookup(conversation, nw_connection, header.task); if (request_value) { if ((request_value->session_start_packet_num == pinfo->num) && ncp_echo_conn) { expert_add_info_format(pinfo, NULL, &ei_ncp_new_server_session, "Detected New Server Session. Connection %d, Task %d", nw_connection, header.task); } } } } tap_queue_packet(ncp_tap.hdr, pinfo, ncp_hdr); col_add_str(pinfo->cinfo, COL_INFO, val_to_str(header.type, ncp_type_vals, "Unknown type (0x%04x)")); /* * Process the packet-type-specific header. */ switch (header.type) { case NCP_BROADCAST_SLOT: /* Server Broadcast */ proto_tree_add_uint(ncp_tree, hf_ncp_seq, tvb, commhdr + 2, 1, header.sequence); proto_tree_add_uint(ncp_tree, hf_ncp_connection,tvb, commhdr + 3, 3, nw_connection); proto_tree_add_item(ncp_tree, hf_ncp_task, tvb, commhdr + 4, 1, ENC_BIG_ENDIAN); proto_tree_add_item(ncp_tree, hf_ncp_oplock_flag, tvb, commhdr + 9, 1, tvb_get_guint8(tvb, commhdr+9)); proto_tree_add_item(ncp_tree, hf_ncp_oplock_handle, tvb, commhdr + 10, 4, ENC_BIG_ENDIAN); if ((tvb_get_guint8(tvb, commhdr+9)==0x24) && ncp_echo_file) { expert_add_info_format(pinfo, NULL, &ei_ncp_oplock_handle, "Server requesting station to clear oplock on handle - %08x", tvb_get_ntohl(tvb, commhdr+10)); } break; case NCP_LIP_ECHO: /* Lip Echo Packet */ proto_tree_add_item(ncp_tree, hf_lip_echo, tvb, commhdr, 13, ENC_ASCII|ENC_NA); break; case NCP_BURST_MODE_XFER: /* Packet Burst Packet */ /* * XXX - we should keep track of whether there's a burst * outstanding on a connection and, if not, treat the * beginning of the data as a burst header. * * The burst header contains: * * 4 bytes of little-endian function number: * 1 = read, 2 = write; * * 4 bytes of file handle; * * 8 reserved bytes; * * 4 bytes of big-endian file offset; * * 4 bytes of big-endian byte count. * * The data follows for a burst write operation. * * The first packet of a burst read reply contains: * * 4 bytes of little-endian result code: * 0: No error * 1: Initial error * 2: I/O error * 3: No data read; * * 4 bytes of returned byte count (big-endian?). * * The data follows. * * Each burst of a write request is responded to with a * burst packet with a 2-byte little-endian result code: * * 0: Write successful * 4: Write error */ flags = tvb_get_guint8(tvb, commhdr + 2); ti = proto_tree_add_uint(ncp_tree, hf_ncp_system_flags, tvb, commhdr + 2, 1, flags); flags_tree = proto_item_add_subtree(ti, ett_ncp_system_flags); proto_tree_add_item(flags_tree, hf_ncp_system_flags_abt, tvb, commhdr + 2, 1, ENC_BIG_ENDIAN); if (flags & ABT) { proto_item_append_text(ti, " ABT"); } flags&=(~( ABT )); proto_tree_add_item(flags_tree, hf_ncp_system_flags_bsy, tvb, commhdr + 2, 1, ENC_BIG_ENDIAN); if (flags & BSY) { proto_item_append_text(ti, " BSY"); } flags&=(~( BSY )); proto_tree_add_item(flags_tree, hf_ncp_system_flags_eob, tvb, commhdr + 2, 1, ENC_BIG_ENDIAN); if (flags & EOB) { proto_item_append_text(ti, " EOB"); } flags&=(~( EOB )); proto_tree_add_item(flags_tree, hf_ncp_system_flags_lst, tvb, commhdr + 2, 1, ENC_BIG_ENDIAN); if (flags & LST) { proto_item_append_text(ti, " LST"); } flags&=(~( LST )); proto_tree_add_item(flags_tree, hf_ncp_system_flags_sys, tvb, commhdr + 2, 1, ENC_BIG_ENDIAN); if (flags & SYS) { proto_item_append_text(ti, " SYS"); } flags&=(~( SYS )); proto_tree_add_item(ncp_tree, hf_ncp_stream_type, tvb, commhdr + 3, 1, ENC_BIG_ENDIAN); proto_tree_add_item(ncp_tree, hf_ncp_src_connection, tvb, commhdr + 4, 4, ENC_BIG_ENDIAN); proto_tree_add_item(ncp_tree, hf_ncp_dst_connection, tvb, commhdr + 8, 4, ENC_BIG_ENDIAN); proto_tree_add_item(ncp_tree, hf_ncp_packet_seqno, tvb, commhdr + 12, 4, ENC_BIG_ENDIAN); proto_tree_add_item(ncp_tree, hf_ncp_delay_time, tvb, commhdr + 16, 4, ENC_BIG_ENDIAN); ncp_burst_seqno = tvb_get_ntohs(tvb, commhdr+20); proto_tree_add_item(ncp_tree, hf_ncp_burst_seqno, tvb, commhdr + 20, 2, ENC_BIG_ENDIAN); ncp_ack_seqno = tvb_get_ntohs(tvb, commhdr+22); proto_tree_add_item(ncp_tree, hf_ncp_ack_seqno, tvb, commhdr + 22, 2, ENC_BIG_ENDIAN); proto_tree_add_item(ncp_tree, hf_ncp_burst_len, tvb, commhdr + 24, 4, ENC_BIG_ENDIAN); data_offset = tvb_get_ntohl(tvb, commhdr + 28); proto_tree_add_uint(ncp_tree, hf_ncp_data_offset, tvb, commhdr + 28, 4, data_offset); data_len = tvb_get_ntohs(tvb, commhdr + 32); proto_tree_add_uint(ncp_tree, hf_ncp_data_bytes, tvb, commhdr + 32, 2, data_len); missing_fraglist_count = tvb_get_ntohs(tvb, commhdr + 34); proto_tree_add_item(ncp_tree, hf_ncp_missing_fraglist_count, tvb, commhdr + 34, 2, ENC_BIG_ENDIAN); offset = commhdr + 36; if (!(flags & SYS) && ncp_burst_seqno == ncp_ack_seqno && data_offset == 0) { /* * This is either a Burst Read or Burst Write * command. The data length includes the burst * mode header, plus any data in the command * (there shouldn't be any in a read, but there * might be some in a write). */ if (data_len < 4) return; ncp_burst_command = tvb_get_ntohl(tvb, offset); proto_tree_add_item(ncp_tree, hf_ncp_burst_command, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; data_len -= 4; if (data_len < 4) return; burst_file = tvb_get_ntohl(tvb, offset); proto_tree_add_item(ncp_tree, hf_ncp_burst_file_handle, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; data_len -= 4; if (data_len < 8) return; proto_tree_add_item(ncp_tree, hf_ncp_burst_reserved, tvb, offset, 8, ENC_NA); offset += 8; data_len -= 8; if (data_len < 4) return; burst_off = tvb_get_ntohl(tvb, offset); proto_tree_add_uint(ncp_tree, hf_ncp_burst_offset, tvb, offset, 4, burst_off); offset += 4; data_len -= 4; if (data_len < 4) return; burst_len = tvb_get_ntohl(tvb, offset); proto_tree_add_uint(ncp_tree, hf_ncp_burst_len, tvb, offset, 4, burst_len); offset += 4; data_len -= 4; col_add_fstr(pinfo->cinfo, COL_INFO, "%s %d bytes starting at offset %d in file 0x%08x", val_to_str(ncp_burst_command, burst_command, "Unknown (0x%08x)"), burst_len, burst_off, burst_file); break; } else { if (tvb_get_guint8(tvb, commhdr + 2) & 0x10) { col_set_str(pinfo->cinfo, COL_INFO, "End of Burst"); } } break; case NCP_ALLOCATE_SLOT: /* Allocate Slot Request */ length_remaining = tvb_reported_length_remaining(tvb, commhdr + 4); if (length_remaining > 4) { testvar = tvb_get_ntohl(tvb, commhdr+4); if (testvar == 0x4c495020) { proto_tree_add_item(ncp_tree, hf_lip_echo, tvb, commhdr+4, 13, ENC_ASCII|ENC_NA); break; } } /* otherwise fall through */ case NCP_POSITIVE_ACK: /* Positive Acknowledgement */ case NCP_SERVICE_REQUEST: /* Server NCP Request */ case NCP_SERVICE_REPLY: /* Server NCP Reply */ case NCP_WATCHDOG: /* Watchdog Packet */ case NCP_DEALLOCATE_SLOT: /* Deallocate Slot Request */ default: proto_tree_add_uint(ncp_tree, hf_ncp_seq, tvb, commhdr + 2, 1, header.sequence); proto_tree_add_uint(ncp_tree, hf_ncp_connection,tvb, commhdr + 3, 3, nw_connection); proto_tree_add_item(ncp_tree, hf_ncp_task, tvb, commhdr + 4, 1, ENC_BIG_ENDIAN); break; } /* * Process the packet body. */ switch (header.type) { case NCP_ALLOCATE_SLOT: /* Allocate Slot Request */ length_remaining = tvb_reported_length_remaining(tvb, commhdr + 4); if (length_remaining > 4) { testvar = tvb_get_ntohl(tvb, commhdr+4); if (testvar == 0x4c495020) { proto_tree_add_item(ncp_tree, hf_lip_echo, tvb, commhdr, -1, ENC_ASCII|ENC_NA); /*break;*/ } } next_tvb = tvb_new_subset_remaining(tvb, commhdr); dissect_ncp_request(next_tvb, pinfo, nw_connection, header.sequence, header.type, ncp_tree); break; case NCP_DEALLOCATE_SLOT: /* Deallocate Slot Request */ next_tvb = tvb_new_subset_remaining(tvb, commhdr); dissect_ncp_request(next_tvb, pinfo, nw_connection, header.sequence, header.type, ncp_tree); break; case NCP_SERVICE_REQUEST: /* Server NCP Request */ case NCP_BROADCAST_SLOT: /* Server Broadcast Packet */ next_tvb = tvb_new_subset_remaining(tvb, commhdr); if (tvb_get_guint8(tvb, commhdr+6) == 0x68) { subfunction = tvb_get_guint8(tvb, commhdr+7); switch (subfunction) { case 0x02: /* NDS Frag Packet to decode */ dissect_nds_request(next_tvb, pinfo, nw_connection, header.sequence, header.type, ncp_tree); break; case 0x01: /* NDS Ping */ dissect_ping_req(next_tvb, pinfo, nw_connection, header.sequence, header.type, ncp_tree); break; default: dissect_ncp_request(next_tvb, pinfo, nw_connection, header.sequence, header.type, ncp_tree); break; } } else { dissect_ncp_request(next_tvb, pinfo, nw_connection, header.sequence, header.type, ncp_tree); } break; case NCP_SERVICE_REPLY: /* Server NCP Reply */ next_tvb = tvb_new_subset_remaining(tvb, commhdr); nds_defrag(next_tvb, pinfo, nw_connection, header.sequence, header.type, ncp_tree, &ncp_tap); break; case NCP_POSITIVE_ACK: /* Positive Acknowledgement */ /* * XXX - this used to call "nds_defrag()", which would * clear out "frags". Was that the right thing to * do? */ next_tvb = tvb_new_subset_remaining(tvb, commhdr); dissect_ncp_reply(next_tvb, pinfo, nw_connection, header.sequence, header.type, ncp_tree, &ncp_tap); break; case NCP_WATCHDOG: /* Watchdog Packet */ /* * XXX - should the completion code be interpreted as * it is in "packet-ncp2222.inc"? If so, this * packet should be handled by "dissect_ncp_reply()". */ proto_tree_add_item(ncp_tree, hf_ncp_completion_code, tvb, commhdr + 6, 1, ENC_LITTLE_ENDIAN); proto_tree_add_item(ncp_tree, hf_ncp_connection_status, tvb, commhdr + 7, 1, ENC_LITTLE_ENDIAN); proto_tree_add_item(ncp_tree, hf_ncp_slot, tvb, commhdr + 8, 1, ENC_LITTLE_ENDIAN); proto_tree_add_item(ncp_tree, hf_ncp_control_code, tvb, commhdr + 9, 1, ENC_LITTLE_ENDIAN); /* * Display the rest of the packet as data. */ if (tvb_offset_exists(tvb, commhdr + 10)) { call_data_dissector(tvb_new_subset_remaining(tvb, commhdr + 10), pinfo, ncp_tree); } break; case NCP_BURST_MODE_XFER: /* Packet Burst Packet */ if (flags & SYS) { /* * System packet; show missing fragments if there * are any. */ while (missing_fraglist_count != 0) { proto_tree_add_item(ncp_tree, hf_ncp_missing_data_offset, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; proto_tree_add_item(ncp_tree, hf_ncp_missing_data_count, tvb, offset, 2, ENC_BIG_ENDIAN); offset += 2; missing_fraglist_count--; } } else { /* * XXX - do this by using -1 and -1 as the length * arguments to "tvb_new_subset()" and then calling * "tvb_set_reported_length()"? That'll throw an * exception if "data_len" goes past the reported * length of the packet, but that's arguably a * feature in this case. */ length_remaining = tvb_captured_length_remaining(tvb, offset); if (length_remaining > data_len) length_remaining = data_len; if (data_len != 0) { call_data_dissector(tvb_new_subset(tvb, offset, length_remaining, data_len), pinfo, ncp_tree); } } break; case NCP_LIP_ECHO: /* LIP Echo Packet */ proto_tree_add_item(ncp_tree, hf_lip_echo, tvb, commhdr, -1, ENC_ASCII|ENC_NA); break; default: proto_tree_add_expert_format(ncp_tree, pinfo, &ei_ncp_type, tvb, commhdr + 6, -1, "%s packets not supported yet", val_to_str(header.type, ncp_type_vals, "Unknown type (0x%04x)")); break; } } static int dissect_ncp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_) { dissect_ncp_common(tvb, pinfo, tree, FALSE); return tvb_captured_length(tvb); } static guint get_ncp_pdu_len(packet_info *pinfo _U_, tvbuff_t *tvb, int offset, void *data _U_) { guint32 signature; /* * Check the NCP-over-TCP header signature, to make sure it's there. * If it's not there, we cannot trust the next 4 bytes to be a * packet length+"has signature" flag, so we just say the length is * "what remains in the packet". */ signature = tvb_get_ntohl(tvb, offset); if (signature != NCPIP_RQST && signature != NCPIP_RPLY) return tvb_captured_length_remaining(tvb, offset); /* * Get the length of the NCP-over-TCP packet. Strip off the "has * signature" flag. */ return tvb_get_ntohl(tvb, offset + 4) & 0x7fffffff; } static int dissect_ncp_tcp_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_) { dissect_ncp_common(tvb, pinfo, tree, TRUE); return tvb_captured_length(tvb); } static int dissect_ncp_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data) { tcp_dissect_pdus(tvb, pinfo, tree, ncp_desegment, 8, get_ncp_pdu_len, dissect_ncp_tcp_pdu, data); return tvb_captured_length(tvb); } void proto_register_ncp(void) { static hf_register_info hf[] = { { &hf_ncp_ip_sig, { "NCP over IP signature", "ncp.ip.signature", FT_UINT32, BASE_HEX, VALS(ncp_ip_signature), 0x0, NULL, HFILL }}, { &hf_ncp_ip_length, { "NCP over IP length", "ncp.ip.length", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_ncp_ip_ver, { "NCP over IP Version", "ncp.ip.version", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_ncp_ip_rplybufsize, { "NCP over IP Reply Buffer Size", "ncp.ip.replybufsize", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_ncp_ip_packetsig, { "NCP over IP Packet Signature", "ncp.ip.packetsig", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }}, { &hf_ncp_type, { "Type", "ncp.type", FT_UINT16, BASE_HEX, VALS(ncp_type_vals), 0x0, "NCP message type", HFILL }}, { &hf_ncp_seq, { "Sequence Number", "ncp.seq", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_ncp_connection, { "Connection Number", "ncp.connection", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_ncp_task, { "Task Number", "ncp.task", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_ncp_oplock_flag, { "Broadcast Message Flag", "ncp.msg_flag", FT_UINT8, BASE_HEX, VALS(ncp_oplock_vals), 0x0, NULL, HFILL }}, { &hf_ncp_oplock_handle, { "File Handle", "ncp.oplock_handle", FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL }}, { &hf_ncp_stream_type, { "Stream Type", "ncp.stream_type", FT_UINT8, BASE_HEX, NULL, 0x0, "Type of burst", HFILL }}, { &hf_ncp_system_flags, { "System Flags", "ncp.system_flags", FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL }}, { &hf_ncp_system_flags_abt, { "ABT", "ncp.system_flags.abt", FT_BOOLEAN, 8, NULL, ABT, "Is this an abort request?", HFILL }}, { &hf_ncp_system_flags_eob, { "EOB", "ncp.system_flags.eob", FT_BOOLEAN, 8, NULL, EOB, "Is this the last packet of the burst?", HFILL }}, { &hf_ncp_system_flags_sys, { "SYS", "ncp.system_flags.sys", FT_BOOLEAN, 8, NULL, SYS, "Is this a system packet?", HFILL }}, { &hf_ncp_system_flags_bsy, { "BSY", "ncp.system_flags.bsy", FT_BOOLEAN, 8, NULL, BSY, "Is the server busy?", HFILL }}, { &hf_ncp_system_flags_lst, { "LST", "ncp.system_flags.lst", FT_BOOLEAN, 8, NULL, LST, "Return Fragment List?", HFILL }}, { &hf_ncp_src_connection, { "Source Connection ID", "ncp.src_connection", FT_UINT32, BASE_DEC, NULL, 0x0, "The workstation's connection identification number", HFILL }}, { &hf_ncp_dst_connection, { "Destination Connection ID", "ncp.dst_connection", FT_UINT32, BASE_DEC, NULL, 0x0, "The server's connection identification number", HFILL }}, { &hf_ncp_packet_seqno, { "Packet Sequence Number", "ncp.packet_seqno", FT_UINT32, BASE_DEC, NULL, 0x0, "Sequence number of this packet in a burst", HFILL }}, { &hf_ncp_delay_time, { "Delay Time", "ncp.delay_time", /* in 100 us increments */ FT_UINT32, BASE_DEC, NULL, 0x0, "Delay time between consecutive packet sends (100 us increments)", HFILL }}, { &hf_ncp_burst_seqno, { "Burst Sequence Number", "ncp.burst_seqno", FT_UINT16, BASE_DEC, NULL, 0x0, "Sequence number of this packet in the burst", HFILL }}, { &hf_ncp_ack_seqno, { "ACK Sequence Number", "ncp.ack_seqno", FT_UINT16, BASE_DEC, NULL, 0x0, "Next expected burst sequence number", HFILL }}, { &hf_ncp_burst_len, { "Burst Length", "ncp.burst_len", FT_UINT32, BASE_DEC, NULL, 0x0, "Total length of data in this burst", HFILL }}, { &hf_ncp_burst_offset, { "Burst Offset", "ncp.burst_offset", FT_UINT32, BASE_DEC, NULL, 0x0, "Offset of data in the burst", HFILL }}, { &hf_ncp_data_offset, { "Data Offset", "ncp.data_offset", FT_UINT32, BASE_DEC, NULL, 0x0, "Offset of this packet", HFILL }}, { &hf_ncp_data_bytes, { "Data Bytes", "ncp.data_bytes", FT_UINT16, BASE_DEC, NULL, 0x0, "Number of data bytes in this packet", HFILL }}, { &hf_ncp_missing_fraglist_count, { "Missing Fragment List Count", "ncp.missing_fraglist_count", FT_UINT16, BASE_DEC, NULL, 0x0, "Number of missing fragments reported", HFILL }}, { &hf_ncp_missing_data_offset, { "Missing Data Offset", "ncp.missing_data_offset", FT_UINT32, BASE_DEC, NULL, 0x0, "Offset of beginning of missing data", HFILL }}, { &hf_ncp_missing_data_count, { "Missing Data Count", "ncp.missing_data_count", FT_UINT16, BASE_DEC, NULL, 0x0, "Number of bytes of missing data", HFILL }}, { &hf_ncp_completion_code, { "Completion Code", "ncp.completion_code", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_ncp_connection_status, { "Connection Status", "ncp.connection_status", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_ncp_slot, { "Slot", "ncp.slot", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_ncp_control_code, { "Control Code", "ncp.control_code", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, #if 0 { &hf_ncp_fragment_handle, { "Fragment Handle", "ncp.fragger_hndl", FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL }}, #endif { &hf_lip_echo, { "Large Internet Packet Echo", "ncp.lip_echo", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }}, { &hf_ncp_burst_command, { "Burst Command", "ncp.burst_command", FT_UINT32, BASE_HEX, VALS(burst_command), 0x0, "Packet Burst Command", HFILL }}, { &hf_ncp_burst_file_handle, { "Burst File Handle", "ncp.burst_file_handle", FT_UINT32, BASE_HEX, NULL, 0x0, "Packet Burst File Handle", HFILL }}, { &hf_ncp_burst_reserved, { "Reserved", "ncp.burst_reserved", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }} }; static gint *ett[] = { &ett_ncp, &ett_ncp_system_flags, &ett_nds, &ett_nds_segments, &ett_nds_segment }; static ei_register_info ei[] = { { &ei_ncp_new_server_session, { "ncp.new_server_session", PI_RESPONSE_CODE, PI_CHAT, "Detected New Server Session", EXPFILL }}, { &ei_ncp_oplock_handle, { "ncp.oplock_handle.clear", PI_RESPONSE_CODE, PI_CHAT, "Server requesting station to clear oplock", EXPFILL }}, { &ei_ncp_type, { "ncp.type.unsupported", PI_UNDECODED, PI_NOTE, "Packet type not supported yet", EXPFILL }}, }; module_t *ncp_module; expert_module_t* expert_ncp; proto_ncp = proto_register_protocol("NetWare Core Protocol", "NCP", "ncp"); proto_register_field_array(proto_ncp, hf, array_length(hf)); proto_register_subtree_array(ett, array_length(ett)); expert_ncp = expert_register_protocol(proto_ncp); expert_register_field_array(expert_ncp, ei, array_length(ei)); ncp_module = prefs_register_protocol(proto_ncp, NULL); prefs_register_obsolete_preference(ncp_module, "initial_hash_size"); prefs_register_bool_preference(ncp_module, "desegment", "Reassemble NCP-over-TCP messages spanning multiple TCP segments", "Whether the NCP dissector should reassemble messages spanning multiple TCP segments." " To use this option, you must also enable \"Allow subdissectors to reassemble TCP streams\" in the TCP protocol settings.", &ncp_desegment); prefs_register_bool_preference(ncp_module, "defragment_nds", "Reassemble fragmented NDS messages spanning multiple reply packets", "Whether the NCP dissector should defragment NDS messages spanning multiple reply packets.", &nds_defragment); prefs_register_bool_preference(ncp_module, "newstyle", "Dissect New Netware Information Structure", "Dissect the NetWare Information Structure as NetWare 5.x or higher or as older NetWare 3.x.", &ncp_newstyle); prefs_register_bool_preference(ncp_module, "eid_2_expert", "Expert: EID to Name lookups?", "Whether the NCP dissector should echo the NDS Entry ID to name resolves to the expert table.", &nds_echo_eid); prefs_register_bool_preference(ncp_module, "connection_2_expert", "Expert: NCP Connections?", "Whether the NCP dissector should echo NCP connection information to the expert table.", &ncp_echo_conn); prefs_register_bool_preference(ncp_module, "error_2_expert", "Expert: NCP Errors?", "Whether the NCP dissector should echo protocol errors to the expert table.", &ncp_echo_err); prefs_register_bool_preference(ncp_module, "server_2_expert", "Expert: Server Information?", "Whether the NCP dissector should echo server information to the expert table.", &ncp_echo_server); prefs_register_bool_preference(ncp_module, "file_2_expert", "Expert: File Information?", "Whether the NCP dissector should echo file open/close/oplock information to the expert table.", &ncp_echo_file); register_init_routine(&mncp_init_protocol); register_cleanup_routine(&mncp_cleanup_protocol); ncp_tap.stat=register_tap("ncp_srt"); ncp_tap.hdr=register_tap("ncp"); register_conversation_table(proto_ncp, FALSE, ncp_conversation_packet, ncp_hostlist_packet); register_srt_table(proto_ncp, "ncp_srt", 24, ncpstat_packet, ncpstat_init, NULL); } void proto_reg_handoff_ncp(void) { dissector_handle_t ncp_handle; dissector_handle_t ncp_tcp_handle; ncp_handle = create_dissector_handle(dissect_ncp, proto_ncp); ncp_tcp_handle = create_dissector_handle(dissect_ncp_tcp, proto_ncp); dissector_add_uint("tcp.port", TCP_PORT_NCP, ncp_tcp_handle); dissector_add_uint("udp.port", UDP_PORT_NCP, ncp_handle); dissector_add_uint("ipx.packet_type", IPX_PACKET_TYPE_NCP, ncp_handle); dissector_add_uint("ipx.socket", IPX_SOCKET_NCP, ncp_handle); } /* * Editor modelines - http://www.wireshark.org/tools/modelines.html * * Local variables: * c-basic-offset: 4 * tab-width: 8 * indent-tabs-mode: nil * End: * * vi: set shiftwidth=4 tabstop=8 expandtab: * :indentSize=4:tabSize=8:noTabs=true: */