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
@@ -33,7 +33,7 @@ module PacketGen
33
33
  # Ethernet MAC address, as a group of 6 bytes
34
34
  # @author Sylvain Daubert
35
35
  class MacAddr < Types::Fields
36
- include Inspectable
36
+ include Types::Fieldable
37
37
 
38
38
  # @!attribute a0
39
39
  # @return [Integer] first byte from MacAddr
@@ -63,12 +63,9 @@ module PacketGen
63
63
  bytes = str.split(/:/)
64
64
  raise ArgumentError, 'not a MAC address' unless bytes.size == 6
65
65
 
66
- self[:a0].read(bytes[0].to_i(16))
67
- self[:a1].read(bytes[1].to_i(16))
68
- self[:a2].read(bytes[2].to_i(16))
69
- self[:a3].read(bytes[3].to_i(16))
70
- self[:a4].read(bytes[4].to_i(16))
71
- self[:a5].read(bytes[5].to_i(16))
66
+ 6.times do |i|
67
+ self["a#{i}".to_sym].read(bytes[i].to_i(16))
68
+ end
72
69
  self
73
70
  end
74
71
 
@@ -77,7 +74,6 @@ module PacketGen
77
74
  def to_human
78
75
  fields.map { |m| '%02x' % self[m] }.join(':')
79
76
  end
80
- alias format_inspect to_human
81
77
 
82
78
  def ==(other)
83
79
  other.is_a?(self.class) &&
@@ -12,7 +12,7 @@ module PacketGen
12
12
  # @abstract Base class for HTTP headers.
13
13
  # @author Kent 'picat' Gruber
14
14
  class Headers
15
- include Inspectable
15
+ include Types::Fieldable
16
16
 
17
17
  # Underlying Headers data (or nil).
18
18
  # @return [Hash, nil]
@@ -20,7 +20,7 @@ module PacketGen
20
20
  alias to_h data
21
21
 
22
22
  def initialize
23
- @data = nil
23
+ @data = {}
24
24
  end
25
25
 
26
26
  # Populate object from a string or directly from a hash.
@@ -48,7 +48,7 @@ module PacketGen
48
48
 
49
49
  d = []
50
50
  @data.map do |k, v|
51
- d << k + ': ' + v
51
+ d << "#{k}: #{v}"
52
52
  end
53
53
  d.join("\r\n") << "\r\n\r\n"
54
54
  end
@@ -58,7 +58,6 @@ module PacketGen
58
58
  def to_human
59
59
  @data
60
60
  end
61
- alias format_inspect to_human
62
61
 
63
62
  # Read human-readable data to populate header data.
64
63
  # @param [String, Hash] data
@@ -67,23 +67,17 @@ module PacketGen
67
67
  # Read in the HTTP portion of the packet, and parse it.
68
68
  # @return [PacketGen::HTTP::Request]
69
69
  def read(str)
70
- str = str.bytes.map!(&:chr).join unless str.valid_encoding?
71
- vrb = HTTP::VERBS.detect { |verb| str.include?(verb) }
72
- str = vrb + str.split(vrb)[-1]
73
- str = str.split("\n").map(&:chomp)
74
- first_line = str.shift.split
75
- self[:verb].read first_line[0]
76
- self[:path].read first_line[1]
77
- self[:version].read first_line[2]
70
+ lines = lines(str)
71
+ first_line_words = lines.shift.split
72
+ self[:verb].read first_line_words[0]
73
+ self[:path].read first_line_words[1]
74
+ self[:version].read first_line_words[2]
75
+
78
76
  # requests can sometimes have a payload
79
- if (data_index = str.find_index(''))
80
- data = str[data_index + 1..-1].join("\n")
81
- headers = str[0..data_index - 1].join("\n")
82
- else
83
- headers = str.join("\n")
84
- end
77
+ headers, data = headers_and_payload_from_lines(lines)
85
78
  self[:headers].read(headers)
86
- self[:body].read data
79
+ self[:body].read(data)
80
+
87
81
  self
88
82
  end
89
83
 
@@ -94,8 +88,29 @@ module PacketGen
94
88
  raise FormatError, 'Missing #path.' if self.path.empty?
