packetgen 2.8.7 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +0 -1
  3. data/README.md +5 -4
  4. data/lib/packetgen.rb +6 -12
  5. data/lib/packetgen/capture.rb +43 -39
  6. data/lib/packetgen/config.rb +0 -1
  7. data/lib/packetgen/deprecation.rb +1 -1
  8. data/lib/packetgen/header.rb +9 -9
  9. data/lib/packetgen/header/asn1_base.rb +10 -10
  10. data/lib/packetgen/header/base.rb +42 -101
  11. data/lib/packetgen/header/dhcp/option.rb +5 -11
  12. data/lib/packetgen/header/dhcpv6/duid.rb +2 -0
  13. data/lib/packetgen/header/dhcpv6/option.rb +2 -19
  14. data/lib/packetgen/header/dhcpv6/options.rb +7 -0
  15. data/lib/packetgen/header/dns.rb +5 -23
  16. data/lib/packetgen/header/dns/name.rb +1 -0
  17. data/lib/packetgen/header/dns/qdsection.rb +1 -0
  18. data/lib/packetgen/header/dns/question.rb +3 -7
  19. data/lib/packetgen/header/dns/rr.rb +3 -0
  20. data/lib/packetgen/header/dns/rrsection.rb +1 -0
  21. data/lib/packetgen/header/dot11.rb +1 -17
  22. data/lib/packetgen/header/dot1x.rb +1 -0
  23. data/lib/packetgen/header/eap.rb +4 -7
  24. data/lib/packetgen/header/eth.rb +2 -0
  25. data/lib/packetgen/header/http/headers.rb +3 -0
  26. data/lib/packetgen/header/http/request.rb +5 -4
  27. data/lib/packetgen/header/http/response.rb +5 -4
  28. data/lib/packetgen/header/icmp.rb +6 -0
  29. data/lib/packetgen/header/icmpv6.rb +6 -0
  30. data/lib/packetgen/header/igmpv3/mq.rb +2 -0
  31. data/lib/packetgen/header/ip.rb +32 -30
  32. data/lib/packetgen/header/ip/addr.rb +1 -0
  33. data/lib/packetgen/header/ip/option.rb +23 -20
  34. data/lib/packetgen/header/ip/options.rb +11 -24
  35. data/lib/packetgen/header/ipv6.rb +45 -34
  36. data/lib/packetgen/header/ipv6/addr.rb +2 -0
  37. data/lib/packetgen/header/ipv6/hop_by_hop.rb +7 -31
  38. data/lib/packetgen/header/mdns.rb +1 -0
  39. data/lib/packetgen/header/mldv2/mlq.rb +2 -0
  40. data/lib/packetgen/header/ospfv2/lsa.rb +15 -25
  41. data/lib/packetgen/header/ospfv3/ipv6_prefix.rb +1 -1
  42. data/lib/packetgen/header/ospfv3/lsa.rb +8 -25
  43. data/lib/packetgen/header/snmp.rb +2 -0
  44. data/lib/packetgen/header/tcp.rb +23 -2
  45. data/lib/packetgen/header/tcp/option.rb +51 -52
  46. data/lib/packetgen/header/tcp/options.rb +17 -52
  47. data/lib/packetgen/header/tftp.rb +3 -0
  48. data/lib/packetgen/header/udp.rb +8 -0
  49. data/lib/packetgen/packet.rb +119 -102
  50. data/lib/packetgen/pcapng/block.rb +4 -10
  51. data/lib/packetgen/pcapng/epb.rb +4 -4
  52. data/lib/packetgen/pcapng/file.rb +7 -3
  53. data/lib/packetgen/pcapng/idb.rb +2 -2
  54. data/lib/packetgen/pcapng/shb.rb +3 -3
  55. data/lib/packetgen/pcapng/spb.rb +1 -8
  56. data/lib/packetgen/pcapng/unknown_block.rb +0 -7
  57. data/lib/packetgen/types.rb +1 -0
  58. data/lib/packetgen/types/array.rb +73 -71
  59. data/lib/packetgen/types/cstring.rb +1 -1
  60. data/lib/packetgen/types/enum.rb +3 -3
  61. data/lib/packetgen/types/fields.rb +66 -106
  62. data/lib/packetgen/types/int.rb +9 -5
  63. data/lib/packetgen/types/length_from.rb +45 -0
  64. data/lib/packetgen/types/oui.rb +2 -0
  65. data/lib/packetgen/types/string.rb +10 -16
  66. data/lib/packetgen/types/tlv.rb +7 -15
  67. data/lib/packetgen/utils.rb +8 -8
  68. data/lib/packetgen/utils/arp_spoofer.rb +1 -2
  69. data/lib/packetgen/version.rb +1 -1
  70. metadata +3 -21
  71. data/lib/packetgen/header/crypto.rb +0 -62
  72. data/lib/packetgen/header/esp.rb +0 -413
  73. data/lib/packetgen/header/ike.rb +0 -243
  74. data/lib/packetgen/header/ike/auth.rb +0 -165
  75. data/lib/packetgen/header/ike/cert.rb +0 -76
  76. data/lib/packetgen/header/ike/certreq.rb +0 -66
  77. data/lib/packetgen/header/ike/id.rb +0 -99
  78. data/lib/packetgen/header/ike/ke.rb +0 -79
  79. data/lib/packetgen/header/ike/nonce.rb +0 -40
  80. data/lib/packetgen/header/ike/notify.rb +0 -176
  81. data/lib/packetgen/header/ike/payload.rb +0 -315
  82. data/lib/packetgen/header/ike/sa.rb +0 -561
  83. data/lib/packetgen/header/ike/sk.rb +0 -261
  84. data/lib/packetgen/header/ike/ts.rb +0 -270
  85. data/lib/packetgen/header/ike/vendor_id.rb +0 -39
  86. data/lib/packetgen/header/netbios.rb +0 -20
  87. data/lib/packetgen/header/netbios/datagram.rb +0 -105
  88. data/lib/packetgen/header/netbios/name.rb +0 -67
  89. data/lib/packetgen/header/netbios/session.rb +0 -64
