packetgen 2.6.0 → 2.7.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 (134) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.rubocop.yml +28 -0
  4. data/Rakefile +6 -6
  5. data/bin/pgconsole +6 -6
  6. data/lib/packetgen.rb +4 -3
  7. data/lib/packetgen/capture.rb +5 -7
  8. data/lib/packetgen/config.rb +3 -3
  9. data/lib/packetgen/deprecation.rb +17 -0
  10. data/lib/packetgen/header.rb +40 -38
  11. data/lib/packetgen/header/arp.rb +46 -31
  12. data/lib/packetgen/header/asn1_base.rb +8 -8
  13. data/lib/packetgen/header/base.rb +108 -36
  14. data/lib/packetgen/header/bootp.rb +28 -19
  15. data/lib/packetgen/header/crypto.rb +6 -8
  16. data/lib/packetgen/header/dhcp.rb +4 -5
  17. data/lib/packetgen/header/dhcp/option.rb +46 -34
  18. data/lib/packetgen/header/dhcp/options.rb +0 -1
  19. data/lib/packetgen/header/dhcpv6.rb +10 -10
  20. data/lib/packetgen/header/dhcpv6/duid.rb +2 -3
  21. data/lib/packetgen/header/dhcpv6/option.rb +16 -21
  22. data/lib/packetgen/header/dhcpv6/relay.rb +6 -4
  23. data/lib/packetgen/header/dns.rb +13 -11
  24. data/lib/packetgen/header/dns/name.rb +4 -6
  25. data/lib/packetgen/header/dns/opt.rb +31 -31
  26. data/lib/packetgen/header/dns/option.rb +0 -2
  27. data/lib/packetgen/header/dns/qdsection.rb +1 -2
  28. data/lib/packetgen/header/dns/question.rb +19 -13
  29. data/lib/packetgen/header/dns/rr.rb +11 -14
  30. data/lib/packetgen/header/dns/rrsection.rb +5 -7
  31. data/lib/packetgen/header/dot11.rb +45 -29
  32. data/lib/packetgen/header/dot11/control.rb +3 -5
  33. data/lib/packetgen/header/dot11/data.rb +34 -6
  34. data/lib/packetgen/header/dot11/element.rb +0 -1
  35. data/lib/packetgen/header/dot11/management.rb +9 -5
  36. data/lib/packetgen/header/dot11/sub_mngt.rb +13 -14
  37. data/lib/packetgen/header/dot1q.rb +2 -2
  38. data/lib/packetgen/header/dot1x.rb +3 -4
  39. data/lib/packetgen/header/eap.rb +62 -53
  40. data/lib/packetgen/header/eap/fast.rb +0 -1
  41. data/lib/packetgen/header/eap/md5.rb +1 -2
  42. data/lib/packetgen/header/eap/tls.rb +9 -10
  43. data/lib/packetgen/header/eap/ttls.rb +9 -10
  44. data/lib/packetgen/header/esp.rb +33 -33
  45. data/lib/packetgen/header/eth.rb +11 -8
  46. data/lib/packetgen/header/gre.rb +5 -6
  47. data/lib/packetgen/header/http/headers.rb +11 -14
  48. data/lib/packetgen/header/http/request.rb +20 -20
  49. data/lib/packetgen/header/http/response.rb +16 -18
  50. data/lib/packetgen/header/http/verbs.rb +5 -5
  51. data/lib/packetgen/header/icmp.rb +1 -3
  52. data/lib/packetgen/header/icmpv6.rb +1 -3
  53. data/lib/packetgen/header/igmp.rb +5 -6
  54. data/lib/packetgen/header/igmpv3.rb +5 -9
  55. data/lib/packetgen/header/igmpv3/group_record.rb +4 -5
  56. data/lib/packetgen/header/igmpv3/mq.rb +2 -2
  57. data/lib/packetgen/header/igmpv3/mr.rb +4 -3
  58. data/lib/packetgen/header/ike.rb +33 -8
  59. data/lib/packetgen/header/ike/auth.rb +4 -6
  60. data/lib/packetgen/header/ike/cert.rb +0 -2
  61. data/lib/packetgen/header/ike/certreq.rb +1 -3
  62. data/lib/packetgen/header/ike/id.rb +1 -3
  63. data/lib/packetgen/header/ike/ke.rb +0 -2
  64. data/lib/packetgen/header/ike/nonce.rb +0 -2
  65. data/lib/packetgen/header/ike/notify.rb +22 -24
  66. data/lib/packetgen/header/ike/payload.rb +198 -199
  67. data/lib/packetgen/header/ike/sa.rb +21 -30
  68. data/lib/packetgen/header/ike/sk.rb +16 -17
  69. data/lib/packetgen/header/ike/ts.rb +22 -24
  70. data/lib/packetgen/header/ike/vendor_id.rb +0 -2
  71. data/lib/packetgen/header/ip.rb +30 -23
  72. data/lib/packetgen/header/ip/addr.rb +5 -6
  73. data/lib/packetgen/header/ip/option.rb +11 -15
  74. data/lib/packetgen/header/ip/options.rb +1 -2
  75. data/lib/packetgen/header/ipv6.rb +27 -12
  76. data/lib/packetgen/header/ipv6/addr.rb +2 -2
  77. data/lib/packetgen/header/ipv6/extension.rb +1 -1
  78. data/lib/packetgen/header/ipv6/hop_by_hop.rb +11 -11
  79. data/lib/packetgen/header/llc.rb +4 -3
  80. data/lib/packetgen/header/mdns.rb +11 -5
  81. data/lib/packetgen/header/mld.rb +4 -4
  82. data/lib/packetgen/header/mldv2.rb +4 -3
  83. data/lib/packetgen/header/mldv2/mcast_address_record.rb +3 -4
  84. data/lib/packetgen/header/mldv2/mlq.rb +3 -4
  85. data/lib/packetgen/header/mldv2/mlr.rb +4 -3
  86. data/lib/packetgen/header/netbios.rb +18 -5
  87. data/lib/packetgen/header/ospfv2.rb +6 -7
  88. data/lib/packetgen/header/ospfv2/db_description.rb +3 -4
  89. data/lib/packetgen/header/ospfv2/hello.rb +2 -3
  90. data/lib/packetgen/header/ospfv2/ls_ack.rb +2 -3
  91. data/lib/packetgen/header/ospfv2/ls_request.rb +2 -3
  92. data/lib/packetgen/header/ospfv2/ls_update.rb +5 -6
  93. data/lib/packetgen/header/ospfv2/lsa.rb +13 -14
  94. data/lib/packetgen/header/ospfv2/lsa_header.rb +4 -5
  95. data/lib/packetgen/header/ospfv3.rb +3 -4
  96. data/lib/packetgen/header/ospfv3/db_description.rb +3 -5
  97. data/lib/packetgen/header/ospfv3/hello.rb +2 -3
  98. data/lib/packetgen/header/ospfv3/ipv6_prefix.rb +7 -8
  99. data/lib/packetgen/header/ospfv3/ls_ack.rb +2 -3
  100. data/lib/packetgen/header/ospfv3/ls_request.rb +2 -3
  101. data/lib/packetgen/header/ospfv3/ls_update.rb +5 -6
  102. data/lib/packetgen/header/ospfv3/lsa.rb +10 -11
  103. data/lib/packetgen/header/ospfv3/lsa_header.rb +3 -4
  104. data/lib/packetgen/header/snmp.rb +45 -32
  105. data/lib/packetgen/header/tcp.rb +13 -9
  106. data/lib/packetgen/header/tcp/option.rb +16 -11
  107. data/lib/packetgen/header/tcp/options.rb +3 -4
  108. data/lib/packetgen/header/tftp.rb +15 -17
  109. data/lib/packetgen/header/udp.rb +10 -4
  110. data/lib/packetgen/inspect.rb +7 -9
  111. data/lib/packetgen/packet.rb +44 -22
  112. data/lib/packetgen/pcapng.rb +1 -5
  113. data/lib/packetgen/pcapng/block.rb +17 -11
  114. data/lib/packetgen/pcapng/epb.rb +6 -11
  115. data/lib/packetgen/pcapng/file.rb +37 -44
  116. data/lib/packetgen/pcapng/idb.rb +17 -22
  117. data/lib/packetgen/pcapng/shb.rb +7 -10
  118. data/lib/packetgen/pcapng/spb.rb +21 -17
  119. data/lib/packetgen/pcapng/unknown_block.rb +17 -13
  120. data/lib/packetgen/proto.rb +1 -2
  121. data/lib/packetgen/types/array.rb +119 -34
  122. data/lib/packetgen/types/cstring.rb +1 -5
  123. data/lib/packetgen/types/enum.rb +8 -10
  124. data/lib/packetgen/types/fields.rb +34 -28
  125. data/lib/packetgen/types/int.rb +11 -13
  126. data/lib/packetgen/types/int_string.rb +6 -8
  127. data/lib/packetgen/types/oui.rb +3 -6
  128. data/lib/packetgen/types/string.rb +4 -6
  129. data/lib/packetgen/types/tlv.rb +11 -14
  130. data/lib/packetgen/utils.rb +15 -23
  131. data/lib/packetgen/utils/arp_spoofer.rb +12 -18
  132. data/lib/packetgen/version.rb +1 -1
  133. data/packetgen.gemspec +9 -8
  134. metadata +19 -17
