-
Notifications
You must be signed in to change notification settings - Fork 413
/
Copy pathsearchpktstruct.go
277 lines (226 loc) · 8.54 KB
/
searchpktstruct.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
// Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source
// code is governed by the MIT license that can be found in the LICENSE
// file.
// Package ui contains user-interface functions and helpers for termshark.
package ui
import (
"fmt"
"github.com/gcla/gowid"
"github.com/gcla/gowid/widgets/table"
"github.com/gcla/gowid/widgets/tree"
"github.com/gcla/termshark/v2/pkg/pdmltree"
"github.com/gcla/termshark/v2/widgets/search"
)
//======================================================================
// StructResult represents a match for a search within the packet structure. The
// result is a specific location in the packet struct model. The UI will be updated
// to show this match, expanded, when a search succeeds.
type StructResult struct {
PacketNum int
TreePos tree.IPos
Model *pdmltree.Model
}
func (s StructResult) PacketNumber() int {
return s.PacketNum
}
//======================================================================
// Search in the packet struct view:
//
// <proto name="ip" showname="Internet Protocol Version 4, Src: 10.215.173.1, Dst: 64.13.139.230" size="20" pos="0">
// <field name="ip.version" showname="0100 .... = Version: 4" size="1" pos="0" show="4" value="45"/>
// <field name="ip.hdr_len" showname=".... 0101 = Header Length: 20 bytes (5)" size="1" pos="0" show="20" value="45"/>
// <field name="ip.dsfield" showname="Differentiated Services Field: 0x00 (DSCP: CS0, ECN: Not-ECT)" size="1" pos="1" show="0x00000000" value="00">
// <field name="ip.dsfield.dscp" showname="0000 00.. = Differentiated Services Codepoint: Default (0)" size="1" pos="1" show="0" value="0" unmaskedvalue="00"/>
// <field name="ip.dsfield.ecn" showname=".... ..00 = Explicit Congestion Notification: Not ECN-Capable Transport (0)" size="1" pos="1" show="0" value="0" unmaskedvalue="00"/>
// </field>
//
type StructSearchCallbacks struct {
*commonSearchCallbacks
*SearchStopper
term search.INeedle
samePacketPos tree.IPos
samePacket bool // set to false if we have advanced beyond the current packet, so don't need to check list pos
search chan search.IntermediateResult
}
var _ tree.ISearchPred = (*StructSearchCallbacks)(nil)
var _ search.IRequestStop = (*StructSearchCallbacks)(nil)
var _ search.ICallbacks = (*StructSearchCallbacks)(nil)
func (w *StructSearchCallbacks) Reset(app gowid.IApp) {
w.SearchStopper.Requested = false
w.ticks = 0
}
func (w *StructSearchCallbacks) StartingPosition() (interface{}, error) {
p, err := packetNumberFromCurrentTableRow()
if err != nil {
return StructResult{}, err
}
return StructResult{
PacketNum: p.Pos,
}, nil
}
// own goroutine
// startPacketNumber >= 1
func (w *StructSearchCallbacks) SearchPacketsFrom(ifrom interface{}, istart interface{}, term search.INeedle, app gowid.IApp) {
start := istart.(StructResult)
from := ifrom.(StructResult)
res := search.Result{}
searchRes := StructResult{}
// Same position, same packet - so set flags to ensure the depth first search skips until we're
// past the current position
if from.PacketNum == start.PacketNum &&
((from.TreePos == nil && start.TreePos == nil) ||
(from.TreePos != nil && start.TreePos != nil && from.TreePos.Equal(start.TreePos))) {
w.samePacket = true
w.samePacketPos = curStructPosition
}
// True if we have packets in the current batch to search (and we aren't blocked waiting for them to load)
curPacketNumber := from.PacketNum
var resumeAt *StructResult
defer func() {
if resumeAt != nil {
w.search <- search.IntermediateResult{
Res: res,
ResumeAt: *resumeAt,
}
} else {
w.search <- search.IntermediateResult{
Res: res,
}
}
}()
searchCount := 0
Loop:
for {
w.DoIfStopped(func() {
res.Interrupted = true
})
if res.Interrupted {
break Loop
}
Loader.PsmlLoader.Lock()
// curPacketNumber is the packet number from the pdml <packet>24</packet>. Remember there might
// be a display filter in place.
packetIndex, ok := Loader.PacketNumberMap[curPacketNumber]
if !ok {
// 1-based - packet number e.g. <packet>24</packet>
resumeAt = &StructResult{
PacketNum: curPacketNumber,
}
Loader.PsmlLoader.Unlock()
break
}
if packetIndex >= len(Loader.PsmlData()) {
panic(nil)
}
Loader.PsmlLoader.Unlock()
//======================================================================
// Returns a new object. Already takes the loader lock
model := getCurrentStructModel(packetIndex)
if model == nil {
// This means the PDML doesn't exist in the cache. We need to request it and wait
resumeAt = &StructResult{
PacketNum: curPacketNumber,
}
break Loop
}
expModel := (*pdmltree.ExpandedModel)(model)
w.term = term
fpos := tree.DepthFirstSearch(expModel, w)
if fpos != nil {
searchRes.PacketNum = curPacketNumber
searchRes.TreePos = fpos
searchRes.Model = model
res.Position = searchRes
res.Success = true
// Terminate the search
break Loop
}
//======================================================================
w.samePacket = false
w.samePacketPos = nil
searchCount += 1
// Can this be more sophisticated?
Loader.PsmlLoader.Lock()
// 32, 44, 45, 134, 209,...
curPacketNumber, ok = Loader.PacketNumberOrder[curPacketNumber]
if !ok {
// PacketNumberOrder is set up by the PSML loader, so if there is no next
// value, it means we're at the end of the packets and we should loop back.
curPacketNumber = Loader.PacketNumberOrder[0]
}
Loader.PsmlLoader.Unlock()
// Go 1 past because if we loop round, we should search the original packet again
// in case there is a hit earlier in its structure
if searchCount > len(Loader.PacketNumberMap) {
break Loop
}
}
}
func (s *StructSearchCallbacks) SearchPacketsResult(res search.Result, app gowid.IApp) {
app.Run(gowid.RunFunction(func(app gowid.IApp) {
// Do this because we might be on the same listview packet, so the change callback
// won't run and adjust the lower view
//
// UPDATE - this assumes the original start position in the table is the same as
// the one now.
ClearProgressWidgetFor(app, SearchOwns)
if res.Interrupted {
return
}
if !res.Success {
OpenError("Not found.", app)
return
}
tableCol := 0
structRes := res.Position.(StructResult)
curTablePos, err := packetListView.FocusXY()
if err == nil {
tableCol = curTablePos.Column
}
tableRow, err := tableRowFromPacketNumber(structRes.PacketNum)
if err != nil {
OpenError(fmt.Sprintf("Could not move to packet %d\n\n%v", structRes.PacketNum, err), app)
return
}
packetListView.SetFocusXY(app, table.Coords{Column: tableCol, Row: tableRow})
// Don't continue to jump to the end
AutoScroll = false
//========================================
curSearchPosition = structRes.TreePos.(*tree.TreePos)
subIModel := structRes.TreePos.GetSubStructure((*pdmltree.ExpandedModel)(structRes.Model))
expSubModel := subIModel.(*pdmltree.ExpandedModel)
subModel := (*pdmltree.Model)(expSubModel)
structRes.Model.MakeParentLinks(&curExpandedStructNodes)
subModel.SetCollapsed(app, false)
expRootModel := (*pdmltree.ExpandedModel)(structRes.Model)
treeAtCurPos := curSearchPosition.GetSubStructure(expRootModel)
// Save [/, tcp, tcp.srcport] - so we can apply if user moves in packet list
curPdmlPosition = (*pdmltree.Model)(treeAtCurPos.(*pdmltree.ExpandedModel)).PathToRoot()
//========================================
// Callback might not run if focus position in table is the same e.g. if we find a match
// on the same row that we started. So in that case, to expand the lower widgets, call
// setLowerWidgets explicitly - don't rely on the focus-changed callback. And I can't
// do a shortcut and call this if start == current because the starting position for the
// search may not be the same as the list-view row on display - maybe the search has
// resumed not that some extra PDML data has been loaded
setLowerWidgets(app)
// It looks better than having the found packet be at the top of the view
packetListView.GoToMiddle(app)
curPacketStructWidget.GoToMiddle(app)
curStructWidgetState = curPacketStructWidget.State()
}))
}
// CheckNode is provided to implement ISearchPred for the gowid tree's depth first search.
func (w *StructSearchCallbacks) CheckNode(tr tree.IModel, pos tree.IPos) bool {
if w.samePacket {
if w.samePacketPos != nil && !pos.GreaterThan(w.samePacketPos) {
return false
}
}
return w.term.Search(tr.Leaf()) != -1
}
//======================================================================
// Local Variables:
// mode: Go
// fill-column: 110
// End: