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
@@ -43,28 +43,32 @@ module PacketGen
43
43
  self << Types::IntString.new
44
44
  end
45
45
 
46
+ # Clear name
47
+ # @return [void]
48
+ def clear
49
+ super
50
+ @pointer = nil
51
+ @pointer_name = nil
52
+ end
53
+
46
54
  # Read a sequence of label from a string
47
55
  # @param [String] str binary string
48
56
  # @return [Name] self
49
57
  def read(str)
50
- @pointer = nil
51
- @pointer_name = nil
52
58
  clear
53
59
  return self if str.nil?
54
60
 
55
61
  PacketGen.force_binary str
56
62
  start = 0
57
63
  loop do
58
- index = str[start, 2].unpack('n').first
64
+ index = str[start, 2].unpack1('n')
59
65
  if pointer? index
60
66
  # Pointer on another label
61
67
  @pointer = str[start, 2]
62
68
  break
63
69
  else
64
- label = Types::IntString.new
65
- label.read(str[start..-1])
70
+ label = add_label_from(str[start..-1])
66
71
  start += label.sz
67
- self << label
68
72
  break if label.length.zero? || str[start..-1].length.zero?
69
73
  end
70
74
  end
@@ -101,7 +105,7 @@ module PacketGen
101
105
  return nil unless @pointer
102
106
  return @pointer_name if @pointer_name
103
107
 
104
- index = @pointer.unpack('n').first
108
+ index = @pointer.unpack1('n')
105
109
  mask = ~POINTER_MASK & 0xffff
106
110
  ptr = index & mask
107
111
  name = Name.new
@@ -112,6 +116,13 @@ module PacketGen
112
116
  def record_from_hash(_hsh)
113
117
  raise NotImplementedError, "not supported by #{self.class}"
114
118
  end
119
+
120
+ def add_label_from(str)
121
+ label = Types::IntString.new
122
+ label.read(str)
123
+ self << label
124
+ label
125
+ end
115
126
  end
116
127
  end
117
128
  end
@@ -35,7 +35,7 @@ module PacketGen
35
35
  while !str.empty? && (self.size < @counter.to_i)
36
36
  question = Question.new(@dns).read(str)
37
37
  str.slice!(0, question.sz)
38
- self.push question
38
+ push question
39
39
  end
40
40
  self
41
41
  end
@@ -11,6 +11,8 @@ module PacketGen
11
11
  # DNS Question
12
12
  # @author Sylvain Daubert
13
13
  class Question < Types::Fields
14
+ include Types::Fieldable
15
+
14
16
  # Ressource Record types
