packetgen 3.1.5 → 3.1.6

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 (58) hide show
  1. checksums.yaml +4 -4
  2. data/bin/pgconsole +1 -0
  3. data/lib/packetgen.rb +2 -2
  4. data/lib/packetgen/capture.rb +9 -0
  5. data/lib/packetgen/header/base.rb +68 -70
  6. data/lib/packetgen/header/dhcpv6/duid.rb +3 -1
  7. data/lib/packetgen/header/dhcpv6/option.rb +3 -1
  8. data/lib/packetgen/header/dns/name.rb +18 -7
  9. data/lib/packetgen/header/dns/question.rb +2 -0
  10. data/lib/packetgen/header/dot11.rb +23 -6
  11. data/lib/packetgen/header/dot11/data.rb +9 -5
  12. data/lib/packetgen/header/eap.rb +3 -2
  13. data/lib/packetgen/header/eth.rb +4 -8
  14. data/lib/packetgen/header/http/headers.rb +3 -4
  15. data/lib/packetgen/header/http/request.rb +32 -17
  16. data/lib/packetgen/header/http/response.rb +1 -1
  17. data/lib/packetgen/header/http/verbs.rb +1 -1
  18. data/lib/packetgen/header/igmpv3/group_record.rb +2 -0
  19. data/lib/packetgen/header/ip.rb +27 -26
  20. data/lib/packetgen/header/ip/addr.rb +2 -3
  21. data/lib/packetgen/header/ip/option.rb +4 -4
  22. data/lib/packetgen/header/ipv6/addr.rb +1 -2
  23. data/lib/packetgen/header/mldv2/mcast_address_record.rb +2 -0
  24. data/lib/packetgen/header/ospfv2/ls_request.rb +2 -0
  25. data/lib/packetgen/header/ospfv2/lsa.rb +6 -0
  26. data/lib/packetgen/header/ospfv2/lsa_header.rb +2 -1
  27. data/lib/packetgen/header/ospfv3/ipv6_prefix.rb +2 -0
  28. data/lib/packetgen/header/ospfv3/ls_request.rb +2 -0
  29. data/lib/packetgen/header/ospfv3/lsa.rb +2 -0
  30. data/lib/packetgen/header/ospfv3/lsa_header.rb +2 -1
  31. data/lib/packetgen/header/snmp.rb +3 -2
  32. data/lib/packetgen/header/tcp/option.rb +8 -6
  33. data/lib/packetgen/packet.rb +7 -3
  34. data/lib/packetgen/pcapng.rb +11 -11
  35. data/lib/packetgen/pcapng/block.rb +15 -2
  36. data/lib/packetgen/pcapng/epb.rb +22 -15
  37. data/lib/packetgen/pcapng/file.rb +164 -81
  38. data/lib/packetgen/pcapng/idb.rb +7 -9
  39. data/lib/packetgen/pcapng/shb.rb +35 -28
  40. data/lib/packetgen/pcapng/spb.rb +16 -12
  41. data/lib/packetgen/pcapng/unknown_block.rb +3 -11
  42. data/lib/packetgen/pcaprub_wrapper.rb +8 -8
  43. data/lib/packetgen/types.rb +1 -0
  44. data/lib/packetgen/types/abstract_tlv.rb +2 -3
  45. data/lib/packetgen/types/array.rb +15 -9
  46. data/lib/packetgen/types/cstring.rb +38 -17
  47. data/lib/packetgen/types/fieldable.rb +65 -0
  48. data/lib/packetgen/types/fields.rb +91 -56
  49. data/lib/packetgen/types/int.rb +2 -2
  50. data/lib/packetgen/types/int_string.rb +7 -2
  51. data/lib/packetgen/types/length_from.rb +18 -10
  52. data/lib/packetgen/types/oui.rb +1 -2
  53. data/lib/packetgen/types/string.rb +45 -8
  54. data/lib/packetgen/types/tlv.rb +1 -2
  55. data/lib/packetgen/utils.rb +2 -2
  56. data/lib/packetgen/version.rb +1 -1
  57. metadata +13 -12
  58. data/lib/packetgen/inspectable.rb +0 -20
