packetgen 3.1.1 → 3.1.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (130) hide show
  1. checksums.yaml +4 -4
  2. data/bin/pgconsole +1 -0
  3. data/lib/packetgen.rb +33 -4
  4. data/lib/packetgen/capture.rb +51 -28
  5. data/lib/packetgen/config.rb +17 -11
  6. data/lib/packetgen/deprecation.rb +34 -8
  7. data/lib/packetgen/header.rb +2 -9
  8. data/lib/packetgen/header/arp.rb +2 -2
  9. data/lib/packetgen/header/asn1_base.rb +2 -2
  10. data/lib/packetgen/header/base.rb +70 -74
  11. data/lib/packetgen/header/bootp.rb +3 -3
  12. data/lib/packetgen/header/dhcp.rb +2 -2
  13. data/lib/packetgen/header/dhcp/option.rb +2 -2
  14. data/lib/packetgen/header/dhcp/options.rb +2 -2
  15. data/lib/packetgen/header/dhcpv6.rb +12 -12
  16. data/lib/packetgen/header/dhcpv6/duid.rb +11 -5
  17. data/lib/packetgen/header/dhcpv6/option.rb +8 -16
  18. data/lib/packetgen/header/dhcpv6/options.rb +2 -2
  19. data/lib/packetgen/header/dhcpv6/relay.rb +2 -2
  20. data/lib/packetgen/header/dns.rb +9 -9
  21. data/lib/packetgen/header/dns/name.rb +20 -9
  22. data/lib/packetgen/header/dns/opt.rb +2 -2
  23. data/lib/packetgen/header/dns/option.rb +2 -2
  24. data/lib/packetgen/header/dns/qdsection.rb +3 -3
  25. data/lib/packetgen/header/dns/question.rb +37 -35
  26. data/lib/packetgen/header/dns/rr.rb +3 -3
  27. data/lib/packetgen/header/dns/rrsection.rb +2 -2
  28. data/lib/packetgen/header/dot11.rb +30 -51
  29. data/lib/packetgen/header/dot11/control.rb +5 -5
  30. data/lib/packetgen/header/dot11/data.rb +11 -7
  31. data/lib/packetgen/header/dot11/element.rb +16 -16
  32. data/lib/packetgen/header/dot11/management.rb +2 -2
  33. data/lib/packetgen/header/dot11/sub_mngt.rb +2 -12
  34. data/lib/packetgen/header/dot1q.rb +2 -2
  35. data/lib/packetgen/header/dot1x.rb +7 -20
  36. data/lib/packetgen/header/eap.rb +30 -33
  37. data/lib/packetgen/header/eap/fast.rb +2 -2
  38. data/lib/packetgen/header/eap/md5.rb +2 -2
  39. data/lib/packetgen/header/eap/tls.rb +2 -2
  40. data/lib/packetgen/header/eap/ttls.rb +2 -2
  41. data/lib/packetgen/header/eth.rb +13 -11
  42. data/lib/packetgen/header/gre.rb +2 -2
  43. data/lib/packetgen/header/http.rb +2 -0
  44. data/lib/packetgen/header/http/headers.rb +6 -4
  45. data/lib/packetgen/header/http/request.rb +36 -21
  46. data/lib/packetgen/header/http/response.rb +7 -7
  47. data/lib/packetgen/header/http/verbs.rb +3 -3
  48. data/lib/packetgen/header/icmp.rb +2 -2
  49. data/lib/packetgen/header/icmpv6.rb +2 -2
  50. data/lib/packetgen/header/igmp.rb +4 -4
  51. data/lib/packetgen/header/igmpv3.rb +3 -3
  52. data/lib/packetgen/header/igmpv3/group_record.rb +8 -6
  53. data/lib/packetgen/header/igmpv3/mq.rb +2 -2
  54. data/lib/packetgen/header/igmpv3/mr.rb +2 -2
  55. data/lib/packetgen/header/ip.rb +30 -31
  56. data/lib/packetgen/header/ip/addr.rb +10 -3
  57. data/lib/packetgen/header/ip/option.rb +8 -10
  58. data/lib/packetgen/header/ip/options.rb +3 -5
  59. data/lib/packetgen/header/ipv6.rb +2 -2
  60. data/lib/packetgen/header/ipv6/addr.rb +9 -2
  61. data/lib/packetgen/header/ipv6/extension.rb +2 -2
  62. data/lib/packetgen/header/ipv6/hop_by_hop.rb +3 -3
  63. data/lib/packetgen/header/llc.rb +2 -2
  64. data/lib/packetgen/header/mdns.rb +2 -2
  65. data/lib/packetgen/header/mld.rb +2 -2
  66. data/lib/packetgen/header/mldv2.rb +2 -2
  67. data/lib/packetgen/header/mldv2/mcast_address_record.rb +4 -2
  68. data/lib/packetgen/header/mldv2/mlq.rb +2 -2
  69. data/lib/packetgen/header/mldv2/mlr.rb +2 -2
  70. data/lib/packetgen/header/ospfv2.rb +9 -9
  71. data/lib/packetgen/header/ospfv2/db_description.rb +2 -2
  72. data/lib/packetgen/header/ospfv2/hello.rb +2 -2
  73. data/lib/packetgen/header/ospfv2/ls_ack.rb +2 -2
  74. data/lib/packetgen/header/ospfv2/ls_request.rb +4 -2
  75. data/lib/packetgen/header/ospfv2/ls_update.rb +2 -2
  76. data/lib/packetgen/header/ospfv2/lsa.rb +9 -5
  77. data/lib/packetgen/header/ospfv2/lsa_header.rb +8 -7
  78. data/lib/packetgen/header/ospfv3.rb +2 -2
  79. data/lib/packetgen/header/ospfv3/db_description.rb +2 -2
  80. data/lib/packetgen/header/ospfv3/hello.rb +2 -2
  81. data/lib/packetgen/header/ospfv3/ipv6_prefix.rb +4 -2
  82. data/lib/packetgen/header/ospfv3/ls_ack.rb +2 -2
  83. data/lib/packetgen/header/ospfv3/ls_request.rb +4 -2
  84. data/lib/packetgen/header/ospfv3/ls_update.rb +2 -2
  85. data/lib/packetgen/header/ospfv3/lsa.rb +5 -5
  86. data/lib/packetgen/header/ospfv3/lsa_header.rb +9 -8
  87. data/lib/packetgen/header/snmp.rb +33 -29
  88. data/lib/packetgen/header/tcp.rb +4 -23
  89. data/lib/packetgen/header/tcp/option.rb +11 -11
  90. data/lib/packetgen/header/tcp/options.rb +2 -2
  91. data/lib/packetgen/header/tftp.rb +6 -6
  92. data/lib/packetgen/header/udp.rb +3 -3
  93. data/lib/packetgen/headerable.rb +5 -4
  94. data/lib/packetgen/inject.rb +23 -0
  95. data/lib/packetgen/inspect.rb +23 -20
  96. data/lib/packetgen/packet.rb +96 -53
  97. data/lib/packetgen/pcap.rb +29 -0
  98. data/lib/packetgen/pcapng.rb +13 -13
  99. data/lib/packetgen/pcapng/block.rb +26 -13
  100. data/lib/packetgen/pcapng/epb.rb +25 -22
  101. data/lib/packetgen/pcapng/file.rb +260 -138
  102. data/lib/packetgen/pcapng/idb.rb +36 -38
  103. data/lib/packetgen/pcapng/shb.rb +51 -53
  104. data/lib/packetgen/pcapng/spb.rb +19 -19
  105. data/lib/packetgen/pcapng/unknown_block.rb +5 -13
  106. data/lib/packetgen/pcaprub_wrapper.rb +81 -0
  107. data/lib/packetgen/proto.rb +2 -2
  108. data/lib/packetgen/types.rb +3 -0
  109. data/lib/packetgen/types/abstract_tlv.rb +28 -8
  110. data/lib/packetgen/types/array.rb +22 -15
  111. data/lib/packetgen/types/cstring.rb +40 -16
  112. data/lib/packetgen/types/enum.rb +8 -3
  113. data/lib/packetgen/types/fieldable.rb +65 -0
  114. data/lib/packetgen/types/fields.rb +182 -117
  115. data/lib/packetgen/types/int.rb +18 -6
  116. data/lib/packetgen/types/int_string.rb +10 -2
  117. data/lib/packetgen/types/length_from.rb +20 -12
  118. data/lib/packetgen/types/oui.rb +4 -2
  119. data/lib/packetgen/types/string.rb +46 -8
  120. data/lib/packetgen/types/tlv.rb +4 -2
  121. data/lib/packetgen/utils.rb +4 -4
  122. data/lib/packetgen/utils/arp_spoofer.rb +2 -2
  123. data/lib/packetgen/version.rb +3 -3
  124. metadata +35 -50
  125. data/.gitignore +0 -13
  126. data/.rubocop.yml +0 -28
  127. data/.travis.yml +0 -17
  128. data/Gemfile +0 -4
  129. data/Rakefile +0 -21
  130. data/packetgen.gemspec +0 -36
