nexmon – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 1 | """ |
2 | Routines for reading PDML produced from TShark. |
||
3 | |||
4 | Copyright (c) 2003, 2013 by Gilbert Ramirez <gram@alumni.rice.edu> |
||
5 | |||
6 | This program is free software; you can redistribute it and/or |
||
7 | modify it under the terms of the GNU General Public License |
||
8 | as published by the Free Software Foundation; either version 2 |
||
9 | of the License, or (at your option) any later version. |
||
10 | |||
11 | This program is distributed in the hope that it will be useful, |
||
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
14 | GNU General Public License for more details. |
||
15 | |||
16 | You should have received a copy of the GNU General Public License |
||
17 | along with this program; if not, write to the Free Software |
||
18 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
||
19 | """ |
||
20 | |||
21 | import sys |
||
22 | import xml.sax |
||
23 | from xml.sax.saxutils import quoteattr |
||
24 | import cStringIO as StringIO |
||
25 | |||
26 | class CaptureFile: |
||
27 | pass |
||
28 | |||
29 | class FoundItException(Exception): |
||
30 | """Used internally for exiting a tree search""" |
||
31 | pass |
||
32 | |||
33 | class PacketList: |
||
34 | """Holds Packet objects, and has methods for finding |
||
35 | items within it.""" |
||
36 | |||
37 | def __init__(self, children=None): |
||
38 | if children == None: |
||
39 | self.children = [] |
||
40 | else: |
||
41 | self.children = children |
||
42 | |||
43 | def __getitem__(self, index): |
||
44 | """We act like a list.""" |
||
45 | return self.children[index] |
||
46 | |||
47 | def __len__(self): |
||
48 | return len(self.children) |
||
49 | |||
50 | def item_exists(self, name): |
||
51 | """Does an item with name 'name' exist in this |
||
52 | PacketList? Returns True or False.""" |
||
53 | for child in self.children: |
||
54 | if child.name == name: |
||
55 | return True |
||
56 | |||
57 | try: |
||
58 | for child in self.children: |
||
59 | child._item_exists(name) |
||
60 | |||
61 | except FoundItException: |
||
62 | return True |
||
63 | |||
64 | return False |
||
65 | |||
66 | def _item_exists(self, name): |
||
67 | for child in self.children: |
||
68 | if child.name == name: |
||
69 | raise FoundItException |
||
70 | child._item_exists(name) |
||
71 | |||
72 | |||
73 | def get_items(self, name, items=None): |
||
74 | """Return all items that match the name 'name'. |
||
75 | They are returned in order of a depth-first-search.""" |
||
76 | if items == None: |
||
77 | top_level = 1 |
||
78 | items = [] |
||
79 | else: |
||
80 | top_level = 0 |
||
81 | |||
82 | for child in self.children: |
||
83 | if child.name == name: |
||
84 | items.append(child) |
||
85 | child.get_items(name, items) |
||
86 | |||
87 | if top_level: |
||
88 | return PacketList(items) |
||
89 | |||
90 | def get_items_before(self, name, before_item, items=None): |
||
91 | """Return all items that match the name 'name' that |
||
92 | exist before the before_item. The before_item is an object. |
||
93 | They results are returned in order of a depth-first-search. |
||
94 | This function allows you to find fields from protocols that occur |
||
95 | before other protocols. For example, if you have an HTTP |
||
96 | protocol, you can find all tcp.dstport fields *before* that HTTP |
||
97 | protocol. This helps analyze in the presence of tunneled protocols.""" |
||
98 | if items == None: |
||
99 | top_level = 1 |
||
100 | items = [] |
||
101 | else: |
||
102 | top_level = 0 |
||
103 | |||
104 | for child in self.children: |
||
105 | if top_level == 1 and child == before_item: |
||
106 | break |
||
107 | if child.name == name: |
||
108 | items.append(child) |
||
109 | # Call get_items because the 'before_item' applies |
||
110 | # only to the top level search. |
||
111 | child.get_items(name, items) |
||
112 | |||
113 | if top_level: |
||
114 | return PacketList(items) |
||
115 | |||
116 | |||
117 | class ProtoTreeItem(PacketList): |
||
118 | def __init__(self, xmlattrs): |
||
119 | PacketList.__init__(self) |
||
120 | |||
121 | self.name = xmlattrs.get("name", "") |
||
122 | self.showname = xmlattrs.get("showname", "") |
||
123 | self.pos = xmlattrs.get("pos", "") |
||
124 | self.size = xmlattrs.get("size", "") |
||
125 | self.value = xmlattrs.get("value", "") |
||
126 | self.show = xmlattrs.get("show", "") |
||
127 | self.hide = xmlattrs.get("hide", "") |
||
128 | |||
129 | def add_child(self, child): |
||
130 | self.children.append(child) |
||
131 | |||
132 | def get_name(self): |
||
133 | return self.name |
||
134 | |||
135 | def get_showname(self): |
||
136 | return self.showname |
||
137 | |||
138 | def get_pos(self): |
||
139 | return self.pos |
||
140 | |||
141 | def get_size(self): |
||
142 | return self.size |
||
143 | |||
144 | def get_value(self): |
||
145 | return self.value |
||
146 | |||
147 | def get_show(self): |
||
148 | return self.show |
||
149 | |||
150 | def get_hide(self): |
||
151 | return self.hide |
||
152 | |||
153 | def dump(self, fh=sys.stdout): |
||
154 | if self.name: |
||
155 | print >> fh, " name=%s" % (quoteattr(self.name),), |
||
156 | |||
157 | if self.showname: |
||
158 | print >> fh, "showname=%s" % (quoteattr(self.showname),), |
||
159 | |||
160 | if self.pos: |
||
161 | print >> fh, "pos=%s" % (quoteattr(self.pos),), |
||
162 | |||
163 | if self.size: |
||
164 | print >> fh, "size=%s" % (quoteattr(self.size),), |
||
165 | |||
166 | if self.value: |
||
167 | print >> fh, "value=%s" % (quoteattr(self.value),), |
||
168 | |||
169 | if self.show: |
||
170 | print >> fh, "show=%s" % (quoteattr(self.show),), |
||
171 | |||
172 | if self.hide: |
||
173 | print >> fh, "hide=%s" % (quoteattr(self.hide),), |
||
174 | |||
175 | class Packet(ProtoTreeItem, PacketList): |
||
176 | def dump(self, fh=sys.stdout, indent=0): |
||
177 | print >> fh, " " * indent, "<packet>" |
||
178 | indent += 1 |
||
179 | for child in self.children: |
||
180 | child.dump(fh, indent) |
||
181 | print >> fh, " " * indent, "</packet>" |
||
182 | |||
183 | |||
184 | class Protocol(ProtoTreeItem): |
||
185 | |||
186 | def dump(self, fh=sys.stdout, indent=0): |
||
187 | print >> fh, "%s<proto " % (" " * indent,), |
||
188 | |||
189 | ProtoTreeItem.dump(self, fh) |
||
190 | |||
191 | print >> fh, '>' |
||
192 | |||
193 | indent += 1 |
||
194 | for child in self.children: |
||
195 | child.dump(fh, indent) |
||
196 | print >> fh, " " * indent, "</proto>" |
||
197 | |||
198 | |||
199 | class Field(ProtoTreeItem): |
||
200 | |||
201 | def dump(self, fh=sys.stdout, indent=0): |
||
202 | print >> fh, "%s<field " % (" " * indent,), |
||
203 | |||
204 | ProtoTreeItem.dump(self, fh) |
||
205 | |||
206 | if self.children: |
||
207 | print >> fh, ">" |
||
208 | indent += 1 |
||
209 | for child in self.children: |
||
210 | child.dump(fh, indent) |
||
211 | print >> fh, " " * indent, "</field>" |
||
212 | |||
213 | else: |
||
214 | print >> fh, "/>" |
||
215 | |||
216 | |||
217 | class ParseXML(xml.sax.handler.ContentHandler): |
||
218 | |||
219 | ELEMENT_FILE = "pdml" |
||
220 | ELEMENT_FRAME = "packet" |
||
221 | ELEMENT_PROTOCOL = "proto" |
||
222 | ELEMENT_FIELD = "field" |
||
223 | |||
224 | def __init__(self, cb): |
||
225 | self.cb = cb |
||
226 | self.chars = "" |
||
227 | self.element_stack = [] |
||
228 | |||
229 | def startElement(self, name, xmlattrs): |
||
230 | self.chars = "" |
||
231 | |||
232 | if name == self.ELEMENT_FILE: |
||
233 | # Eventually, we should check version number of pdml here |
||
234 | elem = CaptureFile() |
||
235 | |||
236 | elif name == self.ELEMENT_FRAME: |
||
237 | elem = Packet(xmlattrs) |
||
238 | |||
239 | elif name == self.ELEMENT_PROTOCOL: |
||
240 | elem = Protocol(xmlattrs) |
||
241 | |||
242 | elif name == self.ELEMENT_FIELD: |
||
243 | elem = Field(xmlattrs) |
||
244 | |||
245 | else: |
||
246 | sys.exit("Unknown element: %s" % (name,)) |
||
247 | |||
248 | self.element_stack.append(elem) |
||
249 | |||
250 | |||
251 | def endElement(self, name): |
||
252 | elem = self.element_stack.pop() |
||
253 | |||
254 | # if isinstance(elem, Field): |
||
255 | # if elem.get_name() == "frame.number": |
||
256 | # print >> sys.stderr, "Packet:", elem.get_show() |
||
257 | |||
258 | # Add element as child to previous element as long |
||
259 | # as there is more than 1 element in the stack. Only |
||
260 | # one element in the stack means that the the element in |
||
261 | # the stack is the single CaptureFile element, and we don't |
||
262 | # want to add this element to that, as we only want one |
||
263 | # Packet element in memory at a time. |
||
264 | if len(self.element_stack) > 1: |
||
265 | parent_elem = self.element_stack[-1] |
||
266 | parent_elem.add_child(elem) |
||
267 | |||
268 | self.chars = "" |
||
269 | |||
270 | # If we just finished a Packet element, hand it to the |
||
271 | # user's callback. |
||
272 | if isinstance(elem, Packet): |
||
273 | self.cb(elem) |
||
274 | |||
275 | def characters(self, chars): |
||
276 | self.chars = self.chars + chars |
||
277 | |||
278 | |||
279 | def _create_parser(cb): |
||
280 | """Internal function for setting up the SAX parser.""" |
||
281 | |||
282 | # Create a parser |
||
283 | parser = xml.sax.make_parser() |
||
284 | |||
285 | # Create the handler |
||
286 | handler = ParseXML(cb) |
||
287 | |||
288 | # Tell the parser to use our handler |
||
289 | parser.setContentHandler(handler) |
||
290 | |||
291 | # Don't fetch the DTD, in case it is listed |
||
292 | parser.setFeature(xml.sax.handler.feature_external_ges, False) |
||
293 | |||
294 | return parser |
||
295 | |||
296 | def parse_fh(fh, cb): |
||
297 | """Parse a PDML file, given filehandle, and call the callback function (cb), |
||
298 | once for each Packet object.""" |
||
299 | |||
300 | parser = _create_parser(cb) |
||
301 | |||
302 | # Parse the file |
||
303 | parser.parse(fh) |
||
304 | |||
305 | # Close the parser ; this is erroring out, but I'm not sure why. |
||
306 | #parser.close() |
||
307 | |||
308 | def parse_string(text, cb): |
||
309 | """Parse the PDML contained in a string.""" |
||
310 | stream = StringIO.StringIO(text) |
||
311 | parse_fh(stream, cb) |
||
312 | |||
313 | def _test(): |
||
314 | import sys |
||
315 | |||
316 | def test_cb(obj): |
||
317 | pass |
||
318 | |||
319 | filename = sys.argv[1] |
||
320 | fh = open(filename, "r") |
||
321 | parse_fh(fh, test_cb) |
||
322 | |||
323 | if __name__ == '__main__': |
||
324 | _test() |