Add ability to pull packet from add_packet_to_packet_list() frame.
[metze/wireshark/wip.git] / tools / pkt-from-core.py
1 #!/usr/bin/env python
2 """
3 Retrieve a packet from a ethereal/tethereal core file
4 and save it in a packet-capture file.
5 """
6
7 import getopt
8 import os
9 import re
10 import sys
11 import tempfile
12         
13 exec_file = None
14 core_file = None
15 output_file = None
16
17 verbose = 0
18
19 class BackTrace:
20         re_frame = re.compile(r"^#(?P<num>\d+) ")
21         re_func1 = re.compile(r"^#\d+\s+(?P<func>\w+) \(")
22         re_func2 = re.compile(r"^#\d+\s+0x[A-Fa-f\d]+ in (?P<func>\w+) \(")
23
24         def __init__(self, lines):
25
26                 # In order; each item is the function name.
27                 self.frames = []
28                 found_non_bt_frame = 0
29                 frame_will_be = 0
30
31                 for line in lines:
32                         m = self.re_frame.search(line)
33                         if m:
34                                 # Skip the first frame that gdb shows,
35                                 # which is not part of the backtrace.
36                                 if not found_non_bt_frame:
37                                         found_non_bt_frame = 1
38                                         continue
39
40                                 # Get the frame number and make sure it's
41                                 # what we expect it should be.
42                                 frame_num = int(m.group("num"))
43                                 if frame_num != frame_will_be:
44                                         sys.exit("Found frame %d instead of %d" % \
45                                                 (frame_num, frame_will_be))
46
47                                 # Find the function name. XXX - need to handle '???'
48                                 n = self.re_func1.search(line)
49                                 if not n:
50                                         n = self.re_func2.search(line)
51
52                                 if n:
53                                         func = n.group("func")
54                                 else:
55                                         sys.exit("Function name not found in %s" % (line,))
56
57                                 # Save the info
58                                 self.frames.append(func)
59                                 frame_will_be += 1
60
61         def Frames(self):
62                 return self.frames
63                         
64
65         def HasFunction(self, func):
66                 return func in self.frames
67
68         def Frame(self, func):
69                 return self.frames.index(func)
70
71
72 # Some values from wiretap; wiretap should be a shared
73 # libray and a Python module should be created for it so
74 # this program could just write a libpcap file directly.
75 WTAP_ENCAP_PER_PACKET                 = -1
76 WTAP_ENCAP_UNKNOWN                    = 0
77 WTAP_ENCAP_ETHERNET                   = 1
78 WTAP_ENCAP_TOKEN_RING                 = 2
79 WTAP_ENCAP_SLIP                       = 3
80 WTAP_ENCAP_PPP                        = 4
81 WTAP_ENCAP_FDDI                       = 5
82 WTAP_ENCAP_FDDI_BITSWAPPED            = 6
83 WTAP_ENCAP_RAW_IP                     = 7
84 WTAP_ENCAP_ARCNET                     = 8
85 WTAP_ENCAP_ATM_RFC1483                = 9
86 WTAP_ENCAP_LINUX_ATM_CLIP             = 10
87 WTAP_ENCAP_LAPB                       = 11
88 WTAP_ENCAP_ATM_SNIFFER                = 12
89 WTAP_ENCAP_NULL                       = 13
90 WTAP_ENCAP_ASCEND                     = 14
91 WTAP_ENCAP_LAPD                       = 15
92 WTAP_ENCAP_V120                       = 16
93 WTAP_ENCAP_PPP_WITH_PHDR              = 17
94 WTAP_ENCAP_IEEE_802_11                = 18
95 WTAP_ENCAP_SLL                        = 19
96 WTAP_ENCAP_FRELAY                     = 20
97 WTAP_ENCAP_CHDLC                      = 21
98 WTAP_ENCAP_CISCO_IOS                  = 22
99 WTAP_ENCAP_LOCALTALK                  = 23
100 WTAP_ENCAP_PRISM_HEADER               = 24
101 WTAP_ENCAP_PFLOG                      = 25
102 WTAP_ENCAP_AIROPEEK                   = 26
103 WTAP_ENCAP_HHDLC                      = 27
104 # last WTAP_ENCAP_ value + 1
105 WTAP_NUM_ENCAP_TYPES                  = 28
106
107 wtap_to_pcap_map = {
108         WTAP_ENCAP_NULL                 : 0,
109         WTAP_ENCAP_ETHERNET             : 1,
110         WTAP_ENCAP_TOKEN_RING           : 6,
111         WTAP_ENCAP_ARCNET               : 7,
112         WTAP_ENCAP_SLIP                 : 8,
113         WTAP_ENCAP_PPP                  : 9,
114         WTAP_ENCAP_FDDI_BITSWAPPED      : 10,
115         WTAP_ENCAP_FDDI                 : 10,
116         WTAP_ENCAP_ATM_RFC1483          : 11,
117         WTAP_ENCAP_RAW_IP               : 12,
118         WTAP_ENCAP_LINUX_ATM_CLIP       : 16, # or 18, or 19...
119         WTAP_ENCAP_CHDLC                : 104,
120         WTAP_ENCAP_IEEE_802_11          : 105,
121         WTAP_ENCAP_SLL                  : 113,
122         WTAP_ENCAP_LOCALTALK            : 114,
123         WTAP_ENCAP_PFLOG                : 117,
124         WTAP_ENCAP_CISCO_IOS            : 118,
125         WTAP_ENCAP_PRISM_HEADER         : 119,
126         WTAP_ENCAP_HHDLC                : 121,
127 }
128
129
130 wtap_name = {
131         WTAP_ENCAP_UNKNOWN                    : "Unknown",
132         WTAP_ENCAP_ETHERNET                   : "Ethernet",
133         WTAP_ENCAP_TOKEN_RING                 : "Token-Ring",
134         WTAP_ENCAP_SLIP                       : "SLIP",
135         WTAP_ENCAP_PPP                        : "PPP",
136         WTAP_ENCAP_FDDI                       : "FDDI",
137         WTAP_ENCAP_FDDI_BITSWAPPED            : "FDDI (Bitswapped)",
138         WTAP_ENCAP_RAW_IP                     : "Raw IP",
139         WTAP_ENCAP_ARCNET                     : "ARCNET",
140         WTAP_ENCAP_ATM_RFC1483                : "ATM RFC1483",
141         WTAP_ENCAP_LINUX_ATM_CLIP             : "Linux ATM CLIP",
142         WTAP_ENCAP_LAPB                       : "LAPB",
143         WTAP_ENCAP_ATM_SNIFFER                : "ATM Sniffer",
144         WTAP_ENCAP_NULL                       : "Null",
145         WTAP_ENCAP_ASCEND                     : "Ascend",
146         WTAP_ENCAP_LAPD                       : "LAPD",
147         WTAP_ENCAP_V120                       : "V.120",
148         WTAP_ENCAP_PPP_WITH_PHDR              : "PPP (with PHDR)",
149         WTAP_ENCAP_IEEE_802_11                : "IEEE 802.11",
150         WTAP_ENCAP_SLL                        : "SLL",
151         WTAP_ENCAP_FRELAY                     : "Frame Relay",
152         WTAP_ENCAP_CHDLC                      : "Cisco HDLC",
153         WTAP_ENCAP_CISCO_IOS                  : "Cisco IOS",
154         WTAP_ENCAP_LOCALTALK                  : "LocalTalk",
155         WTAP_ENCAP_PRISM_HEADER               : "Prism Header",
156         WTAP_ENCAP_PFLOG                      : "PFLog",
157         WTAP_ENCAP_AIROPEEK                   : "AiroPeek",
158         WTAP_ENCAP_HHDLC                      : "HHDLC",
159 }
160
161 def wtap_to_pcap(wtap):
162         if not wtap_to_pcap_map.has_key(wtap):
163                 sys.exit("Don't know how to convert wiretap encoding %d to libpcap." % \
164                         (wtap))
165
166         return wtap_to_pcap_map[wtap]
167
168
169 def run_gdb(*commands):
170         if len(commands) == 0:
171                 return []
172
173         # Create a temporary file
174         fname = tempfile.mktemp()
175         try:
176                 fh = open(fname, "w")
177         except IOError, err:
178                 sys.exit("Cannot open %s for writing: %s" % (fname, err))
179
180         # Put the commands in it
181         for cmd in commands:
182                 fh.write(cmd)
183                 fh.write("\n")
184
185         fh.write("quit\n")
186         try:
187                 fh.close()
188         except IOError, err:
189                 try:
190                         os.unlink(fname)
191                 except:
192                         pass
193                 sys.exit("Cannot close %s: %s" % (fname, err))
194
195
196         # Run gdb
197         cmd = "gdb --nw --quiet --command=%s %s %s" % (fname, exec_file, core_file)
198         if verbose:
199                 print "Invoking %s" % (cmd,)
200         try:
201                 pipe = os.popen(cmd)
202         except OSError, err:
203                 try:
204                         os.unlink(fname)
205                 except:
206                         pass
207                 sys.exit("Cannot run gdb: %s" % (err,))
208
209         # Get gdb's output
210         result = pipe.readlines()
211         error = pipe.close()
212         if error != None:
213                 try:
214                         os.unlink(fname)
215                 except:
216                         pass
217                 sys.exit("gdb returned an exit value of %s" % (error,))
218
219
220         # Remove the temp file and return the results
221         try:
222                 os.unlink(fname)
223         except:
224                 pass
225         return result
226
227 def get_value_from_frame(frame_num, variable, fmt=""):
228         cmds = []
229         if frame_num > 0:
230                 cmds.append("up %d" % (frame_num,))
231
232         cmds.append("print %s %s" % (fmt, variable))
233         lines = apply(run_gdb, cmds)
234
235         LOOKING_FOR_START = 0
236         READING_VALUE = 1
237         state = LOOKING_FOR_START
238         result = ""
239         for line in lines:
240                 if line[-1] == "\n":
241                         line = line[0:-1]
242                 if line[-1] == "\r":
243                         line = line[0:-1]
244
245                 if state == LOOKING_FOR_START:
246                         if len(line) < 4:
247                                 continue
248                         else:
249                                 if line[0:4] == "$1 =":
250                                         result = line[4:]
251                                         state = READING_VALUE
252
253                 elif state == READING_VALUE:
254                         result += line
255
256         return result
257
258 def get_int_from_frame(frame_num, variable):
259         text = get_value_from_frame(frame_num, variable)
260         try:
261                 integer = int(text)
262         except ValueError:
263                 sys.exit("Could not convert '%s' to integer." % (text,))
264         return integer
265
266
267 def get_byte_array_from_frame(frame_num, variable, length):
268         var = "{char}%s@%d" % (variable, length)
269         text = get_value_from_frame(frame_num, var, "/u")
270         
271         # Remove curly braces
272         i = text.find("{")
273         if i == -1:
274                 sys.exit("Left curly brace not found in '%s'" % (text,))
275         text = text[i+1:]
276
277         i = text.find("}")
278         if i == -1:
279                 sys.exit("Right curly brace not found in '%s'" % (text,))
280         text = text[:i]
281
282         # Split the string until many little strings, each representing a byte
283         # in decimal
284         values_as_text = text.split(',')
285
286         # And get the numeric values
287         return map(int, values_as_text)
288
289 def make_cap_file(pkt_data, lnk_t):
290
291         pcap_lnk_t = wtap_to_pcap(lnk_t)
292
293         # Create a temporary file
294         fname = tempfile.mktemp()
295         try:
296                 fh = open(fname, "w")
297         except IOError, err:
298                 sys.exit("Cannot open %s for writing: %s" % (fname, err))
299
300         print "Packet Data:"
301
302         # Put the hex dump in it
303         offset = 0
304         BYTES_IN_ROW = 16
305         for byte in pkt_data:
306                 if (offset % BYTES_IN_ROW) == 0:
307                         print >> fh, "\n%08X  " % (offset,),
308                         print "\n%08X  " % (offset,),
309
310                 print >> fh, "%02X " % (byte,),
311                 print "%02X " % (byte,),
312                 offset += 1
313
314         print >> fh, "\n"
315         print "\n"
316
317         try:
318                 fh.close()
319         except IOError, err:
320                 try:
321                         os.unlink(fname)
322                 except:
323                         pass
324                 sys.exit("Cannot close %s: %s" % (fname, err))
325
326
327         # Run text2pcap
328         cmd = "text2pcap -q -l %s %s %s" % (pcap_lnk_t, fname, output_file)
329 #       print "Command is %s" % (cmd,)
330         try:
331                 retval = os.system(cmd)
332         except OSError, err:
333                 try:
334                         os.unlink(fname)
335                 except:
336                         pass
337                 sys.exit("Cannot run text2pcap: %s" % (err,))
338
339         # Remove the temp file
340         try:
341                 os.unlink(fname)
342         except:
343                 pass
344
345         if retval == 0:
346                 print "%s created with %d bytes in packet, and %s encoding." % \
347                         (output_file, len(pkt_data), wtap_name[lnk_t])
348         else:
349                 sys.exit("text2pcap did not run succesfully.")
350
351
352
353
354 def try_frame(func_text, cap_len_text, lnk_t_text, data_text):
355
356         # Get the back trace
357         bt_text = run_gdb("bt")
358         bt = BackTrace(bt_text)
359         if not bt.HasFunction(func_text):
360                 print "%s() not found in backtrace." % (func_text,)
361                 return 0
362         else:
363                 print "%s() found in backtrace." % (func_text,)
364
365         # Figure out where the call to epan_dissect_run is.
366         frame_num = bt.Frame(func_text)
367
368         # Get the capture length
369         cap_len = get_int_from_frame(frame_num, cap_len_text)
370
371         # Get the encoding type
372         lnk_t = get_int_from_frame(frame_num, lnk_t_text)
373
374         # Get the packet data
375         pkt_data = get_byte_array_from_frame(frame_num, data_text, cap_len)
376
377         if verbose:
378                 print "Length=%d" % (cap_len,)
379                 print "Encoding=%d" % (lnk_t,)
380                 print "Data (%d bytes) = %s" % (len(pkt_data), pkt_data)
381         make_cap_file(pkt_data, lnk_t)
382         return 1
383
384 def run():
385         if try_frame("epan_dissect_run",
386                 "fd->cap_len", "fd->lnk_t", "data"):
387                 return
388         elif try_frame("add_packet_to_packet_list",
389                 "fdata->cap_len", "fdata->lnk_t", "buf"):
390                 return
391         else:
392                 sys.exit("A packet cannot be pulled from this core.")
393
394
395 def usage():
396         print "pkt-from-core.py [-v] -w capture_file executable-file (core-file or process-id)"
397         print ""
398         print "\tGiven an executable file and a core file, this tool"
399         print "\tuses gdb to retrieve the packet that was being dissected"
400         print "\tat the time ethereal/tethereal stopped running. The packet"
401         print "\tis saved in the capture_file specified by the -w option."
402         print ""
403         print "\t-v : verbose"
404         sys.exit(1)
405
406 def main():
407         global exec_file
408         global core_file
409         global output_file
410         global verbose
411
412         optstring = "vw:"
413         try:
414                 opts, args = getopt.getopt(sys.argv[1:], optstring)
415         except getopt.error:
416                 usage()
417
418         for opt, arg in opts:
419                 if opt == "-w":
420                         output_file = arg
421                 elif opt == "-v":
422                         verbose = 1
423                 else:
424                         assert 0
425
426         if output_file == None:
427                 usage()
428
429         if len(args) != 2:
430                 usage()
431
432         exec_file = args[0]
433         core_file = args[1]
434
435         run()
436
437 if __name__ == '__main__':
438         main()