@@ -60,7 +60,7 @@ module PacketGen
60
60
  # @option options [Integer] :block_len2 block total length
61
61
  def initialize(options={})
62
62
  super
63
- set_endianness(options[:endian] || :little)
63
+ endianness(options[:endian] || :little)
64
64
  @packets = []
65
65
  @options_decoded = false
66
66
  recalc_block_len
@@ -74,15 +74,12 @@ module PacketGen
74
74
  io = to_io(str_or_io)
75
75
  return self if io.eof?
76
76
 
77
- self[:type].read io.read(4)
78
- self[:block_len].read io.read(4)
79
- self[:link_type].read io.read(2)
80
- self[:reserved].read io.read(2)
81
- self[:snaplen].read io.read(4)
77
+ %i[type block_len link_type reserved snaplen].each do |attr|
78
+ self[attr].read io.read(self[attr].sz)
79
+ end
82
80
  self[:options].read io.read(self.block_len - MIN_SIZE)
83
- self[:block_len2].read io.read(4)
81
+ read_blocklen2_and_check(io)
84
82
 
85
- check_len_coherency
86
83
  self
87
84
  end
88
85
 
@@ -91,6 +88,7 @@ module PacketGen
91
88
  # @return [self]
92
89
  def <<(xpb)
93
90
  @packets << xpb
91
+ xpb.interface = self
94
92
  self
95
93
  end
96
94
 
@@ -133,7 +131,7 @@ module PacketGen
133
131
 
134
132
  while idx < options.length
135
133
  opt_code, opt_len = options[idx, 4].unpack("#{packstr}2")
136
- return options[idx + 4, 1].unpack('C').first if opt_code == OPTION_IF_TSRESOL && opt_len == 1
134
+ return options[idx + 4, 1].unpack1('C') if opt_code == OPTION_IF_TSRESOL && opt_len == 1
137
135
 
138
136
  idx += 4 + opt_len
139
137
  end
@@ -80,7 +80,7 @@ module PacketGen
80
80
  super
81
81
  @interfaces = []
82
82
  @unknown_blocks = []
83
- set_endianness(options[:endian] || :little)
83
+ endianness(options[:endian] || :little)
84
84
  recalc_block_len
85
85
  self.type = options[:type] || PcapNG::SHB_TYPE.to_i
86
86
  end
@@ -92,29 +92,15 @@ module PacketGen
92
92
  io = to_io(str_or_io)
93
93
  return self if io.eof?
94
94
 
95
- type_str = check_shb(io)
96
- block_len_str = io.read(4)
97
- magic_str = io.read(4)
98
-
99
- case magic_str
100
- when MAGIC_LITTLE
101
- force_endianness :little if @endian == :big
102
- when MAGIC_BIG
103
- force_endianness :big if @endian == :little
104
- else
105
- raise InvalidFileError, 'Incorrect magic for Section Header Block'
95
+ self[:type].read check_shb(io)
96
+ %i[block_len magic ver_major ver_minor section_len].each do |attr|
97
+ self[attr].read io.read(self[attr].sz)
106
98
  end
99
+ handle_magic_and_check(self[:magic].to_s)
107
100
 
108
- self[:type].read type_str
109
- self[:block_len].read block_len_str
110
- self[:magic].read magic_str
111
- self[:ver_major].read io.read(2)
112
- self[:ver_minor].read io.read(2)
113
- self[:section_len].read io.read(8)
114
- self[:options].read io.read(self.block_len - MIN_SIZE)
115
- self[:block_len2].read io.read(4)
101
+ read_options(io)
102
+ read_blocklen2_and_check(io)
116
103
 
117
- check_len_coherency
118
104
  self
119
105
  end
120
106
 
@@ -123,6 +109,7 @@ module PacketGen
123
109
  # @return [self]
124
110
  def <<(idb)
125
111
  @interfaces << idb
112
+ idb.section = self
126
113
  self
127
114
  end
128
115
 
@@ -137,17 +124,22 @@ module PacketGen
137
124
  super + body
