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
@@ -87,15 +87,21 @@ module PacketGen
87
87
  end
88
88
  unless headers.empty?
89
89
  first_line = headers.shift.split
90
- self[:version].read first_line[0]
91
- self[:status_code].read first_line[1]
92
- self[:status_mesg].read first_line[2..-1].join(' ')
90
+ if first_line.size >= 3
91
+ self[:version].read first_line[0]
92
+ self[:status_code].read first_line[1]
93
+ self[:status_mesg].read first_line[2..-1].join(' ')
94
+ end
93
95
  self[:headers].read(headers.join("\n"))
94
96
  end
95
97
  self[:body].read data.join("\n")
96
98
  self
97
99
  end
98
100
 
101
+ def parse?
102
+ version.start_with?('HTTP/1.')
103
+ end
104
+
99
105
  # String representation of data.
100
106
  # @return [String]
101
107
  def to_s
@@ -104,7 +110,7 @@ module PacketGen
104
110
  raise FormatError, 'Missing #version.' if self.version.empty?
105
111
 
106
112
  str = +''
107
- str << self[:version] << ' ' << self[:status_code] << ' ' << self[:status_mesg] << "\r\n"
113
+ str << self.version << ' ' << self.status_code << ' ' << self.status_mesg << "\r\n"
108
114
  str << self[:headers].to_s if self[:headers].given?
109
115
  str << self.body
110
116
  end
@@ -112,6 +118,6 @@ module PacketGen
112
118
  end
113
119
 
114
120
  self.add_class HTTP::Response
115
- TCP.bind HTTP::Response, body: ->(b) { %r[^HTTP/1\.1\s\d{3,}\s.+] =~ b.chars.select(&:valid_encoding?).join }
121
+ TCP.bind HTTP::Response, body: ->(b) { b.nil? ? '' : %r[^HTTP/1\.1\s\d{3,}\s.+] =~ b }
116
122
  end
117
123
  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,6 +11,8 @@ 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 Types::Fieldable
15
+
14
16
  # @!attribute a1
15
17
  # @return [Integer] IP address first byte
16
18
  define_field :a1, Types::Int8
@@ -24,7 +26,7 @@ module PacketGen
24
26
  # @return [Integer] IP address fourth byte
25
27
  define_field :a4, Types::Int8
26
28
 
27
- 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
28
30
 
29
31
  # Read a dotted address
30
32
  # @param [String] str
@@ -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,6 +14,8 @@ 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 Types::Fieldable
18
+
17
19
  # @!attribute a1
18
20
  # 1st 2-byte word of IPv6 address
19
21
  # @return [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
@@ -223,9 +229,13 @@ module PacketGen
223
229
 
224
230
  def get_lsa_class_by_human_type(htype)
225
231
  klassname = "LSA#{htype.to_s.delete('-')}"
226
- if OSPFv2.const_defined? klassname
227
- OSPFv2.const_get klassname
228
- else
232
+ begin
233
+ if OSPFv2.const_defined? klassname
234
+ OSPFv2.const_get klassname
235
+ else
236
+ LSA
237
+ end
238
+ rescue NameError
229
239
  LSA
230
240
  end
231
241
  end
@@ -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
@@ -216,9 +218,13 @@ module PacketGen
216
218
 
217
219
  def get_lsa_class_by_human_type(htype)
218
220
  klassname = "LSA#{htype.to_s.delete('-')}"
219
- if OSPFv3.const_defined? klassname
220
- OSPFv3.const_get klassname
221
- else
221
+ begin
222
+ if OSPFv3.const_defined? klassname
223
+ OSPFv3.const_get klassname
224
+ else
225
+ LSA
226
+ end
227
+ rescue NameError
222
228
  LSA
223
229
  end
224
230
  end
@@ -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
@@ -121,7 +121,7 @@ module PacketGen
121
121
  # @!attribute options
122
122
  # TCP options
123
123
  # @return [Options]
124
- define_field :options, TCP::Options
124
+ define_field :options, TCP::Options, builder: ->(h, t) { t.new(length_from: -> { h.data_offset > 5 ? (h.data_offset - 5) * 4 : 0 }) }
125
125
  # @!attribute body
126
126
  # @return [Types::String,Header::Base]
127
127
  define_field :body, Types::String
@@ -189,25 +189,6 @@ module PacketGen
189
189
  # @return [Boolean] 1-bit FIN flag
190
190
  define_bit_fields_on :u16, :_, 7, :flag_ns, :flag_cwr, :flag_ece, :flag_urg,
191
191
  :flag_ack, :flag_psh, :flag_rst, :flag_syn, :flag_fin
192
- # Read a TCP header from a string
193
- # @param [String] str binary string
194
- # @return [self]
195
- def read(str)
196
- return self if str.nil?
197
-
198
- force_binary str
199
- self[:sport].read str[0, 2]
200
- self[:dport].read str[2, 2]
201
- self[:seqnum].read str[4, 4]
202
- self[:acknum].read str[8, 4]
203
- self[:u16].read str[12, 2]
204
- self[:window].read str[14, 2]
205
- self[:checksum].read str[16, 2]
206
- self[:urg_pointer].read str[18, 2]
207
- self[:options].read str[20, (self.data_offset - 5) * 4] if self.data_offset > 5
208
- self[:body].read str[self.data_offset * 4..-1]
209
- self
210
- end
211
192
 
212
193
  # Compute checksum and set +checksum+ field
213
194
  # @return [Integer]
@@ -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
@@ -68,23 +68,7 @@ module PacketGen
68
68
  # @return [String]
69
69
  def self.inspect_attribute(attr, value, level=1)
70
70
  type = value.class.to_s.sub(/.*::/, '')
71
- val = case value
72
- when Types::Enum
73
- enum_human_hex(value.to_human, value.to_i, value.sz * 2)
74
- when Types::Int
75
- int_dec_hex(value, value.sz * 2)
76
- when Integer
77
- int_dec_hex(value, value.sz * 2)
78
- when String
79
- value.to_s.inspect
80
- else
81
- if value.respond_to? :to_human
82
- value.to_human
83
- else
84
- value.to_s.inspect
85
- end
86
- end
87
- self.format(type, attr, val, level)
71
+ self.format(type, attr, value.format_inspect, level)
88
72
  end
89
73
 
90
74
  # Format a ASN.1 attribute for +#inspect+.