@@ -37,13 +37,6 @@ module PacketGen
37
37
  @fields.key?(:options) && @fields[:options].sz > 0
38
38
  end
39
39
 
40
- # @deprecated Use {#options?} instead.
41
- # @return [Boolean]
42
- def has_options?
43
- Deprecation.deprecated(self.class, __method__, 'options?')
44
- options?
45
- end
46
-
47
40
  # Calculate block length and update :block_len and block_len2 fields
48
41
  # @return [void]
49
42
  def recalc_block_len
@@ -56,8 +49,8 @@ module PacketGen
56
49
  # @return [void]
57
50
  def pad_field(*fields)
58
51
  fields.each do |field|
59
- unless (@fields[field].size % 4).zero?
60
- @fields[field] << "\x00" * (4 - (@fields[field].size % 4))
52
+ unless (@fields[field].sz % 4).zero?
53
+ @fields[field] << "\x00" * (4 - (@fields[field].sz % 4))
61
54
  end
62
55
  end
63
56
  end
@@ -72,13 +65,14 @@ module PacketGen
72
65
  unless %i[little big].include? endian
73
66
  raise ArgumentError, "unknown endianness for #{self.class}"
74
67
  end
68
+
75
69
  @endian = endian
76
70
  @fields.each { |_f, v| v.endian = endian if v.is_a?(Types::Int) }
77
71
  endian
78
72
  end
79
73
 
80
74
  def check_len_coherency
81
- unless self[:block_len].to_i == self[:block_len2].to_i
75
+ unless self.block_len == self.block_len2
82
76
  raise InvalidFileError, 'Incoherency in Block length'
83
77
  end
84
78
  end
@@ -96,10 +96,10 @@ module PacketGen
96
96
  self[:tsl].read io.read(4)
97
97
  self[:cap_len].read io.read(4)
98
98
  self[:orig_len].read io.read(4)
99
- self[:data].read io.read(self[:cap_len].to_i)
99
+ self[:data].read io.read(self.cap_len)
100
100
  data_pad_len = (4 - (self[:cap_len].to_i % 4)) % 4
101
101
  io.read data_pad_len
102
- options_len = self[:block_len].to_i - self[:cap_len].to_i - data_pad_len
102
+ options_len = self.block_len - self.cap_len - data_pad_len
103
103
  options_len -= MIN_SIZE
104
104
  self[:options].read io.read(options_len)
105
105
  self[:block_len2].read io.read(4)
@@ -111,7 +111,7 @@ module PacketGen
111
111
  # Return timestamp as a Time object
112
112
  # @return [Time]
113
113
  def timestamp
114
- Time.at((self[:tsh].to_i << 32 | self[:tsl].to_i) * ts_resol)
114
+ Time.at((self.tsh << 32 | self.tsl) * ts_resol)
115
115
  end