@@ -1,11 +1,11 @@
1
1
  # coding: utf-8
2
+ # frozen_string_literal: true
3
+
2
4
  # This file is part of PacketGen
3
5
  # See https://github.com/sdaubert/packetgen for more informations
4
6
  # Copyright (C) 2016 Sylvain Daubert <sylvain.daubert@laposte.net>
5
7
  # This program is published under MIT license.
6
8
 
7
- # frozen_string_literal: true
8
-
9
9
  require 'socket'
10
10
 
11
11
  module PacketGen
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # This file is part of PacketGen
2
4
  # See https://github.com/sdaubert/packetgen for more informations
3
5
  # Copyright (C) 2016 Sylvain Daubert <sylvain.daubert@laposte.net>
@@ -10,6 +12,7 @@ module PacketGen
10
12
  end
11
13
 
12
14
  require_relative 'types/length_from'
15
+ require_relative 'types/fieldable'
13
16
  require_relative 'types/int'
14
17
  require_relative 'types/enum'
15
18
  require_relative 'types/string'
@@ -1,16 +1,16 @@
1
1
  # coding: utf-8
2
+ # frozen_string_literal: true
3
+
2
4
  # This file is part of PacketGen
3
5
  # See https://github.com/sdaubert/packetgen for more informations
4
6
  # Copyright (C) 2016 Sylvain Daubert <sylvain.daubert@laposte.net>
5
7
  # This program is published under MIT license.
6
8
 
7
- # frozen_string_literal: true
8
-
9
9
  module PacketGen
