packetgen 3.3.3 → 4.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +37 -21
- data/lib/packetgen/capture.rb +2 -2
- data/lib/packetgen/config.rb +0 -1
- data/lib/packetgen/deprecation.rb +7 -7
- data/lib/packetgen/header/arp.rb +13 -13
- data/lib/packetgen/header/asn1_base.rb +1 -1
- data/lib/packetgen/header/base.rb +17 -18
- data/lib/packetgen/header/bootp.rb +32 -34
- data/lib/packetgen/header/dhcp/option.rb +19 -19
- data/lib/packetgen/header/dhcp/options.rb +1 -1
- data/lib/packetgen/header/dhcp.rb +3 -3
- data/lib/packetgen/header/dhcpv6/duid.rb +16 -16
- data/lib/packetgen/header/dhcpv6/option.rb +53 -53
- data/lib/packetgen/header/dhcpv6/options.rb +1 -1
- data/lib/packetgen/header/dhcpv6/relay.rb +5 -5
- data/lib/packetgen/header/dhcpv6.rb +6 -6
- data/lib/packetgen/header/dns/name.rb +14 -10
- data/lib/packetgen/header/dns/opt.rb +2 -2
- data/lib/packetgen/header/dns/option.rb +11 -11
- data/lib/packetgen/header/dns/qdsection.rb +1 -1
- data/lib/packetgen/header/dns/question.rb +6 -8
- data/lib/packetgen/header/dns/rr.rb +56 -43
- data/lib/packetgen/header/dns/rrsection.rb +4 -4
- data/lib/packetgen/header/dns.rb +27 -30
- data/lib/packetgen/header/dot11/control.rb +11 -11
- data/lib/packetgen/header/dot11/data.rb +20 -20
- data/lib/packetgen/header/dot11/element.rb +4 -4
- data/lib/packetgen/header/dot11/management.rb +8 -8
- data/lib/packetgen/header/dot11/sub_mngt.rb +39 -53
- data/lib/packetgen/header/dot11.rb +88 -93
- data/lib/packetgen/header/dot1q.rb +10 -12
- data/lib/packetgen/header/dot1x.rb +9 -9
- data/lib/packetgen/header/eap/fast.rb +4 -4
- data/lib/packetgen/header/eap/md5.rb +6 -6
- data/lib/packetgen/header/eap/tls.rb +13 -15
- data/lib/packetgen/header/eap/ttls.rb +13 -15
- data/lib/packetgen/header/eap.rb +22 -22
- data/lib/packetgen/header/eth.rb +18 -18
- data/lib/packetgen/header/gre.rb +8 -10
- data/lib/packetgen/header/http/headers.rb +2 -2
- data/lib/packetgen/header/http/request.rb +17 -16
- data/lib/packetgen/header/http/response.rb +18 -17
- data/lib/packetgen/header/http/verbs.rb +1 -3
- data/lib/packetgen/header/icmp.rb +8 -8
- data/lib/packetgen/header/icmpv6.rb +3 -3
- data/lib/packetgen/header/igmp.rb +8 -8
- data/lib/packetgen/header/igmpv3/group_record.rb +12 -12
- data/lib/packetgen/header/igmpv3/mq.rb +16 -18
- data/lib/packetgen/header/igmpv3/mr.rb +4 -4
- data/lib/packetgen/header/igmpv3.rb +7 -7
- data/lib/packetgen/header/ip/addr.rb +13 -13
- data/lib/packetgen/header/ip/option.rb +31 -33
- data/lib/packetgen/header/ip/options.rb +1 -1
- data/lib/packetgen/header/ip.rb +37 -72
- data/lib/packetgen/header/ipv6/addr.rb +14 -14
- data/lib/packetgen/header/ipv6/extension.rb +8 -8
- data/lib/packetgen/header/ipv6/hop_by_hop.rb +9 -9
- data/lib/packetgen/header/ipv6.rb +20 -22
- data/lib/packetgen/header/llc.rb +17 -17
- data/lib/packetgen/header/mdns.rb +1 -1
- data/lib/packetgen/header/mld.rb +6 -6
- data/lib/packetgen/header/mldv2/mcast_address_record.rb +11 -11
- data/lib/packetgen/header/mldv2/mlq.rb +21 -23
- data/lib/packetgen/header/mldv2/mlr.rb +8 -8
- data/lib/packetgen/header/ospfv2/db_description.rb +11 -12
- data/lib/packetgen/header/ospfv2/hello.rb +11 -11
- data/lib/packetgen/header/ospfv2/ls_ack.rb +1 -1
- data/lib/packetgen/header/ospfv2/ls_request.rb +9 -9
- data/lib/packetgen/header/ospfv2/ls_update.rb +3 -3
- data/lib/packetgen/header/ospfv2/lsa.rb +54 -58
- data/lib/packetgen/header/ospfv2/lsa_header.rb +9 -9
- data/lib/packetgen/header/ospfv2.rb +27 -29
- data/lib/packetgen/header/ospfv3/db_description.rb +13 -14
- data/lib/packetgen/header/ospfv3/hello.rb +12 -12
- data/lib/packetgen/header/ospfv3/ipv6_prefix.rb +17 -19
- data/lib/packetgen/header/ospfv3/ls_ack.rb +2 -2
- data/lib/packetgen/header/ospfv3/ls_request.rb +9 -9
- data/lib/packetgen/header/ospfv3/ls_update.rb +4 -4
- data/lib/packetgen/header/ospfv3/lsa.rb +48 -51
- data/lib/packetgen/header/ospfv3/lsa_header.rb +9 -9
- data/lib/packetgen/header/ospfv3.rb +25 -27
- data/lib/packetgen/header/sctp/chunk.rb +44 -41
- data/lib/packetgen/header/sctp/error.rb +52 -52
- data/lib/packetgen/header/sctp/parameter.rb +38 -38
- data/lib/packetgen/header/sctp.rb +5 -5
- data/lib/packetgen/header/snmp.rb +2 -2
- data/lib/packetgen/header/tcp/option.rb +45 -39
- data/lib/packetgen/header/tcp/options.rb +2 -2
- data/lib/packetgen/header/tcp.rb +55 -44
- data/lib/packetgen/header/tftp.rb +16 -16
- data/lib/packetgen/header/udp.rb +8 -8
- data/lib/packetgen/header.rb +9 -10
- data/lib/packetgen/headerable.rb +13 -3
- data/lib/packetgen/inspect.rb +2 -2
- data/lib/packetgen/packet.rb +54 -37
- data/lib/packetgen/pcap.rb +15 -4
- data/lib/packetgen/pcapng/block.rb +18 -17
- data/lib/packetgen/pcapng/epb.rb +13 -15
- data/lib/packetgen/pcapng/file.rb +3 -97
- data/lib/packetgen/pcapng/idb.rb +9 -11
- data/lib/packetgen/pcapng/shb.rb +13 -15
- data/lib/packetgen/pcapng/spb.rb +8 -10
- data/lib/packetgen/pcapng/unknown_block.rb +6 -17
- data/lib/packetgen/pcapng.rb +4 -4
- data/lib/packetgen/pcaprub_wrapper.rb +17 -1
- data/lib/packetgen/proto.rb +1 -1
- data/lib/packetgen/unknown_packet.rb +2 -2
- data/lib/packetgen/utils/arp_spoofer.rb +18 -19
- data/lib/packetgen/utils.rb +2 -2
- data/lib/packetgen/version.rb +1 -1
- data/lib/packetgen.rb +4 -3
- metadata +29 -38
- data/lib/packetgen/types/abstract_tlv.rb +0 -278
- data/lib/packetgen/types/array.rb +0 -287
- data/lib/packetgen/types/cstring.rb +0 -109
- data/lib/packetgen/types/enum.rb +0 -171
- data/lib/packetgen/types/fieldable.rb +0 -66
- data/lib/packetgen/types/fields.rb +0 -622
- data/lib/packetgen/types/int.rb +0 -473
- data/lib/packetgen/types/int_string.rb +0 -102
- data/lib/packetgen/types/length_from.rb +0 -54
- data/lib/packetgen/types/oui.rb +0 -52
- data/lib/packetgen/types/string.rb +0 -97
- data/lib/packetgen/types/tlv.rb +0 -161
- data/lib/packetgen/types.rb +0 -26
data/lib/packetgen/packet.rb
CHANGED
@@ -61,20 +61,20 @@ module PacketGen
|
|
61
61
|
# @param [Hash] options specific options for +protocol+
|
62
62
|
# @return [Packet]
|
63
63
|
def self.gen(protocol, options={})
|
64
|
-
self.new.add
|
64
|
+
self.new.add(protocol, options)
|
65
65
|
end
|
66
66
|
|
67
67
|
# Parse a binary string and generate a Packet from it.
|
68
68
|
# # auto-detect first header
|
69
|
-
# Packet.parse
|
69
|
+
# Packet.parse(str)
|
70
70
|
# # force decoding a Ethernet header for first header
|
71
|
-
# Packet.parse
|
71
|
+
# Packet.parse(str, first_header: 'Eth')
|
72
72
|
# @param [String] binary_str
|
73
73
|
# @param [String,nil] first_header First protocol header. +nil+ means discover it!
|
74
74
|
# @return [Packet]
|
75
75
|
# @raise [ArgumentError] +first_header+ is an unknown header
|
76
76
|
def self.parse(binary_str, first_header: nil)
|
77
|
-
new.parse
|
77
|
+
new.parse(binary_str, first_header: first_header)
|
78
78
|
end
|
79
79
|
|
80
80
|
# Capture packets from wire.
|
@@ -112,16 +112,23 @@ module PacketGen
|
|
112
112
|
Pcap.read(filename)
|
113
113
|
end
|
114
114
|
|
115
|
-
# Write packets to +filename
|
115
|
+
# Write packets to +filename+, as pcap or PcapNG file
|
116
116
|
#
|
117
117
|
# For more options, see {PcapNG::File}.
|
118
118
|
# @param [String] filename
|
119
119
|
# @param [Array<Packet>] packets packets to write
|
120
120
|
# @return [void]
|
121
|
+
# @since 4.0.0 write a pcap file if filename extension is +.pcap+
|
122
|
+
# @author Sylvain Daubert
|
123
|
+
# @author LemonTree55 (pcap)
|
121
124
|
def self.write(filename, packets)
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
+
if filename.end_with?('.pcap')
|
126
|
+
Pcap.write(filename, packets)
|
127
|
+
else
|
128
|
+
pf = PcapNG::File.new
|
129
|
+
pf.read_array(packets)
|
130
|
+
pf.to_f(filename)
|
131
|
+
end
|
125
132
|
end
|
126
133
|
|
127
134
|
# @private
|
@@ -142,7 +149,7 @@ module PacketGen
|
|
142
149
|
# options[:packet]= self is speedier than options.merge(packet: self)
|
143
150
|
options[:packet] = self
|
144
151
|
header = klass.new(options)
|
145
|
-
add_header
|
152
|
+
add_header(header)
|
146
153
|
self
|
147
154
|
end
|
148
155
|
|
@@ -159,7 +166,7 @@ module PacketGen
|
|
159
166
|
# options[:packet]= self is speedier than options.merge(packet: self)
|
160
167
|
options[:packet] = self
|
161
168
|
header = klass.new(options)
|
162
|
-
add_header
|
169
|
+
add_header(header, previous_header: prev)
|
163
170
|
idx = headers.index(prev) + 1
|
164
171
|
headers[idx, 0] = header
|
165
172
|
header[:body] = nxt
|
@@ -173,7 +180,7 @@ module PacketGen
|
|
173
180
|
# @return [Boolean]
|
174
181
|
# @raise [ArgumentError] unknown protocol
|
175
182
|
def is?(protocol)
|
176
|
-
klass = check_protocol
|
183
|
+
klass = check_protocol(protocol)
|
177
184
|
headers.any?(klass)
|
178
185
|
end
|
179
186
|
|
@@ -181,7 +188,7 @@ module PacketGen
|
|
181
188
|
# @return [void]
|
182
189
|
def calc_checksum
|
183
190
|
headers.reverse_each do |header|
|
184
|
-
header.calc_checksum if header.respond_to?
|
191
|
+
header.calc_checksum if header.respond_to?(:calc_checksum)
|
185
192
|
end
|
186
193
|
end
|
187
194
|
|
@@ -189,21 +196,24 @@ module PacketGen
|
|
189
196
|
# @return [void]
|
190
197
|
def calc_length
|
191
198
|
headers.reverse_each do |header|
|
192
|
-
header.calc_length if header.respond_to?
|
199
|
+
header.calc_length if header.respond_to?(:calc_length)
|
193
200
|
end
|
194
201
|
end
|
195
202
|
|
196
203
|
# Recalculate all calculatable fields (for now: length and checksum)
|
197
204
|
# @return [void]
|
205
|
+
# @author LemonTree55
|
198
206
|
def calc
|
199
|
-
|
200
|
-
|
207
|
+
headers.reverse_each do |header|
|
208
|
+
header.calc_length if header.respond_to?(:calc_length)
|
209
|
+
header.calc_checksum if header.respond_to?(:calc_checksum)
|
210
|
+
end
|
201
211
|
end
|
202
212
|
|
203
213
|
# Get packet body
|
204
214
|
# @return [Types]
|
205
215
|
def body
|
206
|
-
last_header[:body] if last_header.respond_to?
|
216
|
+
last_header[:body] if last_header.respond_to?(:body)
|
207
217
|
end
|
208
218
|
|
209
219
|
# Set packet body
|
@@ -229,7 +239,7 @@ module PacketGen
|
|
229
239
|
alias write to_f
|
230
240
|
|
231
241
|
# Send packet on wire. Use first header +#to_w+ method.
|
232
|
-
# @param [String] iface interface name. Default to first non-loopback interface
|
242
|
+
# @param [String,nil] iface interface name. Default to first non-loopback interface
|
233
243
|
# @param [Boolean] calc if +true+, call {#calc} on packet before sending it.
|
234
244
|
# @param [Integer] number number of times to send the packets
|
235
245
|
# @param [Integer,Float] interval time, in seconds, between sending 2 packets
|
@@ -239,12 +249,12 @@ module PacketGen
|
|
239
249
|
def to_w(iface=nil, calc: true, number: 1, interval: 1)
|
240
250
|
iface ||= PacketGen.default_iface
|
241
251
|
|
242
|
-
if first_header.respond_to?
|
252
|
+
if first_header.respond_to?(:to_w)
|
243
253
|
self.calc if calc
|
244
254
|
|
245
255
|
number.times do
|
246
256
|
first_header.to_w(iface)
|
247
|
-
sleep
|
257
|
+
sleep(interval) if number > 1
|
248
258
|
end
|
249
259
|
else
|
250
260
|
type = first_header.protocol_name
|
@@ -257,12 +267,12 @@ module PacketGen
|
|
257
267
|
# @param [Boolean] parsing set to +true+ to not update last current header field
|
258
268
|
# from binding with first other's one. Use only when current header field
|
259
269
|
# has its value set accordingly.
|
260
|
-
# @return [self] +self+ with new headers from +other+
|
270
|
+
# @return [self] +self+ updated with new headers from +other+
|
261
271
|
# @raise [BindingError] do not known how to encapsulate
|
262
272
|
# @since 1.1.0
|
263
273
|
def encapsulate(other, parsing: false)
|
264
274
|
other.headers.each_with_index do |h, i|
|
265
|
-
add_header
|
275
|
+
add_header(h, parsing: i.positive? || parsing)
|
266
276
|
end
|
267
277
|
end
|
268
278
|
|
@@ -286,8 +296,8 @@ module PacketGen
|
|
286
296
|
|
287
297
|
# Parse a binary string and populate Packet from it.
|
288
298
|
# @param [String] binary_str
|
289
|
-
# @param [String,nil] first_header First protocol header. +nil+ means discover it!
|
290
|
-
# @return [
|
299
|
+
# @param [String,nil] first_header First protocol header name. +nil+ means discover it!
|
300
|
+
# @return [self]
|
291
301
|
# @raise [ParseError] +first_header+ is an unknown header
|
292
302
|
# @raise [BindingError] unknwon binding between some headers
|
293
303
|
def parse(binary_str, first_header: nil)
|
@@ -299,7 +309,7 @@ module PacketGen
|
|
299
309
|
raise ParseError, "cannot identify first header in string: #{binary_str.inspect}" if first_header.nil?
|
300
310
|
end
|
301
311
|
|
302
|
-
add
|
312
|
+
add(first_header)
|
303
313
|
headers[-1, 1] = last_header.read(binary_str)
|
304
314
|
|
305
315
|
# Decode upper headers recursively
|
@@ -317,12 +327,14 @@ module PacketGen
|
|
317
327
|
str << Inspect.inspect_body(body)
|
318
328
|
end
|
319
329
|
|
330
|
+
# Check equality at binary level
|
320
331
|
# @param [Packet] other
|
321
332
|
# @return [Boolean]
|
322
333
|
def ==(other)
|
323
334
|
to_s == other.to_s
|
324
335
|
end
|
325
336
|
|
337
|
+
# +true+ is {#==} is +true+ with another packet, or if +other+ is a protocol name String, whose protocol is in Packet.
|
326
338
|
# @param [Packet] other
|
327
339
|
# @return [Boolean]
|
328
340
|
# @since 3.1.2
|
@@ -331,13 +343,13 @@ module PacketGen
|
|
331
343
|
when PacketGen::Packet
|
332
344
|
self == other
|
333
345
|
when String
|
334
|
-
is?
|
346
|
+
is?(other)
|
335
347
|
else
|
336
348
|
false
|
337
349
|
end
|
338
350
|
end
|
339
351
|
|
340
|
-
# Invert all possible
|
352
|
+
# Invert all possible attributes.in packet to create a reply.
|
341
353
|
# @return [self]
|
342
354
|
# @since 2.7.0
|
343
355
|
def reply!
|
@@ -363,7 +375,7 @@ module PacketGen
|
|
363
375
|
def initialize_copy(_other)
|
364
376
|
@headers = headers.map(&:dup)
|
365
377
|
headers.each do |header|
|
366
|
-
add_magic_header_method
|
378
|
+
add_magic_header_method(header)
|
367
379
|
end
|
368
380
|
invalidate_header_cache
|
369
381
|
end
|
@@ -410,17 +422,20 @@ module PacketGen
|
|
410
422
|
end
|
411
423
|
|
412
424
|
# @overload header(klass, layer=1)
|
425
|
+
# Get a header given its class and its layer (example: IP-in-IP encapsulation)
|
413
426
|
# @param [Class] klass
|
414
427
|
# @param [Integer] layer
|
415
428
|
# @overload header(klass, options={})
|
429
|
+
# Get a header given its class, and set some attributes
|
416
430
|
# @param [String] klass
|
417
|
-
# @param [Hash] options
|
418
|
-
# @raise [ArgumentError] unknown
|
419
|
-
# @return [Header::Base]
|
431
|
+
# @param [Hash] options attributes to set
|
432
|
+
# @raise [ArgumentError] unknown attribute
|
433
|
+
# @return [Header::Base,nil]
|
420
434
|
def header(klass, arg)
|
421
435
|
layer = arg.is_a?(Integer) ? arg : 1
|
422
436
|
header = find_header(klass, layer)
|
423
|
-
return
|
437
|
+
return nil if header.nil?
|
438
|
+
return header unless arg.is_a?(Hash)
|
424
439
|
|
425
440
|
arg.each do |key, value|
|
426
441
|
raise ArgumentError, "unknown #{key} attribute for #{klass}" unless header.respond_to?(:"#{key}=")
|
@@ -434,12 +449,14 @@ module PacketGen
|
|
434
449
|
# Get header from cache, or find it in packet
|
435
450
|
# @param [Class] klass
|
436
451
|
# @param [Integer] layer
|
437
|
-
# @return [Header::Base]
|
452
|
+
# @return [Header::Base,nil]
|
438
453
|
def find_header(klass, layer)
|
439
454
|
header = fetch_header_from_cache(klass, layer)
|
440
455
|
return header if header
|
441
456
|
|
442
|
-
header = headers.select { |h| h.is_a?
|
457
|
+
header = headers.select { |h| h.is_a?(klass) }[layer - 1]
|
458
|
+
return nil if header.nil?
|
459
|
+
|
443
460
|
add_header_to_cache(header, klass, layer)
|
444
461
|
header
|
445
462
|
end
|
@@ -468,9 +485,9 @@ module PacketGen
|
|
468
485
|
header.packet = self
|
469
486
|
headers << header unless previous_header
|
470
487
|
|
471
|
-
return if respond_to?
|
488
|
+
return if respond_to?(header.method_name)
|
472
489
|
|
473
|
-
add_magic_header_method
|
490
|
+
add_magic_header_method(header)
|
474
491
|
end
|
475
492
|
|
476
493
|
# Bind +header+ to +prev_header+.
|
@@ -497,7 +514,7 @@ module PacketGen
|
|
497
514
|
|
498
515
|
# Try to guess header from +binary_str+
|
499
516
|
# @param [String] binary_str
|
500
|
-
# @return [String] header/protocol name
|
517
|
+
# @return [String,nil] header/protocol name, or nil if cannot be guessed
|
501
518
|
def guess_first_header(binary_str)
|
502
519
|
first_header = nil
|
503
520
|
Header.all.each do |hklass|
|
@@ -529,7 +546,7 @@ module PacketGen
|
|
529
546
|
nheader = nheader.read(last_known_hdr.body)
|
530
547
|
next unless nheader.parse?
|
531
548
|
|
532
|
-
add_header
|
549
|
+
add_header(nheader, parsing: true)
|
533
550
|
break if last_header == last_known_hdr
|
534
551
|
end
|
535
552
|
end
|
data/lib/packetgen/pcap.rb
CHANGED
@@ -8,8 +8,7 @@
|
|
8
8
|
require_relative 'pcaprub_wrapper'
|
9
9
|
|
10
10
|
module PacketGen
|
11
|
-
# Module to read PCAP files
|
12
|
-
# @author Sylvain Daubert
|
11
|
+
# Module to read/write PCAP files
|
13
12
|
# @api private
|
14
13
|
# @since 3.1.4
|
15
14
|
module Pcap
|
@@ -17,14 +16,26 @@ module PacketGen
|
|
17
16
|
# @param [String] filename
|
18
17
|
# @return [Array<Packet>]
|
19
18
|
# @author Kent Gruber
|
19
|
+
# @author LemonTree55
|
20
20
|
def self.read(filename)
|
21
21
|
packets = []
|
22
|
-
PCAPRUBWrapper.read_pcap(filename: filename) do |
|
23
|
-
|
22
|
+
PCAPRUBWrapper.read_pcap(filename: filename) do |raw_packet|
|
23
|
+
packet = PacketGen.parse(raw_packet.to_s)
|
24
|
+
next if packet.nil?
|
24
25
|
|
25
26
|
packets << packet
|
26
27
|
end
|
27
28
|
packets
|
28
29
|
end
|
30
|
+
|
31
|
+
# Write binary packets to a PCAP file
|
32
|
+
# @param [String] filename
|
33
|
+
# @param [Array<String>] packets
|
34
|
+
# @return [void]
|
35
|
+
# @since 4.0.0
|
36
|
+
# @author LemonTree55
|
37
|
+
def self.write(filename, packets)
|
38
|
+
PCAPRUBWrapper.pcap_write(filename, packets.map(&:to_s))
|
39
|
+
end
|
29
40
|
end
|
30
41
|
end
|
@@ -10,63 +10,64 @@ module PacketGen
|
|
10
10
|
module PcapNG
|
11
11
|
# @abstract Base class for all block types
|
12
12
|
# @author Sylvain Daubert
|
13
|
-
class Block <
|
13
|
+
class Block < BinStruct::Struct
|
14
14
|
# @return [:little, :big]
|
15
15
|
attr_accessor :endian
|
16
16
|
|
17
17
|
# @!attribute type
|
18
18
|
# 32-bit block type
|
19
19
|
# @return [Integer]
|
20
|
-
|
20
|
+
define_attr :type, BinStruct::Int32
|
21
21
|
# @!attribute block_len
|
22
22
|
# 32-bit block length
|
23
23
|
# @return [Integer]
|
24
|
-
|
25
|
-
# @!attribute
|
24
|
+
define_attr :block_len, BinStruct::Int32
|
25
|
+
# @!attribute block_len2
|
26
26
|
# 32-bit block length
|
27
27
|
# @return [Integer]
|
28
|
-
|
28
|
+
define_attr :block_len2, BinStruct::Int32
|
29
29
|
|
30
30
|
def initialize(options={})
|
31
31
|
super
|
32
|
+
endianness(options[:endian] || :little)
|
33
|
+
recalc_block_len
|
32
34
|
end
|
33
35
|
|
34
36
|
# Has this block option?
|
35
37
|
# @return [Boolean]
|
36
38
|
# @since 2.7.0
|
37
39
|
def options?
|
38
|
-
@
|
40
|
+
@attributes.key?(:options) && @attributes[:options].sz.positive?
|
39
41
|
end
|
40
42
|
|
41
|
-
# Calculate block length and update
|
43
|
+
# Calculate block length and update +block_len+ and +block_len2+ fields
|
42
44
|
# @return [void]
|
43
45
|
def recalc_block_len
|
44
|
-
len =
|
46
|
+
len = attributes.map { |f| @attributes[f].to_s }.join.size
|
45
47
|
self.block_len = self.block_len2 = len
|
46
48
|
end
|
47
49
|
|
48
50
|
# Pad given field to 32 bit boundary, if needed
|
49
|
-
# @param [Array<Symbol>] fields
|
51
|
+
# @param [Array<Symbol>] fields fields to pad
|
50
52
|
# @return [void]
|
53
|
+
# @author LemonTree55
|
51
54
|
def pad_field(*fields)
|
52
55
|
fields.each do |field|
|
53
|
-
obj = @
|
54
|
-
|
55
|
-
obj << "\x00" * pad_size
|
56
|
+
obj = @attributes[field]
|
57
|
+
obj << "\x00" * -(obj.sz % -4)
|
56
58
|
end
|
57
59
|
end
|
58
60
|
|
59
61
|
private
|
60
62
|
|
61
63
|
# Set the endianness for the various Int classes handled by self.
|
62
|
-
# Must be called by all subclass #initialize method.
|
63
64
|
# @param [:little, :big] endian
|
64
65
|
# @return [:little, :big] returns endian
|
65
66
|
def endianness(endian)
|
66
67
|
raise ArgumentError, "unknown endianness for #{self.class}" unless %i[little big].include?(endian)
|
67
68
|
|
68
69
|
@endian = endian
|
69
|
-
@
|
70
|
+
@attributes.each_value { |v| v.endian = endian if v.is_a?(BinStruct::Int) }
|
70
71
|
endian
|
71
72
|
end
|
72
73
|
|
@@ -75,19 +76,19 @@ module PacketGen
|
|
75
76
|
end
|
76
77
|
|
77
78
|
def to_io(str_or_io)
|
78
|
-
return str_or_io if str_or_io.respond_to?
|
79
|
+
return str_or_io if str_or_io.respond_to?(:read)
|
79
80
|
|
80
81
|
StringIO.new(force_binary(str_or_io.to_s))
|
81
82
|
end
|
82
83
|
|
83
84
|
def remove_padding(io, data_len)
|
84
85
|
data_pad_len = (4 - (data_len % 4)) % 4
|
85
|
-
io.read
|
86
|
+
io.read(data_pad_len)
|
86
87
|
data_pad_len
|
87
88
|
end
|
88
89
|
|
89
90
|
def read_blocklen2_and_check(io)
|
90
|
-
self[:block_len2].read
|
91
|
+
self[:block_len2].read(io.read(4))
|
91
92
|
check_len_coherency
|
92
93
|
end
|
93
94
|
end
|
data/lib/packetgen/pcapng/epb.rb
CHANGED
@@ -34,29 +34,29 @@ module PacketGen
|
|
34
34
|
# @!attribute interface_id
|
35
35
|
# 32-bit interface ID
|
36
36
|
# @return [Integer]
|
37
|
-
|
37
|
+
define_attr_before :block_len2, :interface_id, BinStruct::Int32, default: 0
|
38
38
|
# @!attribute tsh
|
39
39
|
# high 32-bit timestamp value
|
40
40
|
# @return [Integer]
|
41
|
-
|
41
|
+
define_attr_before :block_len2, :tsh, BinStruct::Int32, default: 0
|
42
42
|
# @!attribute tsl
|
43
43
|
# low 32-bit imestamp value
|
44
44
|
# @return [Integer]
|
45
|
-
|
45
|
+
define_attr_before :block_len2, :tsl, BinStruct::Int32, default: 0
|
46
46
|
# @!attribute cap_len
|
47
47
|
# 32-bit capture length
|
48
48
|
# @return [Integer]
|
49
|
-
|
49
|
+
define_attr_before :block_len2, :cap_len, BinStruct::Int32, default: 0
|
50
50
|
# @!attribute orig_len
|
51
51
|
# 32-bit original length
|
52
52
|
# @return [Integer]
|
53
|
-
|
53
|
+
define_attr_before :block_len2, :orig_len, BinStruct::Int32, default: 0
|
54
54
|
# @!attribute data
|
55
|
-
# @return [
|
56
|
-
|
55
|
+
# @return [BinStruct::String]
|
56
|
+
define_attr_before :block_len2, :data, BinStruct::String
|
57
57
|
# @!attribute options
|
58
|
-
# @return [
|
59
|
-
|
58
|
+
# @return [BinStruct::String]
|
59
|
+
define_attr_before :block_len2, :options, BinStruct::String
|
60
60
|
|
61
61
|
# @param [Hash] options
|
62
62
|
# @option options [:little, :big] :endian set block endianness
|
@@ -74,8 +74,6 @@ module PacketGen
|
|
74
74
|
# @option options [Integer] :block_len2 block total length
|
75
75
|
def initialize(options={})
|
76
76
|
super
|
77
|
-
endianness(options[:endian] || :little)
|
78
|
-
recalc_block_len
|
79
77
|
self.type = options[:type] || PcapNG::EPB_TYPE.to_i
|
80
78
|
end
|
81
79
|
|
@@ -87,9 +85,9 @@ module PacketGen
|
|
87
85
|
return self if io.eof?
|
88
86
|
|
89
87
|
%i[type block_len interface_id tsh tsl cap_len orig_len].each do |attr|
|
90
|
-
self[attr].read
|
88
|
+
self[attr].read(io.read(self[attr].sz))
|
91
89
|
end
|
92
|
-
self[:data].read
|
90
|
+
self[:data].read(io.read(self.cap_len))
|
93
91
|
read_options(io)
|
94
92
|
read_blocklen2_and_check(io)
|
95
93
|
|
@@ -115,7 +113,7 @@ module PacketGen
|
|
115
113
|
# Return the object as a String
|
116
114
|
# @return [String]
|
117
115
|
def to_s
|
118
|
-
pad_field
|
116
|
+
pad_field(:data, :options)
|
119
117
|
recalc_block_len
|
120
118
|
super
|
121
119
|
end
|
@@ -133,7 +131,7 @@ module PacketGen
|
|
133
131
|
def read_options(io)
|
134
132
|
data_pad_len = remove_padding(io, self.cap_len)
|
135
133
|
options_len = self.block_len - self.cap_len - data_pad_len - MIN_SIZE
|
136
|
-
self[:options].read
|
134
|
+
self[:options].read(io.read(options_len))
|
137
135
|
end
|
138
136
|
end
|
139
137
|
end
|
@@ -140,35 +140,6 @@ module PacketGen
|
|
140
140
|
@sections.clear
|
141
141
|
end
|
142
142
|
|
143
|
-
# @deprecated
|
144
|
-
# Prefer use of {#to_a} or {#to_h}.
|
145
|
-
# Translates a {File} into an array of packets.
|
146
|
-
# @param [Hash] options
|
147
|
-
# @option options [String] :file if given, object is cleared and filename
|
148
|
-
# is analyzed before generating array. Else, array is generated from +self+
|
149
|
-
# @option options [Boolean] :keep_timestamps if +true+ (default value: +false+),
|
150
|
-
# generates an array of hashes, each one with timestamp as key and packet
|
151
|
-
# as value. There is one hash per packet.
|
152
|
-
# @return [Array<Packet>,Array<Hash>]
|
153
|
-
def file_to_array(options={})
|
154
|
-
Deprecation.deprecated(self.class, __method__)
|
155
|
-
|
156
|
-
file = options[:file] || options[:filename]
|
157
|
-
reread file
|
158
|
-
|
159
|
-
ary = []
|
160
|
-
blk = if options[:keep_timestamps] || options[:keep_ts]
|
161
|
-
proc { |pkt| { pkt.timestamp => pkt.data.to_s } }
|
162
|
-
else
|
163
|
-
proc { |pkt| pkt.data.to_s }
|
164
|
-
end
|
165
|
-
each_packet_with_interface do |pkt, _itf|
|
166
|
-
ary << blk.call(pkt)
|
167
|
-
end
|
168
|
-
|
169
|
-
ary
|
170
|
-
end
|
171
|
-
|
172
143
|
# Translates a {File} into an array of packets.
|
173
144
|
# @return [Array<Packet>]
|
174
145
|
# @since 3.1.6
|
@@ -223,41 +194,6 @@ module PacketGen
|
|
223
194
|
self.to_file(filename.to_s, append: true)
|
224
195
|
end
|
225
196
|
|
226
|
-
# @deprecated Prefer use of {#read_array} or {#read_hash}.
|
227
|
-
# @overload array_to_file(ary)
|
228
|
-
# Update {File} object with packets.
|
229
|
-
# @param [Array] ary as generated by {#file_to_array} or Array of Packet objects.
|
230
|
-
# Update {File} object without writing file on disk
|
231
|
-
# @return [self]
|
232
|
-
# @overload array_to_file(options={})
|
233
|
-
# Update {File} and/or write it to a file
|
234
|
-
# @param [Hash] options
|
235
|
-
# @option options [String] :file file written on disk only if given
|
236
|
-
# @option options [Array] :array can either be an array of packet data,
|
237
|
-
# or a hash-value pair of timestamp => data.
|
238
|
-
# @option options [Time] :timestamp set an initial timestamp
|
239
|
-
# @option options [Integer] :ts_inc set the increment between timestamps.
|
240
|
-
# Defaults to 1
|
241
|
-
# @option options [Boolean] :append if +true+, append packets to the end of
|
242
|
-
# the file
|
243
|
-
# @return [Array] see return value from {#to_file}
|
244
|
-
def array_to_file(options={})
|
245
|
-
filename, ary, ts, ts_inc, append = array_to_file_options(options)
|
246
|
-
|
247
|
-
section = create_new_shb_section
|
248
|
-
|
249
|
-
ary.each do |pkt|
|
250
|
-
classify_block(section, epb_from_pkt(pkt, section, ts))
|
251
|
-
ts += ts_inc
|
252
|
-
end
|
253
|
-
|
254
|
-
if filename
|
255
|
-
self.to_f(filename, append: append)
|
256
|
-
else
|
257
|
-
self
|
258
|
-
end
|
259
|
-
end
|
260
|
-
|
261
197
|
# Update current object from an array of packets
|
262
198
|
# @param [Array<Packet>] packets
|
263
199
|
# @param [Time, nil] timestamp initial timestamp, used for first packet
|
@@ -335,13 +271,13 @@ module PacketGen
|
|
335
271
|
# @param [IO] io stream from which parse SHB
|
336
272
|
# @return [SHB]
|
337
273
|
def parse_shb(shb, io)
|
338
|
-
type =
|
274
|
+
type = BinStruct::Int32.new(value: 0, endian: shb.endian).read(io.read(4))
|
339
275
|
io.seek(-4, IO::SEEK_CUR)
|
340
276
|
parse(type, io, shb)
|
341
277
|
end
|
342
278
|
|
343
279
|
# Parse a block from its type
|
344
|
-
# @param [
|
280
|
+
# @param [BinStruct::Int32] type
|
345
281
|
# @param [IO] io stream from which parse block
|
346
282
|
# @param [SHB] shb header of current section
|
347
283
|
# @return [Block]
|
@@ -352,7 +288,7 @@ module PacketGen
|
|
352
288
|
end
|
353
289
|
|
354
290
|
# Guess class to use from type
|
355
|
-
# @param [
|
291
|
+
# @param [BinStruct::Int] type
|
356
292
|
# @return [Block]
|
357
293
|
def guess_block_type(type)
|
358
294
|
BLOCK_TYPES.key?(type.to_i) ? BLOCK_TYPES[type.to_i] : UnknownBlock
|
@@ -376,36 +312,6 @@ module PacketGen
|
|
376
312
|
end
|
377
313
|
end
|
378
314
|
|
379
|
-
def array_to_file_options(options)
|
380
|
-
case options
|
381
|
-
when Hash
|
382
|
-
array_to_file_options_from_hash(options)
|
383
|
-
when Array
|
384
|
-
[nil, options, Time.now, 1, false]
|
385
|
-
else
|
386
|
-
raise ArgumentError, 'unknown argument. Need either a Hash or Array'
|
387
|
-
end
|
388
|
-
end
|
389
|
-
|
390
|
-
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
391
|
-
# Extract and check options for #array_to_file
|
392
|
-
def array_to_file_options_from_hash(options)
|
393
|
-
%i[filename arr ts].each do |deprecated_opt|
|
394
|
-
Deprecation.deprecated_option(self.class, :array_to_file, deprecated_opt) if options[deprecated_opt]
|
395
|
-
end
|
396
|
-
|
397
|
-
filename = options[:filename] || options[:file]
|
398
|
-
ary = options[:array] || options[:arr]
|
399
|
-
raise ArgumentError, ':array parameter needs to be an array' unless ary.is_a? Array
|
400
|
-
|
401
|
-
ts = options[:timestamp] || options[:ts] || Time.now
|
402
|
-
ts_inc = options[:ts_inc] || 1
|
403
|
-
append = !options[:append].nil?
|
404
|
-
|
405
|
-
[filename, ary, ts, ts_inc, append]
|
406
|
-
end
|
407
|
-
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
408
|
-
|
409
315
|
def create_new_shb_section
|
410
316
|
section = SHB.new
|
411
317
|
@sections << section
|