@@ -7,7 +7,6 @@
7
7
 
8
8
  module PacketGen
9
9
  module PcapNG
10
-
11
10
  # {SPB} represents a Section Simple Packet Block (SPB) of a pcapng file.
12
11
  #
13
12
  # == Pcapng::SPB Definition
@@ -18,9 +17,8 @@ module PacketGen
18
17
  # Int32 :block_len2
19
18
  # @author Sylvain Daubert
20
19
  class SPB < Block
21
-
22
20
  # Minimum SPB size
23
- MIN_SIZE = 4*4
21
+ MIN_SIZE = 4 * 4
24
22
 
25
23
  # @return [:little, :big]
26
24
  attr_accessor :endian
@@ -51,21 +49,29 @@ module PacketGen
51
49
  self.type = options[:type] || PcapNG::SPB_TYPE.to_i
52
50
  end
53
51
 
54
- # Has this block option?
52
+ # Has this block options?
55
53
  # @return [false]
56
- def has_options?
54
+ # @since 2.7.0
55
+ def options?
57
56
  false
58
57
  end
59
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
+
60
66
  # Reads a String or a IO to populate the object
61
67
  # @param [::String,IO] str_or_io
62
68
  # @return [self]
63
69
  def read(str_or_io)
64
- if str_or_io.respond_to? :read
65
- io = str_or_io
66
- else
67
- io = StringIO.new(force_binary(str_or_io.to_s))
68
- end
70
+ io = if str_or_io.respond_to? :read
71
+ str_or_io
72
+ else
73
+ StringIO.new(force_binary(str_or_io.to_s))
74
+ end
69
75
  return self if io.eof?
70
76
 
71
77
  self[:type].read io.read(4)
@@ -73,11 +79,11 @@ module PacketGen
73
79
  self[:orig_len].read io.read(4)
74
80
  # Take care of IDB snaplen
75
81
  # CAUTION: snaplen == 0 -> no capture limit
76
- if interface and interface.snaplen.to_i > 0
77
- data_len = [self[:orig_len].to_i, interface.snaplen.to_i].min
78
- else
79
- data_len = self[:orig_len].to_i
80
- end
82
+ data_len = if interface && (interface.snaplen.to_i > 0)
83
+ [self[:orig_len].to_i, interface.snaplen.to_i].min
84
+ else
85
+ self[:orig_len].to_i
86
+ end
81
87
  data_pad_len = (4 - (data_len % 4)) % 4
82
88
  self[:data].read io.read(data_len)
83
89
  io.read data_pad_len
@@ -95,8 +101,6 @@ module PacketGen
95
101
  recalc_block_len
96
102
  super
97
103
  end
98
-
99
104
  end
100
-
101
105
  end
102
106
  end
@@ -7,13 +7,11 @@
7
7
 
8
8
  module PacketGen
9
9
  module PcapNG
10
-
11
10
  # {UnknownBlock} is used to handle unsupported blocks of a pcapng file.
12
11
  # @author Sylvain Daubert
13
12
  class UnknownBlock < Block
14
-
15
13
  # Minimum Iblock size
16
- MIN_SIZE = 12
14
+ MIN_SIZE = 12
17
15
 
18
16
  # @return [:little, :big]
19
17
  attr_accessor :endian
@@ -35,21 +33,29 @@ module PacketGen
35
33
  recalc_block_len
36
34
  end
37
35
 
38
- # Has this block option?
36
+ # Has this block options?
39
37
  # @return [false]
40
- def has_options?
38
+ # @since 2.7.0
39
+ def options?
41
40
  false
42
41
  end
43
42
 
44
- # Reads a String or a IO to populate the object
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
+ # Reads a String or a IO to populate the object
45
51
  # @param [::String,IO] str_or_io
46
52
  # @return [self]
47
53
  def read(str_or_io)
48
- if str_or_io.respond_to? :read
49
- io = str_or_io
50
- else
51
- io = StringIO.new(force_binary(str_or_io.to_s))
52
- end
54
+ io = if str_or_io.respond_to? :read
55
+ str_or_io
56
+ else
57
+ StringIO.new(force_binary(str_or_io.to_s))
58
+ end
53
59
  return self if io.eof?
54
60
 
55
61
  self[:type].read io.read(4)
@@ -71,8 +77,6 @@ module PacketGen
71
77
  recalc_block_len
72
78
  super
73
79
  end
74
-
75
80
  end
76
-
77
81
  end
78
82
  end
@@ -5,15 +5,14 @@
5
5
  # This program is published under MIT license.
6
6
 
7
7
  # frozen_string_literal: true
8
+
8
9
  require 'socket'
9
10
 
10
11
  module PacketGen
11
-
12
12
  # Module handling some helper methods for protocols
13
13
  # @author Sylvain Daubert
14
14
  # @since 2.1.2
15
15
  module Proto
16
-
17
16
  # @private cache information used by {.getprotobyname} and
18
17
  # {.getprotobynumber}
19
18
  def self.prepare_cache
@@ -7,7 +7,6 @@
7
7
 
8
8
  module PacketGen
9
9
  module Types
10
-
11
10
  # @abstract Base class to define set of {Fields} subclasses.
12
11
  # == #record_from_hash
13
12
  # Subclasses should define private method +#record_from_hash+. This method
@@ -16,7 +15,8 @@ module PacketGen
16
15
  # A default method is defined by {Array}: it calls constructor of class defined
17
16
  # by {.set_of}.
18
17
  # @author Sylvain Daubert
19
- class Array < ::Array
18
+ class Array
19
+ include Enumerable
20
20
 
21
21
  # Separator used in {#to_human}.
22
22
  # May be ovverriden by subclasses
@@ -25,33 +25,95 @@ module PacketGen
25
25
  # Define type of objects in set. Used by {#read} and {#push}.
26
26
  # @param [Class] klass
27
27
  # @return [void]
28
+ # rubocop:disable Naming/AccessorMethodName
28
29
  def self.set_of(klass)
29
30
  @klass = klass
30
31
  end
32
+ # rubocop:enable Naming/AccessorMethodName
31
33
 
32
34
  # @param [Hash] options
33
35
  # @option options [Int] counter Int object used as a counter for this set
34
36
  def initialize(options={})
35
- super()
36
37
  @counter = options[:counter]
38
+ @array = []
37
39
  end
38
40
 
39
- # Populate object from a string
40
- # @param [String] str
41
- # @return [self]
42
- def read(str)
43
- clear
44
- return self if str.nil?
45
- return self if @counter and @counter.to_i == 0
46
- force_binary str
47
- klass = self.class.class_eval { @klass }
48
- while str.length > 0
49
- obj = klass.new.read(str)
50
- self.push obj
51
- str.slice!(0, obj.sz)
52
- break if @counter and self.size == @counter.to_i
41
+ # Initialize array for copy:
42
+ # * duplicate internal array.
43
+ def initialize_copy(_other)
44
+ @array = @array.dup
45
+ end
46
+
47
+ # Return the element at +index+.
48
+ # @param [integer] index
49
+ # @return [Object]
50
+ def [](index)
51
+ @array[index]
52
+ end
53
+
54
+ def ==(other)
55
+ case other
56
+ when Array
57
+ @array == other.to_a
58
+ else
59
+ @array == other
53
60
  end
