packetgen 3.1.4 → 3.2.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 (67) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +0 -1
  3. data/bin/pgconsole +1 -0
  4. data/lib/packetgen.rb +19 -3
  5. data/lib/packetgen/capture.rb +30 -9
  6. data/lib/packetgen/config.rb +15 -9
  7. data/lib/packetgen/deprecation.rb +1 -1
  8. data/lib/packetgen/header/asn1_base.rb +19 -9
  9. data/lib/packetgen/header/base.rb +68 -70
  10. data/lib/packetgen/header/dhcpv6/duid.rb +3 -1
  11. data/lib/packetgen/header/dhcpv6/option.rb +4 -12
  12. data/lib/packetgen/header/dns/name.rb +18 -7
  13. data/lib/packetgen/header/dns/qdsection.rb +1 -1
  14. data/lib/packetgen/header/dns/question.rb +2 -0
  15. data/lib/packetgen/header/dot11.rb +25 -38
  16. data/lib/packetgen/header/dot11/data.rb +28 -34
  17. data/lib/packetgen/header/dot1x.rb +1 -14
  18. data/lib/packetgen/header/eap.rb +14 -17
  19. data/lib/packetgen/header/eth.rb +5 -6
  20. data/lib/packetgen/header/http/headers.rb +4 -2
  21. data/lib/packetgen/header/http/request.rb +37 -18
  22. data/lib/packetgen/header/http/response.rb +11 -5
  23. data/lib/packetgen/header/http/verbs.rb +1 -1
  24. data/lib/packetgen/header/igmpv3/group_record.rb +2 -0
  25. data/lib/packetgen/header/ip.rb +27 -26
  26. data/lib/packetgen/header/ip/addr.rb +3 -1
  27. data/lib/packetgen/header/ip/option.rb +4 -4
  28. data/lib/packetgen/header/ipv6/addr.rb +2 -0
  29. data/lib/packetgen/header/mldv2/mcast_address_record.rb +2 -0
  30. data/lib/packetgen/header/ospfv2/ls_request.rb +2 -0
  31. data/lib/packetgen/header/ospfv2/lsa.rb +13 -3
  32. data/lib/packetgen/header/ospfv2/lsa_header.rb +2 -1
  33. data/lib/packetgen/header/ospfv3/ipv6_prefix.rb +2 -0
  34. data/lib/packetgen/header/ospfv3/ls_request.rb +2 -0
  35. data/lib/packetgen/header/ospfv3/lsa.rb +9 -3
  36. data/lib/packetgen/header/ospfv3/lsa_header.rb +2 -1
  37. data/lib/packetgen/header/snmp.rb +3 -2
  38. data/lib/packetgen/header/tcp.rb +1 -20
  39. data/lib/packetgen/header/tcp/option.rb +8 -6
  40. data/lib/packetgen/inspect.rb +1 -17
  41. data/lib/packetgen/packet.rb +10 -6
  42. data/lib/packetgen/pcapng.rb +11 -11
  43. data/lib/packetgen/pcapng/block.rb +15 -2
  44. data/lib/packetgen/pcapng/epb.rb +22 -15
  45. data/lib/packetgen/pcapng/file.rb +166 -81
  46. data/lib/packetgen/pcapng/idb.rb +7 -9
  47. data/lib/packetgen/pcapng/shb.rb +35 -28
  48. data/lib/packetgen/pcapng/spb.rb +16 -12
  49. data/lib/packetgen/pcapng/unknown_block.rb +3 -11
  50. data/lib/packetgen/pcaprub_wrapper.rb +25 -11
  51. data/lib/packetgen/types.rb +1 -0
  52. data/lib/packetgen/types/abstract_tlv.rb +3 -1
  53. data/lib/packetgen/types/array.rb +17 -10
  54. data/lib/packetgen/types/cstring.rb +56 -19
  55. data/lib/packetgen/types/enum.rb +4 -0
  56. data/lib/packetgen/types/fieldable.rb +65 -0
  57. data/lib/packetgen/types/fields.rb +180 -113
  58. data/lib/packetgen/types/int.rb +15 -1
  59. data/lib/packetgen/types/int_string.rb +8 -0
  60. data/lib/packetgen/types/length_from.rb +18 -10
  61. data/lib/packetgen/types/oui.rb +2 -0
  62. data/lib/packetgen/types/string.rb +58 -7
  63. data/lib/packetgen/types/tlv.rb +2 -0
  64. data/lib/packetgen/unknown_packet.rb +84 -0
  65. data/lib/packetgen/utils.rb +6 -7
  66. data/lib/packetgen/version.rb +1 -1
  67. metadata +18 -15
@@ -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
@@ -23,26 +23,42 @@ module PacketGen
23
23
  # @param [String] iface interface name
24
24
  # @param [Integer] snaplen
25
25
  # @param [Boolean] promisc
26
+ # @param [Boolean] monitor
26
27
  # @return [PCAPRUB::Pcap]
27
- def self.open_iface(iface:, snaplen: DEFAULT_SNAPLEN, promisc: DEFAULT_PROMISC)
28
- PCAPRUB::Pcap.open_live(iface, snaplen, promisc, TIMEOUT)
28
+ # @author Sylvain Daubert
29
+ # @author optix2000 - add support for setting monitor mode
30
+ # @since 3.1.5 add monitor argument
31
+ def self.open_iface(iface:, snaplen: DEFAULT_SNAPLEN, promisc: DEFAULT_PROMISC, monitor: nil)
32
+ pcap = PCAPRUB::Pcap.create(iface)
33
+ pcap.setsnaplen(snaplen)
34
+ pcap.setpromisc(promisc)
35
+ pcap.settimeout(TIMEOUT)
36
+ # Monitor MUST be set before pcap is activated
37
+ pcap.setmonitor monitor unless monitor.nil?
38
+ pcap.activate
29
39
  end