95
89
  raise FormatError, 'Missing #version.' if self.version.empty?
96
90
 
97
- str = +''
98
- str << self[:verb] << ' ' << self[:path] << ' ' << self[:version] << "\r\n" << self[:headers].to_s << self[:body]
91
+ "#{self.verb.dup} #{self.path} #{self.version}\r\n#{self[:headers]}#{self.body}"
92
+ end
93
+
94
+ private
95
+
96
+ # @todo check verb is correct or raise a ParseError
97
+ def lines(str)
98
+ str = str.bytes.map!(&:chr).join unless str.valid_encoding?
99
+ # vrb = HTTP::VERBS.detect { |verb| str.include?(verb) }
100
+
101
+ str.split("\r\n").map(&:chomp)
102
+ end
103
+
104
+ def headers_and_payload_from_lines(lines)
105
+ if (data_index = lines.find_index(''))
106
+ data = lines[data_index + 1..-1].join("\n")
107
+ headers = lines[0..data_index - 1].join("\n")
108
+ else
109
+ headers = lines.join("\n")
110
+ data = nil
111
+ end
112
+
113
+ [headers, data]
99
114
  end
100
115
  end
101
116
  end
@@ -104,7 +104,7 @@ module PacketGen
104
104
  raise FormatError, 'Missing #version.' if self.version.empty?
105
105
 
106
106
  str = +''
107
- str << self[:version] << ' ' << self[:status_code] << ' ' << self[:status_mesg] << "\r\n"
107
+ str << self.version << ' ' << self.status_code << ' ' << self.status_mesg << "\r\n"
108
108
  str << self[:headers].to_s if self[:headers].given?
109
109
  str << self.body
110
110
  end
@@ -16,7 +16,7 @@ module PacketGen
16
16
  VERBS = %w[GET HEAD POST PUT DELETE CONNECT OPTIONS TRACE PATCH].freeze
17
17
 
18
18
  # Identifiable HTTP request regular expression.
19
- REQUEST_REGEX = Regexp.new('(' + VERBS.dup.join('|') + ')' + '\s+\S+\s+HTTP/1.1')
19
+ REQUEST_REGEX = Regexp.new("(#{VERBS.dup.join('|')})\\s+\\S+\\s+HTTP/1.1")
20
20
  end
21
21
  end
22
22
  end
@@ -40,6 +40,8 @@ module PacketGen
40
40
  # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
41
41
  # @author Sylvain Daubert
42
42
  class GroupRecord < Types::Fields
43
+ include Types::Fieldable
44
+
43
45
  # Known record types
