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.
- checksums.yaml +4 -4
- data/README.md +38 -22
- data/lib/packetgen/capture.rb +2 -2
- data/lib/packetgen/config.rb +0 -1
- data/lib/packetgen/deprecation.rb +14 -8
- data/lib/packetgen/header/arp.rb +17 -18
- data/lib/packetgen/header/asn1_base.rb +2 -1
- data/lib/packetgen/header/base.rb +42 -40
- data/lib/packetgen/header/bootp.rb +35 -37
- data/lib/packetgen/header/dhcp/option.rb +21 -21
- data/lib/packetgen/header/dhcp/options.rb +3 -3
- data/lib/packetgen/header/dhcp.rb +8 -9
- data/lib/packetgen/header/dhcpv6/duid.rb +16 -16
- data/lib/packetgen/header/dhcpv6/option.rb +83 -61
- data/lib/packetgen/header/dhcpv6/options.rb +4 -4
- data/lib/packetgen/header/dhcpv6/relay.rb +6 -5
- data/lib/packetgen/header/dhcpv6.rb +17 -18
- data/lib/packetgen/header/dns/name.rb +21 -16
- data/lib/packetgen/header/dns/opt.rb +5 -2
- data/lib/packetgen/header/dns/option.rb +14 -14
- data/lib/packetgen/header/dns/qdsection.rb +3 -3
- data/lib/packetgen/header/dns/question.rb +7 -8
- data/lib/packetgen/header/dns/rr.rb +56 -43
- data/lib/packetgen/header/dns/rrsection.rb +6 -6
- data/lib/packetgen/header/dns.rb +103 -90
- data/lib/packetgen/header/dot11/control.rb +12 -12
- data/lib/packetgen/header/dot11/data.rb +25 -24
- data/lib/packetgen/header/dot11/element.rb +4 -4
- data/lib/packetgen/header/dot11/management.rb +21 -18
- data/lib/packetgen/header/dot11/sub_mngt.rb +40 -53
- data/lib/packetgen/header/dot11.rb +117 -122
- data/lib/packetgen/header/dot1q.rb +12 -13
- data/lib/packetgen/header/dot1x.rb +13 -13
- data/lib/packetgen/header/eap/fast.rb +4 -4
- data/lib/packetgen/header/eap/md5.rb +16 -8
- data/lib/packetgen/header/eap/tls.rb +18 -19
- data/lib/packetgen/header/eap/ttls.rb +22 -21
- data/lib/packetgen/header/eap.rb +73 -48
- data/lib/packetgen/header/eth.rb +41 -27
- data/lib/packetgen/header/gre.rb +33 -11
- data/lib/packetgen/header/http/headers.rb +7 -6
- data/lib/packetgen/header/http/request.rb +38 -29
- data/lib/packetgen/header/http/response.rb +35 -27
- data/lib/packetgen/header/http/verbs.rb +1 -3
- data/lib/packetgen/header/icmp.rb +14 -14
- data/lib/packetgen/header/icmpv6.rb +10 -9
- data/lib/packetgen/header/igmp.rb +26 -15
- data/lib/packetgen/header/igmpv3/group_record.rb +18 -13
- data/lib/packetgen/header/igmpv3/mq.rb +16 -18
- data/lib/packetgen/header/igmpv3/mr.rb +5 -5
- data/lib/packetgen/header/igmpv3.rb +12 -11
- data/lib/packetgen/header/ip/addr.rb +19 -15
- data/lib/packetgen/header/ip/option.rb +47 -36
- data/lib/packetgen/header/ip/options.rb +1 -1
- data/lib/packetgen/header/ip.rb +77 -95
- data/lib/packetgen/header/ipv6/addr.rb +28 -27
- data/lib/packetgen/header/ipv6/extension.rb +13 -11
- data/lib/packetgen/header/ipv6/hop_by_hop.rb +32 -13
- data/lib/packetgen/header/ipv6.rb +42 -35
- data/lib/packetgen/header/llc.rb +28 -21
- data/lib/packetgen/header/mdns.rb +10 -3
- data/lib/packetgen/header/mld.rb +15 -13
- data/lib/packetgen/header/mldv2/mcast_address_record.rb +17 -12
- data/lib/packetgen/header/mldv2/mlq.rb +22 -24
- data/lib/packetgen/header/mldv2/mlr.rb +8 -8
- data/lib/packetgen/header/mldv2.rb +1 -1
- data/lib/packetgen/header/ospfv2/db_description.rb +17 -18
- data/lib/packetgen/header/ospfv2/hello.rb +18 -17
- data/lib/packetgen/header/ospfv2/ls_ack.rb +6 -7
- data/lib/packetgen/header/ospfv2/ls_request.rb +14 -13
- data/lib/packetgen/header/ospfv2/ls_update.rb +9 -9
- data/lib/packetgen/header/ospfv2/lsa.rb +79 -60
- data/lib/packetgen/header/ospfv2/lsa_header.rb +12 -11
- data/lib/packetgen/header/ospfv2.rb +49 -46
- data/lib/packetgen/header/ospfv3/db_description.rb +20 -22
- data/lib/packetgen/header/ospfv3/hello.rb +17 -16
- data/lib/packetgen/header/ospfv3/ipv6_prefix.rb +22 -20
- data/lib/packetgen/header/ospfv3/ls_ack.rb +7 -8
- data/lib/packetgen/header/ospfv3/ls_request.rb +18 -18
- data/lib/packetgen/header/ospfv3/ls_update.rb +10 -10
- data/lib/packetgen/header/ospfv3/lsa.rb +62 -51
- data/lib/packetgen/header/ospfv3/lsa_header.rb +12 -11
- data/lib/packetgen/header/ospfv3.rb +54 -52
- data/lib/packetgen/header/sctp/chunk.rb +80 -56
- data/lib/packetgen/header/sctp/error.rb +174 -202
- data/lib/packetgen/header/sctp/padded32.rb +3 -3
- data/lib/packetgen/header/sctp/parameter.rb +89 -136
- data/lib/packetgen/header/sctp.rb +19 -8
- data/lib/packetgen/header/snmp.rb +108 -7
- data/lib/packetgen/header/tcp/option.rb +52 -39
- data/lib/packetgen/header/tcp/options.rb +13 -5
- data/lib/packetgen/header/tcp.rb +83 -65
- data/lib/packetgen/header/tftp.rb +31 -25
- data/lib/packetgen/header/udp.rb +21 -19
- data/lib/packetgen/header.rb +23 -18
- data/lib/packetgen/headerable.rb +21 -5
- data/lib/packetgen/inspect.rb +3 -8
- data/lib/packetgen/packet.rb +146 -71
- data/lib/packetgen/pcap.rb +15 -4
- data/lib/packetgen/pcapng/block.rb +20 -18
- data/lib/packetgen/pcapng/epb.rb +13 -15
- data/lib/packetgen/pcapng/file.rb +44 -111
- data/lib/packetgen/pcapng/idb.rb +11 -12
- data/lib/packetgen/pcapng/shb.rb +15 -16
- data/lib/packetgen/pcapng/spb.rb +9 -11
- data/lib/packetgen/pcapng/unknown_block.rb +6 -17
- data/lib/packetgen/pcapng.rb +6 -4
- data/lib/packetgen/pcaprub_wrapper.rb +17 -1
- data/lib/packetgen/proto.rb +5 -1
- data/lib/packetgen/unknown_packet.rb +5 -5
- data/lib/packetgen/utils/arp_spoofer.rb +18 -19
- data/lib/packetgen/utils.rb +4 -3
- data/lib/packetgen/version.rb +1 -1
- data/lib/packetgen.rb +12 -5
- metadata +29 -38
- data/lib/packetgen/types/abstract_tlv.rb +0 -278
- data/lib/packetgen/types/array.rb +0 -287
- data/lib/packetgen/types/cstring.rb +0 -109
- data/lib/packetgen/types/enum.rb +0 -171
- data/lib/packetgen/types/fieldable.rb +0 -66
- data/lib/packetgen/types/fields.rb +0 -622
- data/lib/packetgen/types/int.rb +0 -473
- data/lib/packetgen/types/int_string.rb +0 -102
- data/lib/packetgen/types/length_from.rb +0 -54
- data/lib/packetgen/types/oui.rb +0 -52
- data/lib/packetgen/types/string.rb +0 -97
- data/lib/packetgen/types/tlv.rb +0 -161
- 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
|