packetgen 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +12 -0
- data/.travis.yml +14 -0
- data/Gemfile +4 -0
- data/LICENSE +21 -0
- data/README.md +116 -0
- data/Rakefile +18 -0
- data/lib/packetgen.rb +83 -0
- data/lib/packetgen/capture.rb +105 -0
- data/lib/packetgen/header.rb +21 -0
- data/lib/packetgen/header/arp.rb +148 -0
- data/lib/packetgen/header/eth.rb +155 -0
- data/lib/packetgen/header/header_class_methods.rb +28 -0
- data/lib/packetgen/header/header_methods.rb +51 -0
- data/lib/packetgen/header/ip.rb +283 -0
- data/lib/packetgen/header/ipv6.rb +215 -0
- data/lib/packetgen/header/udp.rb +133 -0
- data/lib/packetgen/packet.rb +357 -0
- data/lib/packetgen/pcapng.rb +39 -0
- data/lib/packetgen/pcapng/block.rb +32 -0
- data/lib/packetgen/pcapng/epb.rb +131 -0
- data/lib/packetgen/pcapng/file.rb +345 -0
- data/lib/packetgen/pcapng/idb.rb +145 -0
- data/lib/packetgen/pcapng/shb.rb +173 -0
- data/lib/packetgen/pcapng/spb.rb +103 -0
- data/lib/packetgen/pcapng/unknown_block.rb +80 -0
- data/lib/packetgen/structfu.rb +357 -0
- data/lib/packetgen/version.rb +7 -0
- data/packetgen.gemspec +30 -0
- metadata +155 -0
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'stringio'
|
2
|
+
|
3
|
+
module PacketGen
|
4
|
+
|
5
|
+
# Module to handle PCAP-NG file format.
|
6
|
+
# See http://xml2rfc.tools.ietf.org/cgi-bin/xml2rfc.cgi?url=https://raw.githubusercontent.com/pcapng/pcapng/master/draft-tuexen-opsawg-pcapng.xml&modeAsFormat=html/ascii&type=ascii
|
7
|
+
module PcapNG
|
8
|
+
|
9
|
+
# Section Header Block type number
|
10
|
+
SHB_TYPE = StructFu::Int32.new(0x0A0D0D0A, :little)
|
11
|
+
# Interface Description Block type number
|
12
|
+
IDB_TYPE = StructFu::Int32.new(1, :little)
|
13
|
+
# Simple Packet Block type number
|
14
|
+
SPB_TYPE = StructFu::Int32.new(3, :little)
|
15
|
+
# Enhanced Packet Block type number
|
16
|
+
EPB_TYPE = StructFu::Int32.new(6, :little)
|
17
|
+
|
18
|
+
# Various LINKTYPE values from http://www.tcpdump.org/linktypes.html
|
19
|
+
# FIXME: only ETHERNET type is defined as this is the only link layer
|
20
|
+
# type supported by PacketGen
|
21
|
+
LINKTYPE_ETHERNET = 1
|
22
|
+
|
23
|
+
# Base error class for PcapNG
|
24
|
+
class Error < PacketGen::Error; end
|
25
|
+
# Invalid PcapNG file error
|
26
|
+
class InvalidFileError < Error; end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
require_relative 'pcapng/block.rb'
|
34
|
+
require_relative 'pcapng/unknown_block.rb'
|
35
|
+
require_relative 'pcapng/shb.rb'
|
36
|
+
require_relative 'pcapng/idb.rb'
|
37
|
+
require_relative 'pcapng/epb.rb'
|
38
|
+
require_relative 'pcapng/spb.rb'
|
39
|
+
require_relative 'pcapng/file.rb'
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module PacketGen
|
2
|
+
module PcapNG
|
3
|
+
|
4
|
+
# Mixin module to declare some common methods for block classes.
|
5
|
+
module Block
|
6
|
+
|
7
|
+
# Has this block option?
|
8
|
+
# @return [Boolean]
|
9
|
+
def has_options?
|
10
|
+
self[:options].size > 0
|
11
|
+
end
|
12
|
+
|
13
|
+
# Calculate block length and update :block_len and block_len2 fields
|
14
|
+
# @return [void]
|
15
|
+
def recalc_block_len
|
16
|
+
len = to_a.map(&:to_s).join.size
|
17
|
+
self[:block_len].value = self[:block_len2].value = len
|
18
|
+
end
|
19
|
+
|
20
|
+
# Pad given field to 32 bit boundary, if needed
|
21
|
+
# @param [Array<Symbol>] fields block fields to pad
|
22
|
+
# @return [void]
|
23
|
+
def pad_field(*fields)
|
24
|
+
fields.each do |field|
|
25
|
+
unless self[field].size % 4 == 0
|
26
|
+
self[field] << "\x00" * (4 - (self[field].size % 4))
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,131 @@
|
|
1
|
+
module PacketGen
|
2
|
+
module PcapNG
|
3
|
+
|
4
|
+
# {EPB} represents a Enhanced Packet Block (EPB) of a pcapng file.
|
5
|
+
#
|
6
|
+
# == EPB Definition
|
7
|
+
# Int32 :type Default: 0x00000006
|
8
|
+
# Int32 :block_len
|
9
|
+
# Int32 :interface_id
|
10
|
+
# Int32 :tsh (timestamp high)
|
11
|
+
# Int32 :tsl (timestamp low)
|
12
|
+
# Int32 :cap_len
|
13
|
+
# Int32 :orig_len
|
14
|
+
# String :data
|
15
|
+
# String :options
|
16
|
+
# Int32 :block_len2
|
17
|
+
class EPB < Struct.new(:type, :block_len, :interface_id, :tsh, :tsl,
|
18
|
+
:cap_len, :orig_len, :data, :options, :block_len2)
|
19
|
+
include StructFu
|
20
|
+
include Block
|
21
|
+
|
22
|
+
# @return [:little, :big]
|
23
|
+
attr_accessor :endian
|
24
|
+
# @return [IPB]
|
25
|
+
attr_accessor :interface
|
26
|
+
|
27
|
+
# Minimum EPB size
|
28
|
+
MIN_SIZE = 8*4
|
29
|
+
|
30
|
+
# @param [Hash] options
|
31
|
+
# @option options [:little, :big] :endian set block endianness
|
32
|
+
# @option options [Integer] :type
|
33
|
+
# @option options [Integer] :block_len block total length
|
34
|
+
# @option options [Integer] :interface_id specifies the interface this packet
|
35
|
+
# comes from
|
36
|
+
# @option options [Integer] :tsh timestamp (high nibbles)
|
37
|
+
# @option options [Integer] :tsl timestamp (low nibbles)
|
38
|
+
# @option options [Integer] :cap_len number of octets captured from the packet
|
39
|
+
# @option options [Integer] :orig_len actual length of the packet when it was
|
40
|
+
# transmitted on the network
|
41
|
+
# @option options [::String] :data
|
42
|
+
# @option options [::String] :options
|
43
|
+
# @option options [Integer] :block_len2 block total length
|
44
|
+
def initialize(options={})
|
45
|
+
@endian = set_endianness(options[:endian] || :little)
|
46
|
+
init_fields(options)
|
47
|
+
super(options[:type], options[:block_len], options[:interface_id], options[:tsh],
|
48
|
+
options[:tsl], options[:cap_len], options[:orig_len], options[:data],
|
49
|
+
options[:options], options[:block_len2])
|
50
|
+
end
|
51
|
+
|
52
|
+
# Used by {#initialize} to set the initial fields
|
53
|
+
# @param [Hash] options
|
54
|
+
# @see #initialize possible options
|
55
|
+
# @return [Hash] return +options+
|
56
|
+
def init_fields(options={})
|
57
|
+
options[:type] = @int32.new(options[:type] || PcapNG::EPB_TYPE.to_i)
|
58
|
+
options[:block_len] = @int32.new(options[:block_len] || MIN_SIZE)
|
59
|
+
options[:interface_id] = @int32.new(options[:interface_id] || 0)
|
60
|
+
options[:tsh] = @int32.new(options[:tsh] || 0)
|
61
|
+
options[:tsl] = @int32.new(options[:tsl] || 0)
|
62
|
+
options[:cap_len] = @int32.new(options[:cap_len] || 0)
|
63
|
+
options[:orig_len] = @int32.new(options[:orig_len] || 0)
|
64
|
+
options[:data] = StructFu::String.new(options[:data] || '')
|
65
|
+
options[:options] = StructFu::String.new(options[:options] || '')
|
66
|
+
options[:block_len2] = @int32.new(options[:block_len2] || MIN_SIZE)
|
67
|
+
options
|
68
|
+
end
|
69
|
+
|
70
|
+
# Reads a String or a IO to populate the object
|
71
|
+
# @param [::String,IO] str_or_io
|
72
|
+
# @return [self]
|
73
|
+
def read(str_or_io)
|
74
|
+
if str_or_io.respond_to? :read
|
75
|
+
io = str_or_io
|
76
|
+
else
|
77
|
+
io = StringIO.new(force_binary(str_or_io.to_s))
|
78
|
+
end
|
79
|
+
return self if io.eof?
|
80
|
+
|
81
|
+
self[:type].read io.read(4)
|
82
|
+
self[:block_len].read io.read(4)
|
83
|
+
self[:interface_id].read io.read(4)
|
84
|
+
self[:tsh].read io.read(4)
|
85
|
+
self[:tsl].read io.read(4)
|
86
|
+
self[:cap_len].read io.read(4)
|
87
|
+
self[:orig_len].read io.read(4)
|
88
|
+
self[:data].read io.read(self[:cap_len].to_i)
|
89
|
+
data_pad_len = (4 - (self[:cap_len].to_i % 4)) % 4
|
90
|
+
io.read data_pad_len
|
91
|
+
options_len = self[:block_len].to_i - self[:cap_len].to_i - data_pad_len
|
92
|
+
options_len -= MIN_SIZE
|
93
|
+
self[:options].read io.read(options_len)
|
94
|
+
self[:block_len2].read io.read(4)
|
95
|
+
|
96
|
+
unless self[:block_len].to_i == self[:block_len2].to_i
|
97
|
+
raise InvalidFileError, 'Incoherency in Extended Packet Block'
|
98
|
+
end
|
99
|
+
|
100
|
+
self
|
101
|
+
end
|
102
|
+
|
103
|
+
# Return timestamp as a Time object
|
104
|
+
# @return [Time]
|
105
|
+
def timestamp
|
106
|
+
Time.at((self[:tsh].to_i << 32 | self[:tsl].to_i) * ts_resol)
|
107
|
+
end
|
108
|
+
|
109
|
+
# Return the object as a String
|
110
|
+
# @return [String]
|
111
|
+
def to_s
|
112
|
+
pad_field :data, :options
|
113
|
+
recalc_block_len
|
114
|
+
to_a.map(&:to_s).join
|
115
|
+
end
|
116
|
+
|
117
|
+
|
118
|
+
private
|
119
|
+
|
120
|
+
def ts_resol
|
121
|
+
if @interface.nil?
|
122
|
+
1E-6
|
123
|
+
else
|
124
|
+
@interface.ts_resol
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
end
|
129
|
+
|
130
|
+
end
|
131
|
+
end
|
@@ -0,0 +1,345 @@
|
|
1
|
+
module PacketGen
|
2
|
+
module PcapNG
|
3
|
+
|
4
|
+
# PcapNG::File is a complete Pcap-NG file handler.
|
5
|
+
class File
|
6
|
+
# Get file sections
|
7
|
+
# @return [Array]
|
8
|
+
attr_accessor :sections
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
@sections = []
|
12
|
+
end
|
13
|
+
|
14
|
+
# Read a string to populate the object. Note that this appends new blocks to
|
15
|
+
# the Pcapng::File object.
|
16
|
+
# @param [String] str
|
17
|
+
# @return [self]
|
18
|
+
def read(str)
|
19
|
+
PacketGen.force_binary(str)
|
20
|
+
io = StringIO.new(str)
|
21
|
+
parse_section(io)
|
22
|
+
self
|
23
|
+
end
|
24
|
+
|
25
|
+
# Clear the contents of the Pcapng::File prior to reading in a new string.
|
26
|
+
# This string should contain a Section Header Block and an Interface Description
|
27
|
+
# Block to create a conform pcapng file.
|
28
|
+
# @param [String] str
|
29
|
+
# @return [self]
|
30
|
+
def read!(str)
|
31
|
+
clear
|
32
|
+
read(str)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Read a given file and analyze it.
|
36
|
+
# If given a block, it will yield PcapNG::EPB or PcapNG::SPB objects.
|
37
|
+
# This is the only way to get packet timestamps.
|
38
|
+
# @param [String] fname pcapng file name
|
39
|
+
# @yieldparam [EPB,SPB] block
|
40
|
+
# @return [Integer] return number of yielded blocks (only if a block is given)
|
41
|
+
# @raise [ArgumentError] cannot read +fname+
|
42
|
+
def readfile(fname, &blk)
|
43
|
+
unless ::File.readable?(fname)
|
44
|
+
raise ArgumentError, "cannot read file #{fname}"
|
45
|
+
end
|
46
|
+
|
47
|
+
::File.open(fname, 'rb') do |f|
|
48
|
+
while !f.eof? do
|
49
|
+
parse_section(f)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
if blk
|
54
|
+
count = 0
|
55
|
+
@sections.each do |section|
|
56
|
+
section.interfaces.each do |intf|
|
57
|
+
intf.packets.each { |pkt| count += 1; yield pkt }
|
58
|
+
end
|
59
|
+
end
|
60
|
+
count
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# Give an array of parsed packets (raw data from packets).
|
65
|
+
# If a block is given, yield raw packet data from the given file.
|
66
|
+
# @overload read_packet_bytes(fname)
|
67
|
+
# @param [String] fname pcapng file name
|
68
|
+
# @return [Array] array of packet raw data
|
69
|
+
# @overload read_packet_bytes(fname)
|
70
|
+
# @param [String] fname pcapng file name
|
71
|
+
# @yieldparam [String] raw packet raw data
|
72
|
+
# @return [Integer] number of packets
|
73
|
+
# @raise [ArgumentError] cannot read +fname+
|
74
|
+
def read_packet_bytes(fname, &blk)
|
75
|
+
count = 0
|
76
|
+
packets = [] unless blk
|
77
|
+
|
78
|
+
readfile(fname) do |packet|
|
79
|
+
if blk
|
80
|
+
count += 1
|
81
|
+
yield packet.data.to_s
|
82
|
+
else
|
83
|
+
packets << packet.data.to_s
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
blk ? count : packets
|
88
|
+
end
|
89
|
+
|
90
|
+
# Return an array of parsed packets.
|
91
|
+
# If a block is given, yield parsed packets from the given file.
|
92
|
+
# @overload read_packets(fname)
|
93
|
+
# @param [String] fname pcapng file name
|
94
|
+
# @return [Array<Packet>]
|
95
|
+
# @overload read_packets(fname)
|
96
|
+
# @param [String] fname pcapng file name
|
97
|
+
# @yieldparam [Packet] packet
|
98
|
+
# @return [Integer] number of packets
|
99
|
+
# @raise [ArgumentError] cannot read +fname+
|
100
|
+
def read_packets(fname, &blk)
|
101
|
+
count = 0
|
102
|
+
packets = [] unless blk
|
103
|
+
|
104
|
+
read_packet_bytes(fname) do |packet|
|
105
|
+
if blk
|
106
|
+
count += 1
|
107
|
+
yield Packet.parse(packet)
|
108
|
+
else
|
109
|
+
packets << Packet.parse(packet)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
blk ? count : packets
|
114
|
+
end
|
115
|
+
|
116
|
+
# Return the object as a String
|
117
|
+
# @return [String]
|
118
|
+
def to_s
|
119
|
+
@sections.map { |section| section.to_s }.join
|
120
|
+
end
|
121
|
+
|
122
|
+
# Clear the contents of the Pcapng::File.
|
123
|
+
# @return [void]
|
124
|
+
def clear
|
125
|
+
@sections.clear
|
126
|
+
end
|
127
|
+
|
128
|
+
# Translates a {File} into an array of packets.
|
129
|
+
# Note that this strips out timestamps -- if you'd like to retain
|
130
|
+
# timestamps and other pcapng file information, you will want to
|
131
|
+
# use {#read} instead.
|
132
|
+
# @param [Hash] options
|
133
|
+
# @option options [String] :filename if given, object is cleared and filename
|
134
|
+
# is analyzed before generating array. Else, array is generated from +self+
|
135
|
+
# @option options [String] :file same as +:filename+
|
136
|
+
# @option options [Boolean] :keep_timestamps if +true+ (default value: +false+),
|
137
|
+
# generates an array of hashes, each one with timestamp as key and packet
|
138
|
+
# as value. There is one hash per packet.
|
139
|
+
# @option options [Boolean] :keep_ts same as +:keep_timestamp+
|
140
|
+
# @return [Array<Packet>,Array<Hash>]
|
141
|
+
def file_to_array(options={})
|
142
|
+
filename = options[:filename] || options[:file]
|
143
|
+
if filename
|
144
|
+
clear
|
145
|
+
readfile filename
|
146
|
+
end
|
147
|
+
|
148
|
+
ary = []
|
149
|
+
@sections.each do |section|
|
150
|
+
section.interfaces.each do |itf|
|
151
|
+
if options[:keep_timestamps] || options[:keep_ts]
|
152
|
+
ary.concat itf.packets.map { |pkt| { pkt.timestamp => pkt.data.to_s } }
|
153
|
+
else
|
154
|
+
ary.concat itf.packets.map { |pkt| pkt.data.to_s}
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
ary
|
159
|
+
end
|
160
|
+
|
161
|
+
# Writes the {File} to a file.
|
162
|
+
# @param [Hash] options
|
163
|
+
# @option options [Boolean] :append (default: +false+) if set to +true+,
|
164
|
+
# the packets are appended to the file, rather than overwriting it
|
165
|
+
# @return [Array] array of 2 elements: filename and size written
|
166
|
+
def to_file(filename, options={})
|
167
|
+
mode = ''
|
168
|
+
if options[:append] and ::File.exists? filename
|
169
|
+
mode = 'ab'
|
170
|
+
else
|
171
|
+
mode = 'wb'
|
172
|
+
end
|
173
|
+
::File.open(filename, mode) {|f| f.write(self.to_s)}
|
174
|
+
[filename, self.to_s.size]
|
175
|
+
end
|
176
|
+
alias_method :to_f, :to_file
|
177
|
+
|
178
|
+
# Shorthand method for writing to a file.
|
179
|
+
# @param [#to_s] filename
|
180
|
+
# @return [Array] see return value from {#to_file}
|
181
|
+
def write(filename='out.pcapng')
|
182
|
+
self.to_file(filename.to_s, :append => false)
|
183
|
+
end
|
184
|
+
|
185
|
+
# Shorthand method for appending to a file.
|
186
|
+
# @param [#to_s] filename
|
187
|
+
# @return [Array] see return value from {#to_file}
|
188
|
+
def append(filename='out.pcapng')
|
189
|
+
self.to_file(filename.to_s, :append => true)
|
190
|
+
end
|
191
|
+
|
192
|
+
# @overload array_to_file(ary)
|
193
|
+
# Update {File} object with packets.
|
194
|
+
# @param [Array] ary as generated by {#file_to_array} or Array of Packet objects.
|
195
|
+
# Update {File} object without writing file on disk
|
196
|
+
# @return [self]
|
197
|
+
# @overload array_to_file(options={})
|
198
|
+
# Update {File} and/or write it to a file
|
199
|
+
# @param [Hash] options
|
200
|
+
# @option options [String] :filename file written on disk only if given
|
201
|
+
# @option options [Array] :array can either be an array of packet data,
|
202
|
+
# or a hash-value pair of timestamp => data.
|
203
|
+
# @option options [Time] :timestamp set an initial timestamp
|
204
|
+
# @option options [Integer] :ts_inc set the increment between timestamps.
|
205
|
+
# Defaults to 1
|
206
|
+
# @option options [Boolean] :append if +true+, append packets to the end of
|
207
|
+
# the file
|
208
|
+
# @return [Array] see return value from {#to_file}
|
209
|
+
def array_to_file(options={})
|
210
|
+
case options
|
211
|
+
when Hash
|
212
|
+
filename = options[:filename] || options[:file]
|
213
|
+
ary = options[:array] || options[:arr]
|
214
|
+
unless ary.kind_of? Array
|
215
|
+
raise ArgumentError, ':array parameter needs to be an array'
|
216
|
+
end
|
217
|
+
ts = options[:timestamp] || options[:ts] || Time.now
|
218
|
+
ts_inc = options[:ts_inc] || 1
|
219
|
+
append = !!options[:append]
|
220
|
+
when Array
|
221
|
+
ary = options
|
222
|
+
ts = Time.now
|
223
|
+
ts_inc = 1
|
224
|
+
filename = nil
|
225
|
+
append = false
|
226
|
+
else
|
227
|
+
raise ArgumentError, 'unknown argument. Need either a Hash or Array'
|
228
|
+
end
|
229
|
+
|
230
|
+
section = SHB.new
|
231
|
+
@sections << section
|
232
|
+
itf = IDB.new(:endian => section.endian)
|
233
|
+
classify_block section, itf
|
234
|
+
|
235
|
+
ary.each_with_index do |pkt, i|
|
236
|
+
case pkt
|
237
|
+
when Hash
|
238
|
+
this_ts = pkt.keys.first.to_i
|
239
|
+
this_cap_len = pkt.values.first.to_s.size
|
240
|
+
this_data = pkt.values.first.to_s
|
241
|
+
else
|
242
|
+
this_ts = (ts + ts_inc * i).to_i
|
243
|
+
this_cap_len = pkt.to_s.size
|
244
|
+
this_data = pkt.to_s
|
245
|
+
end
|
246
|
+
this_ts = (this_ts / itf.ts_resol).to_i
|
247
|
+
this_tsh = this_ts >> 32
|
248
|
+
this_tsl = this_ts & 0xffffffff
|
249
|
+
this_pkt = EPB.new(:endian => section.endian,
|
250
|
+
:interface_id => 0,
|
251
|
+
:tsh => this_tsh,
|
252
|
+
:tsl => this_tsl,
|
253
|
+
:cap_len => this_cap_len,
|
254
|
+
:orig_len => this_cap_len,
|
255
|
+
:data => this_data)
|
256
|
+
classify_block section, this_pkt
|
257
|
+
end
|
258
|
+
|
259
|
+
if filename
|
260
|
+
self.to_f(filename, :append => append)
|
261
|
+
else
|
262
|
+
self
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
|
267
|
+
private
|
268
|
+
|
269
|
+
# Parse a section. A section is made of at least a SHB. It than may contain
|
270
|
+
# others blocks, such as IDB, SPB or EPB.
|
271
|
+
# @param [IO] io
|
272
|
+
# @return [void]
|
273
|
+
def parse_section(io)
|
274
|
+
shb = SHB.new
|
275
|
+
type = StructFu::Int32.new(0, shb.endian).read(io.read(4))
|
276
|
+
io.seek(-4, IO::SEEK_CUR)
|
277
|
+
shb = parse(type, io, shb)
|
278
|
+
raise InvalidFileError, 'no Section header found' unless shb.is_a?(SHB)
|
279
|
+
|
280
|
+
if shb.section_len.to_i != 0xffffffffffffffff
|
281
|
+
# Section length is defined
|
282
|
+
section = StringIO.new(io.read(shb.section_len.to_i))
|
283
|
+
while !section.eof? do
|
284
|
+
shb = @sections.last
|
285
|
+
type = StructFu::Int32.new(0, shb.endian).read(section.read(4))
|
286
|
+
section.seek(-4, IO::SEEK_CUR)
|
287
|
+
block = parse(type, section, shb)
|
288
|
+
end
|
289
|
+
else
|
290
|
+
# section length is undefined
|
291
|
+
while !io.eof?
|
292
|
+
shb = @sections.last
|
293
|
+
type = StructFu::Int32.new(0, shb.endian).read(io.read(4))
|
294
|
+
io.seek(-4, IO::SEEK_CUR)
|
295
|
+
block = parse(type, io, shb)
|
296
|
+
end
|
297
|
+
end
|
298
|
+
end
|
299
|
+
|
300
|
+
# Parse a block from its type
|
301
|
+
# @param [StructFu::Int32] type
|
302
|
+
# @param [IO] io stream from which parse block
|
303
|
+
# @param [SHB] shb header of current section
|
304
|
+
# @return [void]
|
305
|
+
def parse(type, io, shb)
|
306
|
+
types = PcapNG.constants(false).select { |c| c.to_s =~ /_TYPE/ }.
|
307
|
+
map { |c| [PcapNG.const_get(c).to_i, c] }
|
308
|
+
types = Hash[types]
|
309
|
+
|
310
|
+
if types.has_key?(type.to_i)
|
311
|
+
klass = PcapNG.const_get(types[type.to_i].to_s.gsub(/_TYPE/, '').to_sym)
|
312
|
+
block = klass.new(endian: shb.endian)
|
313
|
+
else
|
314
|
+
block = UnknownBlock.new(endian: shb.endian)
|
315
|
+
end
|
316
|
+
|
317
|
+
classify_block shb, block
|
318
|
+
block.read(io)
|
319
|
+
end
|
320
|
+
|
321
|
+
# Classify block from its type
|
322
|
+
# @param [SHB] shb header of current section
|
323
|
+
# @param [Block] block block to classify
|
324
|
+
# @return [void]
|
325
|
+
def classify_block(shb, block)
|
326
|
+
case block
|
327
|
+
when SHB
|
328
|
+
@sections << block
|
329
|
+
when IDB
|
330
|
+
shb << block
|
331
|
+
block.section = shb
|
332
|
+
when EPB
|
333
|
+
shb.interfaces[block.interface_id.to_i] << block
|
334
|
+
block.interface = shb.interfaces[block.interface_id.to_i]
|
335
|
+
when SPB
|
336
|
+
shb.interfaces[0] << block
|
337
|
+
block.interface = shb.interfaces[0]
|
338
|
+
else
|
339
|
+
shb.unknown_blocks << block
|
340
|
+
block.section = shb
|
341
|
+
end
|
342
|
+
end
|
343
|
+
end
|
344
|
+
end
|
345
|
+
end
|