54
- self
61
+ end
62
+
63
+ # Clear array.
64
+ # @return [void]
65
+ def clear
66
+ @array.clear
67
+ end
68
+
69
+ # Clear array. Reset associated counter, if any.
70
+ # @return [void]
71
+ def clear!
72
+ @array.clear
73
+ @counter.read(0) if @counter
74
+ end
75
+
76
+ # Delete an object from this array. Update associated counter if any
77
+ # @param [Object] obj
78
+ # @return [Object] deleted object
79
+ def delete(obj)
80
+ deleted = @array.delete(obj)
81
+ @counter.read(@counter.to_i - 1) if @counter && deleted
82
+ deleted
83
+ end
84
+
85
+ # Delete element at +index+.
86
+ # @param [Integer] index
87
+ # @return [Object,nil] deleted object
88
+ def delete_at(index)
89
+ deleted = @array.delete_at(index)
90
+ @counter.read(@counter.to_i - 1) if @counter && deleted
91
+ deleted
92
+ end
93
+
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
55
117
  end
56
118
 
57
119
  # @abstract depend on private method +#record_from_hash+ which should be
@@ -66,7 +128,8 @@ module PacketGen
66
128
  else
67
129
  obj
68
130
  end
69
- super(obj)
131
+ @array << obj
132
+ self
70
133
  end
71
134
 
72
135
  # @abstract depend on private method +#record_from_hash+ which should be
@@ -80,31 +143,53 @@ module PacketGen
80
143
  self
81
144
  end
82
145
 
83
- # Delete an object from this array. Update associated counter if any
84
- # @param [Object] obj
85
- # @return [Object] deleted object
86
- def delete(obj)
87
- deleted = super
88
- @counter.read(@counter.to_i - 1) if @counter && deleted
89
- deleted
146
+ # Populate object from a string
147
+ # @param [String] str
148
+ # @return [self]
149
+ def read(str)
150
+ clear
151
+ return self if str.nil?
152
+ return self if @counter && @counter.to_i.zero?
153
+ force_binary str
154
+ klass = self.class.class_eval { @klass }
155
+ until str.empty?
156
+ obj = klass.new.read(str)
157
+ @array << obj
158
+ str.slice!(0, obj.sz)
159
+ break if @counter && self.size == @counter.to_i
160
+ end
161
+ self
162
+ end
163
+
164
+ # Get number of element in array
165
+ # @return [Integer]
166
+ def size
167
+ @array.size
168
+ end
169
+ alias length size
170
+
171
+ # Get size in bytes
172
+ # @return [Integer]
173
+ def sz
174
+ to_s.size
175
+ end
176
+
177
+ # Return an Array
178
+ # @return [::Array]
179
+ def to_a
180
+ @array
90
181
  end
91
182
 
92
183
  # Get binary string
93
184
  # @return [String]
94
185
  def to_s
95
- map(&:to_s).join
186
+ @array.map(&:to_s).join
96
187
  end
97
188
 
98
189
  # Get a human readable string
99
190
  # @return [String]
100
191
  def to_human
101
- map(&:to_human).join(self.class::HUMAN_SEPARATOR)
102
- end
103
-
104
- # Get size in bytes
105
- # @return [Integer]
106
- def sz
107
- to_s.size
192
+ @array.map(&:to_human).join(self.class::HUMAN_SEPARATOR)
108
193
  end
109
194
 
110
195
  # Force binary encoding for +str+
@@ -8,11 +8,9 @@
8
8
 
9
9
  module PacketGen
10
10
  module Types
11
-
12
11
  # This class handles null-terminated strings (aka C strings).
13
12
  # @author Sylvain Daubert
14
13
  class CString < ::String
15
-
16
14
  # @param [Hash] options
17
15
  # @option options [Integer] :static_length set a static length for this string
18
16
  def initialize(options={})
@@ -24,9 +22,7 @@ module PacketGen
24
22
  # @return [String] self
25
23
  def read(str)
26
24
  s = str.to_s
27
- if @static_length.is_a? Integer
28
- s = s[0, @static_length]
29
- end
25
+ s = s[0, @static_length] if @static_length.is_a? Integer
30
26
  idx = s.index(0.chr)
