packetgen 3.3.3 → 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (126) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +37 -21
  3. data/lib/packetgen/capture.rb +2 -2
  4. data/lib/packetgen/config.rb +0 -1
  5. data/lib/packetgen/deprecation.rb +7 -7
  6. data/lib/packetgen/header/arp.rb +13 -13
  7. data/lib/packetgen/header/asn1_base.rb +1 -1
  8. data/lib/packetgen/header/base.rb +17 -18
  9. data/lib/packetgen/header/bootp.rb +32 -34
  10. data/lib/packetgen/header/dhcp/option.rb +19 -19
  11. data/lib/packetgen/header/dhcp/options.rb +1 -1
  12. data/lib/packetgen/header/dhcp.rb +3 -3
  13. data/lib/packetgen/header/dhcpv6/duid.rb +16 -16
  14. data/lib/packetgen/header/dhcpv6/option.rb +53 -53
  15. data/lib/packetgen/header/dhcpv6/options.rb +1 -1
  16. data/lib/packetgen/header/dhcpv6/relay.rb +5 -5
  17. data/lib/packetgen/header/dhcpv6.rb +6 -6
  18. data/lib/packetgen/header/dns/name.rb +14 -10
  19. data/lib/packetgen/header/dns/opt.rb +2 -2
  20. data/lib/packetgen/header/dns/option.rb +11 -11
  21. data/lib/packetgen/header/dns/qdsection.rb +1 -1
  22. data/lib/packetgen/header/dns/question.rb +6 -8
  23. data/lib/packetgen/header/dns/rr.rb +56 -43
  24. data/lib/packetgen/header/dns/rrsection.rb +4 -4
  25. data/lib/packetgen/header/dns.rb +27 -30
  26. data/lib/packetgen/header/dot11/control.rb +11 -11
  27. data/lib/packetgen/header/dot11/data.rb +20 -20
  28. data/lib/packetgen/header/dot11/element.rb +4 -4
  29. data/lib/packetgen/header/dot11/management.rb +8 -8
  30. data/lib/packetgen/header/dot11/sub_mngt.rb +39 -53
  31. data/lib/packetgen/header/dot11.rb +88 -93
  32. data/lib/packetgen/header/dot1q.rb +10 -12
  33. data/lib/packetgen/header/dot1x.rb +9 -9
  34. data/lib/packetgen/header/eap/fast.rb +4 -4
  35. data/lib/packetgen/header/eap/md5.rb +6 -6
  36. data/lib/packetgen/header/eap/tls.rb +13 -15
  37. data/lib/packetgen/header/eap/ttls.rb +13 -15
  38. data/lib/packetgen/header/eap.rb +22 -22
  39. data/lib/packetgen/header/eth.rb +18 -18
  40. data/lib/packetgen/header/gre.rb +8 -10
  41. data/lib/packetgen/header/http/headers.rb +2 -2
  42. data/lib/packetgen/header/http/request.rb +17 -16
  43. data/lib/packetgen/header/http/response.rb +18 -17
  44. data/lib/packetgen/header/http/verbs.rb +1 -3
  45. data/lib/packetgen/header/icmp.rb +8 -8
  46. data/lib/packetgen/header/icmpv6.rb +3 -3
  47. data/lib/packetgen/header/igmp.rb +8 -8
  48. data/lib/packetgen/header/igmpv3/group_record.rb +12 -12
  49. data/lib/packetgen/header/igmpv3/mq.rb +16 -18
  50. data/lib/packetgen/header/igmpv3/mr.rb +4 -4
  51. data/lib/packetgen/header/igmpv3.rb +7 -7
  52. data/lib/packetgen/header/ip/addr.rb +13 -13
  53. data/lib/packetgen/header/ip/option.rb +31 -33
  54. data/lib/packetgen/header/ip/options.rb +1 -1
  55. data/lib/packetgen/header/ip.rb +37 -72
  56. data/lib/packetgen/header/ipv6/addr.rb +14 -14
  57. data/lib/packetgen/header/ipv6/extension.rb +8 -8
  58. data/lib/packetgen/header/ipv6/hop_by_hop.rb +9 -9
  59. data/lib/packetgen/header/ipv6.rb +20 -22
  60. data/lib/packetgen/header/llc.rb +17 -17
  61. data/lib/packetgen/header/mdns.rb +1 -1
  62. data/lib/packetgen/header/mld.rb +6 -6
  63. data/lib/packetgen/header/mldv2/mcast_address_record.rb +11 -11
  64. data/lib/packetgen/header/mldv2/mlq.rb +21 -23
  65. data/lib/packetgen/header/mldv2/mlr.rb +8 -8
  66. data/lib/packetgen/header/ospfv2/db_description.rb +11 -12
  67. data/lib/packetgen/header/ospfv2/hello.rb +11 -11
  68. data/lib/packetgen/header/ospfv2/ls_ack.rb +1 -1
  69. data/lib/packetgen/header/ospfv2/ls_request.rb +9 -9
  70. data/lib/packetgen/header/ospfv2/ls_update.rb +3 -3
  71. data/lib/packetgen/header/ospfv2/lsa.rb +54 -58
  72. data/lib/packetgen/header/ospfv2/lsa_header.rb +9 -9
  73. data/lib/packetgen/header/ospfv2.rb +27 -29
  74. data/lib/packetgen/header/ospfv3/db_description.rb +13 -14
  75. data/lib/packetgen/header/ospfv3/hello.rb +12 -12
  76. data/lib/packetgen/header/ospfv3/ipv6_prefix.rb +17 -19
  77. data/lib/packetgen/header/ospfv3/ls_ack.rb +2 -2
  78. data/lib/packetgen/header/ospfv3/ls_request.rb +9 -9
  79. data/lib/packetgen/header/ospfv3/ls_update.rb +4 -4
  80. data/lib/packetgen/header/ospfv3/lsa.rb +48 -51
  81. data/lib/packetgen/header/ospfv3/lsa_header.rb +9 -9
  82. data/lib/packetgen/header/ospfv3.rb +25 -27
  83. data/lib/packetgen/header/sctp/chunk.rb +44 -41
  84. data/lib/packetgen/header/sctp/error.rb +52 -52
  85. data/lib/packetgen/header/sctp/parameter.rb +38 -38
  86. data/lib/packetgen/header/sctp.rb +5 -5
  87. data/lib/packetgen/header/snmp.rb +2 -2
  88. data/lib/packetgen/header/tcp/option.rb +45 -39
  89. data/lib/packetgen/header/tcp/options.rb +2 -2
  90. data/lib/packetgen/header/tcp.rb +55 -44
  91. data/lib/packetgen/header/tftp.rb +16 -16
  92. data/lib/packetgen/header/udp.rb +8 -8
  93. data/lib/packetgen/header.rb +9 -10
  94. data/lib/packetgen/headerable.rb +13 -3
  95. data/lib/packetgen/inspect.rb +2 -2
  96. data/lib/packetgen/packet.rb +54 -37
  97. data/lib/packetgen/pcap.rb +15 -4
  98. data/lib/packetgen/pcapng/block.rb +18 -17
  99. data/lib/packetgen/pcapng/epb.rb +13 -15
  100. data/lib/packetgen/pcapng/file.rb +3 -97
  101. data/lib/packetgen/pcapng/idb.rb +9 -11
  102. data/lib/packetgen/pcapng/shb.rb +13 -15
  103. data/lib/packetgen/pcapng/spb.rb +8 -10
  104. data/lib/packetgen/pcapng/unknown_block.rb +6 -17
  105. data/lib/packetgen/pcapng.rb +4 -4
  106. data/lib/packetgen/pcaprub_wrapper.rb +17 -1
  107. data/lib/packetgen/proto.rb +1 -1
  108. data/lib/packetgen/unknown_packet.rb +2 -2
  109. data/lib/packetgen/utils/arp_spoofer.rb +18 -19
  110. data/lib/packetgen/utils.rb +2 -2
  111. data/lib/packetgen/version.rb +1 -1
  112. data/lib/packetgen.rb +4 -3
  113. metadata +29 -38
  114. data/lib/packetgen/types/abstract_tlv.rb +0 -278
  115. data/lib/packetgen/types/array.rb +0 -287
  116. data/lib/packetgen/types/cstring.rb +0 -109
  117. data/lib/packetgen/types/enum.rb +0 -171
  118. data/lib/packetgen/types/fieldable.rb +0 -66
  119. data/lib/packetgen/types/fields.rb +0 -622
  120. data/lib/packetgen/types/int.rb +0 -473
  121. data/lib/packetgen/types/int_string.rb +0 -102
  122. data/lib/packetgen/types/length_from.rb +0 -54
  123. data/lib/packetgen/types/oui.rb +0 -52
  124. data/lib/packetgen/types/string.rb +0 -97
  125. data/lib/packetgen/types/tlv.rb +0 -161
  126. 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