10
10
  module Types
11
11
  # This class is an abstract class to define type-length-value data.
12
12
  #
13
- # This class supersede {TLV} class, which is not well defined on some corner
13
+ # This class supersedes {TLV} class, which is not well defined on some corner
14
14
  # cases.
15
15
  #
16
16
  # ===Usage
@@ -33,7 +33,7 @@ module PacketGen
33
33
  # tlv.value #=> "abcd"
34
34
  #
35
35
  # ===Advanced usage
36
- # Each field's type may be change at generating TLV class:
36
+ # Each field's type may be changed at generating TLV class:
37
37
  # MyTLV = PacketGen::Types::AbstractTLV.create(type_class: PacketGen::Types::Int16,
38
38
  # length_class: PacketGen::Types::Int16,
39
39
  # value_class: PacketGen::Header::IP::Addr)
@@ -58,9 +58,12 @@ module PacketGen
58
58
  # @since 3.1.0
59
59
  # @since 3.1.1 add +:aliases+ keyword to {#initialize}
60
60
  class AbstractTLV < Types::Fields
61
+ include Fieldable
62
+
61
63
  class <<self
62
64
  # @return [Hash]
63
65
  attr_accessor :aliases
66
+ attr_accessor :header_in_length
64
67
  end
65
68
  self.aliases = {}
66
69
 
@@ -68,12 +71,17 @@ module PacketGen
68
71
  # @param [Class] type_class Class to use for +type+
69
72
  # @param [Class] length_class Class to use for +length+
70
73
  # @param [Class] value_class Class to use for +value+
74
+ # @param [Boolean] header_in_length if +true +, +type+ and +length+ fields are
75
+ # included in length
71
76
  # @return [Class]
72
- def self.create(type_class: Int8Enum, length_class: Int8, value_class: String, aliases: {})
77
+ # @since 3.1.4 Add +header_in_length+ parameter
78
+ def self.create(type_class: Int8Enum, length_class: Int8, value_class: String,
79
+ aliases: {}, header_in_length: false)
73
80
  raise Error, '.create cannot be called on a subclass of PacketGen::Types::AbstractTLV' unless self.equal? AbstractTLV
74
81
 
75
82
  klass = Class.new(self)
76
83
  klass.aliases = aliases
84
+ klass.header_in_length = header_in_length
77
85
 
78
86
  if type_class < Enum
79
87
  klass.define_field :type, type_class, enum: {}
@@ -117,6 +125,7 @@ module PacketGen
117
125
  # @option options [Integer] :length
118
126
  # @option options [Object] :value
119
127
  def initialize(options={})
128
+ @header_in_length = self.class.header_in_length
120
129
  self.class.aliases.each do |al, orig|
121
130
  options[orig] = options[al] if options.key?(al)
122
131
  end
@@ -136,7 +145,7 @@ module PacketGen
136
145
  idx += self[:type].sz
137
146
  self[:length].read str[idx, self[:length].sz]
138
147
  idx += self[:length].sz
139
- self[:value].read str[idx, self.length]
148
+ self[:value].read str[idx, real_length]
140
149
  self
141
150
  end
142
151
 
@@ -147,6 +156,7 @@ module PacketGen
147
156
  def value=(val)
148
157
  self[:value].from_human val
149
158
  self.length = self[:value].sz
159
+ self.length += self[:type].sz + self[:length].sz if @header_in_length
150
160
  val
151
161
  end
152
162
 
@@ -160,8 +170,18 @@ module PacketGen
160
170
  # @abstract Should only be called on real TLV class instances.
161
171
  # @return [String]
162
172
  def to_human
163
- my_value = self[:value].is_a?(String) ? value.inspect : value.to_human
164
- "type:%s,length:%u,value:#{my_value}" % [human_type, length]
173
+ my_value = self[:value].is_a?(String) ? self[:value].inspect : self[:value].to_human
174
+ 'type:%s,length:%u,value:%s' % [human_type, length, my_value]
175
+ end
176
+
177
+ private
178
+
179
+ def real_length
180
+ if @header_in_length
181
+ self.length - self[:type].sz - self[:length].sz
182
+ else
183
+ self.length
184
+ end
165
185
  end
166
186
  end
167
187
  end
@@ -1,10 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # This file is part of PacketGen
2
4
  # See https://github.com/sdaubert/packetgen for more informations
3
5
  # Copyright (C) 2016 Sylvain Daubert <sylvain.daubert@laposte.net>
4
6
  # This program is published under MIT license.
5
7
 
6
- # frozen_string_literal: true
7
-
8
8
  require 'forwardable'
9
9
 
10
10
  module PacketGen
@@ -27,6 +27,9 @@ module PacketGen
27
27
  # @author Sylvain Daubert
28
28
  class Array
29
29
  extend Forwardable
30
+ include Enumerable
31
+ include Fieldable
32
+ include LengthFrom
30
33
 
31
34
  # @!method [](index)
32
35
  # Return the element at +index+.
@@ -54,9 +57,6 @@ module PacketGen
54
57
  def_delegators :@array, :[], :clear, :each, :empty?, :first, :last, :size
55
58
  alias length size
56
59
 
57
- include Enumerable
58
- include LengthFrom
59
-
60
60
  # Separator used in {#to_human}.
61
61
  # May be ovverriden by subclasses
