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 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752
|
-- pcap_file_reader.lua
--------------------------------------------------------------------------------
--[[
This is a Wireshark Lua-based pcap capture file reader.
Author: Hadriel Kaplan
This "capture file" reader reads pcap files - the old style ones. Don't expect this to
be as good as the real thing; this is a simplistic implementation to show how to
create such file readers, and for testing purposes.
This script requires Wireshark v1.12 or newer.
--]]
--------------------------------------------------------------------------------
-- do not modify this table
local debug = {
DISABLED = 0,
LEVEL_1 = 1,
LEVEL_2 = 2
}
-- set this DEBUG to debug.LEVEL_1 to enable printing debug info
-- set it to debug.LEVEL_2 to enable really verbose printing
local DEBUG = debug.LEVEL_1
local wireshark_name = "Wireshark"
if not GUI_ENABLED then
wireshark_name = "Tshark"
end
-- verify Wireshark is new enough
local major, minor, micro = get_version():match("(%d+)%.(%d+)%.(%d+)")
if major and tonumber(major) <= 1 and ((tonumber(minor) <= 10) or (tonumber(minor) == 11 and tonumber(micro) < 3)) then
error( "Sorry, but your " .. wireshark_name .. " version (" .. get_version() .. ") is too old for this script!\n" ..
"This script needs " .. wireshark_name .. "version 1.12 or higher.\n" )
end
-- verify we have the Struct library in wireshark
-- technically we should be able to do this with 'require', but Struct is a built-in
assert(Struct.unpack, wireshark_name .. " does not have the Struct library!")
--------------------------------------------------------------------------------
-- early definitions
-- throughout most of this file I try to pre-declare things to help ease
-- reading it and following the logic flow, but some things just have to be done
-- before others, so this sections has such things that cannot be avoided
--------------------------------------------------------------------------------
-- first some variable declarations for functions we'll define later
local parse_file_header, parse_rec_header, read_common
-- these will be set inside of parse_file_header(), but we're declaring them up here
local default_settings =
{
debug = DEBUG,
corrected_magic = 0xa1b2c3d4,
version_major = 2,
version_minor = 4,
timezone = 0,
sigfigs = 0,
read_snaplen = 0, -- the snaplen we read from file
snaplen = 0, -- the snaplen we use (limited by WTAP_MAX_PACKET_SIZE)
linktype = -1, -- the raw linktype number in the file header
wtap_type = wtap_encaps.UNKNOWN, -- the mapped internal wtap number based on linktype
endianess = ENC_BIG_ENDIAN,
time_precision = wtap_tsprecs.USEC,
rec_hdr_len = 16, -- default size of record header
rec_hdr_patt = "I4 I4 I4 I4", -- pattern for Struct to use
num_rec_fields = 4, -- number of vars in pattern
}
local dprint = function() end
local dprint2 = function() end
local function reset_debug()
if default_settings.debug > debug.DISABLED then
dprint = function(...)
print(table.concat({"Lua:", ...}," "))
end
if default_settings.debug > debug.LEVEL_1 then
dprint2 = dprint
end
end
end
-- call it now
reset_debug()
--------------------------------------------------------------------------------
-- file reader handling functions for Wireshark to use
--------------------------------------------------------------------------------
----------------------------------------
-- The read_open() is called by Wireshark once per file, to see if the file is this reader's type.
-- Wireshark passes in (1) a File object and (2) CaptureInfo object to this function
-- It expects in return either nil or false to mean it's not our file type, or true if it is
-- In our case what this means is we figure out if the file has the magic header, and get the
-- endianess of the file, and the encapsulation type of its frames/records
local function read_open(file, capture)
dprint2("read_open() called")
local file_settings = parse_file_header(file)
if file_settings then
dprint2("read_open: success, file is for us")
-- save our state
capture.private_table = file_settings
-- if the file is for us, we MUST set the file position cursor to
-- where we want the first call to read() function to get it the next time
-- for example if we checked a few records to be sure it's or type
-- but in this simple example we only verify the file header (24 bytes)
-- and we want the file position to remain after that header for our read()
-- call, so we don't change it back
--file:seek("set",position)
-- these we can also set per record later during read operations
capture.time_precision = file_settings.time_precision
capture.encap = file_settings.wtap_type
capture.snapshot_length = file_settings.snaplen
return true
end
dprint2("read_open: file not for us")
-- if it's not for us, wireshark will reset the file position itself
return false
end
----------------------------------------
-- Wireshark/tshark calls read() for each frame/record in the file
-- It passes in (1) a File, (2) CaptureInfo, and (3) FrameInfo object to this function
-- It expects in return the file offset position the record starts at,
-- or nil/false if there's an error or end-of-file is reached.
-- The offset position is used later: wireshark remembers it and gives
-- it to seek_read() at various random times
local function read(file, capture, frame)
dprint2("read() called")
-- call our common reader function
local position = file:seek()
if not read_common("read", file, capture, frame) then
-- this isn't' actually an error, because it might just mean we reached end-of-file
-- so let's test for that (read(0) is a special case in Lua, see Lua docs)
if file:read(0) ~= nil then
dprint("read: failed to call read_common")
else
dprint2("read: reached end of file")
end
return false
end
dprint2("read: succeess")
-- return the position we got to (or nil if we hit EOF/error)
return position
end
----------------------------------------
-- Wireshark/tshark calls seek_read() for each frame/record in the file, at random times
-- It passes in (1) a File, (2) CaptureInfo, (3) FrameInfo object, and the offset position number
-- It expects in return true for successful parsing, or nil/false if there's an error.
local function seek_read(file, capture, frame, offset)
dprint2("seek_read() called")
-- first move to the right position in the file
file:seek("set",offset)
if not read_common("seek_read", file, capture, frame) then
dprint("seek_read: failed to call read_common")
return false
end
return true
end
----------------------------------------
-- Wireshark/tshark calls read_close() when it's closing the file completely
-- It passes in (1) a File and (2) CaptureInfo object to this function
-- this is a good opportunity to clean up any state you may have created during
-- file reading. (in our case there's no real state)
local function read_close(file, capture)
dprint2("read_close() called")
-- we don't really have to reset anything, because we used the
-- capture.private_table and wireshark clears it for us after this function
return true
end
----------------------------------------
-- An often unused function, Wireshark calls this when the sequential walk-through is over
-- (i.e., no more calls to read(), only to seek_read()).
-- It passes in (1) a File and (2) CaptureInfo object to this function
-- This gives you a chance to clean up any state you used during read() calls, but remember
-- that there will be calls to seek_read() after this (in Wireshark, though not Tshark)
local function seq_read_close(file, capture)
dprint2("First pass of read() calls are over, but there may be seek_read() calls after this")
return true
end
----------------------------------------
-- ok, so let's create a FileHandler object
local fh = FileHandler.new("Lua-based PCAP reader", "lua_pcap", "A Lua-based file reader for PCAP-type files","rms")
-- set above functions to the FileHandler
fh.read_open = read_open
fh.read = read
fh.seek_read = seek_read
fh.read_close = read_close
fh.seq_read_close = seq_read_close
fh.extensions = "pcap;cap" -- this is just a hint
-- and finally, register the FileHandler!
register_filehandler(fh)
dprint2("FileHandler registered")
--------------------------------------------------------------------------------
-- ok now for the boring stuff that actually does the work
--------------------------------------------------------------------------------
----------------------------------------
-- in Lua, we have access to encapsulation types in the 'wtap_encaps' table, but
-- those numbers don't actually necessarily match the numbers in pcap files
-- for the encapsulation type, because the namespace got screwed up at some
-- point in the past (blame LBL NRG, not wireshark for that)
-- but I'm not going to create the full mapping of these two namespaces
-- instead we'll just use this smaller table to map them
-- these are taken from wiretap/pcap-common.c
local pcap2wtap = {
[0] = wtap_encaps.NULL,
[1] = wtap_encaps.ETHERNET,
[6] = wtap_encaps.TOKEN_RING,
[8] = wtap_encaps.SLIP,
[9] = wtap_encaps.PPP,
[101] = wtap_encaps.RAW_IP,
[105] = wtap_encaps.IEEE_802_11,
[127] = wtap_encaps.IEEE_802_11_RADIOTAP,
[140] = wtap_encaps.MTP2,
[141] = wtap_encaps.MTP3,
[143] = wtap_encaps.DOCSIS,
[147] = wtap_encaps.USER0,
[148] = wtap_encaps.USER1,
[149] = wtap_encaps.USER2,
[150] = wtap_encaps.USER3,
[151] = wtap_encaps.USER4,
[152] = wtap_encaps.USER5,
[153] = wtap_encaps.USER6,
[154] = wtap_encaps.USER7,
[155] = wtap_encaps.USER8,
[156] = wtap_encaps.USER9,
[157] = wtap_encaps.USER10,
[158] = wtap_encaps.USER11,
[159] = wtap_encaps.USER12,
[160] = wtap_encaps.USER13,
[161] = wtap_encaps.USER14,
[162] = wtap_encaps.USER15,
[186] = wtap_encaps.USB,
[187] = wtap_encaps.BLUETOOTH_H4,
[189] = wtap_encaps.USB_LINUX,
[195] = wtap_encaps.IEEE802_15_4,
}
-- we can use the above to directly map very quickly
-- but to map it backwards we'll use this, because I'm lazy:
local function wtap2pcap(encap)
for k,v in pairs(pcap2wtap) do
if v == encap then
return k
end
end
return 0
end
----------------------------------------
-- here are the "structs" we're going to parse, of the various records in a pcap file
-- these pattern string gets used in calls to Struct.unpack()
--
-- we will prepend a '<' or '>' later, once we figure out what endian-ess the files are in
--
-- this is a constant for minimum we need to read before we figure out the filetype
local FILE_HDR_LEN = 24
-- a pcap file header struct
-- this is: magic, version_major, version_minor, timezone, sigfigs, snaplen, encap type
local FILE_HEADER_PATT = "I4 I2 I2 i4 I4 I4 I4"
-- it's too bad Struct doesn't have a way to get the number of vars the pattern holds
-- another thing to add to my to-do list?
local NUM_HDR_FIELDS = 7
-- these will hold the '<'/'>' prepended version of above
--local file_header, rec_header
-- snaplen/caplen can't be bigger than this
local WTAP_MAX_PACKET_SIZE = 65535
----------------------------------------
-- different pcap file types have different magic values
-- we need to know various things about them for various functions
-- in this script, so this table holds all the info
--
-- See default_settings table above for the defaults used if this table
-- doesn't override them.
--
-- Arguably, these magic types represent different "Protocols" to dissect later,
-- but this script treats them all as "pcapfile" protocol.
--
-- From this table, we'll auto-create a value-string table for file header magic field
local magic_spells =
{
normal =
{
magic = 0xa1b2c3d4,
name = "Normal (Big-endian)",
},
swapped =
{
magic = 0xd4c3b2a1,
name = "Swapped Normal (Little-endian)",
endianess = ENC_LITTLE_ENDIAN,
},
modified =
{
-- this is for a ss991029 patched format only
magic = 0xa1b2cd34,
name = "Modified",
rec_hdr_len = 24,
rec_hdr_patt = "I4I4I4I4 I4 I2 I1 I1",
num_rec_fields = 8,
},
swapped_modified =
{
-- this is for a ss991029 patched format only
magic = 0x34cdb2a1,
name = "Swapped Modified",
rec_hdr_len = 24,
rec_hdr_patt = "I4I4I4I4 I4 I2 I1 I1",
num_rec_fields = 8,
endianess = ENC_LITTLE_ENDIAN,
},
nsecs =
{
magic = 0xa1b23c4d,
name = "Nanosecond",
time_precision = wtap_filetypes.TSPREC_NSEC,
},
swapped_nsecs =
{
magic = 0x4d3cb2a1,
name = "Swapped Nanosecond",
endianess = ENC_LITTLE_ENDIAN,
time_precision = wtap_filetypes.TSPREC_NSEC,
},
}
-- create a magic-to-spell entry table from above magic_spells table
-- so we can find them faster during file read operations
-- we could just add them right back into spells table, but this is cleaner
local magic_values = {}
for k,t in pairs(magic_spells) do
magic_values[t.magic] = t
end
-- the function which makes a copy of the default settings per file
local function new_settings()
dprint2("creating new file_settings")
local file_settings = {}
for k,v in pairs(default_settings) do
file_settings[k] = v
end
return file_settings
end
-- set the file_settings that the magic value defines in magic_values
local function set_magic_file_settings(magic)
local t = magic_values[magic]
if not t then
dprint("set_magic_file_settings: did not find magic settings for:",magic)
return false
end
local file_settings = new_settings()
-- the magic_values/spells table uses the same key names, so this is easy
for k,v in pairs(t) do
file_settings[k] = v
end
-- based on endianess, set the file_header and rec_header
-- and determine corrected_magic
if file_settings.endianess == ENC_BIG_ENDIAN then
file_settings.file_hdr_patt = '>' .. FILE_HEADER_PATT
file_settings.rec_hdr_patt = '>' .. file_settings.rec_hdr_patt
file_settings.corrected_magic = magic
else
file_settings.file_hdr_patt = '<' .. FILE_HEADER_PATT
file_settings.rec_hdr_patt = '<' .. file_settings.rec_hdr_patt
local m = Struct.pack(">I4", magic)
file_settings.corrected_magic = Struct.unpack("<I4", m)
end
file_settings.rec_hdr_len = Struct.size(file_settings.rec_hdr_patt)
return file_settings
end
----------------------------------------
-- internal functions declared previously
----------------------------------------
----------------------------------------
-- used by read_open(), this parses the file header
parse_file_header = function(file)
dprint2("parse_file_header() called")
-- by default, file:read() gets the next "string", meaning ending with a newline \n
-- but we want raw byte reads, so tell it how many bytes to read
local line = file:read(FILE_HDR_LEN)
-- it's ok for us to not be able to read it, but we need to tell wireshark the
-- file's not for us, so return false
if not line then return false end
dprint2("parse_file_header: got this line:\n'", Struct.tohex(line,false,":"), "'")
-- let's peek at the magic int32, assuming it's big-endian
local magic = Struct.unpack(">I4", line)
local file_settings = set_magic_file_settings(magic)
if not file_settings then
dprint("magic was: '", magic, "', so not a known pcap file?")
return false
end
-- this is: magic, version_major, version_minor, timezone, sigfigs, snaplen, encap type
local fields = { Struct.unpack(file_settings.file_hdr_patt, line) }
-- sanity check; also note that Struct.unpack() returns the fields plus
-- a number of where in the line it stopped reading (i.e., the end in this case)
-- so we got back number of fields + 1
if #fields ~= NUM_HDR_FIELDS + 1 then
-- this should never happen, since we already told file:read() to grab enough bytes
dprint("parse_file_header: failed to read the file header")
return nil
end
-- fields[1] is the magic, which we already parsed and saved before, but just to be sure
-- our endianess is set right, we validate what we got is what we expect now that
-- endianess has been corrected
if fields[1] ~= file_settings.corrected_magic then
dprint ("parse_file_header: endianess screwed up? Got:'", fields[1],
"', but wanted:", file_settings.corrected_magic)
return nil
end
file_settings.version_major = fields[2]
file_settings.version_minor = fields[3]
file_settings.timezone = fields[4]
file_settings.sigfigs = fields[5]
file_settings.read_snaplen = fields[6]
file_settings.linktype = fields[7]
-- wireshark only supports version 2.0 and later
if fields[2] < 2 then
dprint("got version =",VERSION_MAJOR,"but only version 2 or greater supported")
return false
end
-- convert pcap file interface type to wtap number type
file_settings.wtap_type = pcap2wtap[file_settings.linktype]
if not file_settings.wtap_type then
dprint("file nettype", file_settings.linktype,
"couldn't be mapped to wireshark wtap type")
return false
end
file_settings.snaplen = file_settings.read_snaplen
if file_settings.snaplen > WTAP_MAX_PACKET_SIZE then
file_settings.snaplen = WTAP_MAX_PACKET_SIZE
end
dprint2("read_file_header: got magic='", magic,
"', major version='", file_settings.version_major,
"', minor='", file_settings.version_minor,
"', timezone='", file_settings.timezone,
"', sigfigs='", file_settings.sigfigs,
"', read_snaplen='", file_settings.read_snaplen,
"', snaplen='", file_settings.snaplen,
"', nettype ='", file_settings.linktype,
"', wtap ='", file_settings.wtap_type)
--ok, it's a pcap file
dprint2("parse_file_header: success")
return file_settings
end
----------------------------------------
-- this is used by both read() and seek_read()
-- the calling function to this should have already set the file position correctly
read_common = function(funcname, file, capture, frame)
dprint2(funcname,": read_common() called")
-- get the state info
local file_settings = capture.private_table
-- first parse the record header, which will set the FrameInfo fields
if not parse_rec_header(funcname, file, capture, file_settings, frame) then
dprint2(funcname, ": read_common: hit end of file or error")
return false
end
frame.encap = file_settings.wtap_type
-- now we need to get the packet bytes from the file record into the frame...
-- we *could* read them into a string using file:read(numbytes), and then
-- set them to frame.data so that wireshark gets it...
-- but that would mean the packet's string would be copied into Lua
-- and then sent right back into wireshark, which is gonna slow things
-- down; instead FrameInfo has a read_data() method, which makes
-- wireshark read directly from the file into the frame buffer, so we use that
if not frame:read_data(file, frame.captured_length) then
dprint(funcname, ": read_common: failed to read data from file into buffer")
return false
end
return true
end
----------------------------------------
-- the function to parse individual records
parse_rec_header = function(funcname, file, capture, file_settings, frame)
dprint2(funcname,": parse_rec_header() called")
local line = file:read(file_settings.rec_hdr_len)
-- it's ok for us to not be able to read it, if it's end of file
if not line then return false end
-- this is: time_sec, time_usec, capture_len, original_len
local fields = { Struct.unpack(file_settings.rec_hdr_patt, line) }
-- sanity check; also note that Struct.unpack() returns the fields plus
-- a number of where in the line it stopped reading (i.e., the end in this case)
-- so we got back number of fields + 1
if #fields ~= file_settings.num_rec_fields + 1 then
dprint(funcname, ": parse_rec_header: failed to read the record header, got:",
#fields, ", expected:", file_settings.num_rec_fields)
return nil
end
local nsecs = fields[2]
if file_settings.time_precision == wtap_filetypes.TSPREC_USEC then
nsecs = nsecs * 1000
elseif file_settings.time_precision == wtap_filetypes.TSPREC_MSEC then
nsecs = nsecs * 1000000
end
frame.time = NSTime(fields[1], nsecs)
local caplen, origlen = fields[3], fields[4]
-- sanity check, verify captured length isn't more than original length
if caplen > origlen then
dprint("captured length of", caplen, "is bigger than original length of", origlen)
-- swap them, a cool Lua ability
caplen, origlen = origlen, caplen
end
if caplen > WTAP_MAX_PACKET_SIZE then
dprint("Got a captured_length of", caplen, "which is too big")
caplen = WTAP_MAX_PACKET_SIZE
end
frame:setup_packet_rec(capture.encap)
frame.captured_length = caplen
frame.original_length = origlen
frame.flags = wtap_presence_flags.TS + wtap_presence_flags.CAP_LEN -- for timestamp|cap_len
dprint2(funcname,": parse_rec_header() returning")
return true
end
--------------------------------------------------------------------------------
-- file writer handling functions for Wireshark to use
--------------------------------------------------------------------------------
-- file encaps we can handle writing
local canwrite = {
[ wtap_encaps.NULL ] = true,
[ wtap_encaps.ETHERNET ] = true,
[ wtap_encaps.PPP ] = true,
[ wtap_encaps.RAW_IP ] = true,
[ wtap_encaps.IEEE_802_11 ] = true,
[ wtap_encaps.MTP2 ] = true,
[ wtap_encaps.MTP3 ] = true,
-- etc., etc.
}
-- we can't reuse the variables we used in the reader, because this script might be used to both
-- open a file for reading and write it out, at the same time, so we cerate another file_settings
-- instance.
-- set the file_settings for the little-endian version in magic_spells
local function create_writer_file_settings()
dprint2("create_writer_file_settings called")
local t = magic_spells.swapped
local file_settings = new_settings()
-- the magic_values/spells table uses the same key names, so this is easy
for k,v in pairs(t) do
file_settings[k] = v
end
-- based on endianess, set the file_header and rec_header
-- and determine corrected_magic
if file_settings.endianess == ENC_BIG_ENDIAN then
file_settings.file_hdr_patt = '>' .. FILE_HEADER_PATT
file_settings.rec_hdr_patt = '>' .. file_settings.rec_hdr_patt
file_settings.corrected_magic = file_settings.magic
else
file_settings.file_hdr_patt = '<' .. FILE_HEADER_PATT
file_settings.rec_hdr_patt = '<' .. file_settings.rec_hdr_patt
local m = Struct.pack(">I4", file_settings.magic)
file_settings.corrected_magic = Struct.unpack("<I4", m)
end
file_settings.rec_hdr_len = Struct.size(file_settings.rec_hdr_patt)
return file_settings
end
----------------------------------------
-- The can_write_encap() function is called by Wireshark when it wants to write out a file,
-- and needs to see if this file writer can handle the packet types in the window.
-- We need to return true if we can handle it, else false
local function can_write_encap(encap)
dprint2("can_write_encap() called with encap=",encap)
return canwrite[encap] or false
end
local function write_open(file, capture)
dprint2("write_open() called")
local file_settings = create_writer_file_settings()
-- write out file header
local hdr = Struct.pack(file_settings.file_hdr_patt,
file_settings.corrected_magic,
file_settings.version_major,
file_settings.version_minor,
file_settings.timezone,
file_settings.sigfigs,
capture.snapshot_length,
wtap2pcap(capture.encap))
if not hdr then
dprint("write_open: error generating file header")
return false
end
dprint2("write_open generating:", Struct.tohex(hdr))
if not file:write(hdr) then
dprint("write_open: error writing file header to file")
return false
end
-- save settings
capture.private_table = file_settings
return true
end
local function write(file, capture, frame)
dprint2("write() called")
-- get file settings
local file_settings = capture.private_table
if not file_settings then
dprint("write() failed to get private table file settings")
return false
end
-- write out record header: time_sec, time_usec, capture_len, original_len
-- first get times
local nstime = frame.time
-- pcap format is in usecs, but wireshark's internal is nsecs
local nsecs = nstime.nsecs
if file_settings.time_precision == wtap_filetypes.TSPREC_USEC then
nsecs = nsecs / 1000
elseif file_settings.time_precision == wtap_filetypes.TSPREC_MSEC then
nsecs = nsecs / 1000000
end
local hdr = Struct.pack(file_settings.rec_hdr_patt,
nstime.secs,
nsecs,
frame.captured_length,
frame.original_length)
if not hdr then
dprint("write: error generating record header")
return false
end
if not file:write(hdr) then
dprint("write: error writing record header to file")
return false
end
-- we could write the packet data the same way, by getting frame.data and writing it out
-- but we can avoid copying those bytes into Lua by using the write_data() function
if not frame:write_data(file) then
dprint("write: error writing record data to file")
return false
end
return true
end
local function write_close(file, capture)
dprint2("write_close() called")
dprint2("Good night, and good luck")
return true
end
-- ok, so let's create another FileHandler object
local fh2 = FileHandler.new("Lua-based PCAP writer", "lua_pcap2", "A Lua-based file writer for PCAP-type files","wms")
-- set above functions to the FileHandler
fh2.can_write_encap = can_write_encap
fh2.write_open = write_open
fh2.write = write
fh2.write_close = write_close
fh2.extensions = "pcap;cap" -- this is just a hint
-- and finally, register the FileHandler!
register_filehandler(fh2)
dprint2("Second FileHandler registered")
|