packetgen 2.8.7 → 3.0.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 +4 -4
- data/.rubocop.yml +0 -1
- data/README.md +5 -4
- data/lib/packetgen.rb +6 -12
- data/lib/packetgen/capture.rb +43 -39
- data/lib/packetgen/config.rb +0 -1
- data/lib/packetgen/deprecation.rb +1 -1
- data/lib/packetgen/header.rb +9 -9
- data/lib/packetgen/header/asn1_base.rb +10 -10
- data/lib/packetgen/header/base.rb +42 -101
- data/lib/packetgen/header/dhcp/option.rb +5 -11
- data/lib/packetgen/header/dhcpv6/duid.rb +2 -0
- data/lib/packetgen/header/dhcpv6/option.rb +2 -19
- data/lib/packetgen/header/dhcpv6/options.rb +7 -0
- data/lib/packetgen/header/dns.rb +5 -23
- data/lib/packetgen/header/dns/name.rb +1 -0
- data/lib/packetgen/header/dns/qdsection.rb +1 -0
- data/lib/packetgen/header/dns/question.rb +3 -7
- data/lib/packetgen/header/dns/rr.rb +3 -0
- data/lib/packetgen/header/dns/rrsection.rb +1 -0
- data/lib/packetgen/header/dot11.rb +1 -17
- data/lib/packetgen/header/dot1x.rb +1 -0
- data/lib/packetgen/header/eap.rb +4 -7
- data/lib/packetgen/header/eth.rb +2 -0
- data/lib/packetgen/header/http/headers.rb +3 -0
- data/lib/packetgen/header/http/request.rb +5 -4
- data/lib/packetgen/header/http/response.rb +5 -4
- data/lib/packetgen/header/icmp.rb +6 -0
- data/lib/packetgen/header/icmpv6.rb +6 -0
- data/lib/packetgen/header/igmpv3/mq.rb +2 -0
- data/lib/packetgen/header/ip.rb +32 -30
- data/lib/packetgen/header/ip/addr.rb +1 -0
- data/lib/packetgen/header/ip/option.rb +23 -20
- data/lib/packetgen/header/ip/options.rb +11 -24
- data/lib/packetgen/header/ipv6.rb +45 -34
- data/lib/packetgen/header/ipv6/addr.rb +2 -0
- data/lib/packetgen/header/ipv6/hop_by_hop.rb +7 -31
- data/lib/packetgen/header/mdns.rb +1 -0
- data/lib/packetgen/header/mldv2/mlq.rb +2 -0
- data/lib/packetgen/header/ospfv2/lsa.rb +15 -25
- data/lib/packetgen/header/ospfv3/ipv6_prefix.rb +1 -1
- data/lib/packetgen/header/ospfv3/lsa.rb +8 -25
- data/lib/packetgen/header/snmp.rb +2 -0
- data/lib/packetgen/header/tcp.rb +23 -2
- data/lib/packetgen/header/tcp/option.rb +51 -52
- data/lib/packetgen/header/tcp/options.rb +17 -52
- data/lib/packetgen/header/tftp.rb +3 -0
- data/lib/packetgen/header/udp.rb +8 -0
- data/lib/packetgen/packet.rb +119 -102
- data/lib/packetgen/pcapng/block.rb +4 -10
- data/lib/packetgen/pcapng/epb.rb +4 -4
- data/lib/packetgen/pcapng/file.rb +7 -3
- data/lib/packetgen/pcapng/idb.rb +2 -2
- data/lib/packetgen/pcapng/shb.rb +3 -3
- data/lib/packetgen/pcapng/spb.rb +1 -8
- data/lib/packetgen/pcapng/unknown_block.rb +0 -7
- data/lib/packetgen/types.rb +1 -0
- data/lib/packetgen/types/array.rb +73 -71
- data/lib/packetgen/types/cstring.rb +1 -1
- data/lib/packetgen/types/enum.rb +3 -3
- data/lib/packetgen/types/fields.rb +66 -106
- data/lib/packetgen/types/int.rb +9 -5
- data/lib/packetgen/types/length_from.rb +45 -0
- data/lib/packetgen/types/oui.rb +2 -0
- data/lib/packetgen/types/string.rb +10 -16
- data/lib/packetgen/types/tlv.rb +7 -15
- data/lib/packetgen/utils.rb +8 -8
- data/lib/packetgen/utils/arp_spoofer.rb +1 -2
- data/lib/packetgen/version.rb +1 -1
- metadata +3 -21
- data/lib/packetgen/header/crypto.rb +0 -62
- data/lib/packetgen/header/esp.rb +0 -413
- data/lib/packetgen/header/ike.rb +0 -243
- data/lib/packetgen/header/ike/auth.rb +0 -165
- data/lib/packetgen/header/ike/cert.rb +0 -76
- data/lib/packetgen/header/ike/certreq.rb +0 -66
- data/lib/packetgen/header/ike/id.rb +0 -99
- data/lib/packetgen/header/ike/ke.rb +0 -79
- data/lib/packetgen/header/ike/nonce.rb +0 -40
- data/lib/packetgen/header/ike/notify.rb +0 -176
- data/lib/packetgen/header/ike/payload.rb +0 -315
- data/lib/packetgen/header/ike/sa.rb +0 -561
- data/lib/packetgen/header/ike/sk.rb +0 -261
- data/lib/packetgen/header/ike/ts.rb +0 -270
- data/lib/packetgen/header/ike/vendor_id.rb +0 -39
- data/lib/packetgen/header/netbios.rb +0 -20
- data/lib/packetgen/header/netbios/datagram.rb +0 -105
- data/lib/packetgen/header/netbios/name.rb +0 -67
- data/lib/packetgen/header/netbios/session.rb +0 -64
@@ -5,87 +5,52 @@
|
|
5
5
|
|
6
6
|
# frozen_string_literal: true
|
7
7
|
|
8
|
+
require_relative 'option'
|
9
|
+
|
8
10
|
module PacketGen
|
9
11
|
module Header
|
10
12
|
class TCP
|
11
13
|
# Container for TCP options in {TCP TCP header}.
|
12
14
|
# @author Sylvain Daubert
|
13
15
|
class Options < Types::Array
|
16
|
+
set_of Option
|
17
|
+
|
14
18
|
# Get {Option} subclasses
|
15
19
|
# @return [Array<Class>]
|
16
20
|
def self.option_classes
|
17
21
|
return @klasses if defined? @klasses
|
22
|
+
|
18
23
|
@klasses = []
|
19
24
|
Option.constants.each do |cst|
|
20
25
|
next unless cst.to_s.end_with? '_KIND'
|
26
|
+
|
21
27
|
optname = cst.to_s.sub(/_KIND/, '')
|
22
28
|
@klasses[Option.const_get(cst)] = TCP.const_get(optname)
|
23
29
|
end
|
24
30
|
@klasses
|
25
31
|
end
|
26
32
|
|
27
|
-
# Read TCP header options from a string
|
28
|
-
# @param [String] str binary string
|
29
|
-
# @return [self]
|
30
|
-
def read(str)
|
31
|
-
clear
|
32
|
-
return self if str.nil?
|
33
|
-
PacketGen.force_binary str
|
34
|
-
|
35
|
-
i = 0
|
36
|
-
klasses = self.class.option_classes
|
37
|
-
while i < str.to_s.length
|
38
|
-
kind = str[i, 1].unpack('C').first
|
39
|
-
this_option = if klasses[kind].nil?
|
40
|
-
Option.new
|
41
|
-
else
|
42
|
-
klasses[kind].new
|
43
|
-
end
|
44
|
-
this_option.read str[i, str.size]
|
45
|
-
unless this_option.length?
|
46
|
-
this_option.length = nil
|
47
|
-
this_option.value = nil
|
48
|
-
end
|
49
|
-
self << this_option
|
50
|
-
i += this_option.sz
|
51
|
-
end
|
52
|
-
self
|
53
|
-
end
|
54
|
-
|
55
|
-
# @deprecated use {#push} or {#<<}
|
56
|
-
# Add a well-known option
|
57
|
-
# @param [String] opt option name
|
58
|
-
# @param [Object] value
|
59
|
-
# @return [self]
|
60
|
-
# @raise [ArgumentError] unknown option
|
61
|
-
def add(opt, value=nil)
|
62
|
-
Deprecation.deprecated(self.class, __method__, 'push')
|
63
|
-
option = record_from_hash(opt: opt, value: value)
|
64
|
-
self << option
|
65
|
-
self
|
66
|
-
end
|
67
|
-
|
68
33
|
private
|
69
34
|
|
70
35
|
def record_from_hash(hsh)
|
71
36
|
if hsh.key? :opt
|
72
37
|
klassname = hsh.delete(:opt)
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
else
|
80
|
-
raise ArgumentError, 'opt should be a TCP::Option subclass'
|
81
|
-
end
|
38
|
+
raise ArgumentError, 'opt should be a TCP::Option subclass' unless TCP.const_defined?(klassname)
|
39
|
+
|
40
|
+
klass = TCP.const_get(klassname)
|
41
|
+
raise ArgumentError, 'opt should be a TCP::Option subclass' unless klass < Option
|
42
|
+
|
43
|
+
klass.new(hsh)
|
82
44
|
else
|
83
45
|
hsh
|
84
46
|
end
|
85
47
|
end
|
48
|
+
|
49
|
+
def real_type(opt)
|
50
|
+
klasses = self.class.option_classes
|
51
|
+
klasses[opt.kind].nil? ? OPtion : klasses[opt.kind]
|
52
|
+
end
|
86
53
|
end
|
87
54
|
end
|
88
55
|
end
|
89
56
|
end
|
90
|
-
|
91
|
-
require_relative 'option'
|
@@ -104,9 +104,11 @@ module PacketGen
|
|
104
104
|
ary.each do |pkt|
|
105
105
|
if server_tid.nil?
|
106
106
|
next unless pkt.is?('UDP') && (pkt.udp.dport == client_tid)
|
107
|
+
|
107
108
|
server_tid = pkt.udp.sport
|
108
109
|
else
|
109
110
|
next unless pkt.is?('UDP')
|
111
|
+
|
110
112
|
tids = [server_tid, client_tid]
|
111
113
|
ports = [pkt.udp.sport, pkt.udp.dport]
|
112
114
|
next unless (tids - ports).empty?
|
@@ -132,6 +134,7 @@ module PacketGen
|
|
132
134
|
# @return [void]
|
133
135
|
def added_to_packet(packet)
|
134
136
|
return if packet.respond_to? :tftp
|
137
|
+
|
135
138
|
packet.instance_eval("def tftp(arg=nil); header(#{self.class}, arg); end")
|
136
139
|
end
|
137
140
|
|
data/lib/packetgen/header/udp.rb
CHANGED
@@ -7,6 +7,14 @@
|
|
7
7
|
|
8
8
|
module PacketGen
|
9
9
|
module Header
|
10
|
+
# UDP header ({https://tools.ietf.org/html/rfc768 RFC 768})
|
11
|
+
# 0 1 2 3
|
12
|
+
# 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
13
|
+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
14
|
+
# | Source Port | Destination Port |
|
15
|
+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
16
|
+
# | Length | Checksum |
|
17
|
+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
10
18
|
# A UDP header consists of:
|
11
19
|
# * a source port field ({#sport}, {Types::Int16} type),
|
12
20
|
# * a destination port field ({#dport}, +Int16+ type),
|
data/lib/packetgen/packet.rb
CHANGED
@@ -46,15 +46,10 @@ module PacketGen
|
|
46
46
|
# == Save packets to a file
|
47
47
|
# Packet.write 'file.pcapng', packets
|
48
48
|
#
|
49
|
-
# @since 2.0.0
|
50
|
-
#
|
51
|
-
# Packet accessor has changed. When header class is in a namespace
|
52
|
-
# (for example Dot11::* header classes), to avoid clashes in names, such
|
53
|
-
# accessors are named +namespace_class+. For example {Header::Dot11::Data}
|
54
|
-
# header is now accessed through +Packet#dot11_data+ and nor more +Packet#data+).
|
55
49
|
# @author Sylvain Daubert
|
56
50
|
class Packet
|
57
|
-
#
|
51
|
+
# Get packet headers, ordered as they appear in the packet.
|
52
|
+
# @return [Array<Header::Base>]
|
58
53
|
attr_reader :headers
|
59
54
|
|
60
55
|
# Create a new Packet
|
@@ -78,19 +73,14 @@ module PacketGen
|
|
78
73
|
new.parse binary_str, first_header: first_header
|
79
74
|
end
|
80
75
|
|
81
|
-
# Capture packets
|
82
|
-
#
|
83
|
-
# @
|
84
|
-
#
|
85
|
-
#
|
86
|
-
# @option options [Integer] :timeout maximum number of seconds before end
|
87
|
-
# of capture
|
88
|
-
# @option options [String] :filter bpf filter
|
89
|
-
# @option options [Boolean] :promiscuous
|
90
|
-
# @yieldparam [Packet] packet if a block is given, yield each captured packet
|
76
|
+
# Capture packets from wire.
|
77
|
+
# Same arguments as {Capture#initialize}
|
78
|
+
# @see Capture#initialize
|
79
|
+
# @yieldparam [Packet,String] packet if a block is given, yield each
|
80
|
+
# captured packet (Packet or raw data String, depending on +:parse+ option)
|
91
81
|
# @return [Array<Packet>] captured packet
|
92
|
-
def self.capture(
|
93
|
-
capture = Capture.new(
|
82
|
+
def self.capture(**kwargs)
|
83
|
+
capture = Capture.new(kwargs)
|
94
84
|
if block_given?
|
95
85
|
capture.start { |packet| yield packet }
|
96
86
|
else
|
@@ -105,14 +95,17 @@ module PacketGen
|
|
105
95
|
# @param [String] filename PcapNG or Pcap file.
|
106
96
|
# @return [Array<Packet>]
|
107
97
|
# @author Sylvain Daubert
|
108
|
-
# @author Kent Gruber
|
98
|
+
# @author Kent Gruber - Pcap format
|
99
|
+
# @since 2.0.0 Also read Pcap format.
|
109
100
|
def self.read(filename)
|
110
101
|
PcapNG::File.new.read_packets filename
|
111
102
|
rescue StandardError => e
|
112
103
|
raise ArgumentError, e unless File.extname(filename.downcase) == '.pcap'
|
104
|
+
|
113
105
|
packets = []
|
114
106
|
PCAPRUB::Pcap.open_offline(filename).each_packet do |packet|
|
115
107
|
next unless (packet = PacketGen.parse(packet.to_s))
|
108
|
+
|
116
109
|
packets << packet
|
117
110
|
end
|
118
111
|
packets
|
@@ -135,7 +128,7 @@ module PacketGen
|
|
135
128
|
@headers = []
|
136
129
|
end
|
137
130
|
|
138
|
-
# Add a protocol
|
131
|
+
# Add a protocol header in packet.
|
139
132
|
# @param [String] protocol
|
140
133
|
# @param [Hash] options protocol specific options
|
141
134
|
# @return [self]
|
@@ -160,24 +153,27 @@ module PacketGen
|
|
160
153
|
nxt = prev.body
|
161
154
|
header = klass.new(options.merge!(packet: self))
|
162
155
|
add_header header, previous_header: prev
|
163
|
-
idx =
|
164
|
-
|
156
|
+
idx = headers.index(prev) + 1
|
157
|
+
headers[idx, 0] = header
|
165
158
|
header[:body] = nxt
|
166
159
|
self
|
167
160
|
end
|
168
161
|
|
169
|
-
# Check if a protocol header is embedded in packet
|
162
|
+
# Check if a protocol header is embedded in packet.
|
163
|
+
# pkt = PacketGen.gen('IP').add('UDP')
|
164
|
+
# pkt.is?('IP') #=> true
|
165
|
+
# pkt.is?('TCP') #=> false
|
170
166
|
# @return [Boolean]
|
171
167
|
# @raise [ArgumentError] unknown protocol
|
172
168
|
def is?(protocol)
|
173
169
|
klass = check_protocol protocol
|
174
|
-
|
170
|
+
headers.any? { |h| h.is_a? klass }
|
175
171
|
end
|
176
172
|
|
177
173
|
# Recalculate all packet checksums
|
178
174
|
# @return [void]
|
179
175
|
def calc_checksum
|
180
|
-
|
176
|
+
headers.reverse_each do |header|
|
181
177
|
header.calc_checksum if header.respond_to? :calc_checksum
|
182
178
|
end
|
183
179
|
end
|
@@ -185,7 +181,7 @@ module PacketGen
|
|
185
181
|
# Recalculate all packet length fields
|
186
182
|
# @return [void]
|
187
183
|
def calc_length
|
188
|
-
|
184
|
+
headers.each do |header|
|
189
185
|
header.calc_length if header.respond_to? :calc_length
|
190
186
|
end
|
191
187
|
end
|
@@ -200,23 +196,23 @@ module PacketGen
|
|
200
196
|
# Get packet body
|
201
197
|
# @return [Types]
|
202
198
|
def body
|
203
|
-
|
199
|
+
last_header[:body] if last_header.respond_to? :body
|
204
200
|
end
|
205
201
|
|
206
202
|
# Set packet body
|
207
203
|
# @param [String] str
|
208
204
|
# @return [void]
|
209
205
|
def body=(str)
|
210
|
-
|
206
|
+
last_header.body = str
|
211
207
|
end
|
212
208
|
|
213
|
-
# Get binary string
|
209
|
+
# Get binary string (i.e. binary string sent on or received from network).
|
214
210
|
# @return [String]
|
215
211
|
def to_s
|
216
|
-
|
212
|
+
first_header.to_s
|
217
213
|
end
|
218
214
|
|
219
|
-
# Write a PCapNG file
|
215
|
+
# Write packet to a PCapNG file on disk.
|
220
216
|
# @param [String] filename
|
221
217
|
# @return [Array] see return from {PcapNG::File#to_file}
|
222
218
|
# @see File
|
@@ -225,27 +221,26 @@ module PacketGen
|
|
225
221
|
end
|
226
222
|
alias write to_f
|
227
223
|
|
228
|
-
#
|
224
|
+
# Send packet on wire. Use first header +#to_w+ method.
|
229
225
|
# @param [String] iface interface name. Default to first non-loopback interface
|
230
|
-
# @param [Boolean] calc call {#calc} on packet before sending it
|
226
|
+
# @param [Boolean] calc if +true+, call {#calc} on packet before sending it.
|
231
227
|
# @param [Integer] number number of times to send the packets
|
232
228
|
# @param [Integer,Float] interval time, in seconds, between sending 2 packets
|
233
229
|
# @return [void]
|
234
230
|
# @since 2.1.4 add `calc`, `number` and `interval` parameters
|
235
|
-
|
231
|
+
# @since 3.0.0 +calc+ defaults to +true+
|
232
|
+
def to_w(iface=nil, calc: true, number: 1, interval: 1)
|
236
233
|
iface ||= PacketGen.default_iface
|
237
|
-
|
234
|
+
|
235
|
+
if first_header.respond_to? :to_w
|
238
236
|
self.calc if calc
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
number
|
243
|
-
@headers.first.to_w(iface)
|
244
|
-
sleep interval
|
245
|
-
end
|
237
|
+
|
238
|
+
number.times do
|
239
|
+
first_header.to_w(iface)
|
240
|
+
sleep interval if number > 1
|
246
241
|
end
|
247
242
|
else
|
248
|
-
type =
|
243
|
+
type = first_header.protocol_name
|
249
244
|
raise WireError, "don't known how to send a #{type} packet on wire"
|
250
245
|
end
|
251
246
|
end
|
@@ -264,22 +259,20 @@ module PacketGen
|
|
264
259
|
end
|
265
260
|
|
266
261
|
# Remove headers from +self+
|
267
|
-
# @param [Array<Header>]
|
262
|
+
# @param [Array<Header>] hdrs
|
268
263
|
# @return [self] +self+ with some headers removed
|
269
264
|
# @raise [FormatError] any headers not in +self+
|
270
265
|
# @raise [FormatError] removed headers result in an unknown binding
|
271
266
|
# @since 1.1.0
|
272
|
-
def decapsulate(*
|
273
|
-
|
274
|
-
idx =
|
267
|
+
def decapsulate(*hdrs)
|
268
|
+
hdrs.each do |hdr|
|
269
|
+
idx = headers.index(hdr)
|
275
270
|
raise FormatError, 'header not in packet!' if idx.nil?
|
276
271
|
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
if
|
281
|
-
add_header(next_header, previous_header: prev_header)
|
282
|
-
end
|
272
|
+
prev_hdr = idx > 0 ? headers[idx - 1] : nil
|
273
|
+
next_hdr = (idx + 1) < headers.size ? headers[idx + 1] : nil
|
274
|
+
headers.delete_at(idx)
|
275
|
+
add_header(next_hdr, previous_header: prev_hdr) if prev_hdr && next_hdr
|
283
276
|
end
|
284
277
|
rescue ArgumentError => ex
|
285
278
|
raise FormatError, ex.message
|
@@ -291,7 +284,7 @@ module PacketGen
|
|
291
284
|
# @return [Packet] self
|
292
285
|
# @raise [ArgumentError] +first_header+ is an unknown header
|
293
286
|
def parse(binary_str, first_header: nil)
|
294
|
-
|
287
|
+
headers.clear
|
295
288
|
|
296
289
|
if first_header.nil?
|
297
290
|
# No decoding forced for first header. Have to guess it!
|
@@ -300,18 +293,20 @@ module PacketGen
|
|
300
293
|
raise ParseError, 'cannot identify first header in string'
|
301
294
|
end
|
302
295
|
end
|
296
|
+
|
303
297
|
add first_header
|
304
|
-
|
298
|
+
headers[-1, 1] = last_header.read(binary_str)
|
305
299
|
|
306
300
|
# Decode upper headers recursively
|
307
301
|
decode_bottom_up
|
308
302
|
self
|
309
303
|
end
|
310
304
|
|
305
|
+
# Get packet as a pretty formatted string.
|
311
306
|
# @return [String]
|
312
307
|
def inspect
|
313
308
|
str = Inspect.dashed_line(self.class)
|
314
|
-
|
309
|
+
headers.each do |header|
|
315
310
|
str << header.inspect
|
316
311
|
end
|
317
312
|
str << Inspect.inspect_body(body)
|
@@ -325,8 +320,9 @@ module PacketGen
|
|
325
320
|
|
326
321
|
# Invert all possible fields in packet to create a reply.
|
327
322
|
# @return [self]
|
323
|
+
# @since 2.7.0
|
328
324
|
def reply!
|
329
|
-
|
325
|
+
headers.each do |header|
|
330
326
|
header.reply! if header.respond_to?(:reply!)
|
331
327
|
end
|
332
328
|
self
|
@@ -335,6 +331,7 @@ module PacketGen
|
|
335
331
|
# Forge a new packet from current one with all possible fields
|
336
332
|
# inverted. The new packet may be a reply to current one.
|
337
333
|
# @return [Packet]
|
334
|
+
# @since 2.7.0
|
338
335
|
def reply
|
339
336
|
pkt = dup
|
340
337
|
pkt.reply!
|
@@ -345,12 +342,24 @@ module PacketGen
|
|
345
342
|
# Dup +@headers+ instance variable. Internally used by +#dup+ and +#clone+
|
346
343
|
# @return [void]
|
347
344
|
def initialize_copy(_other)
|
348
|
-
@headers =
|
349
|
-
|
345
|
+
@headers = headers.map(&:dup)
|
346
|
+
headers.each do |header|
|
350
347
|
add_magic_header_method header
|
351
348
|
end
|
352
349
|
end
|
353
350
|
|
351
|
+
# Give first header of packet
|
352
|
+
# @return [Header::Base]
|
353
|
+
def first_header
|
354
|
+
headers.first
|
355
|
+
end
|
356
|
+
|
357
|
+
# Give last header of packet
|
358
|
+
# @return [Header::Base]
|
359
|
+
def last_header
|
360
|
+
headers.last
|
361
|
+
end
|
362
|
+
|
354
363
|
# @overload header(klass, layer=1)
|
355
364
|
# @param [Class] klass
|
356
365
|
# @param [Integer] layer
|
@@ -360,17 +369,15 @@ module PacketGen
|
|
360
369
|
# @raise [ArgumentError] unknown option
|
361
370
|
# @return [Header::Base]
|
362
371
|
def header(klass, arg)
|
363
|
-
headers = @headers.select { |h| h.is_a? klass }
|
364
372
|
layer = arg.is_a?(Integer) ? arg : 1
|
365
|
-
header = headers[layer - 1]
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
end
|
372
|
-
header.send "#{key}=", value
|
373
|
+
header = headers.select { |h| h.is_a? klass }[layer - 1]
|
374
|
+
return header unless arg.is_a? Hash
|
375
|
+
|
376
|
+
arg.each do |key, value|
|
377
|
+
unless header.respond_to? "#{key}="
|
378
|
+
raise ArgumentError, "unknown #{key} attribute for #{klass}"
|
373
379
|
end
|
380
|
+
header.send "#{key}=", value
|
374
381
|
end
|
375
382
|
|
376
383
|
header
|
@@ -382,6 +389,7 @@ module PacketGen
|
|
382
389
|
def check_protocol(protocol)
|
383
390
|
klass = Header.get_header_class_by_name(protocol)
|
384
391
|
raise ArgumentError, "unknown #{protocol} protocol" if klass.nil?
|
392
|
+
|
385
393
|
klass
|
386
394
|
end
|
387
395
|
|
@@ -391,77 +399,86 @@ module PacketGen
|
|
391
399
|
# @param [Boolean] parsing
|
392
400
|
# @return [void]
|
393
401
|
def add_header(header, previous_header: nil, parsing: false)
|
394
|
-
prev_header = previous_header ||
|
402
|
+
prev_header = previous_header || last_header
|
395
403
|
if prev_header
|
396
404
|
bindings = prev_header.class.known_headers[header.class]
|
405
|
+
bindings = prev_header.class.known_headers[header.class.superclass] if bindings.nil?
|
397
406
|
if bindings.nil?
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
msg << "value_for_#{header.method_name})"
|
404
|
-
raise ArgumentError, msg
|
405
|
-
end
|
407
|
+
msg = "#{prev_header.class} knowns no layer association with #{header.protocol_name}. ".dup
|
408
|
+
msg << "Try #{prev_header.class}.bind_layer(#{header.class}, "
|
409
|
+
msg << "#{prev_header.method_name}_proto_field: "
|
410
|
+
msg << "value_for_#{header.method_name})"
|
411
|
+
raise ArgumentError, msg
|
406
412
|
end
|
413
|
+
|
407
414
|
bindings.set(prev_header) if !bindings.empty? && !parsing
|
408
415
|
prev_header[:body] = header
|
409
416
|
end
|
410
417
|
header.packet = self
|
411
|
-
|
418
|
+
headers << header unless previous_header
|
412
419
|
|
413
420
|
return if respond_to? header.method_name
|
421
|
+
|
414
422
|
add_magic_header_method header
|
415
423
|
end
|
416
424
|
|
425
|
+
# Add method to access +header+
|
426
|
+
# @param [Header::Base] header
|
427
|
+
# @return [void]
|
417
428
|
def add_magic_header_method(header)
|
418
429
|
self.instance_eval "def #{header.method_name}(arg=nil);" \
|
419
430
|
"header(#{header.class}, arg); end"
|
420
431
|
end
|
421
432
|
|
433
|
+
# Try to guess header from +binary_str+
|
434
|
+
# @param [String] binary_str
|
435
|
+
# @return [String] header/protocol name
|
422
436
|
def guess_first_header(binary_str)
|
423
437
|
first_header = nil
|
424
438
|
Header.all.each do |hklass|
|
425
439
|
hdr = hklass.new(packet: self)
|
426
440
|
# #read may return another object (more specific class)
|
427
441
|
hdr = hdr.read(binary_str)
|
428
|
-
# First header is found when
|
429
|
-
# * for one known header,
|
442
|
+
# First header is found when, for one known header,
|
430
443
|
# * +#parse?+ is true
|
431
444
|
# * it exists a known binding with a upper header
|
432
445
|
next unless hdr.parse?
|
433
|
-
|
434
|
-
|
435
|
-
end
|
446
|
+
|
447
|
+
first_header = hklass.to_s.gsub(/.*::/, '') if search_upper_header(hdr)
|
436
448
|
break unless first_header.nil?
|
437
449
|
end
|
438
450
|
first_header
|
439
451
|
end
|
440
452
|
|
453
|
+
# Decode packet bottom up
|
454
|
+
# @return [void]
|
441
455
|
def decode_bottom_up
|
442
|
-
|
443
|
-
|
444
|
-
last_known_hdr
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
456
|
+
loop do
|
457
|
+
last_known_hdr = last_header
|
458
|
+
break if !last_known_hdr.respond_to?(:body) || last_known_hdr.body.empty?
|
459
|
+
|
460
|
+
nh = search_upper_header(last_known_hdr)
|
461
|
+
break if nh.nil?
|
462
|
+
|
463
|
+
nheader = nh.new(packet: self)
|
464
|
+
nheader = nheader.read(last_known_hdr.body)
|
465
|
+
next unless nheader.parse?
|
466
|
+
|
467
|
+
add_header nheader, parsing: true
|
468
|
+
break if last_header == last_known_hdr
|
455
469
|
end
|
456
470
|
end
|
457
471
|
|
458
|
-
|
472
|
+
# Search a upper header for +hdr+
|
473
|
+
# @param [Header::Base] hdr
|
474
|
+
# @return [void]
|
475
|
+
# @yieldparam [Header::Base] found upper header
|
476
|
+
def search_upper_header(hdr)
|
459
477
|
hdr.class.known_headers.each do |nh, bindings|
|
460
|
-
if bindings.check?(hdr)
|
461
|
-
yield nh
|
462
|
-
break
|
463
|
-
end
|
478
|
+
return nh if bindings.check?(hdr)
|
464
479
|
end
|
480
|
+
|
481
|
+
nil
|
465
482
|
end
|
466
483
|
end
|
467
484
|
end
|