Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions lib/wiregasm/bindings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,13 @@ EMSCRIPTEN_BINDINGS(Wiregasm) {
emscripten::function("listPreferences", &wg_list_preferences);
emscripten::function("applyPreferences", &wg_prefs_apply_all);
emscripten::function("wiresharkVersion", &wg_ws_version);
// Protocol enable/disable functions
emscripten::function("listProtocols", &wg_list_protocols);
emscripten::function("setProtocolEnabled", &wg_set_protocol_enabled);
emscripten::function("setProtocolEnabledByName", &wg_set_protocol_enabled_by_name);
// Heuristic dissector enable/disable functions
emscripten::function("listHeuristicDissectors", &wg_list_heuristic_dissectors);
emscripten::function("setHeuristicEnabled", &wg_set_heuristic_enabled);
}

EMSCRIPTEN_BINDINGS(DissectSession) {
Expand Down Expand Up @@ -306,3 +313,28 @@ EMSCRIPTEN_BINDINGS(ExportObject) {
EMSCRIPTEN_BINDINGS(MapInput) {
register_map<string, string>("MapInput");
}

EMSCRIPTEN_BINDINGS(ProtocolInfo) {
value_object<ProtocolInfo>("ProtocolInfo")
.field("id", &ProtocolInfo::id)
.field("name", &ProtocolInfo::name)
.field("long_name", &ProtocolInfo::long_name)
.field("enabled", &ProtocolInfo::enabled)
.field("enabled_by_default", &ProtocolInfo::enabled_by_default)
.field("can_toggle", &ProtocolInfo::can_toggle);

register_vector<ProtocolInfo>("VectorProtocolInfo");
}

EMSCRIPTEN_BINDINGS(HeuristicInfo) {
value_object<HeuristicInfo>("HeuristicInfo")
.field("short_name", &HeuristicInfo::short_name)
.field("display_name", &HeuristicInfo::display_name)
.field("list_name", &HeuristicInfo::list_name)
.field("protocol_name", &HeuristicInfo::protocol_name)
.field("protocol_id", &HeuristicInfo::protocol_id)
.field("enabled", &HeuristicInfo::enabled)
.field("enabled_by_default", &HeuristicInfo::enabled_by_default);

register_vector<HeuristicInfo>("VectorHeuristicInfo");
}
135 changes: 135 additions & 0 deletions lib/wiregasm/wiregasm.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
#include "wiregasm.h"
#include "lib.h"
#include <epan/packet.h>
#include <epan/prefs-int.h>
#include <epan/prefs.h>
#include <epan/proto.h>
#include <epan/wslua/init_wslua.h>
#include <wireshark/ws_version.h>
#include <wsutil/privileges.h>
Expand Down Expand Up @@ -549,3 +551,136 @@ IoGraphResult DissectSession::iograph(MapInput args) {
}
return wg_session_process_iograph(&this->capture_file, args);
}

// ============================================================================
// Protocol enable/disable functions
// ============================================================================

vector<ProtocolInfo> wg_list_protocols() {
vector<ProtocolInfo> result;
void *cookie = NULL;
int proto_id;

for (proto_id = proto_get_first_protocol(&cookie); proto_id != -1;
proto_id = proto_get_next_protocol(&cookie)) {

protocol_t *protocol = find_protocol_by_id(proto_id);
if (protocol == NULL)
continue;

ProtocolInfo info;
info.id = proto_id;

const char *filter_name = proto_get_protocol_filter_name(proto_id);
if (filter_name) {
info.name = string(filter_name);
}

const char *long_name = proto_get_protocol_long_name(protocol);
if (long_name) {
info.long_name = string(long_name);
}

info.enabled = proto_is_protocol_enabled(protocol);
info.enabled_by_default = proto_is_protocol_enabled_by_default(protocol);
info.can_toggle = proto_can_toggle_protocol(proto_id);

result.push_back(info);
}

return result;
}

bool wg_set_protocol_enabled(int proto_id, bool enabled) {
protocol_t *protocol = find_protocol_by_id(proto_id);
if (protocol == NULL) {
return false;
}

if (!proto_can_toggle_protocol(proto_id)) {
return false;
}

proto_set_decoding(proto_id, enabled);
return true;
}

bool wg_set_protocol_enabled_by_name(string proto_name, bool enabled) {
int proto_id = proto_get_id_by_filter_name(proto_name.c_str());
if (proto_id == -1) {
return false;
}

return wg_set_protocol_enabled(proto_id, enabled);
}

// ============================================================================
// Heuristic dissector enable/disable functions
// ============================================================================

// Callback data for collecting heuristic dissectors
struct HeurCollectorData {
vector<HeuristicInfo> *results;
};

// Callback for each heuristic entry within a table
static void heur_entry_collector_cb(const char *table_name, struct heur_dtbl_entry *entry, void *user_data) {
HeurCollectorData *data = (HeurCollectorData *)user_data;

HeuristicInfo info;

if (entry->short_name) {
info.short_name = string(entry->short_name);
}

if (entry->display_name) {
info.display_name = string(entry->display_name);
}

if (entry->list_name) {
info.list_name = string(entry->list_name);
}

if (entry->protocol) {
const char *proto_name = proto_get_protocol_short_name(entry->protocol);
if (proto_name) {
info.protocol_name = string(proto_name);
}
// Get the protocol ID so we can link heuristics to their parent protocol
info.protocol_id = proto_get_id(entry->protocol);
} else {
info.protocol_id = -1;
}

info.enabled = entry->enabled;
info.enabled_by_default = entry->enabled_by_default;

data->results->push_back(info);
}

// Callback for each heuristic table
static void heur_table_collector_cb(const char *table_name, struct heur_dissector_list *table, void *user_data) {
// Iterate all entries in this table
heur_dissector_table_foreach(table_name, heur_entry_collector_cb, user_data);
}

vector<HeuristicInfo> wg_list_heuristic_dissectors() {
vector<HeuristicInfo> result;
HeurCollectorData data;
data.results = &result;

// Iterate all heuristic tables, then entries within each
dissector_all_heur_tables_foreach_table(heur_table_collector_cb, &data, NULL);

return result;
}

bool wg_set_heuristic_enabled(string short_name, bool enabled) {
heur_dtbl_entry_t *entry = find_heur_dissector_by_unique_short_name(short_name.c_str());
if (entry == NULL) {
return false;
}

entry->enabled = enabled;
return true;
}
56 changes: 55 additions & 1 deletion lib/wiregasm/wiregasm.h
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,51 @@ struct DownloadResponse {
Download download;
};

// Protocol info struct for listing all protocols
struct ProtocolInfo {
int id; // protocol ID
string name; // protocol filter name (e.g., "tcp")
string long_name; // protocol long name (e.g., "Transmission Control Protocol")
bool enabled; // whether protocol is currently enabled
bool enabled_by_default; // whether protocol is enabled by default
bool can_toggle; // whether protocol can be toggled (some can't)
};

// Heuristic dissector info struct for listing all heuristic dissectors
struct HeuristicInfo {
string short_name; // unique short name (e.g., "mac_nr_udp")
string display_name; // display name for UI
string list_name; // parent dissector table name (e.g., "udp")
string protocol_name; // associated protocol name
int protocol_id; // protocol ID this heuristic belongs to
bool enabled; // whether heuristic is currently enabled
bool enabled_by_default; // whether heuristic is enabled by default
};

// Unified enabled item for building a hierarchical UI like Wireshark's Enabled Protocols dialog
// Can represent either a protocol or a heuristic dissector
struct EnabledProtocolItem {
// Common fields
string name; // display name (short_name for protocols, display_name for heuristics)
string description; // long description
bool enabled; // whether currently enabled
bool enabled_by_default; // whether enabled by default
bool can_toggle; // whether can be toggled

// Type identification
bool is_heuristic; // true if this is a heuristic, false if protocol

// Protocol-specific (when is_heuristic = false)
int protocol_id; // protocol ID (only valid for protocols)

// Heuristic-specific (when is_heuristic = true)
string heuristic_short_name; // unique short name for enabling (only valid for heuristics)
string list_name; // dissector table this heuristic listens on (e.g., "udp")

// Child heuristics (only valid for protocols)
vector<EnabledProtocolItem> heuristics; // heuristic dissectors belonging to this protocol
};

// globals

bool wg_init();
Expand All @@ -259,6 +304,15 @@ vector<PrefData> wg_list_preferences(string module_name);
string wg_get_upload_dir();
string wg_get_plugins_dir();

// Protocol enable/disable functions
vector<ProtocolInfo> wg_list_protocols();
bool wg_set_protocol_enabled(int proto_id, bool enabled);
bool wg_set_protocol_enabled_by_name(string proto_name, bool enabled);

// Heuristic dissector enable/disable functions
vector<HeuristicInfo> wg_list_heuristic_dissectors();
bool wg_set_heuristic_enabled(string short_name, bool enabled);

class DissectSession {
private:
string path;
Expand All @@ -277,4 +331,4 @@ class DissectSession {
~DissectSession();
};

#endif
#endif
Binary file added samples/nr.pcapng
Binary file not shown.
111 changes: 111 additions & 0 deletions src/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -946,6 +946,117 @@ describe("Wiregasm Library - Reloading Lua Plugins", () => {
});
});

