packetgen 3.3.3 → 4.1.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 (128) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +38 -22
  3. data/lib/packetgen/capture.rb +2 -2
  4. data/lib/packetgen/config.rb +0 -1
  5. data/lib/packetgen/deprecation.rb +14 -8
  6. data/lib/packetgen/header/arp.rb +17 -18
  7. data/lib/packetgen/header/asn1_base.rb +2 -1
  8. data/lib/packetgen/header/base.rb +42 -40
  9. data/lib/packetgen/header/bootp.rb +35 -37
  10. data/lib/packetgen/header/dhcp/option.rb +21 -21
  11. data/lib/packetgen/header/dhcp/options.rb +3 -3
  12. data/lib/packetgen/header/dhcp.rb +8 -9
  13. data/lib/packetgen/header/dhcpv6/duid.rb +16 -16
  14. data/lib/packetgen/header/dhcpv6/option.rb +83 -61
  15. data/lib/packetgen/header/dhcpv6/options.rb +4 -4
  16. data/lib/packetgen/header/dhcpv6/relay.rb +6 -5
  17. data/lib/packetgen/header/dhcpv6.rb +17 -18
  18. data/lib/packetgen/header/dns/name.rb +21 -16
  19. data/lib/packetgen/header/dns/opt.rb +5 -2
  20. data/lib/packetgen/header/dns/option.rb +14 -14
  21. data/lib/packetgen/header/dns/qdsection.rb +3 -3
  22. data/lib/packetgen/header/dns/question.rb +7 -8
  23. data/lib/packetgen/header/dns/rr.rb +56 -43
  24. data/lib/packetgen/header/dns/rrsection.rb +6 -6
  25. data/lib/packetgen/header/dns.rb +103 -90
  26. data/lib/packetgen/header/dot11/control.rb +12 -12
  27. data/lib/packetgen/header/dot11/data.rb +25 -24
  28. data/lib/packetgen/header/dot11/element.rb +4 -4
  29. data/lib/packetgen/header/dot11/management.rb +21 -18
  30. data/lib/packetgen/header/dot11/sub_mngt.rb +40 -53
  31. data/lib/packetgen/header/dot11.rb +117 -122
  32. data/lib/packetgen/header/dot1q.rb +12 -13
  33. data/lib/packetgen/header/dot1x.rb +13 -13
  34. data/lib/packetgen/header/eap/fast.rb +4 -4
  35. data/lib/packetgen/header/eap/md5.rb +16 -8
  36. data/lib/packetgen/header/eap/tls.rb +18 -19
  37. data/lib/packetgen/header/eap/ttls.rb +22 -21
  38. data/lib/packetgen/header/eap.rb +73 -48
  39. data/lib/packetgen/header/eth.rb +41 -27
  40. data/lib/packetgen/header/gre.rb +33 -11
  41. data/lib/packetgen/header/http/headers.rb +7 -6
  42. data/lib/packetgen/header/http/request.rb +38 -29
  43. data/lib/packetgen/header/http/response.rb +35 -27
  44. data/lib/packetgen/header/http/verbs.rb +1 -3
  45. data/lib/packetgen/header/icmp.rb +14 -14
  46. data/lib/packetgen/header/icmpv6.rb +10 -9
  47. data/lib/packetgen/header/igmp.rb +26 -15
  48. data/lib/packetgen/header/igmpv3/group_record.rb +18 -13
  49. data/lib/packetgen/header/igmpv3/mq.rb +16 -18
  50. data/lib/packetgen/header/igmpv3/mr.rb +5 -5
  51. data/lib/packetgen/header/igmpv3.rb +12 -11
  52. data/lib/packetgen/header/ip/addr.rb +19 -15
  53. data/lib/packetgen/header/ip/option.rb +47 -36
  54. data/lib/packetgen/header/ip/options.rb +1 -1
  55. data/lib/packetgen/header/ip.rb +77 -95
  56. data/lib/packetgen/header/ipv6/addr.rb +28 -27
  57. data/lib/packetgen/header/ipv6/extension.rb +13 -11
  58. data/lib/packetgen/header/ipv6/hop_by_hop.rb +32 -13
  59. data/lib/packetgen/header/ipv6.rb +42 -35
  60. data/lib/packetgen/header/llc.rb +28 -21
  61. data/lib/packetgen/header/mdns.rb +10 -3
  62. data/lib/packetgen/header/mld.rb +15 -13
  63. data/lib/packetgen/header/mldv2/mcast_address_record.rb +17 -12
  64. data/lib/packetgen/header/mldv2/mlq.rb +22 -24
  65. data/lib/packetgen/header/mldv2/mlr.rb +8 -8
  66. data/lib/packetgen/header/mldv2.rb +1 -1
  67. data/lib/packetgen/header/ospfv2/db_description.rb +17 -18
  68. data/lib/packetgen/header/ospfv2/hello.rb +18 -17
  69. data/lib/packetgen/header/ospfv2/ls_ack.rb +6 -7
  70. data/lib/packetgen/header/ospfv2/ls_request.rb +14 -13
  71. data/lib/packetgen/header/ospfv2/ls_update.rb +9 -9
  72. data/lib/packetgen/header/ospfv2/lsa.rb +79 -60
  73. data/lib/packetgen/header/ospfv2/lsa_header.rb +12 -11
  74. data/lib/packetgen/header/ospfv2.rb +49 -46
  75. data/lib/packetgen/header/ospfv3/db_description.rb +20 -22
  76. data/lib/packetgen/header/ospfv3/hello.rb +17 -16
  77. data/lib/packetgen/header/ospfv3/ipv6_prefix.rb +22 -20
  78. data/lib/packetgen/header/ospfv3/ls_ack.rb +7 -8
  79. data/lib/packetgen/header/ospfv3/ls_request.rb +18 -18
  80. data/lib/packetgen/header/ospfv3/ls_update.rb +10 -10
  81. data/lib/packetgen/header/ospfv3/lsa.rb +62 -51
  82. data/lib/packetgen/header/ospfv3/lsa_header.rb +12 -11
  83. data/lib/packetgen/header/ospfv3.rb +54 -52
  84. data/lib/packetgen/header/sctp/chunk.rb +80 -56
  85. data/lib/packetgen/header/sctp/error.rb +174 -202
  86. data/lib/packetgen/header/sctp/padded32.rb +3 -3
  87. data/lib/packetgen/header/sctp/parameter.rb +89 -136
  88. data/lib/packetgen/header/sctp.rb +19 -8
  89. data/lib/packetgen/header/snmp.rb +108 -7
  90. data/lib/packetgen/header/tcp/option.rb +52 -39
  91. data/lib/packetgen/header/tcp/options.rb +13 -5
  92. data/lib/packetgen/header/tcp.rb +83 -65
  93. data/lib/packetgen/header/tftp.rb +31 -25
  94. data/lib/packetgen/header/udp.rb +21 -19
  95. data/lib/packetgen/header.rb +23 -18
  96. data/lib/packetgen/headerable.rb +21 -5
  97. data/lib/packetgen/inspect.rb +3 -8
  98. data/lib/packetgen/packet.rb +146 -71
  99. data/lib/packetgen/pcap.rb +15 -4
  100. data/lib/packetgen/pcapng/block.rb +20 -18
  101. data/lib/packetgen/pcapng/epb.rb +13 -15
  102. data/lib/packetgen/pcapng/file.rb +44 -111
  103. data/lib/packetgen/pcapng/idb.rb +11 -12
  104. data/lib/packetgen/pcapng/shb.rb +15 -16
  105. data/lib/packetgen/pcapng/spb.rb +9 -11
  106. data/lib/packetgen/pcapng/unknown_block.rb +6 -17
  107. data/lib/packetgen/pcapng.rb +6 -4
  108. data/lib/packetgen/pcaprub_wrapper.rb +17 -1
  109. data/lib/packetgen/proto.rb +5 -1
  110. data/lib/packetgen/unknown_packet.rb +5 -5
  111. data/lib/packetgen/utils/arp_spoofer.rb +18 -19
  112. data/lib/packetgen/utils.rb +4 -3
  113. data/lib/packetgen/version.rb +1 -1
  114. data/lib/packetgen.rb +12 -5
  115. metadata +29 -38
  116. data/lib/packetgen/types/abstract_tlv.rb +0 -278
  117. data/lib/packetgen/types/array.rb +0 -287
  118. data/lib/packetgen/types/cstring.rb +0 -109
  119. data/lib/packetgen/types/enum.rb +0 -171
  120. data/lib/packetgen/types/fieldable.rb +0 -66
  121. data/lib/packetgen/types/fields.rb +0 -622
  122. data/lib/packetgen/types/int.rb +0 -473
  123. data/lib/packetgen/types/int_string.rb +0 -102
  124. data/lib/packetgen/types/length_from.rb +0 -54
  125. data/lib/packetgen/types/oui.rb +0 -52
  126. data/lib/packetgen/types/string.rb +0 -97
  127. data/lib/packetgen/types/tlv.rb +0 -161
  128. data/lib/packetgen/types.rb +0 -26
