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
@@ -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