62
62
  HUMAN_SEPARATOR = ','
@@ -106,7 +106,7 @@ module PacketGen
106
106
  # @return [void]
107
107
  def clear!
108
108
  @array.clear
109
- @counter.read(0) if @counter
109
+ @counter&.read(0)
110
110
  end
111
111
 
112
112
  # Delete an object from this array. Update associated counter if any
@@ -150,7 +150,7 @@ module PacketGen
150
150
  # @return [Array] self
151
151
  def <<(obj)
152
152
  push obj
153
- @counter.read(@counter.to_i + 1) if @counter
153
+ @counter&.read(@counter.to_i + 1)
154
154
  self
155
155
  end
156
156
 
@@ -160,14 +160,11 @@ module PacketGen
160
160
  def read(str)
161
161
  clear
162
162
  return self if str.nil?
163
- return self if @counter && @counter.to_i.zero?
163
+ return self if @counter&.to_i&.zero?
164
164
 
165
165
  str = read_with_length_from(str)
166
- klass = self.class.set_of_klass
167
166
  until str.empty?
168
- obj = klass.new.read(str)
169
- real_klass = real_type(obj)
170
- obj = real_klass.new.read(str) unless real_klass == klass
167
+ obj = create_object_from_str(str)
171
168
  @array << obj
172
169
  str.slice!(0, obj.sz)
173
170
  break if @counter && self.size == @counter.to_i
@@ -203,9 +200,7 @@ module PacketGen
203
200
 
204
201
  def record_from_hash(hsh)
205
202
  obj_klass = self.class.set_of_klass
206
- unless obj_klass
207
- raise NotImplementedError, 'class should define #record_from_hash or declare type of elements in set with .set_of'
208
- end
203
+ raise NotImplementedError, 'class should define #record_from_hash or declare type of elements in set with .set_of' unless obj_klass
209
204
 
210
205
  obj = obj_klass.new(hsh) if obj_klass
211
206
  klass = real_type(obj)
@@ -215,6 +210,18 @@ module PacketGen
215
210
  def real_type(obj)
216
211
  obj.class
217
212
  end
213
+
214
+ def create_object_from_str(str)
215
+ klass = self.class.set_of_klass
216
+ obj = klass.new.read(str)
217
+ real_klass = real_type(obj)
218
+
219
+ if real_klass == klass
220
+ obj
221
+ else
222
+ real_klass.new.read(str)
223
+ end
224
+ end
218
225
  end
219
226
 
220
227
  # Specialized array to handle serie of {Int8}.
@@ -1,20 +1,34 @@
1
1
  # coding: utf-8
2
+ # frozen_string_literal: true
3
+
2
4
  # This file is part of PacketGen
3
5
  # See https://github.com/sdaubert/packetgen for more informations
4
6
  # Copyright (C) 2016 Sylvain Daubert <sylvain.daubert@laposte.net>
5
7
  # This program is published under MIT license.
6
8
 
7
- # frozen_string_literal: true
9
+ require 'forwardable'
8
10
 
9
11
  module PacketGen
10
12
  module Types
11
13
  # This class handles null-terminated strings (aka C strings).
12
14
  # @author Sylvain Daubert
13
- class CString < ::String
15
+ # @since 3.1.6 no more a subclass or regular String
16
+ class CString
17
+ extend Forwardable
18
+ include Fieldable
19
+
20
+ def_delegators :@string, :[], :length, :size, :inspect, :==, :<<,
21
+ :unpack, :force_encoding, :encoding, :index, :empty?
22
+
23
+ # @return [::String]
24
+ attr_reader :string
25
+ # @return [Integer]
26
+ attr_reader :static_length
27
+
14
28
  # @param [Hash] options
15
29
  # @option options [Integer] :static_length set a static length for this string
16
30
  def initialize(options={})
17
- super()
31
+ register_internal_string ''
18
32
  @static_length = options[:static_length]
19
33
  end
20
34
 
@@ -22,38 +36,41 @@ module PacketGen
22
36
  # @return [String] self
23
37
  def read(str)
24
38
  s = str.to_s
25
- s = s[0, @static_length] if @static_length.is_a? Integer
39
+ s = s[0, static_length] if static_length?
26
40
  idx = s.index(0.chr)
27
41
  s = s[0, idx] unless idx.nil?
28
- self.replace s
42
+ register_internal_string s
29
43
  self
30
44
  end
31
45
 
32
46
  # get null-terminated string
33
47
  # @return [String]
34
48
  def to_s
35
- if defined?(@static_length) && @static_length.is_a?(Integer)
36
- if self.size >= @static_length
37
- s = self[0, @static_length]
38
- s[-1] = "\x00".encode(s.encoding)
39
- PacketGen.force_binary s
40
- else
41
- PacketGen.force_binary(self + "\0" * (@static_length - self.length))
42
- end
49
+ if static_length?
50
+ s = string[0, static_length - 1]
51
+ s << "\x00" * (static_length - s.length)
43
52
  else
44
- PacketGen.force_binary(self + +"\x00".encode(self.encoding))
53
+ s = "#{string}\x00"
45
54
  end
55
+ PacketGen.force_binary(s)
46
56
  end
47
57
 
48
58
  # @return [Integer]
49
59
  def sz
50
- if @static_length.is_a? Integer
51
- @static_length
60
+ if static_length?
61
+ static_length
52
62
  else