138
125
  end
139
126
 
127
+ def add_unknown_block(block)
128
+ self.unknown_blocks << block
129
+ block.section = self
130
+ end
131
+
140
132
  private
141
133
 
142
134
  def force_endianness(endian)
143
135
  @endian = endian
144
- self[:type] = Types::Int32.new(self[:type].to_i, endian)
145
- self[:block_len] = Types::Int32.new(self[:block_len].to_i, endian)
146
- self[:magic] = Types::Int32.new(self[:magic].to_i, endian)
147
- self[:ver_major] = Types::Int16.new(self[:ver_major].to_i, endian)
148
- self[:ver_minor] = Types::Int16.new(self[:ver_minor].to_i, endian)
149
- self[:section_len] = Types::Int64.new(self[:section_len].to_i, endian)
150
- self[:block_len2] = Types::Int32.new(self[:block_len2].to_i, endian)
136
+ %i[type block_len magic block_len2].each do |attr|
137
+ self[attr] = Types::Int32.new(0, endian).read(self[attr].to_s)
138
+ end
139
+ %i[ver_major ver_minor].each do |attr|
140
+ self[attr] = Types::Int16.new(0, endian).read(self[attr].to_s)
141
+ end
142
+ self[:section_len] = Types::Int64.new(0, endian).read(self[:section_len].to_s)
151
143
  end
152
144
 
153
145
  # Check io contains a SHB
@@ -160,6 +152,21 @@ module PacketGen
160
152
  type = type_str.unpack('H*').join
161
153
  raise InvalidFileError, "Incorrect type (#{type})for Section Header Block"
162
154
  end
155
+
156
+ def handle_magic_and_check(magic_str)
157
+ case magic_str
158
+ when MAGIC_LITTLE
159
+ force_endianness :little if endian == :big
160
+ when MAGIC_BIG
161
+ force_endianness :big if endian == :little
162
+ else
163
+ raise InvalidFileError, 'Incorrect magic for Section Header Block'
164
+ end
165
+ end
166
+
167
+ def read_options(io)
168
+ self[:options].read io.read(self.block_len - MIN_SIZE)
169
+ end
163
170
  end
164
171
  end
165
172
  end
@@ -44,7 +44,7 @@ module PacketGen
44
44
  # @option options [Integer] :block_len2 block total length
45
45
  def initialize(options={})
46
46
  super
47
- set_endianness(options[:endian] || :little)
47
+ endianness(options[:endian] || :little)
48
48
  recalc_block_len
49
49
  self.type = options[:type] || PcapNG::SPB_TYPE.to_i
50
50
  end
@@ -66,19 +66,11 @@ module PacketGen
66
66
  self[:type].read io.read(4)
67
67
  self[:block_len].read io.read(4)
68
68
  self[:orig_len].read io.read(4)
69
- # Take care of IDB snaplen
70
- # CAUTION: snaplen == 0 -> no capture limit
71
- data_len = if interface && interface.snaplen.to_i.positive?
72
- [self[:orig_len].to_i, interface.snaplen.to_i].min
73
- else
74
- self[:orig_len].to_i
75
- end
76
- data_pad_len = (4 - (data_len % 4)) % 4
69
+ data_len = compute_data_len
77
70
  self[:data].read io.read(data_len)
78
- io.read data_pad_len
79
- self[:block_len2].read io.read(4)
71
+ remove_padding(io, data_len)
72
+ read_blocklen2_and_check(io)
80
73
 
81
- check_len_coherency
82
74
  self.type ||= PcapNG::IDB_TYPE.to_i
83
75
  self
84
76
  end
@@ -90,6 +82,18 @@ module PacketGen
90
82
  recalc_block_len
91
83
  super
92
84
  end
85
+
86
+ private
87
+
88
+ # Take care of IDB snaplen
89
+ # CAUTION: snaplen == 0 -> no capture limit
90
+ def compute_data_len
91
+ if interface && interface.snaplen.to_i.positive?
92
+ [self[:orig_len].to_i, interface.snaplen.to_i].min
93
+ else
94
+ self[:orig_len].to_i
95
+ end
96
+ end
93
97
  end
