TODO epan/dissectors/asn1/kerberos/packet-kerberos-template.c new GSS flags
[wireshark-sm.git] / test / lua / pcap_file.lua
blobc99aaa3a6a9b4372f936a4e4bddf087c828c7df3
1 -- pcap_file_reader.lua
2 --------------------------------------------------------------------------------
3 --[[
4 This is a Wireshark Lua-based pcap capture file reader.
5 Author: Hadriel Kaplan
7 This "capture file" reader reads pcap files - the old style ones. Don't expect this to
8 be as good as the real thing; this is a simplistic implementation to show how to
9 create such file readers, and for testing purposes.
11 This script requires Wireshark v1.12 or newer.
12 --]]
13 --------------------------------------------------------------------------------
15 -- do not modify this table
16 local debug = {
17 DISABLED = 0,
18 LEVEL_1 = 1,
19 LEVEL_2 = 2
22 -- set this DEBUG to debug.LEVEL_1 to enable printing debug info
23 -- set it to debug.LEVEL_2 to enable really verbose printing
24 local DEBUG = debug.LEVEL_1
27 local wireshark_name = "Wireshark"
28 if not GUI_ENABLED then
29 wireshark_name = "Tshark"
30 end
32 -- verify Wireshark is new enough
33 local major, minor, micro = get_version():match("(%d+)%.(%d+)%.(%d+)")
34 if major and tonumber(major) <= 1 and ((tonumber(minor) <= 10) or (tonumber(minor) == 11 and tonumber(micro) < 3)) then
35 error( "Sorry, but your " .. wireshark_name .. " version (" .. get_version() .. ") is too old for this script!\n" ..
36 "This script needs " .. wireshark_name .. "version 1.12 or higher.\n" )
37 end
39 -- verify we have the Struct library in wireshark
40 -- technically we should be able to do this with 'require', but Struct is a built-in
41 assert(Struct.unpack, wireshark_name .. " does not have the Struct library!")
43 --------------------------------------------------------------------------------
44 -- early definitions
45 -- throughout most of this file I try to pre-declare things to help ease
46 -- reading it and following the logic flow, but some things just have to be done
47 -- before others, so this sections has such things that cannot be avoided
48 --------------------------------------------------------------------------------
50 -- first some variable declarations for functions we'll define later
51 local parse_file_header, parse_rec_header, read_common
53 -- these will be set inside of parse_file_header(), but we're declaring them up here
54 local default_settings =
56 debug = DEBUG,
57 corrected_magic = 0xa1b2c3d4,
58 version_major = 2,
59 version_minor = 4,
60 timezone = 0,
61 sigfigs = 0,
62 read_snaplen = 0, -- the snaplen we read from file
63 snaplen = 0, -- the snaplen we use (limited by WTAP_MAX_PACKET_SIZE)
64 linktype = -1, -- the raw linktype number in the file header
65 wtap_type = wtap_encaps.UNKNOWN, -- the mapped internal wtap number based on linktype
66 endianess = ENC_BIG_ENDIAN,
67 time_precision = wtap_tsprecs.USEC,
68 rec_hdr_len = 16, -- default size of record header
69 rec_hdr_patt = "I4 I4 I4 I4", -- pattern for Struct to use
70 num_rec_fields = 4, -- number of vars in pattern
73 local dprint = function() end
74 local dprint2 = function() end
75 local function reset_debug()
76 if default_settings.debug > debug.DISABLED then
77 dprint = function(...)
78 print(table.concat({"Lua:", ...}," "))
79 end
81 if default_settings.debug > debug.LEVEL_1 then
82 dprint2 = dprint
83 end
84 end
85 end
86 -- call it now
87 reset_debug()
89 --------------------------------------------------------------------------------
90 -- file reader handling functions for Wireshark to use
91 --------------------------------------------------------------------------------
93 ----------------------------------------
94 -- The read_open() is called by Wireshark once per file, to see if the file is this reader's type.
95 -- Wireshark passes in (1) a File object and (2) CaptureInfo object to this function
96 -- It expects in return either nil or false to mean it's not our file type, or true if it is
97 -- In our case what this means is we figure out if the file has the magic header, and get the
98 -- endianess of the file, and the encapsulation type of its frames/records
99 local function read_open(file, capture)
100 dprint2("read_open() called")
102 local file_settings = parse_file_header(file)
104 if file_settings then
106 dprint2("read_open: success, file is for us")
108 -- save our state
109 capture.private_table = file_settings
111 -- if the file is for us, we MUST set the file position cursor to
112 -- where we want the first call to read() function to get it the next time
113 -- for example if we checked a few records to be sure it's or type
114 -- but in this simple example we only verify the file header (24 bytes)
115 -- and we want the file position to remain after that header for our read()
116 -- call, so we don't change it back
117 --file:seek("set",position)
119 -- these we can also set per record later during read operations
120 capture.time_precision = file_settings.time_precision
121 capture.encap = file_settings.wtap_type
122 capture.snapshot_length = file_settings.snaplen
124 return true
127 dprint2("read_open: file not for us")
129 -- if it's not for us, wireshark will reset the file position itself
131 return false
134 ----------------------------------------
135 -- Wireshark/tshark calls read() for each frame/record in the file
136 -- It passes in (1) a File, (2) CaptureInfo, and (3) FrameInfo object to this function
137 -- It expects in return the file offset position the record starts at,
138 -- or nil/false if there's an error or end-of-file is reached.
139 -- The offset position is used later: wireshark remembers it and gives
140 -- it to seek_read() at various random times
141 local function read(file, capture, frame)
142 dprint2("read() called")
144 -- call our common reader function
145 local position = file:seek()
147 if not read_common("read", file, capture, frame) then
148 -- this isn't' actually an error, because it might just mean we reached end-of-file
149 -- so let's test for that (read(0) is a special case in Lua, see Lua docs)
150 if file:read(0) ~= nil then
151 dprint("read: failed to call read_common")
152 else
153 dprint2("read: reached end of file")
155 return false
158 dprint2("read: succeess")
160 -- return the position we got to (or nil if we hit EOF/error)
161 return position
164 ----------------------------------------
165 -- Wireshark/tshark calls seek_read() for each frame/record in the file, at random times
166 -- It passes in (1) a File, (2) CaptureInfo, (3) FrameInfo object, and the offset position number
167 -- It expects in return true for successful parsing, or nil/false if there's an error.
168 local function seek_read(file, capture, frame, offset)
169 dprint2("seek_read() called")
171 -- first move to the right position in the file
172 file:seek("set",offset)
174 if not read_common("seek_read", file, capture, frame) then
175 dprint("seek_read: failed to call read_common")
176 return false
179 return true
182 ----------------------------------------
183 -- Wireshark/tshark calls read_close() when it's closing the file completely
184 -- It passes in (1) a File and (2) CaptureInfo object to this function
185 -- this is a good opportunity to clean up any state you may have created during
186 -- file reading. (in our case there's no real state)
187 local function read_close(file, capture)
188 dprint2("read_close() called")
189 -- we don't really have to reset anything, because we used the
190 -- capture.private_table and wireshark clears it for us after this function
191 return true
194 ----------------------------------------
195 -- An often unused function, Wireshark calls this when the sequential walk-through is over
196 -- (i.e., no more calls to read(), only to seek_read()).
197 -- It passes in (1) a File and (2) CaptureInfo object to this function
198 -- This gives you a chance to clean up any state you used during read() calls, but remember
199 -- that there will be calls to seek_read() after this (in Wireshark, though not Tshark)
200 local function seq_read_close(file, capture)
201 dprint2("First pass of read() calls are over, but there may be seek_read() calls after this")
202 return true
205 ----------------------------------------
206 -- ok, so let's create a FileHandler object
207 local fh = FileHandler.new("Lua-based PCAP reader", "lua_pcap", "A Lua-based file reader for PCAP-type files","rms")
209 -- set above functions to the FileHandler
210 fh.read_open = read_open
211 fh.read = read
212 fh.seek_read = seek_read
213 fh.read_close = read_close
214 fh.seq_read_close = seq_read_close
215 fh.extensions = "pcap;cap" -- this is just a hint
217 -- and finally, register the FileHandler!
218 register_filehandler(fh)
220 dprint2("FileHandler registered")
222 --------------------------------------------------------------------------------
223 -- ok now for the boring stuff that actually does the work
224 --------------------------------------------------------------------------------
226 ----------------------------------------
227 -- in Lua, we have access to encapsulation types in the 'wtap_encaps' table, but
228 -- those numbers don't actually necessarily match the numbers in pcap files
229 -- for the encapsulation type, because the namespace got screwed up at some
230 -- point in the past (blame LBL NRG, not wireshark for that)
231 -- but I'm not going to create the full mapping of these two namespaces
232 -- instead we'll just use this smaller table to map them
233 -- these are taken from wiretap/pcap-common.c
234 local pcap2wtap = {
235 [0] = wtap_encaps.NULL,
236 [1] = wtap_encaps.ETHERNET,
237 [6] = wtap_encaps.TOKEN_RING,
238 [8] = wtap_encaps.SLIP,
239 [9] = wtap_encaps.PPP,
240 [101] = wtap_encaps.RAW_IP,
241 [105] = wtap_encaps.IEEE_802_11,
242 [127] = wtap_encaps.IEEE_802_11_RADIOTAP,
243 [140] = wtap_encaps.MTP2,
244 [141] = wtap_encaps.MTP3,
245 [143] = wtap_encaps.DOCSIS,
246 [147] = wtap_encaps.USER0,
247 [148] = wtap_encaps.USER1,
248 [149] = wtap_encaps.USER2,
249 [150] = wtap_encaps.USER3,
250 [151] = wtap_encaps.USER4,
251 [152] = wtap_encaps.USER5,
252 [153] = wtap_encaps.USER6,
253 [154] = wtap_encaps.USER7,
254 [155] = wtap_encaps.USER8,
255 [156] = wtap_encaps.USER9,
256 [157] = wtap_encaps.USER10,
257 [158] = wtap_encaps.USER11,
258 [159] = wtap_encaps.USER12,
259 [160] = wtap_encaps.USER13,
260 [161] = wtap_encaps.USER14,
261 [162] = wtap_encaps.USER15,
262 [186] = wtap_encaps.USB,
263 [187] = wtap_encaps.BLUETOOTH_H4,
264 [189] = wtap_encaps.USB_LINUX,
265 [195] = wtap_encaps.IEEE802_15_4,
268 -- we can use the above to directly map very quickly
269 -- but to map it backwards we'll use this, because I'm lazy:
270 local function wtap2pcap(encap)
271 for k,v in pairs(pcap2wtap) do
272 if v == encap then
273 return k
276 return 0
279 ----------------------------------------
280 -- here are the "structs" we're going to parse, of the various records in a pcap file
281 -- these pattern string gets used in calls to Struct.unpack()
283 -- we will prepend a '<' or '>' later, once we figure out what endian-ess the files are in
285 -- this is a constant for minimum we need to read before we figure out the filetype
286 local FILE_HDR_LEN = 24
287 -- a pcap file header struct
288 -- this is: magic, version_major, version_minor, timezone, sigfigs, snaplen, encap type
289 local FILE_HEADER_PATT = "I4 I2 I2 i4 I4 I4 I4"
290 -- it's too bad Struct doesn't have a way to get the number of vars the pattern holds
291 -- another thing to add to my to-do list?
292 local NUM_HDR_FIELDS = 7
294 -- these will hold the '<'/'>' prepended version of above
295 --local file_header, rec_header
297 -- snaplen/caplen can't be bigger than this
298 local WTAP_MAX_PACKET_SIZE = 65535
300 ----------------------------------------
301 -- different pcap file types have different magic values
302 -- we need to know various things about them for various functions
303 -- in this script, so this table holds all the info
305 -- See default_settings table above for the defaults used if this table
306 -- doesn't override them.
308 -- Arguably, these magic types represent different "Protocols" to dissect later,
309 -- but this script treats them all as "pcapfile" protocol.
311 -- From this table, we'll auto-create a value-string table for file header magic field
312 local magic_spells =
314 normal =
316 magic = 0xa1b2c3d4,
317 name = "Normal (Big-endian)",
319 swapped =
321 magic = 0xd4c3b2a1,
322 name = "Swapped Normal (Little-endian)",
323 endianess = ENC_LITTLE_ENDIAN,
325 modified =
327 -- this is for a ss991029 patched format only
328 magic = 0xa1b2cd34,
329 name = "Modified",
330 rec_hdr_len = 24,
331 rec_hdr_patt = "I4I4I4I4 I4 I2 I1 I1",
332 num_rec_fields = 8,
334 swapped_modified =
336 -- this is for a ss991029 patched format only
337 magic = 0x34cdb2a1,
338 name = "Swapped Modified",
339 rec_hdr_len = 24,
340 rec_hdr_patt = "I4I4I4I4 I4 I2 I1 I1",
341 num_rec_fields = 8,
342 endianess = ENC_LITTLE_ENDIAN,
344 nsecs =
346 magic = 0xa1b23c4d,
347 name = "Nanosecond",
348 time_precision = wtap_filetypes.TSPREC_NSEC,
350 swapped_nsecs =
352 magic = 0x4d3cb2a1,
353 name = "Swapped Nanosecond",
354 endianess = ENC_LITTLE_ENDIAN,
355 time_precision = wtap_filetypes.TSPREC_NSEC,
359 -- create a magic-to-spell entry table from above magic_spells table
360 -- so we can find them faster during file read operations
361 -- we could just add them right back into spells table, but this is cleaner
362 local magic_values = {}
363 for k,t in pairs(magic_spells) do
364 magic_values[t.magic] = t
367 -- the function which makes a copy of the default settings per file
368 local function new_settings()
369 dprint2("creating new file_settings")
370 local file_settings = {}
371 for k,v in pairs(default_settings) do
372 file_settings[k] = v
374 return file_settings
377 -- set the file_settings that the magic value defines in magic_values
378 local function set_magic_file_settings(magic)
379 local t = magic_values[magic]
380 if not t then
381 dprint("set_magic_file_settings: did not find magic settings for:",magic)
382 return false
385 local file_settings = new_settings()
387 -- the magic_values/spells table uses the same key names, so this is easy
388 for k,v in pairs(t) do
389 file_settings[k] = v
392 -- based on endianess, set the file_header and rec_header
393 -- and determine corrected_magic
394 if file_settings.endianess == ENC_BIG_ENDIAN then
395 file_settings.file_hdr_patt = '>' .. FILE_HEADER_PATT
396 file_settings.rec_hdr_patt = '>' .. file_settings.rec_hdr_patt
397 file_settings.corrected_magic = magic
398 else
399 file_settings.file_hdr_patt = '<' .. FILE_HEADER_PATT
400 file_settings.rec_hdr_patt = '<' .. file_settings.rec_hdr_patt
401 local m = Struct.pack(">I4", magic)
402 file_settings.corrected_magic = Struct.unpack("<I4", m)
405 file_settings.rec_hdr_len = Struct.size(file_settings.rec_hdr_patt)
407 return file_settings
410 ----------------------------------------
411 -- internal functions declared previously
412 ----------------------------------------
414 ----------------------------------------
415 -- used by read_open(), this parses the file header
416 parse_file_header = function(file)
417 dprint2("parse_file_header() called")
419 -- by default, file:read() gets the next "string", meaning ending with a newline \n
420 -- but we want raw byte reads, so tell it how many bytes to read
421 local line = file:read(FILE_HDR_LEN)
423 -- it's ok for us to not be able to read it, but we need to tell wireshark the
424 -- file's not for us, so return false
425 if not line then return false end
427 dprint2("parse_file_header: got this line:\n'", Struct.tohex(line,false,":"), "'")
429 -- let's peek at the magic int32, assuming it's big-endian
430 local magic = Struct.unpack(">I4", line)
432 local file_settings = set_magic_file_settings(magic)
434 if not file_settings then
435 dprint("magic was: '", magic, "', so not a known pcap file?")
436 return false
439 -- this is: magic, version_major, version_minor, timezone, sigfigs, snaplen, encap type
440 local fields = { Struct.unpack(file_settings.file_hdr_patt, line) }
442 -- sanity check; also note that Struct.unpack() returns the fields plus
443 -- a number of where in the line it stopped reading (i.e., the end in this case)
444 -- so we got back number of fields + 1
445 if #fields ~= NUM_HDR_FIELDS + 1 then
446 -- this should never happen, since we already told file:read() to grab enough bytes
447 dprint("parse_file_header: failed to read the file header")
448 return nil
451 -- fields[1] is the magic, which we already parsed and saved before, but just to be sure
452 -- our endianess is set right, we validate what we got is what we expect now that
453 -- endianess has been corrected
454 if fields[1] ~= file_settings.corrected_magic then
455 dprint ("parse_file_header: endianess screwed up? Got:'", fields[1],
456 "', but wanted:", file_settings.corrected_magic)
457 return nil
460 file_settings.version_major = fields[2]
461 file_settings.version_minor = fields[3]
462 file_settings.timezone = fields[4]
463 file_settings.sigfigs = fields[5]
464 file_settings.read_snaplen = fields[6]
465 file_settings.linktype = fields[7]
467 -- wireshark only supports version 2.0 and later
468 if fields[2] < 2 then
469 dprint("got version =",VERSION_MAJOR,"but only version 2 or greater supported")
470 return false
473 -- convert pcap file interface type to wtap number type
474 file_settings.wtap_type = pcap2wtap[file_settings.linktype]
475 if not file_settings.wtap_type then
476 dprint("file nettype", file_settings.linktype,
477 "couldn't be mapped to wireshark wtap type")
478 return false
481 file_settings.snaplen = file_settings.read_snaplen
482 if file_settings.snaplen > WTAP_MAX_PACKET_SIZE then
483 file_settings.snaplen = WTAP_MAX_PACKET_SIZE
486 dprint2("read_file_header: got magic='", magic,
487 "', major version='", file_settings.version_major,
488 "', minor='", file_settings.version_minor,
489 "', timezone='", file_settings.timezone,
490 "', sigfigs='", file_settings.sigfigs,
491 "', read_snaplen='", file_settings.read_snaplen,
492 "', snaplen='", file_settings.snaplen,
493 "', nettype ='", file_settings.linktype,
494 "', wtap ='", file_settings.wtap_type)
496 --ok, it's a pcap file
497 dprint2("parse_file_header: success")
498 return file_settings
501 ----------------------------------------
502 -- this is used by both read() and seek_read()
503 -- the calling function to this should have already set the file position correctly
504 read_common = function(funcname, file, capture, frame)
505 dprint2(funcname,": read_common() called")
507 -- get the state info
508 local file_settings = capture.private_table
510 -- first parse the record header, which will set the FrameInfo fields
511 if not parse_rec_header(funcname, file, file_settings, frame) then
512 dprint2(funcname, ": read_common: hit end of file or error")
513 return false
516 frame.encap = file_settings.wtap_type
518 -- now we need to get the packet bytes from the file record into the frame...
519 -- we *could* read them into a string using file:read(numbytes), and then
520 -- set them to frame.data so that wireshark gets it...
521 -- but that would mean the packet's string would be copied into Lua
522 -- and then sent right back into wireshark, which is gonna slow things
523 -- down; instead FrameInfo has a read_data() method, which makes
524 -- wireshark read directly from the file into the frame buffer, so we use that
525 if not frame:read_data(file, frame.captured_length) then
526 dprint(funcname, ": read_common: failed to read data from file into buffer")
527 return false
530 return true
533 ----------------------------------------
534 -- the function to parse individual records
535 parse_rec_header = function(funcname, file, file_settings, frame)
536 dprint2(funcname,": parse_rec_header() called")
538 local line = file:read(file_settings.rec_hdr_len)
540 -- it's ok for us to not be able to read it, if it's end of file
541 if not line then return false end
543 -- this is: time_sec, time_usec, capture_len, original_len
544 local fields = { Struct.unpack(file_settings.rec_hdr_patt, line) }
546 -- sanity check; also note that Struct.unpack() returns the fields plus
547 -- a number of where in the line it stopped reading (i.e., the end in this case)
548 -- so we got back number of fields + 1
549 if #fields ~= file_settings.num_rec_fields + 1 then
550 dprint(funcname, ": parse_rec_header: failed to read the record header, got:",
551 #fields, ", expected:", file_settings.num_rec_fields)
552 return nil
555 local nsecs = fields[2]
557 if file_settings.time_precision == wtap_filetypes.TSPREC_USEC then
558 nsecs = nsecs * 1000
559 elseif file_settings.time_precision == wtap_filetypes.TSPREC_MSEC then
560 nsecs = nsecs * 1000000
563 frame.time = NSTime(fields[1], nsecs)
565 local caplen, origlen = fields[3], fields[4]
567 -- sanity check, verify captured length isn't more than original length
568 if caplen > origlen then
569 dprint("captured length of", caplen, "is bigger than original length of", origlen)
570 -- swap them, a cool Lua ability
571 caplen, origlen = origlen, caplen
574 if caplen > WTAP_MAX_PACKET_SIZE then
575 dprint("Got a captured_length of", caplen, "which is too big")
576 caplen = WTAP_MAX_PACKET_SIZE
579 frame.rec_type = wtap_rec_types.PACKET
581 frame.captured_length = caplen
582 frame.original_length = origlen
584 frame.flags = wtap_presence_flags.TS + wtap_presence_flags.CAP_LEN -- for timestamp|cap_len
586 dprint2(funcname,": parse_rec_header() returning")
587 return true
592 --------------------------------------------------------------------------------
593 -- file writer handling functions for Wireshark to use
594 --------------------------------------------------------------------------------
596 -- file encaps we can handle writing
597 local canwrite = {
598 [ wtap_encaps.NULL ] = true,
599 [ wtap_encaps.ETHERNET ] = true,
600 [ wtap_encaps.PPP ] = true,
601 [ wtap_encaps.RAW_IP ] = true,
602 [ wtap_encaps.IEEE_802_11 ] = true,
603 [ wtap_encaps.MTP2 ] = true,
604 [ wtap_encaps.MTP3 ] = true,
605 -- etc., etc.
608 -- we can't reuse the variables we used in the reader, because this script might be used to both
609 -- open a file for reading and write it out, at the same time, so we cerate another file_settings
610 -- instance.
611 -- set the file_settings for the little-endian version in magic_spells
612 local function create_writer_file_settings()
613 dprint2("create_writer_file_settings called")
614 local t = magic_spells.swapped
616 local file_settings = new_settings()
618 -- the magic_values/spells table uses the same key names, so this is easy
619 for k,v in pairs(t) do
620 file_settings[k] = v
623 -- based on endianess, set the file_header and rec_header
624 -- and determine corrected_magic
625 if file_settings.endianess == ENC_BIG_ENDIAN then
626 file_settings.file_hdr_patt = '>' .. FILE_HEADER_PATT
627 file_settings.rec_hdr_patt = '>' .. file_settings.rec_hdr_patt
628 file_settings.corrected_magic = file_settings.magic
629 else
630 file_settings.file_hdr_patt = '<' .. FILE_HEADER_PATT
631 file_settings.rec_hdr_patt = '<' .. file_settings.rec_hdr_patt
632 local m = Struct.pack(">I4", file_settings.magic)
633 file_settings.corrected_magic = Struct.unpack("<I4", m)
636 file_settings.rec_hdr_len = Struct.size(file_settings.rec_hdr_patt)
638 return file_settings
641 ----------------------------------------
642 -- The can_write_encap() function is called by Wireshark when it wants to write out a file,
643 -- and needs to see if this file writer can handle the packet types in the window.
644 -- We need to return true if we can handle it, else false
645 local function can_write_encap(encap)
646 dprint2("can_write_encap() called with encap=",encap)
647 return canwrite[encap] or false
650 local function write_open(file, capture)
651 dprint2("write_open() called")
653 local file_settings = create_writer_file_settings()
655 -- write out file header
656 local hdr = Struct.pack(file_settings.file_hdr_patt,
657 file_settings.corrected_magic,
658 file_settings.version_major,
659 file_settings.version_minor,
660 file_settings.timezone,
661 file_settings.sigfigs,
662 capture.snapshot_length,
663 wtap2pcap(capture.encap))
665 if not hdr then
666 dprint("write_open: error generating file header")
667 return false
670 dprint2("write_open generating:", Struct.tohex(hdr))
672 if not file:write(hdr) then
673 dprint("write_open: error writing file header to file")
674 return false
677 -- save settings
678 capture.private_table = file_settings
680 return true
683 local function write(file, capture, frame)
684 dprint2("write() called")
686 -- get file settings
687 local file_settings = capture.private_table
688 if not file_settings then
689 dprint("write() failed to get private table file settings")
690 return false
693 -- write out record header: time_sec, time_usec, capture_len, original_len
695 -- first get times
696 local nstime = frame.time
698 -- pcap format is in usecs, but wireshark's internal is nsecs
699 local nsecs = nstime.nsecs
701 if file_settings.time_precision == wtap_filetypes.TSPREC_USEC then
702 nsecs = nsecs / 1000
703 elseif file_settings.time_precision == wtap_filetypes.TSPREC_MSEC then
704 nsecs = nsecs / 1000000
707 local hdr = Struct.pack(file_settings.rec_hdr_patt,
708 nstime.secs,
709 nsecs,
710 frame.captured_length,
711 frame.original_length)
713 if not hdr then
714 dprint("write: error generating record header")
715 return false
718 if not file:write(hdr) then
719 dprint("write: error writing record header to file")
720 return false
723 -- we could write the packet data the same way, by getting frame.data and writing it out
724 -- but we can avoid copying those bytes into Lua by using the write_data() function
725 if not frame:write_data(file) then
726 dprint("write: error writing record data to file")
727 return false
730 return true
733 local function write_close(file, capture)
734 dprint2("write_close() called")
735 dprint2("Good night, and good luck")
736 return true
739 -- ok, so let's create another FileHandler object
740 local fh2 = FileHandler.new("Lua-based PCAP writer", "lua_pcap2", "A Lua-based file writer for PCAP-type files","wms")
742 -- set above functions to the FileHandler
743 fh2.can_write_encap = can_write_encap
744 fh2.write_open = write_open
745 fh2.write = write
746 fh2.write_close = write_close
747 fh2.extensions = "pcap;cap" -- this is just a hint
749 -- and finally, register the FileHandler!
750 register_filehandler(fh2)
752 dprint2("Second FileHandler registered")