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