From 7eb51b6dd8765b7ddbec34b12dd46ffbba223ae3 Mon Sep 17 00:00:00 2001 From: NothNoth Date: Mon, 1 Apr 2024 13:11:27 +0200 Subject: [PATCH] Hierarchical dissection (#10) * Subfields : setting up everything on the Go plugin side tpackage + example) * Wirego package cleanup * SPlit wirego_plugin code to multiple source files * Cleanup * Cleanup * Almost in place, but strange regression * Subtrees working, needsq more tests * Updated documentation for sub-fields --------- Co-authored-by: Benoit --- DEVGUIDE.md | 5 + wirego/example/wirego_example.go | 31 ++- wirego/wirego/detection.go | 61 ++++++ wirego/wirego/dissect.go | 90 ++++++++ wirego/wirego/plugininterface.go | 82 ++++++++ wirego/wirego/version.go | 2 +- wirego/wirego/wirego.go | 210 +------------------ wirego_plugin/CMakeLists.txt | 3 + wirego_plugin/dissect.c | 158 ++++++++++++++ wirego_plugin/dissect.h | 10 + wirego_plugin/eletemet.c | 33 +++ wirego_plugin/helpers.c | 140 +++++++++++++ wirego_plugin/helpers.h | 11 + wirego_plugin/packet-wirego.c | 349 ++----------------------------- wirego_plugin/packet-wirego.h | 14 ++ wirego_plugin/plugin-loader.c | 6 +- wirego_plugin/plugin-loader.h | 2 +- wirego_plugin/preferences.c | 72 +++++++ wirego_plugin/preferences.h | 10 + wirego_plugin/version.h | 2 +- 20 files changed, 745 insertions(+), 546 deletions(-) create mode 100644 wirego/wirego/detection.go create mode 100644 wirego/wirego/dissect.go create mode 100644 wirego/wirego/plugininterface.go create mode 100644 wirego_plugin/dissect.c create mode 100644 wirego_plugin/dissect.h create mode 100644 wirego_plugin/eletemet.c create mode 100644 wirego_plugin/helpers.c create mode 100644 wirego_plugin/helpers.h create mode 100644 wirego_plugin/preferences.c create mode 100644 wirego_plugin/preferences.h diff --git a/DEVGUIDE.md b/DEVGUIDE.md index 2b08e2a..c20cde2 100644 --- a/DEVGUIDE.md +++ b/DEVGUIDE.md @@ -132,6 +132,11 @@ func (WiregoExample) DissectPacket(packetNumber int, src string, dst string, lay //Add a few fields and refer to them using our own "internalId" res.Fields = append(res.Fields, wirego.DissectField{WiregoFieldId: FieldIdCustom1, Offset: 0, Length: 2}) res.Fields = append(res.Fields, wirego.DissectField{WiregoFieldId: FieldIdCustom2, Offset: 2, Length: 4}) + + //And add a field "Custom 1" with a sub-field "Custom 2" + subField := wirego.DissectField{WiregoFieldId: FieldIdCustom2, Offset: 8, Length: 2} + res.Fields = append(res.Fields, wirego.DissectField{WiregoFieldId: FieldIdCustom1, Offset: 0, Length: 2, SubFields: []wirego.DissectField{subField}) + return &res } ``` diff --git a/wirego/example/wirego_example.go b/wirego/example/wirego_example.go index b3474cd..ec9fbba 100644 --- a/wirego/example/wirego_example.go +++ b/wirego/example/wirego_example.go @@ -1,18 +1,16 @@ package main import ( - "encoding/hex" "fmt" "github.com/quarkslab/wirego/wirego/wirego" ) -var fields []wirego.WiresharkField - // Define here enum identifiers, used to refer to a specific field const ( - FieldIdCustom1 wirego.FieldId = 1 - FieldIdCustom2 wirego.FieldId = 2 + FieldIdCustom1 wirego.FieldId = 1 + FieldIdCustom2 wirego.FieldId = 2 + FieldIdCustomWithSubFields wirego.FieldId = 3 ) // Since we implement the wirego.WiregoInterface we need some structure to hold it. @@ -34,10 +32,6 @@ func init() { // This function is called when the plugin is loaded. func (WiregoExample) Setup() error { - //Setup our wireshark custom fields - fields = append(fields, wirego.WiresharkField{WiregoFieldId: FieldIdCustom1, Name: "Custom1", Filter: "wirego.custom01", ValueType: wirego.ValueTypeUInt8, DisplayMode: wirego.DisplayModeHexadecimal}) - fields = append(fields, wirego.WiresharkField{WiregoFieldId: FieldIdCustom2, Name: "Custom2", Filter: "wirego.custom02", ValueType: wirego.ValueTypeUInt16, DisplayMode: wirego.DisplayModeDecimal}) - return nil } @@ -54,6 +48,13 @@ func (WiregoExample) GetFilter() string { // GetFields returns the list of fields descriptor that we may eventually return // when dissecting a packet payload func (WiregoExample) GetFields() []wirego.WiresharkField { + var fields []wirego.WiresharkField + + //Setup our wireshark custom fields + fields = append(fields, wirego.WiresharkField{WiregoFieldId: FieldIdCustom1, Name: "Custom1", Filter: "wirego.custom01", ValueType: wirego.ValueTypeUInt8, DisplayMode: wirego.DisplayModeHexadecimal}) + fields = append(fields, wirego.WiresharkField{WiregoFieldId: FieldIdCustom2, Name: "Custom2", Filter: "wirego.custom02", ValueType: wirego.ValueTypeUInt16, DisplayMode: wirego.DisplayModeDecimal}) + fields = append(fields, wirego.WiresharkField{WiregoFieldId: FieldIdCustomWithSubFields, Name: "CustomWith Subs", Filter: "wirego.custom_subs", ValueType: wirego.ValueTypeUInt32, DisplayMode: wirego.DisplayModeHexadecimal}) + return fields } @@ -100,7 +101,15 @@ func (WiregoExample) DissectPacket(packetNumber int, src string, dst string, lay //Add a few fields and refer to them using our own "internalId" res.Fields = append(res.Fields, wirego.DissectField{WiregoFieldId: FieldIdCustom1, Offset: 0, Length: 2}) res.Fields = append(res.Fields, wirego.DissectField{WiregoFieldId: FieldIdCustom2, Offset: 2, Length: 4}) - fmt.Println(layer, " ", src, " to ", dst) - fmt.Println(hex.Dump(packet)) + + //Add a field with two sub field + subField1 := wirego.DissectField{WiregoFieldId: FieldIdCustom1, Offset: 6, Length: 2} + subField2 := wirego.DissectField{WiregoFieldId: FieldIdCustom1, Offset: 8, Length: 2} + field := wirego.DissectField{WiregoFieldId: FieldIdCustomWithSubFields, Offset: 6, Length: 4, SubFields: []wirego.DissectField{subField1, subField2}} + res.Fields = append(res.Fields, field) + + //Dump packet contents + //fmt.Println(layer, " ", src, " to ", dst) + //fmt.Println(hex.Dump(packet)) return &res } diff --git a/wirego/wirego/detection.go b/wirego/wirego/detection.go new file mode 100644 index 0000000..c81e57f --- /dev/null +++ b/wirego/wirego/detection.go @@ -0,0 +1,61 @@ +package wirego + +import "C" + +//export wirego_detect_int +func wirego_detect_int(matchValue *C.int, idx C.int) *C.char { + if wg.listener == nil { + return nil + } + + cnt := 0 + for _, f := range wg.pluginDetectionFilters { + if f.FilterType == DetectionFilterTypeInt { + if cnt == int(idx) { + *matchValue = C.int(f.ValueInt) + name := C.CString(f.Name) + return name + } + cnt++ + } + } + + *matchValue = 0 + return nil +} + +//export wirego_detect_string +func wirego_detect_string(matchValue **C.char, idx C.int) *C.char { + if wg.listener == nil { + return nil + } + + cnt := 0 + for _, f := range wg.pluginDetectionFilters { + if f.FilterType == DetectionFilterTypeString { + if cnt == int(idx) { + + *matchValue = C.CString(f.ValueString) + name := C.CString(f.Name) + return name + } + cnt++ + } + } + + *matchValue = nil + return nil +} + +//export wirego_detect_heuristic +func wirego_detect_heuristic(idx C.int) *C.char { + if wg.listener == nil { + return nil + } + + if idx >= C.int(len(wg.pluginDetectionHeuristicsParents)) { + return nil + } + + return C.CString(wg.pluginDetectionHeuristicsParents[idx]) +} diff --git a/wirego/wirego/dissect.go b/wirego/wirego/dissect.go new file mode 100644 index 0000000..28b4754 --- /dev/null +++ b/wirego/wirego/dissect.go @@ -0,0 +1,90 @@ +package wirego + +import "C" +import ( + "fmt" + "unsafe" +) + +type DissectResultFlattenEntry struct { + Protocol string + Info string + fields []DissectResultFlatten +} +type DissectResultFlatten struct { + parentIdx int + wiregoFieldId FieldId + offset int + length int +} + +/* + Note: there's probably a way to return the complete DissectResult structure + to the C environment. At the end of the day, this would be super opaque so for now + let's use some dummy accessors and a result cache. +*/ +//export wirego_dissect_packet +func wirego_dissect_packet(packetNumber C.int, src *C.char, dst *C.char, layer *C.char, packet *C.char, packetSize C.int) C.int { + if wg.listener == nil || wg.resultsCache == nil { + return C.int(-1) + } + + if (src == nil) || (dst == nil) || (layer == nil) || (packet == nil) || packetSize == 0 { + return C.int(-1) + } + + wg.lock.Lock() + _, found := wg.resultsCache[packetNumber] + wg.lock.Unlock() + + if found { + return packetNumber + } + + result := wg.listener.DissectPacket(int(packetNumber), C.GoString(src), C.GoString(dst), C.GoString(layer), C.GoBytes(unsafe.Pointer(packet), packetSize)) + + if result == nil { + return C.int(-1) + } + + //Check results + for _, r := range result.Fields { + if C.int(r.Offset) >= packetSize { + fmt.Printf("Wirego plugin did return an invalid Offset : %d (packet size is %d bytes)\n", r.Offset, packetSize) + return C.int(-1) + } + if C.int(r.Offset+r.Length) > packetSize { + fmt.Printf("Wirego plugin did return an invalid Length : %d (offset is %d and packet size is %d bytes)\n", r.Length, r.Offset, packetSize) + return C.int(-1) + } + _, found := wg.wiregoFieldIds[int(r.WiregoFieldId)] + if !found { + fmt.Printf("Wirego plugin did return an invalid WiregoFieldId : %d\n", r.WiregoFieldId) + return C.int(-1) + } + } + + //Flatten results to a simple list with parenIdx pointing to parent's entry + var flatten DissectResultFlattenEntry + flatten.Info = result.Info + flatten.Protocol = result.Protocol + for _, r := range result.Fields { + addFieldsRec(&flatten, -1, &r) + } + + //Add to cache + pinner.Pin(&flatten) + wg.lock.Lock() + defer wg.lock.Unlock() + wg.resultsCache[packetNumber] = &flatten + return packetNumber +} + +func addFieldsRec(flatten *DissectResultFlattenEntry, parentIdx int, field *DissectField) { + flatten.fields = append(flatten.fields, DissectResultFlatten{parentIdx: parentIdx, wiregoFieldId: field.WiregoFieldId, offset: field.Offset, length: field.Length}) + newParentIdx := len(flatten.fields) - 1 + + for _, sub := range field.SubFields { + addFieldsRec(flatten, newParentIdx, &sub) + } +} diff --git a/wirego/wirego/plugininterface.go b/wirego/wirego/plugininterface.go new file mode 100644 index 0000000..26517ee --- /dev/null +++ b/wirego/wirego/plugininterface.go @@ -0,0 +1,82 @@ +package wirego + +// WiregoInterface is implemented by the actual wirego plugin +type WiregoInterface interface { + GetName() string + GetFilter() string + Setup() error + GetFields() []WiresharkField + GetDetectionFilters() []DetectionFilter + GetDetectionHeuristicsParents() []string + DetectionHeuristic(packetNumber int, src string, dst string, stack string, packet []byte) bool + DissectPacket(packetNumber int, src string, dst string, stack string, packet []byte) *DissectResult +} + +// Display modes +type DisplayMode int + +const ( + DisplayModeNone DisplayMode = 0x01 + DisplayModeDecimal DisplayMode = 0x02 + DisplayModeHexadecimal DisplayMode = 0x03 +) + +// Fields types +type ValueType int + +const ( + ValueTypeNone ValueType = 0x01 + ValueTypeBool ValueType = 0x02 + + ValueTypeUInt8 ValueType = 0x03 + ValueTypeInt8 ValueType = 0x04 + + ValueTypeUInt16 ValueType = 0x05 + ValueTypeInt16 ValueType = 0x06 + + ValueTypeUInt32 ValueType = 0x07 + ValueTypeInt32 ValueType = 0x08 + + ValueTypeCString ValueType = 0x09 + ValueTypeString ValueType = 0x10 +) + +// A field descriptor, to be provided by the actual plugin +type FieldId int +type WiresharkField struct { + WiregoFieldId FieldId + Name string + Filter string + ValueType ValueType + DisplayMode DisplayMode +} + +type DetectionFilterType int + +const ( + DetectionFilterTypeInt DetectionFilterType = iota + DetectionFilterTypeString DetectionFilterType = iota +) + +// A detection filter provides a wireshark filter use to match traffic to be sent to the dissector +type DetectionFilter struct { + FilterType DetectionFilterType + Name string + ValueInt int + ValueString string +} + +// A field, as returned by the dissector +type DissectField struct { + WiregoFieldId FieldId + Offset int + Length int + SubFields []DissectField +} + +// A dissector result is a protocol name, an info string and a list of extracted fields +type DissectResult struct { + Protocol string + Info string + Fields []DissectField +} diff --git a/wirego/wirego/version.go b/wirego/wirego/version.go index 90ae984..4732a1e 100644 --- a/wirego/wirego/version.go +++ b/wirego/wirego/version.go @@ -3,4 +3,4 @@ package wirego // Wirego version, for API compability issues management // Note: must match the ones defined in ../wirego_plugin/version.h const WIREGO_VERSION_MAJOR = 1 -const WIREGO_VERSION_MINOR = 1 +const WIREGO_VERSION_MINOR = 2 diff --git a/wirego/wirego/wirego.go b/wirego/wirego/wirego.go index badb431..10f9f58 100644 --- a/wirego/wirego/wirego.go +++ b/wirego/wirego/wirego.go @@ -19,23 +19,11 @@ import ( "unsafe" ) -// WiregoInterface is implemented by the actual wirego plugin -type WiregoInterface interface { - GetName() string - GetFilter() string - Setup() error - GetFields() []WiresharkField - GetDetectionFilters() []DetectionFilter - GetDetectionHeuristicsParents() []string - DetectionHeuristic(packetNumber int, src string, dst string, stack string, packet []byte) bool - DissectPacket(packetNumber int, src string, dst string, stack string, packet []byte) *DissectResult -} - // Just a simple holder type Wirego struct { listener WiregoInterface wiregoFieldIds map[int]bool - resultsCache map[C.int]*DissectResult + resultsCache map[C.int]*DissectResultFlattenEntry lock sync.Mutex //Fetched and duplicated from plugin, to trick gc @@ -49,73 +37,6 @@ type Wirego struct { // We use a static "object" here var wg Wirego -// Fields types -type ValueType int - -const ( - ValueTypeNone ValueType = 0x01 - ValueTypeBool ValueType = 0x02 - - ValueTypeUInt8 ValueType = 0x03 - ValueTypeInt8 ValueType = 0x04 - - ValueTypeUInt16 ValueType = 0x05 - ValueTypeInt16 ValueType = 0x06 - - ValueTypeUInt32 ValueType = 0x07 - ValueTypeInt32 ValueType = 0x08 - - ValueTypeCString ValueType = 0x09 - ValueTypeString ValueType = 0x10 -) - -// Display types -type DisplayMode int - -const ( - DisplayModeNone DisplayMode = 0x01 - DisplayModeDecimal DisplayMode = 0x02 - DisplayModeHexadecimal DisplayMode = 0x03 -) - -// A field descriptor, to be provided by the actual plugin -type FieldId int -type WiresharkField struct { - WiregoFieldId FieldId - Name string - Filter string - ValueType ValueType - DisplayMode DisplayMode -} - -// A field, as returned by the dissector -type DissectField struct { - WiregoFieldId FieldId - Offset int - Length int -} - -// A dissector result is a protocol name, an info string and a list of extracted fields -type DissectResult struct { - Protocol string - Info string - Fields []DissectField -} - -type DetectionFilterType int - -const ( - DetectionFilterTypeInt DetectionFilterType = iota - DetectionFilterTypeString DetectionFilterType = iota -) - -type DetectionFilter struct { - FilterType DetectionFilterType - Name string - ValueInt int - ValueString string -} - var resultatsCacheEnable bool = true var pinner runtime.Pinner @@ -149,7 +70,7 @@ func wirego_setup() C.int { return C.int(-1) } - wg.resultsCache = make(map[C.int]*DissectResult) + wg.resultsCache = make(map[C.int]*DissectResultFlattenEntry) wg.wiregoFieldIds = make(map[int]bool) //Preload all "static" values to bypass the GC and keep local copies @@ -207,64 +128,6 @@ func wirego_plugin_filter() *C.char { return C.CString(wg.pluginFilter) } -//export wirego_detect_int -func wirego_detect_int(matchValue *C.int, idx C.int) *C.char { - if wg.listener == nil { - return nil - } - - cnt := 0 - for _, f := range wg.pluginDetectionFilters { - if f.FilterType == DetectionFilterTypeInt { - if cnt == int(idx) { - *matchValue = C.int(f.ValueInt) - name := C.CString(f.Name) - return name - } - cnt++ - } - } - - *matchValue = 0 - return nil -} - -//export wirego_detect_string -func wirego_detect_string(matchValue **C.char, idx C.int) *C.char { - if wg.listener == nil { - return nil - } - - cnt := 0 - for _, f := range wg.pluginDetectionFilters { - if f.FilterType == DetectionFilterTypeString { - if cnt == int(idx) { - - *matchValue = C.CString(f.ValueString) - name := C.CString(f.Name) - return name - } - cnt++ - } - } - - *matchValue = nil - return nil -} - -//export wirego_detect_heuristic -func wirego_detect_heuristic(idx C.int) *C.char { - if wg.listener == nil { - return nil - } - - if idx >= C.int(len(wg.pluginDetectionHeuristicsParents)) { - return nil - } - - return C.CString(wg.pluginDetectionHeuristicsParents[idx]) -} - //export wirego_get_fields_count func wirego_get_fields_count() C.int { if wg.listener == nil { @@ -316,60 +179,6 @@ func wirego_detection_heuristic(packetNumber C.int, src *C.char, dst *C.char, la return 0 } -/* - Note: there's probably a way to return the complete DissectResult structure - to the C environment. At the end of the day, this would be super opaque so for now - let's use some dummy accessors and a result cache. -*/ -//export wirego_dissect_packet -func wirego_dissect_packet(packetNumber C.int, src *C.char, dst *C.char, layer *C.char, packet *C.char, packetSize C.int) C.int { - if wg.listener == nil || wg.resultsCache == nil { - return C.int(-1) - } - - if (src == nil) || (dst == nil) || (layer == nil) || (packet == nil) || packetSize == 0 { - return C.int(-1) - } - - wg.lock.Lock() - _, found := wg.resultsCache[packetNumber] - wg.lock.Unlock() - - if found { - return packetNumber - } - - result := wg.listener.DissectPacket(int(packetNumber), C.GoString(src), C.GoString(dst), C.GoString(layer), C.GoBytes(unsafe.Pointer(packet), packetSize)) - - if result == nil { - return C.int(-1) - } - - //Check results - for _, r := range result.Fields { - if C.int(r.Offset) >= packetSize { - fmt.Printf("Wirego plugin did return an invalid Offset : %d (packet size is %d bytes)\n", r.Offset, packetSize) - return C.int(-1) - } - if C.int(r.Offset+r.Length) > packetSize { - fmt.Printf("Wirego plugin did return an invalid Length : %d (offset is %d and packet size is %d bytes)\n", r.Length, r.Offset, packetSize) - return C.int(-1) - } - _, found := wg.wiregoFieldIds[int(r.WiregoFieldId)] - if !found { - fmt.Printf("Wirego plugin did return an invalid WiregoFieldId : %d\n", r.WiregoFieldId) - return C.int(-1) - } - } - - //Add to cache - pinner.Pin(&result) - wg.lock.Lock() - defer wg.lock.Unlock() - wg.resultsCache[packetNumber] = result - return packetNumber -} - //export wirego_result_get_protocol func wirego_result_get_protocol(h C.int) *C.char { if wg.listener == nil || wg.resultsCache == nil { @@ -414,12 +223,12 @@ func wirego_result_get_fields_count(h C.int) C.int { if !found { return C.int(0) } - - return C.int(len(desc.Fields)) + return C.int(len(desc.fields)) } //export wirego_result_get_field -func wirego_result_get_field(h C.int, idx C.int, wiregoFieldId *C.int, offset *C.int, length *C.int) { +func wirego_result_get_field(h C.int, idx C.int, parentIdx *C.int, wiregoFieldId *C.int, offset *C.int, length *C.int) { + *parentIdx = -1 *wiregoFieldId = -1 *offset = -1 *length = -1 @@ -436,12 +245,13 @@ func wirego_result_get_field(h C.int, idx C.int, wiregoFieldId *C.int, offset *C return } - if idx >= C.int(len(desc.Fields)) { + if idx >= C.int(len(desc.fields)) { return } - *wiregoFieldId = C.int(desc.Fields[idx].WiregoFieldId) - *offset = C.int(desc.Fields[idx].Offset) - *length = C.int(desc.Fields[idx].Length) + *parentIdx = C.int(desc.fields[idx].parentIdx) + *wiregoFieldId = C.int(desc.fields[idx].wiregoFieldId) + *offset = C.int(desc.fields[idx].offset) + *length = C.int(desc.fields[idx].length) } //export wirego_result_release diff --git a/wirego_plugin/CMakeLists.txt b/wirego_plugin/CMakeLists.txt index 745905d..0ea2bad 100644 --- a/wirego_plugin/CMakeLists.txt +++ b/wirego_plugin/CMakeLists.txt @@ -14,6 +14,9 @@ set_module_info(wirego 0 1 0 0) set(DISSECTOR_SRC plugin-loader.c + helpers.c + preferences.c + dissect.c packet-wirego.c ) diff --git a/wirego_plugin/dissect.c b/wirego_plugin/dissect.c new file mode 100644 index 0000000..595525b --- /dev/null +++ b/wirego_plugin/dissect.c @@ -0,0 +1,158 @@ + +#include "dissect.h" +#include "helpers.h" +#include "plugin-loader.h" +#include "packet-wirego.h" +void tree_add_item(proto_item *parent_node, int dissectHandle, tvbuff_t *tvb, int idx, int count); + +int dissect_wirego(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) +{ + int pdu_len; + char * golang_buff = NULL; + char src[255]; + char dst[255]; + char * full_layer = NULL; + int dissectHandle = -1; + + /* + In a more classic Wireshark plugin we would use all the tvb_* accessors here + Since processing of the packet is performed in the golang plugin (that's actually the very purpose + of this insanity), and since I won't write bindings for the complete wireshark API, we need to push + the packet buffer to the plugin. + + We have two options here: + + - use tvb_get_ptr + - use tvb_memcpy + + The get_ptr would be the more obvious, but it is marked as very dangerous. Since this buffer would be pushed + to a golang plugin it could eventually be even more dangerous. + Thus we're using tvb_memcpy, which will provide us a dedicated buffer to play with. + That's not optimal at all, but we'll start with this. + */ + + + if (!tvb || !pinfo) + return -1; + + pdu_len = tvb_reported_length(tvb); + if (pdu_len <= 0) + return 0; + + src[0] = 0x00; + dst[0] = 0x00; + extract_adresses_from_packet_info(pinfo, src, dst); + + + full_layer = compile_network_stack(pinfo); + + //Pass everything to the golang plugin + golang_buff = (char*) malloc(pdu_len); + tvb_memcpy(tvb, golang_buff, 0, pdu_len); + dissectHandle = wirego_dissect_packet_cb(pinfo->num, src, dst, full_layer, golang_buff, pdu_len); + free(golang_buff); + golang_buff = NULL; + free(full_layer); + full_layer = NULL; + + if (dissectHandle == -1) { + //col_set_str will keep a pointer to the given value + //while col_add_str will duplicate + col_set_str(pinfo->cinfo, COL_PROTOCOL, "Wirego plugin failed."); + col_set_str(pinfo->cinfo, COL_INFO, "Wirego plugin failed."); + return -1; + } + + //Analyse plugin results + + //Flag protocol name + col_set_str(pinfo->cinfo, COL_PROTOCOL, wirego_result_get_protocol_cb(dissectHandle)); + + //Fill "info" column + col_set_str(pinfo->cinfo, COL_INFO, wirego_result_get_info_cb(dissectHandle)); + + //During the first pass, tree can eventually be NULL + //Wireshark does not ask the plugin to fill detailed structures + if (!tree) + goto DONE; + + //How many custom fields did the plugin return? + int result_fields_count = wirego_result_get_fields_count_cb(dissectHandle); + + if (result_fields_count == 0) + goto DONE; + + //Add a subtree on this packet + proto_item *ti = proto_tree_add_item(tree, proto_wirego, tvb, 0, -1, ENC_BIG_ENDIAN); + if (!ti) { + goto DONE; + } + proto_tree *wirego_tree = proto_item_add_subtree(ti, ett_wirego); + if (!wirego_tree) { + goto DONE; + } + + //Process all custom fields + +for (int i = 0; i < result_fields_count; i++) { + int wireshark_field_id = -1; + int parent_idx; + int wirego_field_id; + int offset; + int length; + + + //Ask plugin for result + wirego_result_get_field_cb(dissectHandle, i, &parent_idx, &wirego_field_id, &offset, &length); + +if (parent_idx == -1) + tree_add_item(wirego_tree, dissectHandle, tvb, i,result_fields_count); +} + +DONE: + wirego_result_release_cb(dissectHandle); + return tvb_captured_length(tvb); +} + +void tree_add_item(proto_item *parent_node, int dissectHandle, tvbuff_t *tvb, int idx, int count) { + int wireshark_field_id = -1; + int parent_idx; + int wirego_field_id; + int offset; + int length; + + + //Ask plugin for result + wirego_result_get_field_cb(dissectHandle, idx, &parent_idx, &wirego_field_id, &offset, &length); + + //Convert plugin field id to wireshark id + wireshark_field_id = get_wireshark_field_id_from_wirego_field_id(wirego_field_id); + + //Add tree entry + if (wireshark_field_id == -1) { + ws_warning("Wirego plugin returned unknown field id %d, cannot map to Wireshark field id", wirego_field_id); + return; + } + + proto_item *sub = proto_tree_add_item(parent_node, wireshark_field_id, tvb, offset, length, ENC_BIG_ENDIAN); + + + //look for childs + proto_tree *subsub = NULL; + + for (int i = 0; i < count; i++) { + wirego_result_get_field_cb(dissectHandle, i, &parent_idx, &wirego_field_id, &offset, &length); + if (parent_idx == idx) { + if (!subsub) + subsub = proto_item_add_subtree(sub, ett_wirego); + tree_add_item(sub, dissectHandle, tvb, i, count); + } + } + +} + + + + + + diff --git a/wirego_plugin/dissect.h b/wirego_plugin/dissect.h new file mode 100644 index 0000000..0f99d08 --- /dev/null +++ b/wirego_plugin/dissect.h @@ -0,0 +1,10 @@ +#ifndef _DISSECT_H_ +#define _DISSECT_H_ + +#include + + +int dissect_wirego(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_); + + +#endif \ No newline at end of file diff --git a/wirego_plugin/eletemet.c b/wirego_plugin/eletemet.c new file mode 100644 index 0000000..e8928e8 --- /dev/null +++ b/wirego_plugin/eletemet.c @@ -0,0 +1,33 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "plugin-loader.h" +#include "packet-wirego.h" + +void proto_register_wirego(void) { + + // Register protocol + proto_wirego = proto_register_protocol("Wirego Example", "wg example", "wgexample"); + + // Register custom fields + proto_register_field_array(proto_wirego, fields_array, fields_count); + proto_register_subtree_array(ett, array_length(ett)); +} + + +void proto_reg_handoff_wirego(void) { + static dissector_handle_t wirego_handle; + + wirego_handle = create_dissector_handle(dissect_wirego, proto_wirego); + dissector_add_uint("tcp.port", 25, wirego_handle); + +} + + diff --git a/wirego_plugin/helpers.c b/wirego_plugin/helpers.c new file mode 100644 index 0000000..a006046 --- /dev/null +++ b/wirego_plugin/helpers.c @@ -0,0 +1,140 @@ +#include "helpers.h" +#include +#include "packet-wirego.h" + +//Extract src and dst addresses from packet_info structure. It can be an IPv4, IPv6 or Ethernet address +void extract_adresses_from_packet_info(packet_info *pinfo, char *src, char *dst) { + //Very suboptimal, FIXME. + + src[0] = 0x00; + dst[0] = 0x00; + + if (!pinfo || !pinfo->net_src.data || !pinfo->net_dst.data) + return; + + switch (pinfo->net_src.type) { + case AT_IPv4: + ip_to_str_buf((const guint8*)pinfo->net_src.data, src, 255); + break; + case AT_IPv6: + ip6_to_str_buf((const ws_in6_addr *)pinfo->net_src.data, src, 255); + break; + case AT_ETHER: + sprintf(src, "%02x:%02x:%02x:%02x:%02x:%02x", + ((const char*)pinfo->net_src.data)[0]&0xFF, + ((const char*)pinfo->net_src.data)[1]&0xFF, + ((const char*)pinfo->net_src.data)[2]&0xFF, + ((const char*)pinfo->net_src.data)[3]&0xFF, + ((const char*)pinfo->net_src.data)[4]&0xFF, + ((const char*)pinfo->net_src.data)[5]&0xFF); + break; + } + switch (pinfo->net_dst.type) { + case AT_IPv4: + ip_to_str_buf((const guint8*)pinfo->net_dst.data, dst, 255); + break; + case AT_IPv6: + ip6_to_str_buf((const ws_in6_addr *)pinfo->net_dst.data, dst, 255); + break; + case AT_ETHER: + sprintf(dst, "%02x:%02x:%02x:%02x:%02x:%02x", + ((const char*)pinfo->net_dst.data)[0]&0xFF, + ((const char*)pinfo->net_dst.data)[1]&0xFF, + ((const char*)pinfo->net_dst.data)[2]&0xFF, + ((const char*)pinfo->net_dst.data)[3]&0xFF, + ((const char*)pinfo->net_dst.data)[4]&0xFF, + ((const char*)pinfo->net_dst.data)[5]&0xFF); + break; + } +} + +//Try to nuild a relevant network stack string from a packet_info. +//Since this structure is quite complex, we don't really want to try mapping this +//to a full Golang structure (through the C/Go bindings) +char * compile_network_stack(packet_info *pinfo) { + unsigned int full_layer_size = 512; + char * full_layer = calloc(full_layer_size, sizeof(char)); + wmem_list_frame_t *protos; + int proto_id; + const char *name; + + if (!pinfo || !pinfo->layers) + return full_layer; + + protos = wmem_list_head(pinfo->layers); + while (protos != NULL) + { + proto_id = GPOINTER_TO_INT(wmem_list_frame_data(protos)); + name = proto_get_protocol_filter_name(proto_id); + + if (strlen(full_layer) + 1 + strlen(name) + 1 >= full_layer_size) { + full_layer_size += 512 + 1 + strlen(name); + full_layer = realloc(full_layer, full_layer_size); + } + strcat(full_layer, name); + strcat(full_layer, "."); + protos = wmem_list_frame_next(protos); + } + //Strip trailing '.' + if (strlen(full_layer)) + full_layer[strlen(full_layer) - 1] = 0x00; + + return full_layer; +} + + +enum ftenum field_value_type_to_ws(int vtype) { + switch (vtype) { + case 0x01: + return FT_NONE; + break; + case 0x02: + return FT_BOOLEAN; + break; + case 0x03: + return FT_UINT8; + break; + case 0x04: + return FT_INT8; + break; + case 0x05: + return FT_UINT16; + break; + case 0x06: + return FT_INT16; + break; + case 0x07: + return FT_UINT32; + break; + case 0x08: + return FT_INT32; + break; + case 0x09: + return FT_STRINGZ; + break; + case 0x10: + return FT_STRING; + break; + default: + return FT_NONE; + }; + return FT_NONE; +} + +field_display_e field_display_type_to_ws(int dtype) { + switch (dtype) { + case 0x01: + return BASE_NONE; + break; + case 0x02: + return BASE_DEC; + break; + case 0x03: + return BASE_HEX; + break; + default: + return BASE_NONE; + break; + } + return BASE_NONE; +} diff --git a/wirego_plugin/helpers.h b/wirego_plugin/helpers.h new file mode 100644 index 0000000..b08cb33 --- /dev/null +++ b/wirego_plugin/helpers.h @@ -0,0 +1,11 @@ +#ifndef _HELPERS_H_ +#define _HELPERS_H_ + +#include + +void extract_adresses_from_packet_info(packet_info *pinfo, char *src, char *dst); +char * compile_network_stack(packet_info *pinfo); +field_display_e field_display_type_to_ws(int dtype); +enum ftenum field_value_type_to_ws(int vtype); + +#endif diff --git a/wirego_plugin/packet-wirego.c b/wirego_plugin/packet-wirego.c index 6e26763..3fb030e 100644 --- a/wirego_plugin/packet-wirego.c +++ b/wirego_plugin/packet-wirego.c @@ -26,36 +26,34 @@ #include "plugin-loader.h" #include "packet-wirego.h" +#include "preferences.h" +#include "dissect.h" +#include "helpers.h" void proto_register_wirego(void); void proto_reg_handoff_wirego(void); -static int dissect_wirego(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree _U_, void *data _U_); -void register_preferences_menu(void); -char * get_plugin_path(void); -enum ftenum field_value_type_to_ws(int vtype); -field_display_e field_display_type_to_ws(int dtype); -int get_wireshark_field_id_from_wirego_field_id(int wirego_field_id); -char * compile_network_stack(packet_info *pinfo); -void pinfo_to_proto_stack(packet_info *pinfo, char *src, char *dst); +void extract_adresses_from_packet_info(packet_info *pinfo, char *src, char *dst); -static int proto_wirego = -1; //WireGo's subtree -static int ett_wirego = -1; - -//Plugin path -static const gchar* pref_wirego_config_filename = ""; +int ett_wirego = -1; +int fields_count = -1; +int proto_wirego = -1; +field_id_to_plugin_field_id_t * fields_mapping = NULL; -//Map our go plugin internal field identifiers to the ones provided by Wireshark -typedef struct { - int wirego_field_id; - int wireshark_field_id; -} field_id_to_plugin_field_id_t; +//Convert a field id, as provided by the Golang plugin to a Wireshark filed id, +//as returned by wireshark backend during declaration +int get_wireshark_field_id_from_wirego_field_id(int wirego_field_id) { + for (int idx = 0; idx < fields_count; idx++) { + if (fields_mapping[idx].wirego_field_id == wirego_field_id) { + return fields_mapping[idx].wireshark_field_id; + } + } + return -1; +} -int fields_count = -1; -field_id_to_plugin_field_id_t * fields_mapping = NULL; //Register protocol when plugin is loaded. void proto_register_wirego(void) { @@ -133,7 +131,7 @@ void proto_register_wirego(void) { //Don't release name and filter, since those are used by wireshark's internals //Register our custom fields proto_register_field_array(proto_wirego, hfx, fields_count); - +ws_warning("registered %d fields", fields_count); //Register the protocol subtree proto_register_subtree_array(ett, array_length(ett)); } @@ -157,7 +155,7 @@ static gboolean wirego_heuristic_check(tvbuff_t *tvb, packet_info *pinfo, proto_ src[0] = 0x00; dst[0] = 0x00; - pinfo_to_proto_stack(pinfo, src, dst); + extract_adresses_from_packet_info(pinfo, src, dst); full_layer = compile_network_stack(pinfo); @@ -228,310 +226,3 @@ void proto_reg_handoff_wirego(void) { } } - -static int -dissect_wirego(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) -{ - int pdu_len; - char * golang_buff = NULL; - char src[255]; - char dst[255]; - char * full_layer = NULL; - int dissectHandle = -1; - - /* - In a more classic Wireshark plugin we would use all the tvb_* accessors here - Since processing of the packet is performed in the golang plugin (that's actually the very purpose - of this insanity), and since I won't write bindings for the complete wireshark API, we need to push - the packet buffer to the plugin. - - We have two options here: - - - use tvb_get_ptr - - use tvb_memcpy - - The get_ptr would be the more obvious, but it is marked as very dangerous. Since this buffer would be pushed - to a golang plugin it could eventually be even more dangerous. - Thus we're using tvb_memcpy, which will provide us a dedicated buffer to play with. - That's not optimal at all, but we'll start with this. - */ - - - if (!tvb || !pinfo) - return -1; - - pdu_len = tvb_reported_length(tvb); - if (pdu_len <= 0) - return 0; - - src[0] = 0x00; - dst[0] = 0x00; - pinfo_to_proto_stack(pinfo, src, dst); - - - full_layer = compile_network_stack(pinfo); - - //Pass everything to the golang plugin - golang_buff = (char*) malloc(pdu_len); - tvb_memcpy(tvb, golang_buff, 0, pdu_len); - dissectHandle = wirego_dissect_packet_cb(pinfo->num, src, dst, full_layer, golang_buff, pdu_len); - free(golang_buff); - golang_buff = NULL; - free(full_layer); - full_layer = NULL; - - if (dissectHandle == -1) { - //col_set_str will keep a pointer to the given value - //while col_add_str will duplicate - col_set_str(pinfo->cinfo, COL_PROTOCOL, "Wirego plugin failed."); - col_set_str(pinfo->cinfo, COL_INFO, "Wirego plugin failed."); - return -1; - } - - //Analyse plugin results - - //Flag protocol name - col_set_str(pinfo->cinfo, COL_PROTOCOL, wirego_result_get_protocol_cb(dissectHandle)); - - //Fill "info" column - col_set_str(pinfo->cinfo, COL_INFO, wirego_result_get_info_cb(dissectHandle)); - - //During the first pass, tree can eventually be NULL - //Wireshark does not ask the plugin to fill detailed structures - if (tree) { - //How many custom fields did the plugin return? - int result_fields_count = wirego_result_get_fields_count_cb(dissectHandle); - if (result_fields_count != 0) { - - //Add a subtree on this packet - proto_item *ti = proto_tree_add_item(tree, proto_wirego, tvb, 0, -1, ENC_BIG_ENDIAN); - if (!ti) goto DONE; - proto_tree *wirego_tree = proto_item_add_subtree(ti, ett_wirego); - if (!wirego_tree) goto DONE; - - //Process all custom fields - for (int i = 0; i < result_fields_count; i++) { - int wireshark_field_id = -1; - int wirego_field_id; - int offset; - int length; - //Ask plugin for result - wirego_result_get_field_cb(dissectHandle, i, &wirego_field_id, &offset, &length); - //Convert plugin field id to wireshark id - wireshark_field_id = get_wireshark_field_id_from_wirego_field_id(wirego_field_id); - //Add tree entry - if (wireshark_field_id != -1) { - proto_tree_add_item(wirego_tree, wireshark_field_id, tvb, offset, length, ENC_BIG_ENDIAN); - } - } - } - } -DONE: - wirego_result_release_cb(dissectHandle); - return tvb_captured_length(tvb); -} - - - -char * get_plugin_path(void) { - char config_path[1024]; - static char plugin_path[1024]; - FILE * f; - memset(plugin_path, 0x00, 1024); - char * home = getenv("HOME"); - - if (!home) - return ""; - - snprintf(config_path, 1024, "%s/.wirego", home); - f = fopen(config_path, "r"); - if (!f) - return ""; - - unsigned long r = fread(plugin_path, 1, 1024, f); - fclose(f); - if (r && plugin_path[r-1] == 0x0a) - plugin_path[r-1] = 0x00; - return plugin_path; -} - -int save_plugin_path(const char * path) { - FILE * f; - char config_path[1024]; - char * home = getenv("HOME"); - snprintf(config_path, 1024, "%s/.wirego", home); - f = fopen(config_path, "w"); - if (!f) - return -1; - fwrite(path, 1, strlen(path), f); - fclose(f); - return 0; -} - -void preferences_apply_cb(void) { - if (strcmp(get_plugin_path(), pref_wirego_config_filename)) { - save_plugin_path(pref_wirego_config_filename); - ws_warning("Wirego: Updated plugin path to %s\n",pref_wirego_config_filename); - } -} - -// Define the Wirego preferences panel -void register_preferences_menu(void) { - module_t *wirego_module; - int proto_main_wirego = proto_register_protocol("Wirego", "Wirego", "wirego"); - wirego_module = prefs_register_protocol(proto_main_wirego, preferences_apply_cb); - - prefs_register_filename_preference(wirego_module, "pluginpath", - "Wirego plugin path", - "The fullpath to the wirego plugin, written in Go", - &pref_wirego_config_filename, FALSE); - - prefs_register_static_text_preference(wirego_module, "helper", - "You will need to restart Wireshark after changing the plugin path.", - ""); - -} - - -enum ftenum field_value_type_to_ws(int vtype) { - switch (vtype) { - case 0x01: - return FT_NONE; - break; - case 0x02: - return FT_BOOLEAN; - break; - case 0x03: - return FT_UINT8; - break; - case 0x04: - return FT_INT8; - break; - case 0x05: - return FT_UINT16; - break; - case 0x06: - return FT_INT16; - break; - case 0x07: - return FT_UINT32; - break; - case 0x08: - return FT_INT32; - break; - case 0x09: - return FT_STRINGZ; - break; - case 0x10: - return FT_STRING; - break; - default: - return FT_NONE; - }; - return FT_NONE; -} - -field_display_e field_display_type_to_ws(int dtype) { - switch (dtype) { - case 0x01: - return BASE_NONE; - break; - case 0x02: - return BASE_DEC; - break; - case 0x03: - return BASE_HEX; - break; - default: - return BASE_NONE; - break; - } - return BASE_NONE; -} - - -void pinfo_to_proto_stack(packet_info *pinfo, char *src, char *dst) { - //Very suboptimal, FIXME. - - src[0] = 0x00; - dst[0] = 0x00; - - if (!pinfo || !pinfo->net_src.data || !pinfo->net_dst.data) - return; - - switch (pinfo->net_src.type) { - case AT_IPv4: - ip_to_str_buf((const guint8*)pinfo->net_src.data, src, 255); - break; - case AT_IPv6: - ip6_to_str_buf((const ws_in6_addr *)pinfo->net_src.data, src, 255); - break; - case AT_ETHER: - sprintf(src, "%02x:%02x:%02x:%02x:%02x:%02x", - ((const char*)pinfo->net_src.data)[0]&0xFF, - ((const char*)pinfo->net_src.data)[1]&0xFF, - ((const char*)pinfo->net_src.data)[2]&0xFF, - ((const char*)pinfo->net_src.data)[3]&0xFF, - ((const char*)pinfo->net_src.data)[4]&0xFF, - ((const char*)pinfo->net_src.data)[5]&0xFF); - break; - } - switch (pinfo->net_dst.type) { - case AT_IPv4: - ip_to_str_buf((const guint8*)pinfo->net_dst.data, dst, 255); - break; - case AT_IPv6: - ip6_to_str_buf((const ws_in6_addr *)pinfo->net_dst.data, dst, 255); - break; - case AT_ETHER: - sprintf(dst, "%02x:%02x:%02x:%02x:%02x:%02x", - ((const char*)pinfo->net_dst.data)[0]&0xFF, - ((const char*)pinfo->net_dst.data)[1]&0xFF, - ((const char*)pinfo->net_dst.data)[2]&0xFF, - ((const char*)pinfo->net_dst.data)[3]&0xFF, - ((const char*)pinfo->net_dst.data)[4]&0xFF, - ((const char*)pinfo->net_dst.data)[5]&0xFF); - break; - } -} - -char * compile_network_stack(packet_info *pinfo) { - unsigned int full_layer_size = 512; - char * full_layer = calloc(full_layer_size, sizeof(char)); - wmem_list_frame_t *protos; - int proto_id; - const char *name; - - if (!pinfo || !pinfo->layers) - return full_layer; - - protos = wmem_list_head(pinfo->layers); - while (protos != NULL) - { - proto_id = GPOINTER_TO_INT(wmem_list_frame_data(protos)); - name = proto_get_protocol_filter_name(proto_id); - - if (strlen(full_layer) + 1 + strlen(name) + 1 >= full_layer_size) { - full_layer_size += 512 + 1 + strlen(name); - full_layer = realloc(full_layer, full_layer_size); - } - strcat(full_layer, name); - strcat(full_layer, "."); - protos = wmem_list_frame_next(protos); - } - //Strip trailing '.' - if (strlen(full_layer)) - full_layer[strlen(full_layer) - 1] = 0x00; - - return full_layer; -} - -int get_wireshark_field_id_from_wirego_field_id(int wirego_field_id) { - for (int idx = 0; idx < fields_count; idx++) { - if (fields_mapping[idx].wirego_field_id == wirego_field_id) { - return fields_mapping[idx].wireshark_field_id; - } - } - return -1; -} - diff --git a/wirego_plugin/packet-wirego.h b/wirego_plugin/packet-wirego.h index 468574d..4e53394 100644 --- a/wirego_plugin/packet-wirego.h +++ b/wirego_plugin/packet-wirego.h @@ -10,6 +10,20 @@ */ + +//Map our go plugin internal field identifiers to the ones provided by Wireshark +typedef struct { + int wirego_field_id; + int wireshark_field_id; +} field_id_to_plugin_field_id_t; + +int get_wireshark_field_id_from_wirego_field_id(int wirego_field_id); + +extern int ett_wirego; +extern int proto_wirego; + + + /* * Editor modelines - https://www.wireshark.org/tools/modelines.html * diff --git a/wirego_plugin/plugin-loader.c b/wirego_plugin/plugin-loader.c index 5fd0042..3bb2b5c 100644 --- a/wirego_plugin/plugin-loader.c +++ b/wirego_plugin/plugin-loader.c @@ -30,7 +30,7 @@ int (*wirego_dissect_packet_cb)(int, char *, char*, char*, char*, int) = NULL; char* (*wirego_result_get_protocol_cb)(int) = NULL; char* (*wirego_result_get_info_cb)(int) = NULL; int (*wirego_result_get_fields_count_cb)(int) = NULL; -void (*wirego_result_get_field_cb)(int, int, int*, int*, int*) = NULL; +void (*wirego_result_get_field_cb)(int, int, int*, int*, int*, int*) = NULL; void (*wirego_result_release_cb)(int); @@ -133,7 +133,7 @@ int wirego_load_plugin(char *plugin_path) { return wirego_load_failure_helper("Failed to find symbol wirego_result_get_fields_count"); } - wirego_result_get_field_cb = (void (*) (int, int, int*, int*, int*)) GetProcAddress(plugin_h, "wirego_result_get_field"); + wirego_result_get_field_cb = (void (*) (int, int, int*, int*, int*, int*)) GetProcAddress(plugin_h, "wirego_result_get_field"); if (wirego_result_get_field_cb == NULL) { return wirego_load_failure_helper("Failed to find symbol wirego_result_get_field"); } @@ -256,7 +256,7 @@ int wirego_load_plugin(char *plugin_path) { return wirego_load_failure_helper("Failed to find symbol wirego_result_get_fields_count"); } - wirego_result_get_field_cb = (void (*) (int, int, int*, int*, int*)) dlsym(plugin_h, "wirego_result_get_field"); + wirego_result_get_field_cb = (void (*) (int, int, int*, int*, int*, int*)) dlsym(plugin_h, "wirego_result_get_field"); if (wirego_result_get_field_cb == NULL) { return wirego_load_failure_helper("Failed to find symbol wirego_result_get_field"); } diff --git a/wirego_plugin/plugin-loader.h b/wirego_plugin/plugin-loader.h index 53aea1f..165a72e 100644 --- a/wirego_plugin/plugin-loader.h +++ b/wirego_plugin/plugin-loader.h @@ -19,7 +19,7 @@ extern int (*wirego_dissect_packet_cb)(int, char *, char *, char*, char*, int); extern char* (*wirego_result_get_protocol_cb)(int); extern char* (*wirego_result_get_info_cb)(int); extern int (*wirego_result_get_fields_count_cb)(int); -extern void (*wirego_result_get_field_cb)(int, int, int*, int*, int*); +extern void (*wirego_result_get_field_cb)(int, int, int*, int*, int*, int*); extern void (*wirego_result_release_cb)(int); diff --git a/wirego_plugin/preferences.c b/wirego_plugin/preferences.c new file mode 100644 index 0000000..dda606e --- /dev/null +++ b/wirego_plugin/preferences.c @@ -0,0 +1,72 @@ +#include "preferences.h" +#include +#include +#include +#include +#include + + +//Plugin path +static const gchar* pref_wirego_config_filename = ""; + + + + +char * get_plugin_path(void) { + char config_path[1024]; + static char plugin_path[1024]; + FILE * f; + memset(plugin_path, 0x00, 1024); + char * home = getenv("HOME"); + + if (!home) + return ""; + + snprintf(config_path, 1024, "%s/.wirego", home); + f = fopen(config_path, "r"); + if (!f) + return ""; + + unsigned long r = fread(plugin_path, 1, 1024, f); + fclose(f); + if (r && plugin_path[r-1] == 0x0a) + plugin_path[r-1] = 0x00; + return plugin_path; +} + +int save_plugin_path(const char * path) { + FILE * f; + char config_path[1024]; + char * home = getenv("HOME"); + snprintf(config_path, 1024, "%s/.wirego", home); + f = fopen(config_path, "w"); + if (!f) + return -1; + fwrite(path, 1, strlen(path), f); + fclose(f); + return 0; +} + +void preferences_apply_cb(void) { + if (strcmp(get_plugin_path(), pref_wirego_config_filename)) { + save_plugin_path(pref_wirego_config_filename); + ws_warning("Wirego: Updated plugin path to %s\n",pref_wirego_config_filename); + } +} + +// Define the Wirego preferences panel +void register_preferences_menu(void) { + module_t *wirego_module; + int proto_main_wirego = proto_register_protocol("Wirego", "Wirego", "wirego"); + wirego_module = prefs_register_protocol(proto_main_wirego, preferences_apply_cb); + + prefs_register_filename_preference(wirego_module, "pluginpath", + "Wirego plugin path", + "The fullpath to the wirego plugin, written in Go", + &pref_wirego_config_filename, FALSE); + + prefs_register_static_text_preference(wirego_module, "helper", + "You will need to restart Wireshark after changing the plugin path.", + ""); + +} \ No newline at end of file diff --git a/wirego_plugin/preferences.h b/wirego_plugin/preferences.h new file mode 100644 index 0000000..e8312ff --- /dev/null +++ b/wirego_plugin/preferences.h @@ -0,0 +1,10 @@ +#ifndef _PREFS_H_ +#define _PREFS_H_ + +char * get_plugin_path(void); +int save_plugin_path(const char * path); +void register_preferences_menu(void); +void preferences_apply_cb(void); + + +#endif \ No newline at end of file diff --git a/wirego_plugin/version.h b/wirego_plugin/version.h index 0dffff4..93158e8 100644 --- a/wirego_plugin/version.h +++ b/wirego_plugin/version.h @@ -3,6 +3,6 @@ // Note: must match the ones defined in ../wirego/version.go #define WIREGO_VERSION_MAJOR 1 -#define WIREGO_VERSION_MINOR 1 +#define WIREGO_VERSION_MINOR 2 #endif \ No newline at end of file