116
116
 
117
117
  # Return the object as a String
@@ -125,7 +125,7 @@ module PacketGen
125
125
  private
126
126
 
127
127
  def ts_resol
128
- if @interface.nil?
128
+ if !defined?(@interface) || @interface.nil?
129
129
  1E-6
130
130
  else
131
131
  @interface.ts_resol
@@ -66,10 +66,14 @@ module PacketGen
66
66
  end
67
67
 
68
68
  return unless blk
69
+
69
70
  count = 0
70
71
  @sections.each do |section|
71
72
  section.interfaces.each do |intf|
72
- intf.packets.each { |pkt| count += 1; yield pkt }
73
+ intf.packets.each do |pkt|
74
+ count += 1
75
+ yield pkt
76
+ end
73
77
  end
74
78
  end
75
79
  count
@@ -347,8 +351,8 @@ module PacketGen
347
351
  shb << block
348
352
  block.section = shb
349
353
  when EPB
350
- shb.interfaces[block.interface_id.to_i] << block
351
- block.interface = shb.interfaces[block.interface_id.to_i]
354
+ shb.interfaces[block.interface_id] << block
355
+ block.interface = shb.interfaces[block.interface_id]
352
356
  when SPB
353
357
  shb.interfaces[0] << block
354
358
  block.interface = shb.interfaces[0]
@@ -83,7 +83,7 @@ module PacketGen
83
83
  self[:link_type].read io.read(2)
84
84
  self[:reserved].read io.read(2)
85
85
  self[:snaplen].read io.read(4)
86
- self[:options].read io.read(self[:block_len].to_i - MIN_SIZE)
86
+ self[:options].read io.read(self.block_len - MIN_SIZE)
87
87
  self[:block_len2].read io.read(4)
88
88
 
89
89
  check_len_coherency
@@ -105,7 +105,7 @@ module PacketGen
105
105
  if @options_decoded && !force
106
106
  @ts_resol
107
107
  else
108
- packstr = @endian == :little ? 'v' : 'n'
108
+ packstr = endian == :little ? 'v' : 'n'
109
109
  idx = 0
110
110
  options = self[:options]
111
111
 
@@ -130,7 +130,7 @@ module PacketGen
130
130
  self[:ver_major].read io.read(2)
131
131
  self[:ver_minor].read io.read(2)
132
132
  self[:section_len].read io.read(8)
133
- self[:options].read io.read(self[:block_len].to_i - MIN_SIZE)
133
+ self[:options].read io.read(self.block_len - MIN_SIZE)
134
134
  self[:block_len2].read io.read(4)
135
135
 
136
136
  check_len_coherency
@@ -149,8 +149,8 @@ module PacketGen
149
149
  # @return [String]
150
150
  def to_s
151
151
  body = @interfaces.map(&:to_s).join
152
- unless self[:section_len].to_i == SECTION_LEN_UNDEFINED
153
- self.section_len.value = body.size
152
+ unless self.section_len == SECTION_LEN_UNDEFINED
153
+ self.section_len = body.size
154
154
  end
155
155
  pad_field :options
156
156
  recalc_block_len
@@ -56,13 +56,6 @@ module PacketGen
56
56
  false
57
57
  end
58
58
 
59
- # @deprecated Use {#options?} instead.
60
- # @return [false]
61
- def has_options?
62
- Deprecation.deprecated(self.class, __method__, 'options?')
63
- options?
64
- end
65
-
66
59
  # Reads a String or a IO to populate the object
67
60
  # @param [::String,IO] str_or_io
68
61
  # @return [self]
@@ -90,7 +83,7 @@ module PacketGen
90
83
  self[:block_len2].read io.read(4)
91
84
 
92
85
  check_len_coherency
93
- self.type = self[:type] || PcapNG::IDB_TYPE.to_i
86
+ self.type ||= PcapNG::IDB_TYPE.to_i
94
87
  self
95
88
  end
96
89
 
@@ -40,13 +40,6 @@ module PacketGen
40
40
  false
41
41
  end
42
42
 
43
- # @deprecated Use {#options?} instead.
44
- # @return [false]
45
- def has_options?
46
- Deprecation.deprecated(self.class, __method__, 'options?')
47
- options?
48
- end
49
-
50
43
  # Reads a String or a IO to populate the object
51
44
  # @param [::String,IO] str_or_io
52
45
  # @return [self]
@@ -9,6 +9,7 @@ module PacketGen
9
9
  end