53
63
  to_s.size
54
64
  end
55
65
  end
56
66
 
67
+ # Say if a static length is defined
68
+ # @return [Boolean]
69
+ # @since 3.1.6
70
+ def static_length?
71
+ !static_length.nil?
72
+ end
73
+
57
74
  # Populate CString from a human readable string
58
75
  # @param [String] str
59
76
  # @return [self]
@@ -66,6 +83,13 @@ module PacketGen
66
83
  idx = self.index(+"\x00".encode(self.encoding)) || self.sz
67
84
  self[0, idx]
68
85
  end
86
+
87
+ private
88
+
89
+ def register_internal_string(str)
90
+ @string = str
91
+ PacketGen.force_binary(@string)
92
+ end
69
93
  end
70
94
  end
71
95
  end
@@ -1,11 +1,11 @@
1
1
  # coding: utf-8
2
+ # frozen_string_literal: true
3
+
2
4
  # This file is part of PacketGen
3
5
  # See https://github.com/sdaubert/packetgen for more informations
4
- # Copyright (C) 2032 Sylvain Daubert <sylvain.daubert@laposte.net>
6
+ # Copyright (C) 2016 Sylvain Daubert <sylvain.daubert@laposte.net>
5
7
  # This program is published under MIT license.
6
8
 
7
- # frozen_string_literal: true
8
-
9
9
  module PacketGen
10
10
  module Types
11
11
  # @abstract Base enum class to handle binary integers with limited
@@ -55,6 +55,7 @@ module PacketGen
55
55
  nil
56
56
  when ::String
57
57
  raise ArgumentError, "#{value.inspect} not in enumeration" unless @enum.key? value
58
+
58
59
  @enum[value]
59
60
  else
60
61
  value.to_i
@@ -70,6 +71,10 @@ module PacketGen
70
71
  def to_human
71
72
  @enum.key(to_i) || "<unknown:#{@value}>"
72
73
  end
74
+
75
+ def format_inspect
76
+ format_str % [to_human, to_i]
77
+ end
73
78
  end
74
79
 
75
80
  # Enumeration on one byte. See {Enum}.
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This file is part of PacketGen
4
+ # See https://github.com/sdaubert/packetgen for more informations
5
+ # Copyright (C) 2016 Sylvain Daubert <sylvain.daubert@laposte.net>
6
+ # This program is published under MIT license.
7
+
8
+ module PacketGen
9
+ module Types
10
+ # Mixin to define minimal API for a class to be embbeded as a field in
11
+ # {Fields} type.
12
+ #
13
+ # == Optional methods
14
+ # These methods may, optionally, be defined by fieldable types:
15
+ # * +from_human+ to load data from a human-readable string.
16
+ # @author Sylvain Daubert
17
+ # @since 3.1.6
18
+ module Fieldable
19
+ # Get type name
20
+ # @return [String]
21
+ def type_name
22
+ self.class.to_s.split('::').last
23
+ end
24
+
25
+ # rubocop:disable Lint/UselessMethodDefinition
26
+ # These methods are defined for documentation.
27
+
28
+ # Populate object from a binary string
29
+ # @param [String] str
30
+ # @return [Fields] self
31
+ # @abstract subclass should overload it.
32
+ def read(str)
33
+ super
34
+ end
35
+
36
+ # Return object as a binary string
37
+ # @return [String]
38
+ # @abstract subclass should overload it.
39
+ def to_s
40
+ super
41
+ end
42
+
43
+ # Size of object as binary string
44
+ # @return [Integer]
45
+ def sz
46
+ to_s.size
47
+ end
48
+
49
+ # Return a human-readbale string
50
+ # @return [String]
51
+ # @abstract subclass should overload it.
52
+ def to_human
53
+ super
54
+ end
55
+
56
+ # rubocop:enable Lint/UselessMethodDefinition
57
+
58
+ # Format object when inspecting a {Field} object
59
+ # @return [String]
60
+ def format_inspect
61
+ to_human
62
+ end
63
+ end
64
+ end
65
+ end
@@ -1,9 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # This file is part of PacketGen
2
4
  # See https://github.com/sdaubert/packetgen for more informations
3
5
  # Copyright (C) 2016 Sylvain Daubert <sylvain.daubert@laposte.net>
4
6
  # This program is published under MIT license.
5
7
 
6
- # frozen_string_literal: true
8
+ # rubocop:disable Metrics/ClassLength
7
9
 
8
10
  module PacketGen
9
11
  module Types
@@ -13,8 +15,7 @@ module PacketGen
13
15
  #
14
16
  # == Basics
15
17
  # A {Fields} subclass is generaly composed of multiple binary fields. These fields
16
- # have each a given type. All types from {Types} module are supported, and all
17
- # {Fields} subclasses may also be used as field type.
18
+ # have each a given type. All {Fieldable} types are supported.
18
19
  #
19
20
  # To define a new subclass, it has to inherit from {Fields}. And some class
20
21
  # methods have to be used to declare attributes/fields:
@@ -119,17 +120,23 @@ module PacketGen
119
120
  # @return [Hash]
120
121
  # @since 3.1.0
121
122
  attr_reader :field_defs
123
+ # Get bit fields defintions for this class
124
+ # @return [Hash]
125
+ # @since 3.1.5
126
+ attr_reader :bit_fields
122
127
 
