packetgen 3.1.5 → 3.1.6

Sign up to get free protection for your applications and to get access to all the features.
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