packetgen 3.1.4 → 3.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +0 -1
  3. data/bin/pgconsole +1 -0
  4. data/lib/packetgen.rb +19 -3
  5. data/lib/packetgen/capture.rb +30 -9
  6. data/lib/packetgen/config.rb +15 -9
  7. data/lib/packetgen/deprecation.rb +1 -1
  8. data/lib/packetgen/header/asn1_base.rb +19 -9
  9. data/lib/packetgen/header/base.rb +68 -70
  10. data/lib/packetgen/header/dhcpv6/duid.rb +3 -1
  11. data/lib/packetgen/header/dhcpv6/option.rb +4 -12
  12. data/lib/packetgen/header/dns/name.rb +18 -7
  13. data/lib/packetgen/header/dns/qdsection.rb +1 -1
  14. data/lib/packetgen/header/dns/question.rb +2 -0
  15. data/lib/packetgen/header/dot11.rb +25 -38
  16. data/lib/packetgen/header/dot11/data.rb +28 -34
  17. data/lib/packetgen/header/dot1x.rb +1 -14
  18. data/lib/packetgen/header/eap.rb +14 -17
  19. data/lib/packetgen/header/eth.rb +5 -6
  20. data/lib/packetgen/header/http/headers.rb +4 -2
  21. data/lib/packetgen/header/http/request.rb +37 -18
  22. data/lib/packetgen/header/http/response.rb +11 -5
  23. data/lib/packetgen/header/http/verbs.rb +1 -1
  24. data/lib/packetgen/header/igmpv3/group_record.rb +2 -0
  25. data/lib/packetgen/header/ip.rb +27 -26
  26. data/lib/packetgen/header/ip/addr.rb +3 -1
  27. data/lib/packetgen/header/ip/option.rb +4 -4
  28. data/lib/packetgen/header/ipv6/addr.rb +2 -0
  29. data/lib/packetgen/header/mldv2/mcast_address_record.rb +2 -0
  30. data/lib/packetgen/header/ospfv2/ls_request.rb +2 -0
  31. data/lib/packetgen/header/ospfv2/lsa.rb +13 -3
  32. data/lib/packetgen/header/ospfv2/lsa_header.rb +2 -1
  33. data/lib/packetgen/header/ospfv3/ipv6_prefix.rb +2 -0
  34. data/lib/packetgen/header/ospfv3/ls_request.rb +2 -0
  35. data/lib/packetgen/header/ospfv3/lsa.rb +9 -3
  36. data/lib/packetgen/header/ospfv3/lsa_header.rb +2 -1
  37. data/lib/packetgen/header/snmp.rb +3 -2
  38. data/lib/packetgen/header/tcp.rb +1 -20
  39. data/lib/packetgen/header/tcp/option.rb +8 -6
  40. data/lib/packetgen/inspect.rb +1 -17
  41. data/lib/packetgen/packet.rb +10 -6
  42. data/lib/packetgen/pcapng.rb +11 -11
  43. data/lib/packetgen/pcapng/block.rb +15 -2
  44. data/lib/packetgen/pcapng/epb.rb +22 -15
  45. data/lib/packetgen/pcapng/file.rb +166 -81
  46. data/lib/packetgen/pcapng/idb.rb +7 -9
  47. data/lib/packetgen/pcapng/shb.rb +35 -28
  48. data/lib/packetgen/pcapng/spb.rb +16 -12
  49. data/lib/packetgen/pcapng/unknown_block.rb +3 -11
  50. data/lib/packetgen/pcaprub_wrapper.rb +25 -11
  51. data/lib/packetgen/types.rb +1 -0
  52. data/lib/packetgen/types/abstract_tlv.rb +3 -1
  53. data/lib/packetgen/types/array.rb +17 -10
  54. data/lib/packetgen/types/cstring.rb +56 -19
  55. data/lib/packetgen/types/enum.rb +4 -0
  56. data/lib/packetgen/types/fieldable.rb +65 -0
  57. data/lib/packetgen/types/fields.rb +180 -113
  58. data/lib/packetgen/types/int.rb +15 -1
  59. data/lib/packetgen/types/int_string.rb +8 -0
  60. data/lib/packetgen/types/length_from.rb +18 -10
  61. data/lib/packetgen/types/oui.rb +2 -0
  62. data/lib/packetgen/types/string.rb +58 -7
  63. data/lib/packetgen/types/tlv.rb +2 -0
  64. data/lib/packetgen/unknown_packet.rb +84 -0
  65. data/lib/packetgen/utils.rb +6 -7
  66. data/lib/packetgen/version.rb +1 -1
  67. metadata +18 -15
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2adf2b07c2d2d334e4f0ae6e4e7b49571d4d436788bef51df6cf7c1a5aaba9c8
4
- data.tar.gz: 8997321a31379ea6952ab42a4642d98a717587ed777652ebdb9e25b5928cc8d4
3
+ metadata.gz: 8f3faa715cd36e918df79355267c50690bdde15f1566e23947d26890e7b89a82
4
+ data.tar.gz: d374447f230500b0c5be84b225d3a5576f019afc4870dc6512c659bc2e069d67
5
5
  SHA512:
6
- metadata.gz: b5af6e0d04f8bb03af01d63cde3879a6a13c8a78477811be2b95fee5c597820f4714f8ac8b69f4aa8f04d3d5b81c5e25b5787815ee2237a8c3329721b5dc7895
7
- data.tar.gz: 6eebb203f8dfde49505987d16778c1ecbc81c463d6ac8b5f005e2c6f486166ba0cceabd848d3b3aeebe867b138993171026e8dcacfb04c84a126bc149ff5c616
6
+ metadata.gz: de7911facea630eaa2d3d4afd2ae15e1cbd927241ebf0c63ec096c64eac31c58e519d379476763c50fc1b4290029cec4b14dbf9e96ae9aa05dc06701cf45af32
7
+ data.tar.gz: 69881fc66b4af80177bdad4b5b2daa4bd28259596bbf8a36112998aa9a8c117c7c9898180372635fcc1b47376e2fab4fb5cece88be5c000277cdce825fb03168
data/README.md CHANGED
@@ -1,6 +1,5 @@
1
1
 
2
2
  [![Gem Version](https://badge.fury.io/rb/packetgen.svg)](https://badge.fury.io/rb/packetgen)
3
- [![Build Status](https://travis-ci.org/sdaubert/packetgen.svg?branch=master)](https://travis-ci.org/sdaubert/packetgen)
4
3
 
5
4
  # PacketGen
6
5
 
data/bin/pgconsole CHANGED
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
4
  require 'packetgen'
4
5
  require 'packetgen/config'
data/lib/packetgen.rb CHANGED
@@ -32,6 +32,7 @@ module PacketGen
32
32
  attr_reader :hdr
33
33
 
34
34
  def initialize(prev_hdr, hdr)
35
+ super()
35
36
  @prev_hdr = prev_hdr
36
37
  @hdr = hdr
37
38
  end
@@ -66,7 +67,7 @@ module PacketGen
66
67
  # @yieldparam [Packet] packet
67
68
  # @return [Array<Packet>]
68
69
  def self.capture(**kwargs)
69
- Packet.capture(kwargs) { |packet| yield packet if block_given? }
70
+ Packet.capture(**kwargs) { |packet| yield packet if block_given? }
70
71
  end
71
72
 
72
73
  # Shortcut for {Packet.read}
@@ -94,7 +95,21 @@ module PacketGen
94
95
  # Get default network interface (ie. first non-loopback declared interface)
95
96
  # @return [String]
96
97
  def self.default_iface
97
- Interfacez.default
98
+ return @default_iface if defined? @default_iface
99
+
100
+ @default_iface = Interfacez.raw_interface_addresses.each do |iface|
101
+ next unless iface.broadaddr
102
+ next unless Interfacez.ipv4_address_of(iface.name)
103
+ next unless Interfacez.ipv6_address_of(iface.name)
104
+
105
+ break iface.name
106
+ end
107
+ end
108
+
109
+ # Get loopback network interface
110
+ # @return [String]
111
+ def self.loopback_iface
112
+ Interfacez.loopback
98
113
  end
99
114
 
100
115
  # Shortcut to get a header class
@@ -114,11 +129,12 @@ module PacketGen
114
129
  end
115
130
 
116
131
  require 'packetgen/deprecation'
117
- require 'packetgen/types'
118
132
  require 'packetgen/inspect'
133
+ require 'packetgen/types'
119
134
  require 'packetgen/pcapng'
120
135
  require 'packetgen/pcap'
121
136
  require 'packetgen/packet'
137
+ require 'packetgen/unknown_packet'
122
138
  require 'packetgen/capture'
123
139
  require 'packetgen/inject'
124
140
  require 'packetgen/proto'
@@ -13,7 +13,9 @@ module PacketGen
13
13
  class Capture
14
14
  private
15
15
 
16
- attr_reader :filter, :cap_thread, :snaplen, :promisc
16
+ attr_reader :filter, :cap_thread, :snaplen, :promisc, :monitor
17
+
18
+ # rubocop:disable Metrics/ParameterLists
17
19
 
18
20
  public
19
21
 
@@ -41,14 +43,18 @@ module PacketGen
41
43
  # yielding. Default: +true+
42
44
  # @param [Integer] snaplen maximum number of bytes to capture for
43
45
  # each packet.
46
+ # @param [Boolean] monitor enable or disable monitor mode on interface (if supported by +iface+).
44
47
  # @since 2.0.0 remove old 1.x API
45
48
  # @since 3.0.0 arguments are kwargs and no more a hash
46
- def initialize(iface: nil, max: nil, timeout: nil, filter: nil, promisc: false, parse: true, snaplen: nil)
47
- @iface = iface || Interfacez.default || Interfacez.loopback
49
+ # @since 3.1.5 add monitor argument
50
+ # @author Sylvain Daubert
51
+ # @author optix2000 - add monitor argument
52
+ def initialize(iface: nil, max: nil, timeout: nil, filter: nil, promisc: false, parse: true, snaplen: nil, monitor: nil)
53
+ @iface = iface || PacketGen.default_iface || PacketGen.loopback_iface
48
54
 
49
55
  @packets = []
50
56
  @raw_packets = []
51
- set_options iface, max, timeout, filter, promisc, parse, snaplen
57
+ set_options iface, max, timeout, filter, promisc, parse, snaplen, monitor
52
58
  end
53
59
 
54
60
  # Start capture
@@ -56,8 +62,11 @@ module PacketGen
56
62
  # @yieldparam [Packet,String] packet if a block is given, yield each
57
63
  # captured packet (Packet or raw data String, depending on +:parse+ option)
58
64
  # @since 3.0.0 arguments are kwargs and no more a hash
59
- def start(iface: nil, max: nil, timeout: nil, filter: nil, promisc: false, parse: true, snaplen: nil, &block)
60
- set_options iface, max, timeout, filter, promisc, parse, snaplen
65
+ # @since 3.1.5 add monitor argument
66
+ # @author Sylvain Daubert
67
+ # @author optix2000 - add monitor argument
68
+ def start(iface: nil, max: nil, timeout: nil, filter: nil, promisc: false, parse: true, snaplen: nil, monitor: nil, &block)
69
+ set_options iface, max, timeout, filter, promisc, parse, snaplen, monitor
61
70
 
62
71
  @cap_thread = Thread.new do
63
72
  PCAPRUBWrapper.capture(**capture_args) do |packet_data|
@@ -79,7 +88,10 @@ module PacketGen
79
88
 
80
89
  private
81
90
 
82
- def set_options(iface, max, timeout, filter, promisc, parse, snaplen)
91
+ # rubocop:disable Metrics/CyclomaticComplexity
92
+ # rubocop:disable Metrics/PerceivedComplexity
93
+
94
+ def set_options(iface, max, timeout, filter, promisc, parse, snaplen, monitor)
83
95
  @max = max if max
84
96
  @filter = filter unless filter.nil?
85
97
  @timeout = timeout unless timeout.nil?
@@ -87,10 +99,15 @@ module PacketGen
87
99
  @snaplen = snaplen unless snaplen.nil?
88
100
  @parse = parse unless parse.nil?
89
101
  @iface = iface unless iface.nil?
102
+ @monitor = monitor unless monitor.nil?
90
103
  end
91
104
 
105
+ # rubocop:enable Metrics/CyclomaticComplexity
106
+ # rubocop:enable Metrics/PerceivedComplexity
107
+ # rubocop:enable Metrics/ParameterLists
108
+
92
109
  def capture_args
93
- h = { iface: iface, filter: filter }
110
+ h = { iface: iface, filter: filter, monitor: monitor }
94
111
  h[:snaplen] = snaplen unless snaplen.nil?
95
112
  h[:promisc] = promisc unless promisc.nil?
96
113
  h
@@ -105,7 +122,11 @@ module PacketGen
105
122
  def add_packet(data, &block)
106
123
  raw_packets << data
107
124
  if @parse
108
- packet = Packet.parse(data)
125
+ begin
126
+ packet = Packet.parse(data)
127
+ rescue ParseError
128
+ packet = UnknownPacket.new.parse(data)
129
+ end
109
130
  packets << packet
110
131
  block&.call(packet)
111
132
  elsif block
@@ -22,37 +22,43 @@ module PacketGen
22
22
  attr_reader :default_iface
23
23
 
24
24
  def initialize
25
- @default_iface = Interfacez.default || Interfacez.loopback
25
+ @default_iface = PacketGen.default_iface || PacketGen.loopback_iface
26
26
  @hwaddr = {}
27
27
  @ipaddr = {}
28
28
  @ip6addr = {}
29
29
 
30
- Interfacez.all do |iface_name|
31
- @hwaddr[iface_name] = Interfacez.mac_address_of(iface_name)
32
- @ipaddr[iface_name] = Interfacez.ipv4_address_of(iface_name)
33
- @ip6addr[iface_name] = Interfacez.ipv6_addresses_of(iface_name)
34
- end
30
+ initialize_local_addresses
35
31
  end
36
32
 
37
33
  # Get MAC address for given network interface
38
34
  # @param [String,nil] iface network interface. If +nil+, use default one.
39
35
  # @return [String]
40
36
  def hwaddr(iface=nil)
41
- @hwaddr[iface || @default_iface]
37
+ @hwaddr[iface || default_iface]
42
38
  end
43
39
 
44
40
  # Get IP address for given network interface
45
41
  # @param [String,nil] iface network interface. If +nil+, use default one.
46
42
  # @return [String]
47
43
  def ipaddr(iface=nil)
48
- @ipaddr[iface || @default_iface]
44
+ @ipaddr[iface || default_iface]
49
45
  end
50
46
 
51
47
  # Get IPv6 addresses for given network interface
52
48
  # @param [String,nil] iface network interface. If +nil+, use default one.
53
49
  # @return [Array<String>]
54
50
  def ip6addr(iface=nil)
55
- @ip6addr[iface || @default_iface]
51
+ @ip6addr[iface || default_iface]
52
+ end
53
+
54
+ private
55
+
56
+ def initialize_local_addresses
57
+ Interfacez.all do |iface_name|
58
+ @hwaddr[iface_name] = Interfacez.mac_address_of(iface_name)
59
+ @ipaddr[iface_name] = Interfacez.ipv4_address_of(iface_name)
60
+ @ip6addr[iface_name] = Interfacez.ipv6_addresses_of(iface_name)
61
+ end
56
62
  end
57
63
  end
58
64
  end
@@ -31,7 +31,7 @@ module PacketGen
31
31
  complete_deprecated_method_name = "#{base_name}#{deprecated_method}"
32
32
  complete_new_method_name = "#{base_name}#{new_method}" unless new_method.nil?
33
33
 
34
- file, line = caller(2..2).split(':')[0, 2]
34
+ file, line = caller(2..2).first.split(':')[0, 2]
35
35
  message = +"#{file}:#{line}: #{complete_deprecated_method_name} is deprecated"
36
36
  message << " in favor of #{complete_new_method_name}" unless new_method.nil?
37
37
  message << '. ' << self.removed(remove_version)
@@ -19,14 +19,20 @@ module PacketGen
19
19
  class ASN1Base < RASN1::Model
20
20
  include Headerable
21
21
 
22
- # Define some methods from given ASN.1 fields to mimic {Base} attributes
23
- # @param [Array<Symbol>] attributes
24
- # @return [void]
25
- def self.define_attributes(*attributes)
26
- @attributes = attributes
27
- attributes.each do |attr|
28
- class_eval "def #{attr}; @elements[:#{attr}].value; end\n" \
29
- "def #{attr}=(v); @elements[:#{attr}].value = v; end"
22
+ class <<self
23
+ # Define some methods from given ASN.1 fields to mimic {Base} attributes
24
+ # @param [Array<Symbol>] attributes
25
+ # @return [void]
26
+ def define_attributes(*attributes)
27
+ @attributes = attributes
28
+ attributes.each do |attr|
29
+ class_eval "def #{attr}; @elements[:#{attr}].value; end\n" \
30
+ "def #{attr}=(v); @elements[:#{attr}].value = v; end"
31
+ end
32
+ end
33
+
34
+ def known_headers
35
+ @known_headers ||= {}.freeze
30
36
  end
31
37
  end
32
38
 
@@ -37,7 +43,11 @@ module PacketGen
37
43
  # @param [String] str
38
44
  # @return [ASN1Base] self
39
45
  def read(str)
40
- parse(str, ber: true)
46
+ begin
47
+ parse(str, ber: true)
48
+ rescue RASN1::ASN1Error
49
+ # suppress exception to allow guessing
50
+ end
41
51
  self
42
52
  end
43
53
 
@@ -99,10 +99,8 @@ module PacketGen
99
99
 
100
100
  # each iterator
101
101
  # @return [void]
102
- def each
103
- @bindings.each do |b|
104
- yield b
105
- end
102
+ def each(&block)
103
+ @bindings.each(&block)
106
104
  end
107
105
 
108
106
  # @return [Boolean]
@@ -144,74 +142,74 @@ module PacketGen
144
142
  klass.class_eval { @known_headers = {} }
145
143
  end
146
144
 
147
- # Bind a upper header to current one.
148
- # @param [Class] header_klass header class to bind to current class
149
- # @param [Hash] args current class fields and their value when +header_klass+
150
- # is embedded in current class.
151
- #
152
- # Given value may be a lambda, whose alone argument is the value extracted
153
- # from header field (or +nil+ when lambda is used to set field while adding
154
- # a header).
155
- #
156
- # Special key +procs+ may be used to set 2 lambdas, the former to set
157
- # fields, the latter to check bindings. This may be used when multiple and
158
- # non-trivial checks should be made.
159
- # @return [void]
160
- # @example Basic examples
161
- # # Bind Header2 to Header1 when field1 from Header1 has a value of 42
162
- # Header1.bind Header2, field1: 42
163
- # # Bind Header3 to Header1 when field1 from Header1 has a value of 43
164
- # # and field2 has value 43 or 44
165
- # Header1.bind Header3, field1: 43, field2: 43
166
- # Header1.bind Header3, field1: 43, field2: 44
167
- # @example Defining a binding on a field using a lambda.
168
- # # Bind Header4 to Header1 when field1 from Header1 has a value
169
- # # greater or equal to 44. When adding a Header2 to a Header1
170
- # # with Packet#add, force value to 44.
171
- # Header1.bind Header4, field1: ->(v) { v.nil? ? 44 : v >= 44 }
172
- # @example Defining a binding using procs key
173
- # # Bind Header5 to Header1 when field1 from Header1 has a value of 41
174
- # # and first two bytes of header1's body are null.
175
- # # When adding a Header2 to a Header1 with Packet#add, force value to 44.
176
- # Header1.bind Header5, procs: [->(hdr) { hdr.field1 = 41 }
177
- # ->(hdr) { hdr.field1 == 41 && hdr.body[0..1] == "\x00\x00" }]
178
- # @since 2.7.0
179
- def self.bind(header_klass, args={})
180
- if @known_headers[header_klass].nil?
181
- bindings = Bindings.new
182
- @known_headers[header_klass] = bindings
183
- else
184
- bindings = @known_headers[header_klass]
185
- end
186
- bindings.new_set
187
- args.each do |key, value|
188
- bindings << if key == :procs
189
- ProcBinding.new(value)
190
- else
191
- Binding.new(key, value)
192
- end
193
- end
194
- end
145
+ class <<self
146
+ # @api private
147
+ # Get known headers
148
+ # @return [Hash] keys: header classes, values: hashes
149
+ attr_reader :known_headers
195
150
 
196
- # Helper method to calculate length of +hdr+ and set its +length+ field.
197
- # To be used by +#calc_length+ in Base subclasses.
198
- # @param [Base] hdr
199
- # @param [Boolean] header_in_size if +true+ header is included in length,
200
- # if +false+, only +body+ is taken into account
201
- def self.calculate_and_set_length(hdr, header_in_size: true)
202
- length = if header_in_size
203
- hdr.sz
204
- else
205
- hdr[:body].sz
206
- end
207
- hdr.length = length
208
- end
151
+ # Bind a upper header to current one.
152
+ # @param [Class] header_klass header class to bind to current class
153
+ # @param [Hash] args current class fields and their value when +header_klass+
154
+ # is embedded in current class.
155
+ #
156
+ # Given value may be a lambda, whose alone argument is the value extracted
157
+ # from header field (or +nil+ when lambda is used to set field while adding
158
+ # a header).
159
+ #
160
+ # Special key +procs+ may be used to set 2 lambdas, the former to set
161
+ # fields, the latter to check bindings. This may be used when multiple and
162
+ # non-trivial checks should be made.
163
+ # @return [void]
164
+ # @example Basic examples
165
+ # # Bind Header2 to Header1 when field1 from Header1 has a value of 42
166
+ # Header1.bind Header2, field1: 42
167
+ # # Bind Header3 to Header1 when field1 from Header1 has a value of 43
168
+ # # and field2 has value 43 or 44
169
+ # Header1.bind Header3, field1: 43, field2: 43
170
+ # Header1.bind Header3, field1: 43, field2: 44
171
+ # @example Defining a binding on a field using a lambda.
172
+ # # Bind Header4 to Header1 when field1 from Header1 has a value
173
+ # # greater or equal to 44. When adding a Header2 to a Header1
174
+ # # with Packet#add, force value to 44.
175
+ # Header1.bind Header4, field1: ->(v) { v.nil? ? 44 : v >= 44 }
176
+ # @example Defining a binding using procs key
177
+ # # Bind Header5 to Header1 when field1 from Header1 has a value of 41
178
+ # # and first two bytes of header1's body are null.
179
+ # # When adding a Header2 to a Header1 with Packet#add, force value to 44.
180
+ # Header1.bind Header5, procs: [->(hdr) { hdr.field1 = 41 }
181
+ # ->(hdr) { hdr.field1 == 41 && hdr.body[0..1] == "\x00\x00" }]
182
+ # @since 2.7.0
183
+ def bind(header_klass, args={})
184
+ if @known_headers[header_klass].nil?
185
+ bindings = Bindings.new
186
+ @known_headers[header_klass] = bindings
187
+ else
188
+ bindings = @known_headers[header_klass]
189
+ end
190
+ bindings.new_set
191
+ args.each do |key, value|
192
+ bindings << if key == :procs
193
+ ProcBinding.new(value)
194
+ else
195
+ Binding.new(key, value)
196
+ end
197
+ end
198
+ end
209
199
 
210
- # @api private
211
- # Get known headers
212
- # @return [Hash] keys: header classes, values: hashes
213
- def self.known_headers
214
- @known_headers
200
+ # Helper method to calculate length of +hdr+ and set its +length+ field.
201
+ # To be used by +#calc_length+ in Base subclasses.
202
+ # @param [Base] hdr
203
+ # @param [Boolean] header_in_size if +true+ header is included in length,
204
+ # if +false+, only +body+ is taken into account
205
+ def calculate_and_set_length(hdr, header_in_size: true)
206
+ length = if header_in_size
207
+ hdr.sz
208
+ else
209
+ hdr[:body].sz
210
+ end
211
+ hdr.length = length
212
+ end
215
213
  end
216
214
 
217
215
  # @see Types::Fields#initialize
@@ -11,6 +11,8 @@ module PacketGen
11
11
  # @abstract Base class for DUID (DHCP Unique ID)
12
12
  # @author Sylvain Daubert
13
13
  class DUID < Types::Fields
14
+ include Types::Fieldable
15
+
14
16
  TYPES = {
15
17
  'DUID-LLT' => 1,
16
18
  'DUID-EN' => 2,
@@ -34,7 +36,7 @@ module PacketGen
34
36
  # @param [String] str
35
37
  # @return [DUID]
36
38
  def read(str)
37
- if self.class == DUID
39
+ if self.instance_of?(DUID)
38
40
  super
39
41
  case type
40
42
  when 1
@@ -17,6 +17,8 @@ module PacketGen
17
17
  # field to replace it by specific option field(s).
18
18
  # @author Sylvain Daubert
19
19
  class Option < Types::Fields
20
+ include Types::Fieldable
21
+
20
22
  # @!attribute type
21
23
  # 16-bit option type
22
24
  # @return [Integer]
@@ -85,7 +87,7 @@ module PacketGen
85
87
  # Get human-readable {#type}
86
88
  # @return [String]
87
89
  def human_type
88
- if self.class == Option
90
+ if self.instance_of?(Option)
89
91
  "option#{type}"
90
92
  else
91
93
  self.class.to_s.sub(/.*::/, '')
@@ -230,17 +232,7 @@ module PacketGen
230
232
 
231
233
  # @!attribute options
232
234
  # @return [RequestedOptions]
233
- define_field :options, RequestedOptions
234
-
235
- # Populate object from +str+
236
- # @param [String] str
237
- # @return [self]
238
- def read(str)
239
- self[:type].read str[0, 2]
240
- self[:length].read str[2, 2]
241
- self[:options].read str[4, self.length]
242
- self
243
- end
235
+ define_field :options, RequestedOptions, builder: ->(h, t) { t.new(length_from: h[:length]) }
244
236
 
245
237
  # Get human-readable data
246
238
  # @return [String]