44
46
  RECORD_TYPES = {
45
47
  'MODE_IS_INCLUDE' => 1,
@@ -131,7 +131,7 @@ module PacketGen
131
131
  # @since 2.2.0
132
132
  # @return [Types::String]
133
133
  define_field :options, Options, optional: ->(h) { h.ihl > 5 },
134
- builder: ->(h, t) { t.new(length_from: -> { (h.ihl - 5) * 4 }) }
134
+ builder: ->(h, t) { t.new(length_from: -> { (h.ihl - 5) * 4 }) }
135
135
  # @!attribute body
136
136
  # @return [Types::String,Header::Base]
137
137
  define_field :body, Types::String
@@ -166,7 +166,7 @@ module PacketGen
166
166
 
167
167
  data = hdr.to_s
168
168
  data << "\x00" if data.size.odd?
169
- sum = data.unpack('n*').reduce(:+)
169
+ sum = data.unpack('n*').sum
170
170
 
171
171
  hdr.checksum = old_checksum if old_checksum
172
172
 
@@ -191,17 +191,10 @@ module PacketGen
191
191
  # @return [Integer]
192
192
  def calc_checksum
193
193
  # Checksum is only on header, so cannot use IP.sum16,
194
- # which also calcultes checksum on #body.
195
- checksum = (self[:u8].to_i << 8) | self.tos
196
- checksum += self.length
197
- checksum += self.id
198
- checksum += self.frag
199
- checksum += (self.ttl << 8) | self.protocol
200
- checksum += (self[:src].to_i >> 16)
201
- checksum += (self[:src].to_i & 0xffff)
202
- checksum += self[:dst].to_i >> 16
203
- checksum += self[:dst].to_i & 0xffff
204
- options.to_s.unpack('n*').each { |x| checksum += x }
194
+ # which also calculates checksum on #body.
195
+ nb_words = ihl * 2
196
+ self.checksum = 0
197
+ checksum = to_s.unpack("n#{nb_words}").sum
205
198
  self[:checksum].value = IP.reduce_checksum(checksum)
206
199
  end
207
200
 
@@ -238,20 +231,9 @@ module PacketGen
238
231
  super do |attr|
239
232
  case attr
240
233
  when :u8
241
- shift = Inspect.shift_level
242
- str = Inspect.inspect_attribute(attr, self[attr])
243
- str << shift << Inspect::FMT_ATTR % ['', 'version', version]
244
- str << shift << Inspect::FMT_ATTR % ['', 'ihl', ihl]
234
+ inspect_u8
245
235
  when :frag
246
- shift = Inspect.shift_level
247
- str = Inspect.inspect_attribute(attr, self[attr])
248
- flags = flag_rsv? ? %w[RSV] : []
249
- flags << 'DF' if flag_df?
250
- flags << 'MF' if flag_mf?
251
- flags_str = flags.empty? ? 'none' : flags.join(',')
252
- str << shift << Inspect::FMT_ATTR % ['', 'flags', flags_str]
253
- foff = Inspect.int_dec_hex(fragment_offset, 4)
254
- str << shift << Inspect::FMT_ATTR % ['', 'frag_offset', foff]
236
+ inspect_frag
255
237
  end
256
238
  end
257
239
  end
@@ -276,6 +258,25 @@ module PacketGen
276
258
  self[:src], self[:dst] = self[:dst], self[:src]
277
259
  self
278
260
  end
261
+
262
+ private
263
+
264
+ def inspect_u8
265
+ shift = Inspect.shift_level
266
+ str = Inspect.inspect_attribute(:u8, self[:u8])
267
+ str << shift << Inspect::FMT_ATTR % ['', 'version', version]
268
+ str << shift << Inspect::FMT_ATTR % ['', 'ihl', ihl]
269
+ end
270
+
271
+ def inspect_frag
272
+ shift = Inspect.shift_level
273
+ str = Inspect.inspect_attribute(:frag, self[:frag])
274
+ flags = %i[rsv df mf].select { |flag| send("flag_#{flag}?") }.map(&:upcase)
275
+ flags_str = flags.empty? ? 'none' : flags.join(',')
276
+ str << shift << Inspect::FMT_ATTR % ['', 'flags', flags_str]
277
+ foff = Inspect.int_dec_hex(fragment_offset, 4)
278
+ str << shift << Inspect::FMT_ATTR % ['', 'frag_offset', foff]
279
+ end
279
280
  end
280
281
 
281
282
  self.add_class IP
@@ -11,7 +11,7 @@ module PacketGen
11
11
  # IP address, as a group of 4 bytes
12
12
  # @author Sylvain Daubert
13
13
  class Addr < Types::Fields
14
- include Inspectable
14
+ include Types::Fieldable
15
15
 
16
16
  # @!attribute a1
17
17
  # @return [Integer] IP address first byte
@@ -26,7 +26,7 @@ module PacketGen
26
26
  # @return [Integer] IP address fourth byte
27
27
  define_field :a4, Types::Int8
28
28
 
29
- IPV4_ADDR_REGEX = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/
29
+ IPV4_ADDR_REGEX = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/.freeze
30
30
 
31
31
  # Read a dotted address
32
32
  # @param [String] str
@@ -49,7 +49,6 @@ module PacketGen
49
49
  def to_human
50
50
  fields.map { |f| self[f].to_i.to_s }.join('.')
51
51
  end
52
- alias format_inspect to_human
53
52
 
54
53
  # Addr as an integer
55
54
  # @return [Integer]
@@ -26,6 +26,8 @@ module PacketGen
26
26
  # Base class for IP options
27
27
  # @author Sylvain Daubert
28
28
  class Option < Types::Fields
29
+ include Types::Fieldable
30
+
29
31
  # EOL option type
30
32
  EOL_TYPE = 0x00
31
33
  # NOP option type
@@ -115,10 +117,8 @@ module PacketGen
115
117
  # Get a human readable string
116
118
  # @return [String]
117
119
  def to_human
118
- str = self.class == Option ? +"unk-#{type}" : self.class.to_s.sub(/.*::/, '')
119
- if respond_to?(:length) && (length > 2) && !self[:data].to_s.empty?
120
- str << ":#{self[:data].to_s.inspect}"
121
- end
120
+ str = self.instance_of?(Option) ? +"unk-#{type}" : self.class.to_s.sub(/.*::/, '')
121
+ str << ":#{self[:data].to_s.inspect}" if respond_to?(:length) && (length > 2) && !self[:data].to_s.empty?
122
122
  str
123
123
  end
124
124
  end
@@ -14,7 +14,7 @@ module PacketGen
14
14
  # IPv6 address, as a group of 8 2-byte words
15
15
  # @author Sylvain Daubert
16
16
  class Addr < Types::Fields
17
- include Inspectable
17
+ include Types::Fieldable
18
18
 
19
19
  # @!attribute a1
20
20
  # 1st 2-byte word of IPv6 address
@@ -75,7 +75,6 @@ module PacketGen
75
75
  def to_human
76
76
  IPAddr.new(to_a.map { |a| a.to_i.to_s(16) }.join(':')).to_s
77
77
  end
78
- alias format_inspect to_human
79
78
 
80
79
  # Return an array of address 16-bit words
81
80
  # @return [Array<Integer>]
@@ -52,6 +52,8 @@ module PacketGen
52
52
  # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
53
53
  # @author Sylvain Daubert
54
54
  class McastAddressRecord < Types::Fields
55
+ include Types::Fieldable
56
+
55
57
  # Known record types
56
58
  RECORD_TYPES = IGMPv3::GroupRecord::RECORD_TYPES
57
59
 
@@ -14,6 +14,8 @@ module PacketGen
14
14
  # * and {#advertising_router}.
15
15
  # @author Sylvain Daubert
16
16
  class LSR < Types::Fields
17
+ include Types::Fieldable
18
+
17
19
  # @!attribute type
18
20
  # The type of the LSA to request.
19
21
  # @return [Integer]
@@ -23,6 +23,8 @@ module PacketGen
23
23
  # LSA router payload}.
24
24
  # @author Sylvain Daubert
25
25
  class TosMetric < Types::Fields
26
+ include Types::Fieldable
27
+
26
28
  # @!attribute tos
27
29
  # 8-bit IP Type of Service that this metric refers to.
28
30
  # @return [Integer]
@@ -52,6 +54,8 @@ module PacketGen
52
54
  # This class handles links in a {LSARouter LSA router payload}.
53
55
  # @author Sylvain Daubert
54
56
  class Link < Types::Fields
57
+ include Types::Fieldable
58
+
55
59
  # @!attribute id
56
60
  # @return [IP::Addr]
57
61
  define_field :id, IP::Addr
@@ -136,6 +140,8 @@ module PacketGen
136
140
  # This class handles external links in {LSAASExternal LSA AS-External payloads}.
137
141
  # @author Sylvain Daubert
138
142
  class External < Types::Fields
143
+ include Types::Fieldable
144
+
139
145
  # @!attribute u8
140
146
  # @return [Integer]
141
147
  define_field :u8, Types::Int8
@@ -29,6 +29,8 @@ module PacketGen
29
29
  # is also a base class for different LSA class, as {LSARouter}.
30
30
  # @author Sylvain Daubert
31
31
  class LSAHeader < Types::Fields
32
+ include Types::Fieldable
33
+
32
34
  # LSA Types
33
35
  TYPES = {
34
36
  'Router' => 1,
@@ -75,7 +77,6 @@ module PacketGen
75
77
  # Compute and set Fletcher-16 checksum on LSA
76
78
  # @return [Integer]
77
79
  def calc_checksum
78
- self.checksum = 0
79
80
  bytes = to_s[2..-1].unpack('C*')
80
81
 
81
82
  c0 = c1 = 0
@@ -17,6 +17,8 @@ module PacketGen
17
17
  # array consumes ((PrefixLength + 31) / 32) 32-bit words.
18
18
  # @author Sylvain Daubert
19
19
  class IPv6Prefix < Types::Fields
20
+ include Types::Fieldable
21
+
20
22
  # @!attribute length
21
23
  # Prefix length, in bits
22
24
  # @return [Integer]
@@ -27,6 +27,8 @@ module PacketGen
27
27
  # * and a 32-bit {#advertising_router} field.
28
28
  # @author Sylvain Daubert
29
29
  class LSR < Types::Fields
30
+ include Types::Fieldable
31
+
30
32
  # @!attribute reserved
31
33
  # reserved field.
32
34
  # @return [Integer]
@@ -11,6 +11,8 @@ module PacketGen
11
11
  # This class handles links in a {LSARouter OSPFv3 LSA router payload}.
12
12
  # @author Sylvain Daubert
13
13
  class Link < Types::Fields
14
+ include Types::Fieldable
15
+
14
16
  # @!attribute type
15
17
  # @return [Integer]
16
18
  define_field :type, Types::Int8
@@ -29,6 +29,8 @@ module PacketGen
29
29
  # But this class is also a base class for different LSA class, as {LSARouter}.
30
30
  # @author Sylvain Daubert
31
31
  class LSAHeader < Types::Fields
32
+ include Types::Fieldable
33
+
32
34
  # LSA known types
33
35
  TYPES = {
34
36
  'Router' => 0x2001,
@@ -76,7 +78,6 @@ module PacketGen
76
78
  # Compute and set Fletcher-16 checksum on LSA
77
79
  # @return [Integer]
78
80
  def calc_checksum
79
- self.checksum = 0
80
81
  bytes = to_s[2..-1].unpack('C*')
81
82
 
82
83
  c0 = c1 = 0
@@ -246,7 +246,8 @@ module PacketGen
246
246
  end
247
247
 
248
248
  sequence :message,
249
- content: [integer(:version, value: 'v2c',
249
+ content: [integer(:version,
250
+ value: 'v2c',
250
251
  enum: { 'v1' => 0, 'v2c' => 1, 'v2' => 2, 'v3' => 3 }),
251
252
  octet_string(:community, value: 'public'),
252
253
  model(:data, PDUs)]
@@ -295,7 +296,7 @@ module PacketGen
295
296
  begin
296
297
  str << Inspect.inspect_body(self[:message].to_der, 'ASN.1 DER')
297
298
  rescue StandardError => e
298
- raise unless e.message =~ /TAG.*not handled/
299
+ raise unless e.message.match?(/TAG.*not handled/)
299
300
  end
300
301
  str
301
302
  end
@@ -11,6 +11,8 @@ module PacketGen
11
11
  # Base class to describe a TCP option
12
12
  # @author Sylvain Daubert
13
13
  class Option < Types::Fields
14
+ include Types::Fieldable
15
+
14
16
  # EOL option value
15
17
  EOL_KIND = 0
16
18
  # NOP option value
@@ -91,15 +93,15 @@ module PacketGen
91
93
  # Setter for value attribute
92
94
  # @param[String,Integer]
93
95
  # @return [String, Integer]
94
- def value=(v)
96
+ def value=(val)
95
97
  case self[:value]
96
98
  when Types::Int
97
99
  self.length = 2 + self[:value].sz
98
- when String
99
- self.length = 2 + Types::String.new.read(v).sz
100
+ when Types::String
101
+ self.length = 2 + Types::String.new.read(val).sz
100
102
  end
101
- self[:value].read v
102
- v
103
+ self[:value].read val
104
+ val
103
105
  end
104
106
 
105
107
  # Get binary string
@@ -112,7 +114,7 @@ module PacketGen
112
114
  # Get option as a human readable string
113
115
  # @return [String]
114
116
  def to_human
115
- str = self.class == Option ? +"unk-#{kind}" : self.class.to_s.sub(/.*::/, '')
117
+ str = self.instance_of?(Option) ? +"unk-#{kind}" : self.class.to_s.sub(/.*::/, '')
116
118
  str << ":#{self[:value].to_s.inspect}" if (length > 2) && !self[:value].to_s.empty?
117
119
  str
118
120
  end