packetgen 3.1.4 → 3.2.0

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