94
98
  end
95
99
  end
@@ -29,7 +29,7 @@ module PacketGen
29
29
  # @option options [Integer] :block_len2 block total length
30
30
  def initialize(options={})
31
31
  super
32
- set_endianness(options[:endian] || :little)
32
+ endianness(options[:endian] || :little)
33
33
  recalc_block_len
34
34
  end
35
35
 
@@ -44,21 +44,13 @@ module PacketGen
44
44
  # @param [::String,IO] str_or_io
45
45
  # @return [self]
46
46
  def read(str_or_io)
47
- io = if str_or_io.respond_to? :read
48
- str_or_io
49
- else
50
- StringIO.new(force_binary(str_or_io.to_s))
51
- end
47
+ io = to_io(str_or_io)
52
48
  return self if io.eof?
53
49
 
54
50
  self[:type].read io.read(4)
55
51
  self[:block_len].read io.read(4)
56
52
  self[:body].read io.read(self[:block_len].to_i - MIN_SIZE)
57
- self[:block_len2].read io.read(4)
58
-
59
- unless self[:block_len].to_i == self[:block_len2].to_i
60
- raise InvalidFileError, 'Incoherency in Header Block'
61
- end
53
+ read_blocklen2_and_check(io)
62
54
 
63
55
  self
64
56
  end
@@ -38,6 +38,8 @@ module PacketGen
38
38
  pcap.activate
39
39
  end
40
40
 
41
+ # rubocop:disable Metrics/ParameterLists
42
+
41
43
  # Capture packets from a network interface
42
44
  # @param [String] iface interface name
43
45
  # @param [Integer] snaplen
@@ -49,14 +51,14 @@ module PacketGen
49
51
  # @author Sylvain Daubert
50
52
  # @author optix2000 - add support for setting monitor mode
51
53
  # @since 3.1.5 add monitor argument
52
- def self.capture(iface:, snaplen: DEFAULT_SNAPLEN, promisc: DEFAULT_PROMISC, filter: nil, monitor: nil)
54
+ def self.capture(iface:, snaplen: DEFAULT_SNAPLEN, promisc: DEFAULT_PROMISC, filter: nil, monitor: nil, &block)
53
55
  pcap = self.open_iface(iface: iface, snaplen: snaplen, promisc: promisc, monitor: monitor)
54
56
  pcap.setfilter filter unless filter.nil?
55
- pcap.each do |packet_data|
56
- yield packet_data
57
- end
57
+ pcap.each(&block)
58
58
  end
59
59
 
60
+ # rubocop:enable Metrics/ParameterLists
61
+
60
62
  # Inject given data onto wire
61
63
  # @param [String] iface interface name
62
64
  # @param [String] data to inject
@@ -72,10 +74,8 @@ module PacketGen
72
74
  # @yieldparam [String] data binary packet data
73
75
  # @return [void]
74
76
  # @author Kent Gruber
75
- def self.read_pcap(filename:)
76
- PCAPRUB::Pcap.open_offline(filename).each_packet do |packet|
77
- yield packet
78
- end
77
+ def self.read_pcap(filename:, &block)
78
+ PCAPRUB::Pcap.open_offline(filename).each_packet(&block)
79
79
  end
80
80
  end
81
81
  end
@@ -12,6 +12,7 @@ module PacketGen
12
12
  end
13
13
 
14
14
  require_relative 'types/length_from'
15
+ require_relative 'types/fieldable'
15
16
  require_relative 'types/int'
16
17
  require_relative 'types/enum'
17
18
  require_relative 'types/string'
@@ -58,7 +58,7 @@ 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 Inspectable
61
+ include Fieldable
62
62
 
63
63
  class <<self
64
64
  # @return [Hash]
@@ -171,9 +171,8 @@ module PacketGen
171
171
  # @return [String]
172
172
  def to_human
173
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]
174
+ 'type:%s,length:%u,value:%s' % [human_type, length, my_value]
175
175
  end
176
- alias format_inspect to_human
177
176
 
