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.
Files changed (89) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +0 -1
  3. data/README.md +5 -4
  4. data/lib/packetgen.rb +6 -12
  5. data/lib/packetgen/capture.rb +43 -39
  6. data/lib/packetgen/config.rb +0 -1
  7. data/lib/packetgen/deprecation.rb +1 -1
  8. data/lib/packetgen/header.rb +9 -9
  9. data/lib/packetgen/header/asn1_base.rb +10 -10
  10. data/lib/packetgen/header/base.rb +42 -101
  11. data/lib/packetgen/header/dhcp/option.rb +5 -11
  12. data/lib/packetgen/header/dhcpv6/duid.rb +2 -0
  13. data/lib/packetgen/header/dhcpv6/option.rb +2 -19
  14. data/lib/packetgen/header/dhcpv6/options.rb +7 -0
  15. data/lib/packetgen/header/dns.rb +5 -23
  16. data/lib/packetgen/header/dns/name.rb +1 -0
  17. data/lib/packetgen/header/dns/qdsection.rb +1 -0
  18. data/lib/packetgen/header/dns/question.rb +3 -7
  19. data/lib/packetgen/header/dns/rr.rb +3 -0
  20. data/lib/packetgen/header/dns/rrsection.rb +1 -0
  21. data/lib/packetgen/header/dot11.rb +1 -17
  22. data/lib/packetgen/header/dot1x.rb +1 -0
  23. data/lib/packetgen/header/eap.rb +4 -7
  24. data/lib/packetgen/header/eth.rb +2 -0
  25. data/lib/packetgen/header/http/headers.rb +3 -0
  26. data/lib/packetgen/header/http/request.rb +5 -4
  27. data/lib/packetgen/header/http/response.rb +5 -4
  28. data/lib/packetgen/header/icmp.rb +6 -0
  29. data/lib/packetgen/header/icmpv6.rb +6 -0
  30. data/lib/packetgen/header/igmpv3/mq.rb +2 -0
  31. data/lib/packetgen/header/ip.rb +32 -30
  32. data/lib/packetgen/header/ip/addr.rb +1 -0
  33. data/lib/packetgen/header/ip/option.rb +23 -20
  34. data/lib/packetgen/header/ip/options.rb +11 -24
  35. data/lib/packetgen/header/ipv6.rb +45 -34
  36. data/lib/packetgen/header/ipv6/addr.rb +2 -0
  37. data/lib/packetgen/header/ipv6/hop_by_hop.rb +7 -31
  38. data/lib/packetgen/header/mdns.rb +1 -0
  39. data/lib/packetgen/header/mldv2/mlq.rb +2 -0
  40. data/lib/packetgen/header/ospfv2/lsa.rb +15 -25
  41. data/lib/packetgen/header/ospfv3/ipv6_prefix.rb +1 -1
  42. data/lib/packetgen/header/ospfv3/lsa.rb +8 -25
  43. data/lib/packetgen/header/snmp.rb +2 -0
  44. data/lib/packetgen/header/tcp.rb +23 -2
  45. data/lib/packetgen/header/tcp/option.rb +51 -52
  46. data/lib/packetgen/header/tcp/options.rb +17 -52
  47. data/lib/packetgen/header/tftp.rb +3 -0
  48. data/lib/packetgen/header/udp.rb +8 -0
  49. data/lib/packetgen/packet.rb +119 -102
  50. data/lib/packetgen/pcapng/block.rb +4 -10
  51. data/lib/packetgen/pcapng/epb.rb +4 -4
  52. data/lib/packetgen/pcapng/file.rb +7 -3
  53. data/lib/packetgen/pcapng/idb.rb +2 -2
  54. data/lib/packetgen/pcapng/shb.rb +3 -3
  55. data/lib/packetgen/pcapng/spb.rb +1 -8
  56. data/lib/packetgen/pcapng/unknown_block.rb +0 -7
  57. data/lib/packetgen/types.rb +1 -0
  58. data/lib/packetgen/types/array.rb +73 -71
  59. data/lib/packetgen/types/cstring.rb +1 -1
  60. data/lib/packetgen/types/enum.rb +3 -3
  61. data/lib/packetgen/types/fields.rb +66 -106
  62. data/lib/packetgen/types/int.rb +9 -5
  63. data/lib/packetgen/types/length_from.rb +45 -0
  64. data/lib/packetgen/types/oui.rb +2 -0
  65. data/lib/packetgen/types/string.rb +10 -16
  66. data/lib/packetgen/types/tlv.rb +7 -15
  67. data/lib/packetgen/utils.rb +8 -8
  68. data/lib/packetgen/utils/arp_spoofer.rb +1 -2
  69. data/lib/packetgen/version.rb +1 -1
  70. metadata +3 -21
  71. data/lib/packetgen/header/crypto.rb +0 -62
  72. data/lib/packetgen/header/esp.rb +0 -413
  73. data/lib/packetgen/header/ike.rb +0 -243
  74. data/lib/packetgen/header/ike/auth.rb +0 -165
  75. data/lib/packetgen/header/ike/cert.rb +0 -76
  76. data/lib/packetgen/header/ike/certreq.rb +0 -66
  77. data/lib/packetgen/header/ike/id.rb +0 -99
  78. data/lib/packetgen/header/ike/ke.rb +0 -79
  79. data/lib/packetgen/header/ike/nonce.rb +0 -40
  80. data/lib/packetgen/header/ike/notify.rb +0 -176
  81. data/lib/packetgen/header/ike/payload.rb +0 -315
  82. data/lib/packetgen/header/ike/sa.rb +0 -561
  83. data/lib/packetgen/header/ike/sk.rb +0 -261
  84. data/lib/packetgen/header/ike/ts.rb +0 -270
  85. data/lib/packetgen/header/ike/vendor_id.rb +0 -39
  86. data/lib/packetgen/header/netbios.rb +0 -20
  87. data/lib/packetgen/header/netbios/datagram.rb +0 -105
  88. data/lib/packetgen/header/netbios/name.rb +0 -67
  89. 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