123
128
  # On inheritage, create +@field_defs+ class variable
124
129
  # @param [Class] klass
125
130
  # @return [void]
126
131
  def inherited(klass)
132
+ super
133
+
127
134
  field_defs = {}
128
135
  @field_defs.each do |k, v|
129
136
  field_defs[k] = v.clone
130
137
  end
131
138
  ordered = @ordered_fields.clone
132
- bf = @bit_fields.clone
139
+ bf = bit_fields.clone
133
140
 
134
141
  klass.class_eval do
135
142
  @ordered_fields = ordered
@@ -158,7 +165,7 @@ module PacketGen
158
165
  # bs[value1] # => Types::Int8
159
166
  # bs.value1 # => Integer
160
167
  # @param [Symbol] name field name
161
- # @param [Object] type class or instance
168
+ # @param [Fieldable] type class or instance
162
169
  # @param [Hash] options Unrecognized options are passed to object builder if
163
170
  # +:builder+ option is not set.
164
171
  # @option options [Object] :default default value. May be a proc. This lambda
@@ -172,44 +179,22 @@ module PacketGen
172
179
  # Define enumeration: hash's keys are +String+, and values are +Integer+.
173
180
  # @return [void]
174
181
  def define_field(name, type, options={})
175
- define = []
176
- if type < Types::Enum
177
- define << "def #{name}; self[:#{name}].to_i; end"
178
- define << "def #{name}=(val) self[:#{name}].value = val; end"
179
- else
180
- define << "def #{name}\n" \
181
- " if self[:#{name}].respond_to?(:to_human) && self[:#{name}].respond_to?(:from_human)\n" \
182
- " self[:#{name}].to_human\n" \
183
- " else\n" \
184
- " self[:#{name}]\n" \
185
- " end\n" \
186
- "end"
187
- define << "def #{name}=(val)\n" \
188
- " if self[:#{name}].respond_to?(:to_human) && self[:#{name}].respond_to?(:from_human)\n" \
189
- " self[:#{name}].from_human val\n" \
190
- " else\n" \
191
- " self[:#{name}].read val\n" \
192
- " end\n" \
193
- "end"
194
- end
195
-
196
- define.delete_at(1) if instance_methods.include? "#{name}=".to_sym
197
- define.delete_at(0) if instance_methods.include? name
198
- class_eval define.join("\n")
182
+ fields << name
199
183
  field_defs[name] = FieldDef.new(type,
200
184
  options.delete(:default),
201
185
  options.delete(:builder),
202
186
  options.delete(:optional),
203
187
  options.delete(:enum),
204
188
  options)
205
- fields << name
189
+
190
+ add_methods(name, type)
206
191
  end
207
192
 
208
193
  # Define a field, before another one
209
194
  # @param [Symbol,nil] other field name to create a new one before. If +nil+,
210
195
  # new field is appended.
211
196
  # @param [Symbol] name field name to create
212
- # @param [Object] type class or instance
197
+ # @param [Fieldable] type class or instance
213
198
  # @param [Hash] options See {.define_field}.
214
199
  # @return [void]
215
200
  # @see .define_field
@@ -228,7 +213,7 @@ module PacketGen
228
213
  # @param [Symbol,nil] other field name to create a new one after. If +nil+,
229
214
  # new field is appended.
230
215
  # @param [Symbol] name field name to create
231
- # @param [Object] type class or instance
216
+ # @param [Fieldable] type class or instance
232
217
  # @param [Hash] options See {.define_field}.
233
218
  # @return [void]
234
219
  # @see .define_field
@@ -262,12 +247,12 @@ module PacketGen
262
247
  # @raise [ArgumentError] unknown +field+
263
248
  # @since 2.8.4
264
249
  def update_field(field, options)
265
- raise ArgumentError, "unkown #{field} field for #{self}" unless field_defs.key?(field)
250
+ check_existence_of field
251
+
252
+ %i[default builder optional enum].each do |property|
253
+ field_defs_property_from(field, property, options)
254
+ end
266
255
 
267
- field_defs[field].default = options.delete(:default) if options.key?(:default)
268
- field_defs[field].builder = options.delete(:builder) if options.key?(:builder)
269
- field_defs[field].optional = options.delete(:optional) if options.key?(:optional)
270
- field_defs[field].enum = options.delete(:enum) if options.key?(:enum)
271
256
  field_defs[field].options.merge!(options)
272
257
  end
273
258
 
@@ -289,63 +274,29 @@ module PacketGen
289
274
  # subclass)
290
275
  # @param [Array] args list of bitfield names. Name may be followed
291
276
  # by bitfield size. If no size is given, 1 bit is assumed.
277
+ # @raise [ArgumentError] unknown +attr+
292
278
  # @return [void]
293
279
  def define_bit_fields_on(attr, *args)
294
- attr_def = field_defs[attr]
295
- raise ArgumentError, "unknown #{attr} field" if attr_def.nil?
280
+ check_existence_of attr
296
281
 
297
- type = attr_def.type
298
- unless type < Types::Int
299
- raise TypeError, "#{attr} is not a PacketGen::Types::Int"
300
- end
282
+ type = field_defs[attr].type
283
+ raise TypeError, "#{attr} is not a PacketGen::Types::Int" unless type < Types::Int
301
284
 
302
285
  total_size = type.new.width * 8