10
10
  end
11
11
 
12
+ require_relative 'types/length_from'
12
13
  require_relative 'types/int'
13
14
  require_relative 'types/enum'
14
15
  require_relative 'types/string'
@@ -5,6 +5,8 @@
5
5
 
6
6
  # frozen_string_literal: true
7
7
 
8
+ require 'forwardable'
9
+
8
10
  module PacketGen
9
11
  module Types
10
12
  # @abstract Base class to define set of {Fields} subclasses.
@@ -14,20 +16,66 @@ module PacketGen
14
16
  #
15
17
  # A default method is defined by {Array}: it calls constructor of class defined
16
18
  # by {.set_of}.
19
+ #
20
+ # == #real_type
21
+ # Subclasses should define private method +#real_type+ is {.set_of} type
22
+ # may be subclassed. This method should return real class to use. It
23
+ # takes an only argument, which is of type given by {.set_of}.
24
+ #
25
+ # Default behaviour of this method is to argument's class.
26
+ #
17
27
  # @author Sylvain Daubert
18
28
  class Array
29
+ extend Forwardable
30
+
31
+ # @!method [](index)
32
+ # Return the element at +index+.
33
+ # @param [integer] index
34
+ # @return [Object]
35
+ # @!method clear
36
+ # Clear array.
37
+ # @return [void]
38
+ # @!method each
39
+ # Calls the given block once for each element in self, passing that
40
+ # element as a parameter. Returns the array itself.
41
+ # @return [Array]
42
+ # @method empty?
43
+ # Return +true+ if contains no element.
44
+ # @return [Booelan]
45
+ # @!method first
46
+ # Return first element
47
+ # @return [Object]
48
+ # @!method last
49
+ # Return last element.
50
+ # @return [Object]
51
+ # @!method size
52
+ # Get number of element in array
53
+ # @return [Integer]
54
+ def_delegators :@array, :[], :clear, :each, :empty?, :first, :last, :size
55
+ alias length size
56
+
19
57
  include Enumerable
58
+ include LengthFrom
20
59
 
21
60
  # Separator used in {#to_human}.
22
61
  # May be ovverriden by subclasses
23
62
  HUMAN_SEPARATOR = ','
24
63
 
25
- # Define type of objects in set. Used by {#read} and {#push}.
26
- # @param [Class] klass
27
- # @return [void]
28
64
  # rubocop:disable Naming/AccessorMethodName
29
- def self.set_of(klass)
30
- @klass = klass
65
+ class <<self
66
+ # Get class set with {.set_of}.
67
+ # @return [Class]
68
+ # @since 3.0.0
69
+ def set_of_klass
70
+ @klass
71
+ end
72
+
73
+ # Define type of objects in set. Used by {#read} and {#push}.
74
+ # @param [Class] klass
75
+ # @return [void]
76
+ def set_of(klass)
77
+ @klass = klass
78
+ end
31
79
  end
32
80
  # rubocop:enable Naming/AccessorMethodName
33
81
 
@@ -36,6 +84,7 @@ module PacketGen
36
84
  def initialize(options={})
37
85
  @counter = options[:counter]
38
86
  @array = []
87
+ initialize_length_from(options)
39
88
  end
40
89
 
41
90
  # Initialize array for copy:
@@ -44,26 +93,13 @@ module PacketGen
44
93
  @array = @array.dup
45
94
  end
46
95
 
47
- # Return the element at +index+.
48
- # @param [integer] index
49
- # @return [Object]
50
- def [](index)
51
- @array[index]
52
- end
53
-
54
96
  def ==(other)
55
- case other
56
- when Array
57
- @array == other.to_a
58
- else
59
- @array == other
60
- end
61
- end
62
-
63
- # Clear array.
64
- # @return [void]
65
- def clear
66
- @array.clear
97
+ @array == case other
98
+ when Array
99
+ other.to_a
100
+ else
101
+ other
102
+ end
67
103
  end
68
104
 
69
105
  # Clear array. Reset associated counter, if any.
@@ -91,31 +127,6 @@ module PacketGen
91
127
  deleted
92
128
  end
93
129
 
94
- # Calls the given block once for each element in self, passing that
95
- # element as a parameter. Returns the array itself.
96
- # @return [Array]
97
- def each
98
- @array.each { |el| yield el }
99
- end
100
-
101
- # Return +true+ if contains no element.
102
- # @return [Booelan]
103
- def empty?
104
- @array.empty?
105
- end
106
-
107
- # Return first element
108
- # @return [Object]
109
- def first
110
- @array.first
111
- end
112
-
113
- # Return last element.
114
- # @return [Object]
115
- def last
116
- @array.last
117
- end
118
-
119
130
  # @abstract depend on private method +#record_from_hash+ which should be