15
17
  TYPES = {
16
18
  'A' => 1,
@@ -28,7 +28,7 @@ module PacketGen
28
28
  define_field :dlt, Types::Int32le
29
29
  # @!attribute ppi_fields
30
30
  # @return [Type::String] concatenation of PPI fields
31
- define_field :ppi_fields, Types::String
31
+ define_field :ppi_fields, Types::String, builder: ->(h, t) { t.new(length_from: -> { h.length - 8 }) }
32
32
  # @!attribute body
33
33
  # @return [Type::String]
34
34
  define_field :body, Types::String
@@ -36,21 +36,6 @@ module PacketGen
36
36
  # @return [Boolean] align flag from {#flags} attribute
37
37
  define_bit_fields_on :flags, :reserved, 7, :align
38
38
 
39
- # @param [String] str
40
- # @return [PPI] self
41
- def read(str)
42
- return self if str.nil?
43
-
44
- force_binary str
45
- self[:version].read str[0, 1]
46
- self[:flags].read str[1, 1]
47
- self[:length].read str[2, 2]
48
- self[:dlt].read str[4, 4]
49
- self[:ppi_fields].read str[8, length - 8]
50
- self[:body].read str[length, str.size]
51
- self
52
- end
53
-
54
39
  # Check version field
55
40
  # @see [Base#parse?]
56
41
  def parse?
@@ -91,26 +76,11 @@ module PacketGen
91
76
  define_field :present_flags, Types::Int32le
92
77
  # @!attribute radio_fields
93
78
  # @return [Type::String] concatenation of RadioTap fields
94
- define_field :radio_fields, Types::String
79
+ define_field :radio_fields, Types::String, builder: ->(h, t) { t.new(length_from: -> { h.length - 8 }) }
95
80
  # @!attribute body
96
81
  # @return [Type::String]
97
82
  define_field :body, Types::String
98
83
 
99
- # @param [String] str
100
- # @return [RadioTap] self
101
- def read(str)
102
- return self if str.nil?
103
-
104
- force_binary str
105
- self[:version].read str[0, 1]
106
- self[:pad].read str[1, 1]
107
- self[:length].read str[2, 2]
108
- self[:present_flags].read str[4, 4]
109
- self[:radio_fields].read str[8, length - 8]
110
- self[:body].read str[length, str.size]
111
- self
112
- end
113
-
114
84
  # Check version field
115
85
  # @see [Base#parse?]
116
86
  def parse?
@@ -310,7 +280,7 @@ module PacketGen
310
280
  def read(str)
311
281
  fcs = Dot11.fcs?
312
282
 
313
- if self.class == Dot11
283
+ if self.instance_of? Dot11
314
284
  return self if str.nil?
315
285
 
316
286
  force_binary str
@@ -353,7 +323,7 @@ module PacketGen
353
323
 
354
324
  # @return [String]
355
325
  def inspect
356
- str = if self.class == Dot11
326
+ str = if self.instance_of? Dot11
357
327
  Inspect.dashed_line("#{self.class} #{human_type}", 1)
358
328
  elsif self.respond_to? :human_subtype
359
329
  Inspect.dashed_line("#{self.class} #{human_subtype}", 1)
@@ -390,27 +360,44 @@ module PacketGen
390
360
 
391
361
  private
392
362
 
393
- def define_applicable_fields
363
+ def remove_from_applicable_fields(fields)
364
+ fields = [fields] unless fields.is_a? Array
365
+ @applicable_fields -= fields
366
+ end
367
+
368
+ def handle_mac4
394
369
  if to_ds? && from_ds?
395
370
  @applicable_fields[6, 0] = :mac4 unless @applicable_fields.include? :mac4
396
371
  else
397
- @applicable_fields -= %i[mac4]
372
+ remove_from_applicable_fields :mac4
398
373
  end
374
+ end
375
+
376
+ def handle_ht_ctrl
399
377
  if order?
400
378
  unless @applicable_fields.include? :ht_ctrl
401
379
  idx = @applicable_fields.index(:body)
402
380
  @applicable_fields[idx, 0] = :ht_ctrl
403
381
  end
404
382
  else
405
- @applicable_fields -= %i[ht_ctrl]
383
+ remove_from_applicable_fields %i[ht_ctrl]
406
384
  end
385
+ end
386
+
387
+ def handle_fcs
407
388
  if Dot11.fcs?
408
389
  @applicable_fields << :fcs unless @applicable_fields.include? :fcs
409
390
  else
410
- @applicable_fields -= %i[fcs]
391
+ remove_from_applicable_fields :fcs
411
392
  end
412
393
  end
413
394
 
395
+ def define_applicable_fields
396
+ handle_mac4
397
+ handle_ht_ctrl
398
+ handle_fcs
399
+ end
400
+
414
401
  def private_read(str, fcs)
415
402
  self[:frame_ctrl].read str[0, 2]
416
403
  define_applicable_fields
@@ -44,22 +44,22 @@ module PacketGen
44
44
  case ds
45
45
  when 0
46
46
  # MAC1: RA/DA, MAC2: TA/SA
47
- self[:mac1], self[:mac2] = self[:mac2], self[:mac1]
47
+ invert_mac :mac1, :mac2
48
48
  when 1
49
49
  # MAC1: RA/BSSID, MAC2: TA/SA, MAC3: DA
50
- self[:mac2], self[:mac1] = self[:mac1], self[:mac2]
50
+ invert_mac :mac1, :mac2
51
51
  self.to_ds = false
52
52
  self.from_ds = true
53
53
  when 2
54
54
  # MAC1: RA/DA, MAC2: BSSID, MAC3: SA or BSSID
55
- self[:mac1], self[:mac2] = self[:mac2], self[:mac1]
55
+ invert_mac :mac1, :mac2
56
56
  self.to_ds = true
57
57
  self.from_ds = false
58
58
  when 3
59
59
  # MAC1: RA, MAC2: TA
60
- self[:mac1], self[:mac2] = self[:mac2], self[:mac1]
60
+ invert_mac :mac1, :mac2
61
61
  # MAC3: DA, MAC4: SA
62
- self[:mac4], self[:mac3] = self[:mac3], self[:mac4]
62
+ invert_mac :mac3, :mac4
63
63
  end
64
64
  self
65
65
  end
@@ -67,59 +67,49 @@ module PacketGen
67
67
  # Get destination MAC address
68
68
  # @return [String]
69
69
  def dst
70
- ds = frame_ctrl & 3
71
- case ds
72
- when 0, 2
73
- self.mac1
74
- when 1, 3
75
- self.mac3
76
- end
70
+ _src_mac, dst_mac = src_dst_from_mac
71
+ self.send(dst_mac)
77
72
  end
78
73
 
79
74
  # Set destination MAC address
80
75
  # @param [String] mac MAC address to set
81
76
  # @return [String]
82
77
  def dst=(mac)
83
- ds = frame_ctrl & 3
84
- case ds
85
- when 0, 2
86
- self.mac1 = mac
87
- when 1, 3
88
- self.mac3 = mac
89
- end
78
+ _src_mac, dst_mac = src_dst_from_mac
79
+ self.send("#{dst_mac}=", mac)
90
80
  end
91
81
 
92
82
  # Get source MAC address
93
83
  # @return [String]
94
84
  def src
95
- ds = frame_ctrl & 3
96
- case ds
97
- when 0, 1
98
- self.mac2
99
- when 2
100
- self.mac3
101
- when 3
102
- self.mac4
103
- end
85
+ src_mac, = src_dst_from_mac
86
+ self.send(src_mac)
104
87
  end
105
88
 
106
89
  # Set source MAC address
107
90
  # @param [String] mac MAC address to set
108
91
  # @return [String]
109
92
  def src=(mac)
93
+ src_mac, = src_dst_from_mac
94
+ self.send("#{src_mac}=", mac)
95
+ end
96
+
97
+ private
98
+
99
+ def src_dst_from_mac
110
100
  ds = frame_ctrl & 3
111
101
  case ds
112
- when 0, 1
113
- self.mac2 = mac
102
+ when 0
103
+ %i[mac2 mac1]
104
+ when 1
105
+ %i[mac2 mac3]
114
106
  when 2
115
- self.mac3 = mac
107
+ %i[mac3 mac1]
116
108
  when 3
117
- self.mac4 = mac
109
+ %i[mac4 mac3]
118
110
  end
119
111
  end
120
112
 
121
- private
122
-
123
113
  def define_applicable_fields
124
114
  super
125
115
  if (subtype >= 8) && !@applicable_fields.include?(:qos_ctrl)
@@ -135,6 +125,10 @@ module PacketGen
135
125
  @applicable_fields -= %i[qos_ctrl]
136
126
  end
137
127
  end
128
+
129
+ def invert_mac(mac1, mac2)
130
+ self[mac1], self[mac2] = self[mac2], self[mac1]
131
+ end
138
132
  end
139
133
  end
140
134
  end
@@ -45,20 +45,7 @@ module PacketGen
45
45
  define_field :length, Types::Int16
46
46
  # @!attribute body
47
47
  # @return [Types::String,Header::Base]
48
- define_field :body, Types::String
49
-
50
- # Populate object from string
51
- # @param [String] str
52
- # @return [self]
53
- def read(str)
54
- return self if str.nil?
55
-
56
- self[:version].read(str[0, 1])
57
- self[:type].read(str[1, 1])
58
- self[:length].read(str[2, 2])
59
- self[:body].read(str[4, self.length])
60
- self
61
- end
48
+ define_field :body, Types::String, builder: ->(h, t) { t.new(length_from: h[:length]) }
62
49
 
63
50
  # Get human readable type
64
51
  # @return [String]
@@ -95,7 +95,8 @@ module PacketGen
95
95
  # This field is present only for Request or Response packets,
96
96
  # with type different from Expanded Types (254).
97
97
  # @return [Integer] 8-bit request or response type
98
- define_field :type, Types::Int8Enum, enum: TYPES,
98
+ define_field :type, Types::Int8Enum,
99
+ enum: TYPES,
99
100
  optional: ->(eap) { eap.type? }
100
101
 
101
102
  # @!attribute vendor_id
@@ -131,22 +132,18 @@ module PacketGen
131
132
  # may be determined
132
133
  def read(str)
133
134
  super str
134
- return self unless self.class == EAP
135
-
136
- if type?
137
- obj = case self.type
138
- when 4
139
- EAP::MD5.new.read(str)
140
- when 13
141
- EAP::TLS.new.read(str)
142
- when 21
143
- EAP::TTLS.new.read(str)
144
- when 43
145
- EAP::FAST.new.read(str)
146
- else
147
- self
148
- end
149
- obj
135
+ return self unless self.instance_of?(EAP)
136
+ return self unless type?
137
+
138
+ case self.type
139
+ when 4
140
+ EAP::MD5.new.read(str)
141
+ when 13
142
+ EAP::TLS.new.read(str)
143
+ when 21
144
+ EAP::TTLS.new.read(str)
145
+ when 43
146
+ EAP::FAST.new.read(str)
150
147
  else
151
148
  self
152
149
  end
@@ -33,6 +33,8 @@ 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 Types::Fieldable
37
+
36
38
  # @!attribute a0
37
39
  # @return [Integer] first byte from MacAddr
38
40
  define_field :a0, Types::Int8
@@ -61,12 +63,9 @@ module PacketGen
61
63
  bytes = str.split(/:/)
62
64
  raise ArgumentError, 'not a MAC address' unless bytes.size == 6
63
65
 
64
- self[:a0].read(bytes[0].to_i(16))
65
- self[:a1].read(bytes[1].to_i(16))
66
- self[:a2].read(bytes[2].to_i(16))
67
- self[:a3].read(bytes[3].to_i(16))
68
- self[:a4].read(bytes[4].to_i(16))
69
- 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
70
69
  self
71
70
  end
72
71
 
@@ -12,13 +12,15 @@ module PacketGen
12
12
  # @abstract Base class for HTTP headers.
13
13
  # @author Kent 'picat' Gruber
14
14
  class Headers
15
+ include Types::Fieldable
16
+
15
17
  # Underlying Headers data (or nil).
16
18
  # @return [Hash, nil]
17
19
  attr_reader :data
18
20
  alias to_h data
19
21
 
20
22
  def initialize
21
- @data = nil
23
+ @data = {}
22
24
  end
23
25
 
24
26
  # Populate object from a string or directly from a hash.
@@ -46,7 +48,7 @@ module PacketGen
46
48
 
47
49
  d = []
48
50
  @data.map do |k, v|
49
- d << k + ': ' + v
51
+ d << "#{k}: #{v}"
50
52
  end
51
53
  d.join("\r\n") << "\r\n\r\n"
52
54
  end
@@ -67,26 +67,24 @@ 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
 
84
+ def parse?
85
+ VERBS.include?(self.verb) && self.version.start_with?('HTTP/1.')
86
+ end
87
+
90
88
  # String representation of data.
91
89
  # @return [String]
92
90
  def to_s
@@ -94,13 +92,34 @@ module PacketGen
94
92
  raise FormatError, 'Missing #path.' if self.path.empty?
95
93
  raise FormatError, 'Missing #version.' if self.version.empty?
96
94
 
97
- str = +''
98
- str << self[:verb] << ' ' << self[:path] << ' ' << self[:version] << "\r\n" << self[:headers].to_s << self[:body]
95
+ "#{self.verb.dup} #{self.path} #{self.version}\r\n#{self[:headers]}#{self.body}"
96
+ end
97
+
98
+ private
99
+
100
+ # @todo check verb is correct or raise a ParseError
101
+ def lines(str)
102
+ str = str.bytes.map!(&:chr).join unless str.valid_encoding?
103
+ # vrb = HTTP::VERBS.detect { |verb| str.include?(verb) }
104
+
105
+ str.split("\r\n").map(&:chomp)
106
+ end
107
+
108
+ def headers_and_payload_from_lines(lines)
109
+ if (data_index = lines.find_index(''))
110
+ data = lines[data_index + 1..-1].join("\n")
111
+ headers = lines[0..data_index - 1].join("\n")
112
+ else
113
+ headers = lines.join("\n")
114
+ data = nil
115
+ end
116
+
117
+ [headers, data]
99
118
  end
100
119
  end
101
120
  end
102
121
 
103
122
  self.add_class HTTP::Request
104
- TCP.bind HTTP::Request, body: ->(b) { HTTP::REQUEST_REGEX =~ b.chars.select(&:valid_encoding?).join }
123
+ TCP.bind HTTP::Request, body: ->(b) { b.nil? ? '' : HTTP::REQUEST_REGEX =~ b }
105
124
  end
106
125
  end