178
177
  private
179
178
 
@@ -27,8 +27,8 @@ module PacketGen
27
27
  # @author Sylvain Daubert
28
28
  class Array
29
29
  extend Forwardable
30
- include Inspectable
31
30
  include Enumerable
31
+ include Fieldable
32
32
  include LengthFrom
33
33
 
34
34
  # @!method [](index)
@@ -163,11 +163,8 @@ module PacketGen
163
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
@@ -198,15 +195,12 @@ module PacketGen
198
195
  def to_human
199
196
  @array.map(&:to_human).join(self.class::HUMAN_SEPARATOR)
200
197
  end
201
- alias format_inspect to_human
202
198
 
203
199
  private
204
200
 
205
201
  def record_from_hash(hsh)
206
202
  obj_klass = self.class.set_of_klass
207
- unless obj_klass
208
- raise NotImplementedError, 'class should define #record_from_hash or declare type of elements in set with .set_of'
209
- end
203
+ raise NotImplementedError, 'class should define #record_from_hash or declare type of elements in set with .set_of' unless obj_klass
210
204
 
211
205
  obj = obj_klass.new(hsh) if obj_klass
212
206
  klass = real_type(obj)
@@ -216,6 +210,18 @@ module PacketGen
216
210
  def real_type(obj)
217
211
  obj.class
218
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
219
225
  end
220
226
 
221
227
  # Specialized array to handle serie of {Int8}.
@@ -6,17 +6,29 @@
6
6
  # Copyright (C) 2016 Sylvain Daubert <sylvain.daubert@laposte.net>
7
7
  # This program is published under MIT license.
8
8
 
9
+ require 'forwardable'
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
14
- include Inspectable
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
15
27
 
16
28
  # @param [Hash] options
17
29
  # @option options [Integer] :static_length set a static length for this string
18
30
  def initialize(options={})
19
- super()
31
+ register_internal_string ''
20
32
  @static_length = options[:static_length]
21
33
  end
22
34
 
@@ -24,38 +36,41 @@ module PacketGen
24
36
  # @return [String] self
25
37
  def read(str)
26
38
  s = str.to_s
27
- s = s[0, @static_length] if @static_length.is_a? Integer
39
+ s = s[0, static_length] if static_length?
28
40
  idx = s.index(0.chr)
29
41
  s = s[0, idx] unless idx.nil?
30
- self.replace s
42
+ register_internal_string s
31
43
  self
32
44
  end
33
45
 
34
46
  # get null-terminated string
35
47
  # @return [String]
36
48
  def to_s
37
- if defined?(@static_length) && @static_length.is_a?(Integer)
38
- if self.size >= @static_length
39
- s = self[0, @static_length]
40
- s[-1] = "\x00".encode(s.encoding)
41
- PacketGen.force_binary s
42
- else
43
- PacketGen.force_binary(self + "\0" * (@static_length - self.length))
44
- end
49
+ if static_length?
50
+ s = string[0, static_length - 1]
51
+ s << "\x00" * (static_length - s.length)
45
52
  else
46
- PacketGen.force_binary(self + +"\x00".encode(self.encoding))
53
+ s = "#{string}\x00"
47
54
  end
55
+ PacketGen.force_binary(s)
48
56
  end
49
57
 
50
58
  # @return [Integer]
51
59
  def sz
52
- if @static_length.is_a? Integer
53
- @static_length
60
+ if static_length?
61
+ static_length
54
62
  else
55
63
  to_s.size
56
64
  end
57
65
  end
58
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
+
59
74
  # Populate CString from a human readable string
60
75
  # @param [String] str
61
76
  # @return [self]
@@ -68,7 +83,13 @@ module PacketGen
68
83
  idx = self.index(+"\x00".encode(self.encoding)) || self.sz
69
84
  self[0, idx]
70
85
  end
71
- alias format_inspect to_human
86
+
87
+ private
88
+
89
+ def register_internal_string(str)
90
+ @string = str
91
+ PacketGen.force_binary(@string)
92
+ end
72
93
  end
73
94
  end
74
95
  end