31
27
  s = s[0, idx] unless idx.nil?
32
28
  self.replace s
@@ -8,7 +8,6 @@
8
8
 
9
9
  module PacketGen
10
10
  module Types
11
-
12
11
  # @abstract Base enum class to handle binary integers with limited
13
12
  # authorized values
14
13
  # An {Enum} type is used to handle an {Int} field with limited
@@ -31,7 +30,6 @@ module PacketGen
31
30
  # @since 2.1.3
32
31
  # @author Sylvain Daubert
33
32
  class Enum < Int
34
-
35
33
  # @return [Hash]
36
34
  attr_reader :enum
37
35
 
@@ -47,25 +45,25 @@ module PacketGen
47
45
  end
48
46
 
49
47
  # Setter for value attribute
50
- # @param [Integer, String,nil] v value as an Integer or as a String
48
+ # @param [Integer, String,nil] value value as an Integer or as a String
51
49
  # from enumration
52
50
  # @return [Integer]
53
- def value=(v)
54
- ival = case v
51
+ def value=(value)
52
+ ival = case value
55
53
  when NilClass
56
54
  nil
57
55
  when ::String
58
- raise ArgumentError, "#{v.inspect} not in enumeration" unless @enum.has_key? v
59
- @enum[v]
56
+ raise ArgumentError, "#{value.inspect} not in enumeration" unless @enum.key? value
57
+ @enum[value]
60
58
  else
61
- raise ArgumentError, "#{v.inspect} not in enumeration" unless @enum.has_value? v
62
- v
59
+ raise ArgumentError, "#{value.inspect} not in enumeration" unless @enum.value? value
60
+ value
63
61
  end
64
62
  @value = ival
65
63
  end
66
64
 
67
65
  # To handle human API: set value from a String
68
- alias :from_human :value=
66
+ alias from_human value=
69
67
 
70
68
  # Get human readable value (enum name)
71
69
  # @return [String]
@@ -7,7 +7,6 @@
7
7
 
8
8
  module PacketGen
9
9
  module Types
10
-
11
10
  # @abstract Set of fields
12
11
  # This class is a base class to define headers or anything else with a binary
13
12
  # format containing multiple fields.
@@ -93,7 +92,6 @@ module PacketGen
93
92
  # offset subfield from +frag+ field.
94
93
  # @author Sylvain Daubert
95
94
  class Fields
96
-
97
95
  # @private
98
96
  @ordered_fields = []
99
97
  # @private
@@ -149,10 +147,10 @@ module PacketGen
149
147
  elsif type < Types::Int
150
148
  define << "def #{name}; self[:#{name}].to_i; end"
151
149
  define << "def #{name}=(val) self[:#{name}].read val; end"
152
- elsif type.instance_methods.include? :to_human and
153
- type.instance_methods.include? :from_human
154
- define << "def #{name}; self[:#{name}].to_human; end"
155
- define << "def #{name}=(val) self[:#{name}].from_human val; end"
150
+ elsif type.instance_methods.include?(:to_human) &&
151
+ type.instance_methods.include?(:from_human)
152
+ define << "def #{name}; self[:#{name}].to_human; end"
153
+ define << "def #{name}=(val) self[:#{name}].from_human val; end"
156
154
  else
157
155
  define << "def #{name}; self[:#{name}]; end\n"
158
156
  define << "def #{name}=(val) self[:#{name}].read val; end"
@@ -179,12 +177,11 @@ module PacketGen
179
177
  # @see .define_field
180
178
  def self.define_field_before(other, name, type, options={})
181
179
  define_field name, type, options
182
- unless other.nil?
183
- @ordered_fields.delete name
184
- idx = @ordered_fields.index(other)
185
- raise ArgumentError, "unknown #{other} field" if idx.nil?
186
- @ordered_fields[idx, 0] = name
187
- end
180
+ return if other.nil?
181
+ @ordered_fields.delete name
182
+ idx = @ordered_fields.index(other)
183
+ raise ArgumentError, "unknown #{other} field" if idx.nil?
184
+ @ordered_fields[idx, 0] = name
188
185
  end
189
186
 
190
187
  # Define a field, after another one
@@ -197,12 +194,11 @@ module PacketGen
197
194
  # @see .define_field