303
286
  idx = total_size - 1
304
287
 
305
- field = args.shift
306
- while field
288
+ until args.empty?
289
+ field = args.shift
307
290
  next unless field.is_a? Symbol
308
291
 
309
- size = if args.first.is_a? Integer
310
- args.shift
311
- else
312
- 1
313
- end
292
+ size = size_from(args)
293
+
314
294
  unless field == :_
315
- shift = idx - (size - 1)
316
- field_mask = (2**size - 1) << shift
317
- clear_mask = (2**total_size - 1) & (~field_mask & (2**total_size - 1))
318
-
319
- if size == 1
320
- class_eval <<-METHODS
321
- def #{field}?
322
- val = (self[:#{attr}].to_i & #{field_mask}) >> #{shift}
323
- val != 0
324
- end
325
- def #{field}=(v)
326
- val = v ? 1 : 0
327
- self[:#{attr}].value = self[:#{attr}].to_i & #{clear_mask}
328
- self[:#{attr}].value |= val << #{shift}
329
- end
330
- METHODS
331
- else
332
- class_eval <<-METHODS
333
- def #{field}
334
- (self[:#{attr}].to_i & #{field_mask}) >> #{shift}
335
- end
336
- def #{field}=(v)
337
- self[:#{attr}].value = self[:#{attr}].to_i & #{clear_mask}
338
- self[:#{attr}].value |= (v & #{2**size - 1}) << #{shift}
339
- end
340
- METHODS
341
- end
342
-
343
- @bit_fields[attr] = {} if @bit_fields[attr].nil?
344
- @bit_fields[attr][field] = size
295
+ add_bit_methods(attr, field, size, total_size, idx)
296
+ register_bit_field_size(attr, field, size)
345
297
  end
346
298
 
347
299
  idx -= size
348
- field = args.shift
349
300
  end
350
301
  end
351
302
 
@@ -354,7 +305,7 @@ module PacketGen
354
305
  # @return [void]
355
306
  # @since 2.8.4
356
307
  def remove_bit_fields_on(attr)
357
- fields = @bit_fields.delete(attr)
308
+ fields = bit_fields.delete(attr)
358
309
  return if fields.nil?
359
310
 
360
311
  fields.each do |field, size|
@@ -362,6 +313,98 @@ module PacketGen
362
313
  undef_method(size == 1 ? "#{field}?" : field)
363
314
  end
364
315
  end
316
+
317
+ private
318
+
319
+ def add_methods(name, type)
320
+ define = []
321
+ if type < Types::Enum
322
+ define << "def #{name}; self[:#{name}].to_i; end"
323
+ define << "def #{name}=(val) self[:#{name}].value = val; end"
324
+ else
325
+ define << "def #{name}\n" \
326
+ " to_and_from_human?(:#{name}) ? self[:#{name}].to_human : self[:#{name}]\n" \
327
+ 'end'
328
+ define << "def #{name}=(val)\n" \
329
+ " to_and_from_human?(:#{name}) ? self[:#{name}].from_human(val) : self[:#{name}].read(val)\n" \
330
+ 'end'
331
+ end
332
+
333
+ define.delete_at(1) if instance_methods.include? "#{name}=".to_sym
334
+ define.delete_at(0) if instance_methods.include? name
335
+ class_eval define.join("\n")
336
+ end
337
+
338
+ def add_bit_methods(attr, name, size, total_size, idx)
339
+ shift = idx - (size - 1)
340
+
341
+ if size == 1
342
+ add_single_bit_methods(attr, name, size, total_size, shift)
343
+ else
344
+ add_multibit_methods(attr, name, size, total_size, shift)
345
+ end
346
+ end
347
+
348
+ def compute_field_mask(size, shift)
349
+ (2**size - 1) << shift
350
+ end
351
+
352
+ def compute_clear_mask(total_size, field_mask)
353
+ (2**total_size - 1) & (~field_mask & (2**total_size - 1))
354
+ end
355
+
356
+ def add_single_bit_methods(attr, name, size, total_size, shift)
357
+ field_mask = compute_field_mask(size, shift)
358
+ clear_mask = compute_clear_mask(total_size, field_mask)
359
+
360
+ class_eval <<-METHODS
361
+ def #{name}?
362
+ val = (self[:#{attr}].to_i & #{field_mask}) >> #{shift}
363
+ val != 0
364
+ end
365
+ def #{name}=(v)
366
+ val = v ? 1 : 0
367
+ self[:#{attr}].value = self[:#{attr}].to_i & #{clear_mask}
368
+ self[:#{attr}].value |= val << #{shift}
369
+ end
370
+ METHODS
371
+ end
372
+
373
+ def add_multibit_methods(attr, name, size, total_size, shift)
374
+ field_mask = compute_field_mask(size, shift)
375
+ clear_mask = compute_clear_mask(total_size, field_mask)
376
+
377
+ class_eval <<-METHODS
378
+ def #{name}
379
+ (self[:#{attr}].to_i & #{field_mask}) >> #{shift}
380
+ end
381
+ def #{name}=(v)
382
+ self[:#{attr}].value = self[:#{attr}].to_i & #{clear_mask}
383
+ self[:#{attr}].value |= (v & #{2**size - 1}) << #{shift}
384
+ end
385
+ METHODS
386
+ end
387
+
388
+ def register_bit_field_size(attr, field, size)
389
+ bit_fields[attr] = {} if bit_fields[attr].nil?
390
+ bit_fields[attr][field] = size
391
+ end
392
+
393
+ def field_defs_property_from(field, property, options)
394
+ field_defs[field].send("#{property}=", options.delete(property)) if options.key?(property)
395
+ end
396
+
397
+ def size_from(args)
398
+ if args.first.is_a? Integer
399
+ args.shift
400
+ else
401
+ 1
402
+ end
403
+ end
404
+
405
+ def check_existence_of(field)
406
+ raise ArgumentError, "unknown #{field} field for #{self}" unless field_defs.key?(field)
407
+ end
365
408
  end
366
409
 
367
410
  # Create a new fields object
@@ -371,36 +414,13 @@ module PacketGen
371
414
  @fields = {}
372
415
  @optional_fields = {}
373
416
 
374
- field_defs = self.class.field_defs
375
417
  self.class.fields.each do |field|
376
- type = field_defs[field].type
377
- default = field_defs[field].default
378
- default = default.to_proc.call(self) if default.is_a?(Proc)
379
- builder = field_defs[field].builder
380
- optional = field_defs[field].optional
381
- enum = field_defs[field].enum
382
- field_options = field_defs[field].options
383
-
384
- @fields[field] = if builder
385
- builder.call(self, type)
386
- elsif enum
387
- type.new(enum)
388
- elsif !field_options.empty?
389
- type.new(field_options)
390
- else
391
- type.new
392
- end
393
-
394
- value = options[field] || default
395
- if value.class <= type
396
- @fields[field] = value
397
- elsif @fields[field].respond_to? :from_human
398
- @fields[field].from_human(value)
399
- end
400
-
401
- @optional_fields[field] = optional if optional
418
+ build_field field
419
+ initialize_value field, options[field]
420
+ initialize_optional field
402
421
  end
403
- self.class.class_eval { @bit_fields }.each do |_, hsh|
422
+
423
+ self.class.bit_fields.each do |_, hsh|
404
424
  hsh.each_key do |bit_field|
405
425
  self.send "#{bit_field}=", options[bit_field] if options[bit_field]
406
426
  end
@@ -409,7 +429,7 @@ module PacketGen
409
429
 
410
430
  # Get field object
411
431
  # @param [Symbol] field
412
- # @return [Object]
432
+ # @return [Fieldable]
413
433
  def [](field)
414
434
  @fields[field]
415
435
  end
@@ -429,6 +449,7 @@ module PacketGen
429
449
  end
430
450
 
431
451
  # Get all optional field name
452
+ # @return[Array<Symbol>,nil]
432
453
  def optional_fields
433
454
  @optional_fields.keys
434
455
  end
@@ -459,11 +480,7 @@ module PacketGen
459
480
  next unless present?(field)
460
481
 
461
482
  obj = self[field].read str[start..-1]
462
- if self[field].respond_to? :sz
463
- start += self[field].sz
464
- else
465
- start = str.size
466
- end
483
+ start += self[field].sz
467
484
  self[field] = obj unless obj == self[field]
468
485
  end
469
486
 
@@ -530,7 +547,7 @@ module PacketGen
530
547
  # @return [Hash,nil] keys: bit fields, values: their size in bits
531
548
  # @since 2.8.3
532
549
  def bits_on(field)
533
- self.class.class_eval { @bit_fields }[field]
550
+ self.class.bit_fields[field]
534
551
  end
535
552
 
536
553
  private
@@ -549,6 +566,54 @@ module PacketGen
549
566
  def force_binary(str)
550
567
  PacketGen.force_binary(str)
551
568
  end
569
+
570
+ # @param [Symbol] attr attribute
571
+ # @return [Boolean] +tru+e if #from_human and #to_human are both defined for given attribute
572
+ def to_and_from_human?(attr)
573
+ self[attr].respond_to?(:to_human) && self[attr].respond_to?(:from_human)
574
+ end
575
+
576
+ def field_defs
577
+ self.class.field_defs
578
+ end
579
+
580
+ # rubocop:disable Metrics/AbcSize
581
+ def build_field(field)
582
+ type = field_defs[field].type
583
+
584
+ @fields[field] = if field_defs[field].builder
585
+ field_defs[field].builder.call(self, type)
586
+ elsif field_defs[field].enum
587
+ type.new(field_defs[field].enum)
588
+ elsif !field_defs[field].options.empty?
589
+ type.new(field_defs[field].options)
590
+ else
591
+ type.new
592
+ end
593
+ end
594
+ # rubocop:enable Metrics/AbcSize
595
+
596
+ def initialize_value(field, val)
597
+ type = field_defs[field].type
598
+ default = field_defs[field].default
599
+ default = default.to_proc.call(self) if default.is_a?(Proc)
600
+
601
+ value = val || default
602
+ if value.class <= type
603
+ @fields[field] = value
604
+ elsif @fields[field].respond_to? :from_human
605
+ @fields[field].from_human(value)
606
+ else
607
+ @fields[field].read(value)
608
+ end
609
+ end
610
+
611
+ def initialize_optional(field)
612
+ optional = field_defs[field].optional
613
+ @optional_fields[field] = optional if optional
614
+ end
552
615
  end
553
616
  end
554
617
  end
618
+
619
+ # rubocop:enable Metrics/ClassLength