packetgen 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|