30
40
 
41
+ # rubocop:disable Metrics/ParameterLists
42
+
31
43
  # Capture packets from a network interface
32
44
  # @param [String] iface interface name
33
45
  # @param [Integer] snaplen
34
46
  # @param [Boolean] promisc
35
47
  # @param [String] filter BPF filter
48
+ # @param [Boolean] monitor
36
49
  # @yieldparam [String] packet_data binary packet data
37
50
  # @return [void]
38
- def self.capture(iface:, snaplen: DEFAULT_SNAPLEN, promisc: DEFAULT_PROMISC, filter: nil)
39
- pcap = self.open_iface(iface: iface, snaplen: snaplen, promisc: promisc)
51
+ # @author Sylvain Daubert
52
+ # @author optix2000 - add support for setting monitor mode
53
+ # @since 3.1.5 add monitor argument
54
+ def self.capture(iface:, snaplen: DEFAULT_SNAPLEN, promisc: DEFAULT_PROMISC, filter: nil, monitor: nil, &block)
55
+ pcap = self.open_iface(iface: iface, snaplen: snaplen, promisc: promisc, monitor: monitor)
40
56
  pcap.setfilter filter unless filter.nil?
41
- pcap.each do |packet_data|
42
- yield packet_data
43
- end
57
+ pcap.each(&block)
44
58
  end
45
59
 
60
+ # rubocop:enable Metrics/ParameterLists
61
+
46
62
  # Inject given data onto wire
47
63
  # @param [String] iface interface name
48
64
  # @param [String] data to inject
@@ -58,10 +74,8 @@ module PacketGen
58
74
  # @yieldparam [String] data binary packet data
59
75
  # @return [void]
60
76
  # @author Kent Gruber
61
- def self.read_pcap(filename:)
62
- PCAPRUB::Pcap.open_offline(filename).each_packet do |packet|
63
- yield packet
64
- end
77
+ def self.read_pcap(filename:, &block)
78
+ PCAPRUB::Pcap.open_offline(filename).each_packet(&block)
65
79
  end
66
80
  end
67
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,6 +58,8 @@ 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
@@ -169,7 +171,7 @@ module PacketGen
169
171
  # @return [String]
170
172
  def to_human
171
173
  my_value = self[:value].is_a?(String) ? self[:value].inspect : self[:value].to_human
172
- "type:%s,length:%u,value:#{my_value}" % [human_type, length]
174
+ 'type:%s,length:%u,value:%s' % [human_type, length, my_value]
173
175
  end
174
176
 
175
177
  private
@@ -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 = ','
@@ -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
@@ -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}.
@@ -6,15 +6,30 @@
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
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
+ :encode, :slice, :slice!
23
+
24
+ # @return [::String]
25
+ attr_reader :string
26
+ # @return [Integer]
27
+ attr_reader :static_length
28
+
14
29
  # @param [Hash] options
15
30
  # @option options [Integer] :static_length set a static length for this string
16
31
  def initialize(options={})
17
- super()
32
+ register_internal_string(+'')
18
33
  @static_length = options[:static_length]
19
34
  end
20
35
 
@@ -22,38 +37,49 @@ module PacketGen
22
37
  # @return [String] self
23
38
  def read(str)
24
39
  s = str.to_s
25
- s = s[0, @static_length] if @static_length.is_a? Integer
26
- idx = s.index(0.chr)
27
- s = s[0, idx] unless idx.nil?
28
- self.replace s
40
+ s = s[0, static_length] if static_length?
41
+ register_internal_string s
42
+ remove_null_character
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)
56
+ end
57
+
58
+ # Append the given string to CString
59
+ # @param [#to_s] str
60
+ # @return [self]
61
+ def <<(str)
62
+ @string << str.to_s
63
+ remove_null_character
64
+ self
46
65
  end
47
66
 
48
67
  # @return [Integer]
49
68
  def sz
50
- if @static_length.is_a? Integer
51
- @static_length
69
+ if static_length?
70
+ static_length
52
71
  else
53
72
  to_s.size
54
73
  end
55
74
  end
56
75
 
76
+ # Say if a static length is defined
77
+ # @return [Boolean]
78
+ # @since 3.1.6
79
+ def static_length?
80
+ !static_length.nil?
81
+ end
82
+
57
83
  # Populate CString from a human readable string
58
84
  # @param [String] str
59
85
  # @return [self]
@@ -63,8 +89,19 @@ module PacketGen
63
89
 
64
90
  # @return [String]
65
91
  def to_human
66
- idx = self.index(+"\x00".encode(self.encoding)) || self.sz
67
- self[0, idx]
92
+ string
93
+ end
94
+
95
+ private
96
+
97
+ def register_internal_string(str)
98
+ @string = str
99
+ PacketGen.force_binary(@string)
100
+ end
101
+
102
+ def remove_null_character
103
+ idx = string.index(0.chr)
104
+ register_internal_string(string[0, idx]) unless idx.nil?
68
105
  end
69
106
  end
70
107
  end