- if TCP.const_defined?(klassname)
74
- klass = TCP.const_get(klassname)
75
- unless klass < Option
76
- raise ArgumentError, 'opt should be a TCP::Option subclass'
77
- end
78
- klass.new(hsh)
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
 
@@ -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),
@@ -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
- # @return [Array<Header::Base]
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
- # @param [Hash] options capture options
83
- # @option options [String] :iface interface on which capture
84
- # packets on. Default: Use default interface lookup.
85
- # @option options [Integer] :max maximum number of packets to capture
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(options={})
93
- capture = Capture.new(options)
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 on packet stack
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 = @headers.index(prev) + 1
164
- @headers[idx, 0] = header
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
- @headers.any? { |h| h.is_a? klass }
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
- @headers.reverse_each do |header|
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
- @headers.each do |header|
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
- @headers.last.body if @headers.last.respond_to? :body
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
- @headers.last.body = str
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
- @headers.first.to_s
212
+ first_header.to_s
217
213
  end
218
214
 
219
- # Write a PCapNG file to disk.
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
- # send packet on wire. Use first header +#to_w+ method.
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
- def to_w(iface=nil, calc: false, number: 1, interval: 1)
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
- if @headers.first.respond_to? :to_w
234
+
235
+ if first_header.respond_to? :to_w
238
236
  self.calc if calc
239
- if number == 1
240
- @headers.first.to_w(iface)
241
- else
242
- number.times do
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 = @headers.first.protocol_name
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>] headers
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(*headers)
273
- headers.each do |header|
274
- idx = @headers.index(header)
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
- prev_header = idx > 0 ? @headers[idx - 1] : nil
278
- next_header = (idx + 1) < @headers.size ? @headers[idx + 1] : nil
279
- @headers.delete_at(idx)
280
- if prev_header && next_header
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
- @headers.clear
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
- @headers[-1, 1] = @headers.last.read(binary_str)
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
- @headers.each do |header|
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
- @headers.each do |header|
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 = @headers.map(&:dup)
349
- @headers.each do |header|
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
- if arg.is_a? Hash
368
- arg.each do |key, value|
369
- unless header.respond_to? "#{key}="
370
- raise ArgumentError, "unknown #{key} attribute for #{klass}"
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 || @headers.last
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
- bindings = prev_header.class.known_headers[header.class.superclass]
399
- if bindings.nil?
400
- msg = "#{prev_header.class} knowns no layer association with #{header.protocol_name}. ".dup
401
- msg << "Try #{prev_header.class}.bind_layer(#{header.class}, "
402
- msg << "#{prev_header.method_name}_proto_field: "
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
- @headers << header unless previous_header
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
- search_header(hdr) do
434
- first_header = hklass.to_s.gsub(/.*::/, '')
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
- decode_packet_bottom_up = true
443
- while decode_packet_bottom_up
444
- last_known_hdr = @headers.last
445
- break unless last_known_hdr.respond_to? :body
446
- break if last_known_hdr.body.empty?
447
- search_header(last_known_hdr) do |nh|
448
- str = last_known_hdr.body
449
- nheader = nh.new(packet: self)
450
- nheader = nheader.read(str)
451
- next unless nheader.parse?
452
- add_header nheader, parsing: true
453
- end
454
- decode_packet_bottom_up = (@headers.last != last_known_hdr)
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
- def search_header(hdr)
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