@@ -1,622 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # This file is part of PacketGen
4
- # See https://github.com/lemontree55/packetgen for more informations
5
- # Copyright (C) 2016 Sylvain Daubert <sylvain.daubert@laposte.net>
6
- # Copyright (C) 2024 LemonTree55 <lenontree@proton.me>
7
- # This program is published under MIT license.
8
-
9
- # rubocop:disable Metrics/ClassLength
10
-
11
- module PacketGen
12
- module Types
13
- # @abstract Set of fields
14
- # This class is a base class to define headers or anything else with a binary
15
- # format containing multiple fields.
16
- #
17
- # == Basics
18
- # A {Fields} subclass is generaly composed of multiple binary fields. These fields
19
- # have each a given type. All {Fieldable} types are supported.
20
- #
21
- # To define a new subclass, it has to inherit from {Fields}. And some class
22
- # methods have to be used to declare attributes/fields:
23
- # class MyBinaryStructure < PacketGen::Types::Fields
24
- # # define a first Int8 attribute, with default value: 1
25
- # define_field :attr1, PacketGen::Types::Int8, default: 1
26
- # #define a second attribute, of kind Int32
27
- # define_field :attr2, PacketGen::Types::Int32
28
- # end
29
- #
30
- # These defintions create 4 methods: +#attr1+, +#attr1=+, +#attr2+ and +#attr2=+.
31
- # All these methods take and/or return Integers.
32
- #
33
- # Fields may also be accessed through {#[]} ans {#[]=}. These methods give access
34
- # to type object:
35
- # mybs = MyBinaryStructure.new
36
- # mybs.attr1 # => Integer
37
- # mybs[:attr1] # => PacketGen::Types::Int8
38
- #
39
- # {#initialize} accepts an option hash to populate attributes. Keys are attribute
40
- # name symbols, and values are those expected by writer accessor.
41
- #
42
- # {#read} is able to populate object from a binary string.
43
- #
44
- # {#to_s} returns binary string from object.
45
- #
46
- # == Add Fields
47
- # {.define_field} adds a field to Fields subclass. A lot of field types may be
48
- # defined: integer types, string types (to handle a stream of bytes). More
49
- # complex field types may be defined using others Fields subclasses:
50
- # # define a 16-bit little-endian integer field, named type
51
- # define_field :type, PacketGen::Types::Int16le
52
- # # define a string field
53
- # define_field :body, PacketGen::Types::String
54
- # # define a field using a complex type (Fields subclass)
55
- # define_field :mac_addr, PacketGen::Eth::MacAddr
56
- #
57
- # This example creates six methods on our Fields subclass: +#type+, +#type=+,
58
- # +#body+, +#body=+, +#mac_addr+ and +#mac_addr=+.
59
- #
60
- # {.define_field} has many options (third optional Hash argument):
61
- # * +:default+ gives default field value. It may be a simple value (an Integer
62
- # for an Int field, for example) or a lambda,
63
- # * +:builder+ to give a builder/constructor lambda to create field. The lambda
64
- # takes 2 argument: {Fields} subclass object owning field, and type class as passes
65
- # as second argument to .define_field,
66
- # * +:optional+ to define this field as optional. This option takes a lambda
67
- # parameter used to say if this field is present or not. The lambda takes an argument
68
- # ({Fields} subclass object owning field),
69
- # * +:enum+ to define Hash enumeration for an {Enum} type.
70
- # For example:
71
- # # 32-bit integer field defaulting to 1
72
- # define_field :type, PacketGen::Types::Int32, default: 1
73
- # # 16-bit integer field, created with a random value. Each instance of this
74
- # # object will have a different value.
75
- # define_field :id, PacketGen::Types::Int16, default: ->(obj) { rand(65535) }
76
- # # a size field
77
- # define_field :body_size, PacketGen::Types::Int16
78
- # # String field which length is taken from body_size field
79
- # define_field :body, PacketGen::Types::String, builder: ->(obj, type) { type.new(length_from: obj[:body_size]) }
80
- # # 16-bit enumeration type. As :default not specified, default to first value of enum
81
- # define_field :type_class, PacketGen::Types::Int16Enum, enum: { 'class1' => 1, 'class2' => 2}
82
- # # optional field. Only present if another field has a certain value
83
- # define_field :opt1, PacketGen::Types::Int16, optional: ->(h) { h.type == 42 }
84
- #
85
- # {.define_field_before} and {.define_field_after} are also defined to relatively
86
- # create a field from anoher one (for example, when adding a field in a subclass).
87
- # == Generating bit fields
88
- # {.define_bit_fields_on} creates a bit field on a previuously declared integer
89
- # field. For example, +frag+ field in IP header:
90
- # define_field :frag, Types::Int16, default: 0
91
- # define_bit_fields_on :frag, :flag_rsv, :flag_df, :flag_mf, :fragment_offset, 13
92
- #
93
- # This example generates methods:
94
- # * +#frag+ and +#frag=+ to access +frag+ field as a 16-bit integer,
95
- # * +#flag_rsv?+, +#flag_rsv=+, +#flag_df?+, +#flag_df=+, +#flag_mf?+ and +#flag_mf=+
96
- # to access Boolean RSV, MF and DF flags from +frag+ field,
97
- # * +#fragment_offset+ and +#fragment_offset=+ to access 13-bit integer fragment
98
- # offset subfield from +frag+ field.
99
- #
100
- # == Creating a new field class from another one
101
- # Some methods may help in this case:
102
- # * {.define_field_before} to define a new field before an existing one,
103
- # * {.define_field_after} to define a new field after an existing onr,
104
- # * {.remove_field} to remove an existing field,
105
- # * {.uptade_fied} to change options of a field (but not its type),
106
- # * {.remove_bit_fields_on} to remove a bit fields definition.
107
- #
108
- # @author Sylvain Daubert
109
- class Fields
110
- # @private
111
- FieldDef = Struct.new(:type, :default, :builder, :optional, :enum, :options)
112
- # @private field names, ordered as they were declared
113
- @ordered_fields = []
114
- # @private field definitions
115
- @field_defs = {}
116
- # @private bit field definitions
117
- @bit_fields = {}
118
-
119
- class << self
120
- # Get field definitions for this class.
121
- # @return [Hash]
122
- # @since 3.1.0
123
- attr_reader :field_defs
124
- # Get bit fields defintions for this class
125
- # @return [Hash]
126
- # @since 3.1.5
127
- attr_reader :bit_fields
128
-
129
- # On inheritage, create +@field_defs+ class variable
130
- # @param [Class] klass
131
- # @return [void]
132
- def inherited(klass)
133
- super
134
-
135
- field_defs = {}
136
- @field_defs.each do |k, v|
137
- field_defs[k] = v.clone
138
- end
139
- ordered = @ordered_fields.clone
140
- bf = bit_fields.clone
141
-
142
- klass.class_eval do
143
- @ordered_fields = ordered
144
- @field_defs = field_defs
145
- @bit_fields = bf
146
- end
147
- end
148
-
149
- # Get field names
150
- # @return [Array<Symbol>]
151
- def fields
152
- @ordered_fields
153
- end
154
-
155
- # Define a field in class
156
- # class BinaryStruct < PacketGen::Types::Fields
157
- # # 8-bit value
158
- # define_field :value1, Types::Int8
159
- # # 16-bit value
160
- # define_field :value2, Types::Int16
161
- # # specific class, may use a specific constructor
162
- # define_field :value3, MyClass, builder: ->(obj, type) { type.new(obj) }
163
- # end
164
- #
165
- # bs = BinaryStruct.new
166
- # bs[value1] # => Types::Int8
167
- # bs.value1 # => Integer
168
- # @param [Symbol] name field name
169
- # @param [Fieldable] type class or instance
170
- # @param [Hash] options Unrecognized options are passed to object builder if
171
- # +:builder+ option is not set.
172
- # @option options [Object] :default default value. May be a proc. This lambda
173
- # take one argument: the caller object.
174
- # @option options [Lambda] :builder lambda to construct this field.
175
- # Parameters to this lambda is the caller object and the field type class.
176
- # @option options [Lambda] :optional define this field as optional. Given lambda
177
- # is used to known if this field is present or not. Parameter to this lambda is
178
- # the being defined Field object.
179
- # @option options [Hash] :enum mandatory option for an {Enum} type.
180
- # Define enumeration: hash's keys are +String+, and values are +Integer+.
181
- # @return [void]
182
- def define_field(name, type, options={})
183
- fields << name
184
- field_defs[name] = FieldDef.new(type,
185
- options.delete(:default),
186
- options.delete(:builder),
187
- options.delete(:optional),
188
- options.delete(:enum),
189
- options)
190
-
191
- add_methods(name, type)
192
- end
193
-
194
- # Define a field, before another one
195
- # @param [Symbol,nil] other field name to create a new one before. If +nil+,
196
- # new field is appended.
197
- # @param [Symbol] name field name to create
198
- # @param [Fieldable] type class or instance
199
- # @param [Hash] options See {.define_field}.
200
- # @return [void]
201
- # @see .define_field
202
- def define_field_before(other, name, type, options={})
203
- define_field name, type, options
204
- return if other.nil?
205
-
206
- fields.delete name
207
- idx = fields.index(other)
208
- raise ArgumentError, "unknown #{other} field" if idx.nil?
209
-
210
- fields[idx, 0] = name
211
- end
212
-
213
- # Define a field, after another one
214
- # @param [Symbol,nil] other field name to create a new one after. If +nil+,
215
- # new field is appended.
216
- # @param [Symbol] name field name to create
217
- # @param [Fieldable] type class or instance
218
- # @param [Hash] options See {.define_field}.
219
- # @return [void]
220
- # @see .define_field
221
- def define_field_after(other, name, type, options={})
222
- define_field name, type, options
223
- return if other.nil?
224
-
225
- fields.delete name
226
- idx = fields.index(other)
227
- raise ArgumentError, "unknown #{other} field" if idx.nil?
228
-
229
- fields[idx + 1, 0] = name
230
- end
231
-
232
- # Remove a previously defined field
233
- # @param [Symbol] name
234
- # @return [void]
235
- # @since 2.8.4
236
- def remove_field(name)
237
- fields.delete name
238
- @field_defs.delete name
239
- undef_method name if method_defined?(name)
240
- undef_method :"#{name}=" if method_defined?(:"#{name}=")
241
- end
242
-
243
- # Update a previously defined field
244
- # @param [Symbol] field field name to create
245
- # @param [Hash] options See {.define_field}.
246
- # @return [void]
247
- # @see .define_field
248
- # @raise [ArgumentError] unknown +field+
249
- # @since 2.8.4
250
- def update_field(field, options)
251
- check_existence_of field
252
-
253
- %i[default builder optional enum].each do |property|
254
- field_defs_property_from(field, property, options)
255
- end
256
-
257
- field_defs[field].options.merge!(options)
258
- end
259
-
260
- # Define a bitfield on given attribute
261
- # class MyHeader < PacketGen::Types::Fields
262
- # define_field :flags, Types::Int16
263
- # # define a bit field on :flag attribute:
264
- # # flag1, flag2 and flag3 are 1-bit fields
265
- # # type and stype are 3-bit fields. reserved is a 6-bit field
266
- # define_bit_fields_on :flags, :flag1, :flag2, :flag3, :type, 3, :stype, 3, :reserved, 7
267
- # end
268
- # A bitfield of size 1 bit defines 2 methods:
269
- # * +#field?+ which returns a Boolean,
270
- # * +#field=+ which takes and returns a Boolean.
271
- # A bitfield of more bits defines 2 methods:
272
- # * +#field+ which returns an Integer,
273
- # * +#field=+ which takes and returns an Integer.
274
- # @param [Symbol] attr attribute name (attribute should be a {Types::Int}
275
- # subclass)
276
- # @param [Array] args list of bitfield names. Name may be followed
277
- # by bitfield size. If no size is given, 1 bit is assumed.
278
- # @raise [ArgumentError] unknown +attr+
279
- # @return [void]
280
- def define_bit_fields_on(attr, *args)
281
- check_existence_of attr
282
-
283
- type = field_defs[attr].type
284
- raise TypeError, "#{attr} is not a PacketGen::Types::Int" unless type < Types::Int
285
-
286
- total_size = type.new.nbits
287
- idx = total_size - 1
288
-
289
- until args.empty?
290
- field = args.shift
291
- next unless field.is_a? Symbol
292
-
293
- size = size_from(args)
294
-
295
- unless field == :_
296
- add_bit_methods(attr, field, size, total_size, idx)
297
- register_bit_field_size(attr, field, size)
298
- end
299
-
300
- idx -= size
301
- end
302
- end
303
-
304
- # Remove all bit fields defined on +attr+
305
- # @param [Symbol] attr attribute defining bit fields
306
- # @return [void]
307
- # @since 2.8.4
308
- def remove_bit_fields_on(attr)
309
- fields = bit_fields.delete(attr)
310
- return if fields.nil?
311
-
312
- fields.each do |field, size|
313
- undef_method :"#{field}="
314
- undef_method(size == 1 ? "#{field}?" : field)
315
- end
316
- end
317
-
318
- private
319
-
320
- def add_methods(name, type)
321
- define = []
322
- if type < Types::Enum
323
- define << "def #{name}; self[:#{name}].to_i; end"
324
- define << "def #{name}=(val) self[:#{name}].value = val; end"
325
- else
326
- # rubocop:disable Layout/LineContinuationLeadingSpace
327
- define << "def #{name}\n" \
328
- " to_and_from_human?(:#{name}) ? self[:#{name}].to_human : self[:#{name}]\n" \
329
- 'end'
330
- define << "def #{name}=(val)\n" \
331
- " to_and_from_human?(:#{name}) ? self[:#{name}].from_human(val) : self[:#{name}].read(val)\n" \
332
- 'end'
333
- # rubocop:enable Layout/LineContinuationLeadingSpace
334
- end
335
-
336
- define.delete_at(1) if instance_methods.include?(:"#{name}=")
337
- define.delete_at(0) if instance_methods.include? name
338
- class_eval define.join("\n")
339
- end
340
-
341
- def add_bit_methods(attr, name, size, total_size, idx)
342
- shift = idx - (size - 1)
343
-
344
- if size == 1
345
- add_single_bit_methods(attr, name, size, total_size, shift)
346
- else
347
- add_multibit_methods(attr, name, size, total_size, shift)
348
- end
349
- end
350
-
351
- def compute_field_mask(size, shift)
352
- (2**size - 1) << shift
353
- end
354
-
355
- def compute_clear_mask(total_size, field_mask)
356
- (2**total_size - 1) & (~field_mask & (2**total_size - 1))
357
- end
358
-
359
- def add_single_bit_methods(attr, name, size, total_size, shift)
360
- field_mask = compute_field_mask(size, shift)
361
- clear_mask = compute_clear_mask(total_size, field_mask)
362
-
363
- class_eval <<-METHODS
364
- def #{name}? # def bit?
365
- val = (self[:#{attr}].to_i & #{field_mask}) >> #{shift} # val = (self[:attr}].to_i & 1}) >> 1
366
- val != 0 # val != 0
367
- end # end
368
- def #{name}=(v) # def bit=(v)
369
- val = v ? 1 : 0 # val = v ? 1 : 0
370
- self[:#{attr}].value = self[:#{attr}].to_i & #{clear_mask} # self[:attr].value = self[:attr].to_i & 0xfffd
371
- self[:#{attr}].value |= val << #{shift} # self[:attr].value |= val << 1
372
- end # end
373
- METHODS
374
- end
375
-
376
- def add_multibit_methods(attr, name, size, total_size, shift)
377
- field_mask = compute_field_mask(size, shift)
378
- clear_mask = compute_clear_mask(total_size, field_mask)
379
-
380
- class_eval <<-METHODS
381
- def #{name} # def multibit
382
- (self[:#{attr}].to_i & #{field_mask}) >> #{shift} # (self[:attr].to_i & 6) >> 1
383
- end # end
384
- def #{name}=(v) # def multibit=(v)
385
- self[:#{attr}].value = self[:#{attr}].to_i & #{clear_mask} # self[:attr].value = self[:attr].to_i & 0xfff9
386
- self[:#{attr}].value |= (v & #{2**size - 1}) << #{shift} # self[:attr].value |= (v & 3) << 1
387
- end # end
388
- METHODS
389
- end
390
-
391
- def register_bit_field_size(attr, field, size)
392
- bit_fields[attr] = {} if bit_fields[attr].nil?
393
- bit_fields[attr][field] = size
394
- end
395
-
396
- def field_defs_property_from(field, property, options)
397
- field_defs[field].send(:"#{property}=", options.delete(property)) if options.key?(property)
398
- end
399
-
400
- def size_from(args)
401
- if args.first.is_a? Integer
402
- args.shift
403
- else
404
- 1
405
- end
406
- end
407
-
408
- def check_existence_of(field)
409
- raise ArgumentError, "unknown #{field} field for #{self}" unless field_defs.key?(field)
410
- end
411
- end
412
-
413
- # Create a new fields object
414
- # @param [Hash] options Keys are symbols. They should have name of object
415
- # attributes, as defined by {.define_field} and by {.define_bit_fields_on}.
416
- def initialize(options={})
417
- @fields = {}
418
- @optional_fields = {}
419
-
420
- self.class.fields.each do |field|
421
- build_field field
422
- initialize_value field, options[field]
423
- initialize_optional field
424
- end
425
-
426
- self.class.bit_fields.each_value do |hsh|
427
- hsh.each_key do |bit_field|
428
- self.send(:"#{bit_field}=", options[bit_field]) if options[bit_field]
429
- end
430
- end
431
- end
432
-
433
- # Get field object
434
- # @param [Symbol] field
435
- # @return [Fieldable]
436
- def [](field)
437
- @fields[field]
438
- end
439
-
440
- # Set field object
441
- # @param [Symbol] field
442
- # @param [Object] obj
443
- # @return [Object]
444
- def []=(field, obj)
445
- @fields[field] = obj
446
- end
447
-
448
- # Get all field names
449
- # @return [Array<Symbol>]
450
- def fields
451
- self.class.fields
452
- end
453
-
454
- # Get all optional field name
455
- # @return[Array<Symbol>,nil]
456
- def optional_fields
457
- @optional_fields.keys
458
- end
459
-
460
- # Say if this field is optional
461
- # @return [Boolean]
462
- def optional?(field)
463
- @optional_fields.key? field
464
- end
465
-
466
- # Say if an optional field is present
467
- # @return [Boolean]
468
- def present?(field)
469
- return true unless optional?(field)
470
-
471
- @optional_fields[field].call(self)
472
- end
473
-
474
- # Populate object from a binary string
475
- # @param [String] str
476
- # @return [Fields] self
477
- def read(str)
478
- return self if str.nil?
479
-
480
- force_binary str
481
- start = 0
482
- fields.each do |field|
483
- next unless present?(field)
484
-
485
- obj = self[field].read str[start..]
486
- start += self[field].sz
487
- self[field] = obj unless obj == self[field]
488
- end
489
-
490
- self
491
- end
492
-
493
- # Common inspect method for headers.
494
- #
495
- # A block may be given to differently format some attributes. This
496
- # may be used by subclasses to handle specific fields.
497
- # @yieldparam attr [Symbol] attribute to inspect
498
- # @yieldreturn [String,nil] the string to print for +attr+, or +nil+
499
- # to let +inspect+ generate it
500
- # @return [String]
501
- def inspect
502
- str = Inspect.dashed_line(self.class, 1)
503
- fields.each do |attr|
504
- next if attr == :body
505
- next unless present?(attr)
506
-
507
- result = yield(attr) if block_given?
508
- str << (result || Inspect.inspect_attribute(attr, self[attr], 1))
509
- end
510
- str
511
- end
512
-
513
- # Return object as a binary string
514
- # @return [String]
515
- def to_s
516
- fields.select { |f| present?(f) }
517
- .map! { |f| force_binary @fields[f].to_s }.join
518
- end
519
-
520
- # Size of object as binary string
521
- # @return [nteger]
522
- def sz
523
- to_s.size
524
- end
525
-
526
- # Return object as a hash
527
- # @return [Hash] keys: attributes, values: attribute values
528
- def to_h
529
- fields.to_h { |f| [f, @fields[f].to_human] }
530
- end
531
-
532
- # Get offset of given field in {Fields} structure.
533
- # @param [Symbol] field
534
- # @return [Integer]
535
- # @raise [ArgumentError] unknown field
536
- def offset_of(field)
537
- raise ArgumentError, "#{field} is an unknown field of #{self.class}" unless @fields.include?(field)
538
-
539
- offset = 0
540
- fields.each do |f|
541
- break offset if f == field
542
- next unless present?(f)
543
-
544
- offset += self[f].sz
545
- end
546
- end
547
-
548
- # Get bit fields definition for given field.
549
- # @param [Symbol] field defining bit fields
550
- # @return [Hash,nil] keys: bit fields, values: their size in bits
551
- # @since 2.8.3
552
- def bits_on(field)
553
- self.class.bit_fields[field]
554
- end
555
-
556
- private
557
-
558
- # Deeply duplicate +@fields+
559
- # @return [void]
560
- def initialize_copy(_other)
561
- fields = {}
562
- @fields.each { |k, v| fields[k] = v.dup }
563
- @fields = fields
564
- end
565
-
566
- # Force str to binary encoding
567
- # @param [String] str
568
- # @return [String]
569
- def force_binary(str)
570
- PacketGen.force_binary(str)
571
- end
572
-
573
- # @param [Symbol] attr attribute
574
- # @return [Boolean] +tru+e if #from_human and #to_human are both defined for given attribute
575
- def to_and_from_human?(attr)
576
- self[attr].respond_to?(:to_human) && self[attr].respond_to?(:from_human)
577
- end
578
-
579
- def field_defs
580
- self.class.field_defs
581
- end
582
-
583
- # rubocop:disable Metrics/AbcSize
584
- def build_field(field)
585
- type = field_defs[field].type
586
-
587
- @fields[field] = if field_defs[field].builder
588
- field_defs[field].builder.call(self, type)
589
- elsif field_defs[field].enum
590
- type.new(field_defs[field].enum)
591
- elsif !field_defs[field].options.empty?
592
- type.new(field_defs[field].options)
593
- else
594
- type.new
595
- end
596
- end
597
- # rubocop:enable Metrics/AbcSize
598
-
599
- def initialize_value(field, val)
600
- type = field_defs[field].type
601
- default = field_defs[field].default
602
- default = default.to_proc.call(self) if default.is_a?(Proc)
603
-
604
- value = val || default
605
- if value.class <= type
606
- @fields[field] = value
607
- elsif @fields[field].respond_to? :from_human
608
- @fields[field].from_human(value)
609
- else
610
- @fields[field].read(value)
611
- end
612
- end
613
-
614
- def initialize_optional(field)
615
- optional = field_defs[field].optional
616
- @optional_fields[field] = optional if optional
617
- end
618
- end
619
- end
620
- end
621
-
622
- # rubocop:enable Metrics/ClassLength