120
131
  # declared by subclasses.
121
132
  # Add an object to this array
@@ -150,10 +161,13 @@ module PacketGen
150
161
  clear
151
162
  return self if str.nil?
152
163
  return self if @counter && @counter.to_i.zero?
153
- force_binary str
154
- klass = self.class.class_eval { @klass }
164
+
165
+ str = read_with_length_from(str)
166
+ klass = self.class.set_of_klass
155
167
  until str.empty?
156
168
  obj = klass.new.read(str)
169
+ real_klass = real_type(obj)
170
+ obj = real_klass.new.read(str) unless real_klass == klass
157
171
  @array << obj
158
172
  str.slice!(0, obj.sz)
159
173
  break if @counter && self.size == @counter.to_i
@@ -161,13 +175,6 @@ module PacketGen
161
175
  self
162
176
  end
163
177
 
164
- # Get number of element in array
165
- # @return [Integer]
166
- def size
167
- @array.size
168
- end
169
- alias length size
170
-
171
178
  # Get size in bytes
172
179
  # @return [Integer]
173
180
  def sz
@@ -192,22 +199,17 @@ module PacketGen
192
199
  @array.map(&:to_human).join(self.class::HUMAN_SEPARATOR)
193
200
  end
194
201
 
195
- # Force binary encoding for +str+
196
- # @param [String] str
197
- # @return [String] binary encoded string
198
- def force_binary(str)
199
- PacketGen.force_binary str
200
- end
201
-
202
202
  private
203
203
 
204
204
  def record_from_hash(obj)
205
- obj_klass = self.class.class_eval { @klass }
206
- if obj_klass
207
- obj_klass.new(obj)
208
- else
209
- raise NotImplementedError, 'class should define #record_from_hash or declare type of elements in set with .set_of'
210
- end
205
+ obj_klass = self.class.set_of_klass
206
+ return obj_klass.new(obj) if obj_klass
207
+
208
+ raise NotImplementedError, 'class should define #record_from_hash or declare type of elements in set with .set_of'
209
+ end
210
+
211
+ def real_type(obj)
212
+ obj.class
211
213
  end
212
214
  end
213
215
 
@@ -32,7 +32,7 @@ module PacketGen
32
32
  # get null-terminated string
33
33
  # @return [String]
34
34
  def to_s
35
- if @static_length.is_a? Integer
35
+ if defined?(@static_length) && @static_length.is_a?(Integer)
36
36
  if self.size >= @static_length
37
37
  s = self[0, @static_length]
38
38
  s[-1] = "\x00".encode(s.encoding)
@@ -45,9 +45,10 @@ module PacketGen
45
45
  end
46
46
 
47
47
  # Setter for value attribute
48
- # @param [Integer, String,nil] value value as an Integer or as a String
48
+ # @param [#to_i, String,nil] value value as an Integer or as a String
49
49
  # from enumration
50
50
  # @return [Integer]
51
+ # @raise [ArgumentError] String value is unknown
51
52
  def value=(value)
52
53
  ival = case value
53
54
  when NilClass
@@ -56,8 +57,7 @@ module PacketGen
56
57
  raise ArgumentError, "#{value.inspect} not in enumeration" unless @enum.key? value
57
58
  @enum[value]
58
59
  else
59
- raise ArgumentError, "#{value.inspect} not in enumeration" unless @enum.value? value
60
- value
60
+ value.to_i
61
61
  end
62
62
  @value = ival
63
63
  end
@@ -49,7 +49,7 @@ module PacketGen
49
49
  # define_field :type, PacketGen::Types::Int16le
50
50
  # # define a string field
51
51
  # define_field :body, PacketGen::Types::String
52
- # # define afield using a complex type (Fields subclass)
52
+ # # define a field using a complex type (Fields subclass)
53
53
  # define_field :mac_addr, PacketGen::Eth::MacAddr
54
54
  #
55
55
  # This example creates six methods on our Fields subclass: +#type+, +#type=+,
@@ -59,20 +59,22 @@ module PacketGen
59
59
  # * +:default+ gives default field value. It may be a simple value (an Integer
60
60
  # for an Int field, for example) or a lambda,