198
195
  def self.define_field_after(other, name, type, options={})
199
196
  define_field name, type, options
200
- unless other.nil?
201
- @ordered_fields.delete name
202
- idx = @ordered_fields.index(other)
203
- raise ArgumentError, "unknown #{other} field" if idx.nil?
204
- @ordered_fields[idx+1, 0] = name
205
- end
197
+ return if other.nil?
198
+ @ordered_fields.delete name
199
+ idx = @ordered_fields.index(other)
200
+ raise ArgumentError, "unknown #{other} field" if idx.nil?
201
+ @ordered_fields[idx + 1, 0] = name
206
202
  end
207
203
 
208
204
  # Delete a previously defined field
@@ -256,7 +252,7 @@ module PacketGen
256
252
  clear_mask = (2**total_size - 1) & (~field_mask & (2**total_size - 1))
257
253
 
258
254
  if size == 1
259
- class_eval <<-EOM
255
+ class_eval <<-METHODS
260
256
  def #{field}?
261
257
  val = (self[:#{attr}].to_i & #{field_mask}) >> #{shift}
262
258
  val != 0
@@ -266,9 +262,9 @@ module PacketGen
266
262
  self[:#{attr}].value = self[:#{attr}].to_i & #{clear_mask}
267
263
  self[:#{attr}].value |= val << #{shift}
268
264
  end
269
- EOM
265
+ METHODS
270
266
  else
271
- class_eval <<-EOM
267
+ class_eval <<-METHODS
272
268
  def #{field}
273
269
  (self[:#{attr}].to_i & #{field_mask}) >> #{shift}
274
270
  end
@@ -276,7 +272,7 @@ module PacketGen
276
272
  self[:#{attr}].value = self[:#{attr}].to_i & #{clear_mask}
277
273
  self[:#{attr}].value |= (v & #{2**size - 1}) << #{shift}
278
274
  end
279
- EOM
275
+ METHODS
280
276
  end
281
277
 
282
278
  @bit_fields << field
@@ -321,8 +317,8 @@ module PacketGen
321
317
  @fields[field].read(value)
322
318
  elsif type <= Types::String
323
319
  @fields[field].read(value)
324
- else
325
- @fields[field].from_human(value) if @fields[field].respond_to? :from_human
320
+ elsif @fields[field].respond_to? :from_human
321
+ @fields[field].from_human(value)
326
322
  end
327
323
 
328
324
  @optional_fields[field] = optional if optional
@@ -361,7 +357,7 @@ module PacketGen
361
357
  # Say if this field is optional
362
358
  # @return [Boolean]
363
359
  def is_optional?(field)
364
- @optional_fields.has_key? field
360
+ @optional_fields.key? field
365
361
  end
366
362
 
367
363
  # Say if an optional field is present
@@ -414,8 +410,8 @@ module PacketGen
414
410
  # Return object as a binary string
415
411
  # @return [String]
416
412
  def to_s
417
- fields.select { |f| is_present?(f) }.
418
- map! { |f| force_binary @fields[f].to_s }.join
413
+ fields.select { |f| is_present?(f) }
414
+ .map! { |f| force_binary @fields[f].to_s }.join
419
415
  end
420
416
 
421
417
  # Size of object as binary string
@@ -436,7 +432,7 @@ module PacketGen
436
432
  # @raise [BodyError] no body on given object
437
433
  # @raise [ArgumentError] cannot cram +body+ in +:body+ field
438
434
  def body=(value)
439
- raise BodyError, 'no body field' unless @fields.has_key? :body
435
+ raise BodyError, 'no body field' unless @fields.key? :body
440
436
  case body
441
437
  when ::String
442
438
  self[:body].read value
@@ -455,6 +451,16 @@ module PacketGen
455
451
  def force_binary(str)
456
452
  PacketGen.force_binary(str)
457
453
  end
454
+
455
+ private
456
+
457
+ # Deeply duplicate +@fields+
458
+ # @return [void]
459
+ def initialize_copy(_other)
460
+ fields = {}
461
+ @fields.each { |k,v| fields[k] = v.dup }
462
+ @fields = fields
463
+ end
458
464
  end
459
465
  end
460
466
  end