describe("Wiregasm Library - Heuristic Dissectors", () => {
const wg = new Wiregasm();

beforeAll(async () => {
return wg.init(loadWiregasm, await buildCompressedOverrides());
});

afterAll(() => {
wg.destroy();
});

test("list heuristic dissectors works", async () => {
const heuristics = wg.list_heuristic_dissectors();
expect(heuristics.length).toBeGreaterThan(0);

// Find mac_nr_udp heuristic
const macNrUdp = heuristics.find((h) => h.short_name === "mac_nr_udp");
expect(macNrUdp).toBeDefined();
expect(macNrUdp?.display_name).toBe("MAC-NR over UDP");
expect(macNrUdp?.list_name).toBe("udp");
expect(macNrUdp?.enabled_by_default).toBe(false);
});

test("list protocols works", async () => {
const protocols = wg.list_protocols();
expect(protocols.length).toBeGreaterThan(0);

// Find MAC-NR protocol
const macNr = protocols.find((p) => p.name === "mac-nr");
expect(macNr).toBeDefined();
expect(macNr?.long_name).toContain("MAC");
});

test("enable/disable heuristic dissector works", async () => {
// Check initial state
let heuristics = wg.list_heuristic_dissectors();
let macNrUdp = heuristics.find((h) => h.short_name === "mac_nr_udp");
expect(macNrUdp?.enabled).toBe(false);

// Enable the heuristic
const result = wg.set_heuristic_enabled("mac_nr_udp", true);
expect(result).toBe(true);

// Verify it's enabled
heuristics = wg.list_heuristic_dissectors();
macNrUdp = heuristics.find((h) => h.short_name === "mac_nr_udp");
expect(macNrUdp?.enabled).toBe(true);

// Disable it again
wg.set_heuristic_enabled("mac_nr_udp", false);
heuristics = wg.list_heuristic_dissectors();
macNrUdp = heuristics.find((h) => h.short_name === "mac_nr_udp");
expect(macNrUdp?.enabled).toBe(false);
});

test("mac_nr_udp heuristic dissector decodes NR pcap correctly", async () => {
// Enable the mac_nr_udp heuristic before loading
wg.set_heuristic_enabled("mac_nr_udp", true);

// Load the NR capture file
const data = await fs.readFile("samples/nr.pcapng");
const ret = wg.load("nr.pcapng", data);
expect(ret.code).toEqual(0);

// Get the first frame and check if MAC-NR is detected
const frame = wg.frame(1);
expect(frame.number).toEqual(1);

// Find the MAC-NR protocol in the tree
let foundMacNr = false;
for (let i = 0; i < frame.tree.size(); i++) {
const tree = frame.tree.get(i);
if (tree.label.includes("MAC-NR")) {
foundMacNr = true;
break;
}
}

expect(foundMacNr).toBe(true);

// Disable the heuristic for cleanup
wg.set_heuristic_enabled("mac_nr_udp", false);
});

test("without heuristic enabled, MAC-NR is not detected", async () => {
// Make sure heuristic is disabled
wg.set_heuristic_enabled("mac_nr_udp", false);

// Load the NR capture file
const data = await fs.readFile("samples/nr.pcapng");
const ret = wg.load("nr2.pcapng", data);
expect(ret.code).toEqual(0);

// Get the first frame
const frame = wg.frame(1);
expect(frame.number).toEqual(1);

// MAC-NR should NOT be in the tree (only UDP)
let foundMacNr = false;
for (let i = 0; i < frame.tree.size(); i++) {
const tree = frame.tree.get(i);
if (tree.label.includes("MAC-NR")) {
foundMacNr = true;
break;
}
}

expect(foundMacNr).toBe(false);
});
});

describe("Wiregasm Library - IoGraph", () => {
const wg = new Wiregasm();

Expand Down
Loading