61
61
  # * +:builder+ to give a builder/constructor lambda to create field. The lambda
62
- # takes one argument: {Fields} subclass object owning field,
62
+ # takes 2 argument: {Fields} subclass object owning field, and type class as passes
63
+ # as second argument to .define_field,
63
64
  # * +:optional+ to define this field as optional. This option takes a lambda
64
- # parameter used to say if this field is present or not,
65
+ # parameter used to say if this field is present or not. The lambda takes an argument
66
+ # ({Fields} subclass object owning field),
65
67
  # * +:enum+ to define Hash enumeration for an {Enum} type.
66
68
  # For example:
67
69
  # # 32-bit integer field defaulting to 1
68
70
  # define_field :type, PacketGen::Types::Int32, default: 1
69
71
  # # 16-bit integer field, created with a random value. Each instance of this
70
72
  # # object will have a different value.
71
- # define_field :id, PacketGen::Types::Int16, default: ->{ rand(65535) }
73
+ # define_field :id, PacketGen::Types::Int16, default: ->(obj) { rand(65535) }
72
74
  # # a size field
73
75
  # define_field :body_size, PacketGen::Types::Int16
74
76
  # # String field which length is taken from body_size field
75
- # define_field :body, PacketGen::Types::String, builder: ->(obj, type) { type.new('', length_from: obj[:body_size]) }
77
+ # define_field :body, PacketGen::Types::String, builder: ->(obj, type) { type.new(length_from: obj[:body_size]) }
76
78
  # # 16-bit enumeration type. As :default not specified, default to first value of enum
77
79
  # define_field :type_class, PacketGen::Types::Int16Enum, enum: { 'class1' => 1, 'class2' => 2}
78
80
  # # optional field. Only present if another field has a certain value
@@ -92,8 +94,19 @@ module PacketGen
92
94
  # to access Boolean RSV, MF and DF flags from +frag+ field,
93
95
  # * +#fragment_offset+ and +#fragment_offset=+ to access 13-bit integer fragment
94
96
  # offset subfield from +frag+ field.
97
+ #
98
+ # == Creating a new field class from another one
99
+ # Some methods may help in this case:
100
+ # * {.define_field_before} to define a new field before an existing one,
101
+ # * {.define_field_after} to define a new field after an existing onr,
102
+ # * {.remove_field} to remove an existing field,
103
+ # * {.uptade_fied} to change options of a field (but not its type),
104
+ # * {.remove_bit_fields_on} to remove a bit fields definition.
105
+ #
95
106
  # @author Sylvain Daubert
96
107
  class Fields
108
+ # @private
109
+ FieldDef = Struct.new(:type, :default, :builder, :optional, :enum, :options)
97
110
  # @private field names, ordered as they were declared
98
111
  @ordered_fields = []
99
112
  # @private field definitions
@@ -112,6 +125,7 @@ module PacketGen
112
125
  end
113
126
  ordered = @ordered_fields.clone
114
127
  bf = @bit_fields.clone
128
+
115
129
  klass.class_eval do
116
130
  @ordered_fields = ordered
117
131
  @field_defs = field_defs
@@ -157,26 +171,32 @@ module PacketGen
157
171
  if type < Types::Enum
158
172
  define << "def #{name}; self[:#{name}].to_i; end"
159
173
  define << "def #{name}=(val) self[:#{name}].value = val; end"
160
- elsif type < Types::Int
161
- define << "def #{name}; self[:#{name}].to_i; end"
162
- define << "def #{name}=(val) self[:#{name}].read val; end"
163
- elsif type.instance_methods.include?(:to_human) &&
164
- type.instance_methods.include?(:from_human)
165
- define << "def #{name}; self[:#{name}].to_human; end"
166
- define << "def #{name}=(val) self[:#{name}].from_human val; end"
167
174
  else
168
- define << "def #{name}; self[:#{name}]; end\n"
169
- define << "def #{name}=(val) self[:#{name}].read val; end"
175
+ define << "def #{name}\n" \
176
+ " if self[:#{name}].respond_to?(:to_human) && self[:#{name}].respond_to?(:from_human)\n" \
177
+ " self[:#{name}].to_human\n" \
178
+ " else\n" \
179
+ " self[:#{name}]\n" \
180
+ " end\n" \
181
+ "end"
182
+ define << "def #{name}=(val)\n" \
183
+ " if self[:#{name}].respond_to?(:to_human) && self[:#{name}].respond_to?(:from_human)\n" \
184
+ " self[:#{name}].from_human val\n" \
185
+ " else\n" \
186
+ " self[:#{name}].read val\n" \
187
+ " end\n" \
188
+ "end"
170
189
  end
171
190
 
172
191
  define.delete(1) if type.instance_methods.include? "#{name}=".to_sym
173
192
  define.delete(0) if type.instance_methods.include? name
174
193
  class_eval define.join("\n")
175
- @field_defs[name] = [type, options.delete(:default),
176
- options.delete(:builder),
177
- options.delete(:optional),
178
- options.delete(:enum),
179
- options]
194
+ @field_defs[name] = FieldDef.new(type,
195
+ options.delete(:default),
196
+ options.delete(:builder),
197
+ options.delete(:optional),
198
+ options.delete(:enum),
199
+ options)
180
200
  fields << name
181
201
  end
182
202
 
@@ -229,16 +249,6 @@ module PacketGen
229
249
  undef_method "#{name}=" if method_defined?("#{name}=")
230
250
  end
231
251
 
232
- # Delete a previously defined field
233
- # @param [Symbol] name
234
- # @return [void]
235
- # @deprecated Use {.remove_field} instead.
236
- # @since 2.8.4 deprecated
237
- def delete_field(name)
238
- Deprecation.deprecated(self, __method__, 'remove_field', klass_method: true)
239
- remove_field name
240
- end
241
-
242
252
  # Update a previously defined field
243
253
  # @param [Symbol] field field name to create
244
254
  # @param [Hash] options See {.define_field}.
@@ -249,11 +259,11 @@ module PacketGen
249
259
  def update_field(field, options)
250
260
  raise ArgumentError, "unkown #{field} field for #{self}" unless @field_defs.key?(field)
251
261
 
252
- @field_defs[field][1] = options.delete(:default) if options.key?(:default)
253
- @field_defs[field][2] = options.delete(:builder) if options.key?(:builder)
254
- @field_defs[field][3] = options.delete(:optional) if options.key?(:optional)
255
- @field_defs[field][4] = options.delete(:enum) if options.key?(:enum)
256
- @field_defs[field][5].merge!(options)
262
+ @field_defs[field].default = options.delete(:default) if options.key?(:default)
263
+ @field_defs[field].builder = options.delete(:builder) if options.key?(:builder)
264
+ @field_defs[field].optional = options.delete(:optional) if options.key?(:optional)
265
+ @field_defs[field].enum = options.delete(:enum) if options.key?(:enum)
266
+ @field_defs[field].options.merge!(options)
257
267
  end
258
268
 
259
269
  # Define a bitfield on given attribute
@@ -279,7 +289,7 @@ module PacketGen
279
289
  attr_def = @field_defs[attr]
280
290
  raise ArgumentError, "unknown #{attr} field" if attr_def.nil?
281
291
 
282
- type = attr_def.first
292
+ type = attr_def.type
283
293
  unless type < Types::Int
284
294
  raise TypeError, "#{attr} is not a PacketGen::Types::Int"
285
295
  end
@@ -344,12 +354,12 @@ module PacketGen
344
354
 
345
355
  fields.each do |field, size|
346
356
  undef_method "#{field}="
347
- undef_method(size == 1 ? "#{field}?" : "#{field}")
357
+ undef_method(size == 1 ? "#{field}?" : field)
348
358
  end
349
359
  end
350
360
  end
351
361
 
352
- # Create a new header object
362
+ # Create a new fields object
353
363
  # @param [Hash] options Keys are symbols. They should have name of object
354
364
  # attributes, as defined by {.define_field} and by {.define_bit_fields_on}.
355
365
  def initialize(options={})
@@ -358,9 +368,14 @@ module PacketGen
358
368
 
359
369
  field_defs = self.class.class_eval { @field_defs }
360
370
  self.class.fields.each do |field|
361
- ary = field_defs[field]
362
- type, default, builder, optional, enum, field_options = ary
371
+ type = field_defs[field].type
372
+ default = field_defs[field].default
363
373
  default = default.to_proc.call(self) if default.is_a?(Proc)
374
+ builder = field_defs[field].builder
375
+ optional = field_defs[field].optional
376
+ enum = field_defs[field].enum
377
+ field_options = field_defs[field].options
378
+
364
379
  @fields[field] = if builder
365
380
  builder.call(self, type)
366
381
  elsif enum
@@ -374,17 +389,6 @@ module PacketGen
374
389
  value = options[field] || default
375
390
  if value.class <= type
376
391
  @fields[field] = value
377
- elsif type < Types::Enum
378
- case value
379
- when ::String
380
- @fields[field].value = value
381
- else
382
- @fields[field].read(value)
383
- end
384
- elsif type < Types::Int
385
- @fields[field].read(value)
386
- elsif type <= Types::String
387
- @fields[field].read(value)
388
392
  elsif @fields[field].respond_to? :from_human
389
393
  @fields[field].from_human(value)
390
394
  end
@@ -430,12 +434,6 @@ module PacketGen
430
434
  @optional_fields.key? field
431
435
  end
432
436
 
433
- # @deprecated Use {#optional?} instead.
434
- def is_optional?(field)
435
- Deprecation.deprecated(self.class, __method__, 'optional?', klass_method: true)
436
- optional? field
437
- end
438
-
439
437
  # Say if an optional field is present
440
438
  # @return [Boolean]
441
439
  def present?(field)
@@ -444,14 +442,6 @@ module PacketGen
444
442
  @optional_fields[field].call(self)
445
443
  end
446
444
 
447
- # Say if an optional field is present
448
- # @return [Boolean]
449
- # @deprecated Use {#present?} instead.
450
- def is_present?(field)
451
- Deprecation.deprecated(self.class, __method__, 'present?', klass_method: true)
452
- present? field
453
- end
454
-
455
445
  # Populate object from a binary string
456
446
  # @param [String] str
457
447
  # @return [Fields] self
@@ -463,17 +453,10 @@ module PacketGen
463
453
  fields.each do |field|
464
454
  next unless present?(field)
465
455
 
466
- obj = nil
467
- if self[field].respond_to? :width
468
- width = self[field].width
469
- obj = self[field].read str[start, width]
470
- start += width
471
- elsif self[field].respond_to? :sz
472
- obj = self[field].read str[start..-1]
473
- size = self[field].sz
474
- start += size
456
+ obj = self[field].read str[start..-1]
457
+ if self[field].respond_to? :sz
458
+ start += self[field].sz
475
459
  else
476
- obj = self[field].read str[start..-1]
477
460
  start = str.size
478
461
  end
479
462
  self[field] = obj unless obj == self[field]
@@ -496,7 +479,7 @@ module PacketGen
496
479
  next if attr == :body
497
480
  next unless present?(attr)
498
481
 
499
- result = yield(attr)if block_given?
482
+ result = yield(attr) if block_given?
500
483
  str << (result || Inspect.inspect_attribute(attr, self[attr], 1))
501
484
  end
502
485
  str
@@ -521,36 +504,6 @@ module PacketGen
521
504
  Hash[fields.map { |f| [f, @fields[f].to_human] }]
522
505
  end
523
506
 
524
- # Used to set body as value of body object.
525
- # @param [String,Int,Fields,nil] value
526
- # @return [void]
527
- # @raise [BodyError] no body on given object
528
- # @raise [ArgumentError] cannot cram +body+ in +:body+ field
529
- # @deprecated
530
- def body=(value)
531
- Deprecation.deprecated(self.class, __method__)
532
- raise BodyError, 'no body field' unless @fields.key? :body
533
-
534
- case body
535
- when ::String
536
- self[:body].read value
537
- when Int, Fields
538
- self[:body] = value
539
- when NilClass
540
- self[:body] = Types::String.new.read('')
541
- else
542
- raise ArgumentError, "Can't cram a #{body.class} in a :body field"
543
- end
544
- end
545
-
546
- # Force str to binary encoding
547
- # @param [String] str
548
- # @return [String]
549
- # @deprecated Will be a private method
550
- def force_binary(str)
551
- PacketGen.force_binary(str)
552
- end
553
-
554
507
  # Get offset of given field in {Fields} structure.
555
508
  # @param [Symbol] field
556
509
  # @return [Integer]
@@ -581,9 +534,16 @@ module PacketGen
581
534
  # @return [void]
582
535
  def initialize_copy(_other)
583
536
  fields = {}
584
- @fields.each { |k,v| fields[k] = v.dup }
537
+ @fields.each { |k, v| fields[k] = v.dup }
585
538
  @fields = fields
586
539
  end
540
+
541
+ # Force str to binary encoding
542
+ # @param [String] str
543
+ # @return [String]
544
+ def force_binary(str)
545
+ PacketGen.force_binary(str)
546
+ end
587
547
  end
588
548
  end
589
549
  end