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
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This file is part of PacketGen
4
+ # See https://github.com/sdaubert/packetgen for more informations
5
+ # Copyright (C) 2016 Sylvain Daubert <sylvain.daubert@laposte.net>
6
+ # This program is published under MIT license.
7
+
8
+ module PacketGen
9
+ module Types
10
+ # Mixin to define minimal API for a class to be embbeded as a field in
11
+ # {Fields} type.
12
+ #
13
+ # == Optional methods
14
+ # These methods may, optionally, be defined by fieldable types:
15
+ # * +from_human+ to load data from a human-readable string.
16
+ # @author Sylvain Daubert
17
+ # @since 3.1.6
18
+ module Fieldable
19
+ # Get type name
20
+ # @return [String]
21
+ def type_name
22
+ self.class.to_s.split('::').last
23
+ end
24
+
25
+ # rubocop:disable Lint/UselessMethodDefinition
26
+ # These methods are defined for documentation.
27
+
28
+ # Populate object from a binary string
29
+ # @param [String] str
30
+ # @return [Fields] self
31
+ # @abstract subclass should overload it.
32
+ def read(str)
33
+ super
34
+ end
35
+
36
+ # Return object as a binary string
37
+ # @return [String]
38
+ # @abstract subclass should overload it.
39
+ def to_s
40
+ super
41
+ end
42
+
43
+ # Size of object as binary string
44
+ # @return [Integer]
45
+ def sz
46
+ to_s.size
47
+ end
48
+
49
+ # Return a human-readbale string
50
+ # @return [String]
51
+ # @abstract subclass should overload it.
52
+ def to_human
53
+ super
54
+ end
55
+
56
+ # rubocop:enable Lint/UselessMethodDefinition
57
+
58
+ # Format object when inspecting a {Field} object
59
+ # @return [String]
60
+ def format_inspect
61
+ to_human
62
+ end
63
+ end
64
+ end
65
+ end
@@ -5,6 +5,8 @@
5
5
  # Copyright (C) 2016 Sylvain Daubert <sylvain.daubert@laposte.net>
6
6
  # This program is published under MIT license.
7
7
 
8
+ # rubocop:disable Metrics/ClassLength
9
+
8
10
  module PacketGen
9
11
  module Types
10
12
  # @abstract Set of fields
@@ -13,8 +15,7 @@ module PacketGen
13
15
  #
14
16
  # == Basics
15
17
  # A {Fields} subclass is generaly composed of multiple binary fields. These fields
16
- # have each a given type. All types from {Types} module are supported, and all
17
- # {Fields} subclasses may also be used as field type.
18
+ # have each a given type. All {Fieldable} types are supported.
18
19
  #
19
20
  # To define a new subclass, it has to inherit from {Fields}. And some class
20
21
  # methods have to be used to declare attributes/fields:
@@ -128,6 +129,8 @@ module PacketGen
128
129
  # @param [Class] klass
129
130
  # @return [void]
130
131
  def inherited(klass)
132
+ super
133
+
131
134
  field_defs = {}
132
135
  @field_defs.each do |k, v|
133
136
  field_defs[k] = v.clone
@@ -162,7 +165,7 @@ module PacketGen
162
165
  # bs[value1] # => Types::Int8
163
166
  # bs.value1 # => Integer
164
167
  # @param [Symbol] name field name
165
- # @param [Object] type class or instance
168
+ # @param [Fieldable] type class or instance
166
169
  # @param [Hash] options Unrecognized options are passed to object builder if
167
170
  # +:builder+ option is not set.
168
171
  # @option options [Object] :default default value. May be a proc. This lambda
@@ -191,7 +194,7 @@ module PacketGen
191
194
  # @param [Symbol,nil] other field name to create a new one before. If +nil+,
192
195
  # new field is appended.
193
196
  # @param [Symbol] name field name to create
194
- # @param [Object] type class or instance
197
+ # @param [Fieldable] type class or instance
195
198
  # @param [Hash] options See {.define_field}.
196
199
  # @return [void]
197
200
  # @see .define_field
@@ -210,7 +213,7 @@ module PacketGen
210
213
  # @param [Symbol,nil] other field name to create a new one after. If +nil+,
211
214
  # new field is appended.
212
215
  # @param [Symbol] name field name to create
213
- # @param [Object] type class or instance
216
+ # @param [Fieldable] type class or instance
214
217
  # @param [Hash] options See {.define_field}.
215
218
  # @return [void]
216
219
  # @see .define_field
@@ -244,12 +247,12 @@ module PacketGen
244
247
  # @raise [ArgumentError] unknown +field+
245
248
  # @since 2.8.4
246
249
  def update_field(field, options)
247
- raise ArgumentError, "unkown #{field} field for #{self}" unless field_defs.key?(field)
250
+ check_existence_of field
251
+
252
+ %i[default builder optional enum].each do |property|
253
+ field_defs_property_from(field, property, options)
254
+ end
248
255
 
249
- field_defs[field].default = options.delete(:default) if options.key?(:default)
250
- field_defs[field].builder = options.delete(:builder) if options.key?(:builder)
251
- field_defs[field].optional = options.delete(:optional) if options.key?(:optional)
252
- field_defs[field].enum = options.delete(:enum) if options.key?(:enum)
253
256
  field_defs[field].options.merge!(options)
254
257
  end
255
258
 
@@ -271,12 +274,12 @@ module PacketGen
271
274
  # subclass)
272
275
  # @param [Array] args list of bitfield names. Name may be followed
273
276
  # by bitfield size. If no size is given, 1 bit is assumed.
277
+ # @raise [ArgumentError] unknown +attr+
274
278
  # @return [void]
275
279
  def define_bit_fields_on(attr, *args)
276
- attr_def = field_defs[attr]
277
- raise ArgumentError, "unknown #{attr} field" if attr_def.nil?
280
+ check_existence_of attr
278
281
 
279
- type = attr_def.type
282
+ type = field_defs[attr].type
280
283
  raise TypeError, "#{attr} is not a PacketGen::Types::Int" unless type < Types::Int
281
284
 
282
285
  total_size = type.new.width * 8
@@ -286,11 +289,7 @@ module PacketGen
286
289
  field = args.shift
287
290
  next unless field.is_a? Symbol
288
291
 
289
- size = if args.first.is_a? Integer
290
- args.shift
291
- else
292
- 1
293
- end
292
+ size = size_from(args)
294
293
 
295
294
  unless field == :_
296
295
  add_bit_methods(attr, field, size, total_size, idx)
@@ -338,38 +337,74 @@ module PacketGen
338
337
 
339
338
  def add_bit_methods(attr, name, size, total_size, idx)
340
339
  shift = idx - (size - 1)
341
- field_mask = (2**size - 1) << shift
342
- clear_mask = (2**total_size - 1) & (~field_mask & (2**total_size - 1))
343
340
 
344
341
  if size == 1
345
- class_eval <<-METHODS
346
- def #{name}?
347
- val = (self[:#{attr}].to_i & #{field_mask}) >> #{shift}
348
- val != 0
349
- end
350
- def #{name}=(v)
351
- val = v ? 1 : 0
352
- self[:#{attr}].value = self[:#{attr}].to_i & #{clear_mask}
353
- self[:#{attr}].value |= val << #{shift}
354
- end
355
- METHODS
342
+ add_single_bit_methods(attr, name, size, total_size, shift)
356
343
  else
357
- class_eval <<-METHODS
358
- def #{name}
359
- (self[:#{attr}].to_i & #{field_mask}) >> #{shift}
360
- end
361
- def #{name}=(v)
362
- self[:#{attr}].value = self[:#{attr}].to_i & #{clear_mask}
363
- self[:#{attr}].value |= (v & #{2**size - 1}) << #{shift}
364
- end
365
- METHODS
344
+ add_multibit_methods(attr, name, size, total_size, shift)
345
+ end
346
+ end
347
+
348
+ def compute_field_mask(size, shift)
349
+ (2**size - 1) << shift
350
+ end
351
+
352
+ def compute_clear_mask(total_size, field_mask)
353
+ (2**total_size - 1) & (~field_mask & (2**total_size - 1))
354
+ end
355
+
356
+ def add_single_bit_methods(attr, name, size, total_size, shift)
357
+ field_mask = compute_field_mask(size, shift)
358
+ clear_mask = compute_clear_mask(total_size, field_mask)
359
+
360
+ class_eval <<-METHODS
361
+ def #{name}?
362
+ val = (self[:#{attr}].to_i & #{field_mask}) >> #{shift}
363
+ val != 0
366
364
  end
365
+ def #{name}=(v)
366
+ val = v ? 1 : 0
367
+ self[:#{attr}].value = self[:#{attr}].to_i & #{clear_mask}
368
+ self[:#{attr}].value |= val << #{shift}
369
+ end
370
+ METHODS
371
+ end
372
+
373
+ def add_multibit_methods(attr, name, size, total_size, shift)
374
+ field_mask = compute_field_mask(size, shift)
375
+ clear_mask = compute_clear_mask(total_size, field_mask)
376
+
377
+ class_eval <<-METHODS
378
+ def #{name}
379
+ (self[:#{attr}].to_i & #{field_mask}) >> #{shift}
380
+ end
381
+ def #{name}=(v)
382
+ self[:#{attr}].value = self[:#{attr}].to_i & #{clear_mask}
383
+ self[:#{attr}].value |= (v & #{2**size - 1}) << #{shift}
384
+ end
385
+ METHODS
367
386
  end
368
387
 
369
388
  def register_bit_field_size(attr, field, size)
370
389
  bit_fields[attr] = {} if bit_fields[attr].nil?
371
390
  bit_fields[attr][field] = size
372
391
  end
392
+
393
+ def field_defs_property_from(field, property, options)
394
+ field_defs[field].send("#{property}=", options.delete(property)) if options.key?(property)
395
+ end
396
+
397
+ def size_from(args)
398
+ if args.first.is_a? Integer
399
+ args.shift
400
+ else
401
+ 1
402
+ end
403
+ end
404
+
405
+ def check_existence_of(field)
406
+ raise ArgumentError, "unknown #{field} field for #{self}" unless field_defs.key?(field)
407
+ end
373
408
  end
374
409
 
375
410
  # Create a new fields object
@@ -394,7 +429,7 @@ module PacketGen
394
429
 
395
430
  # Get field object
396
431
  # @param [Symbol] field
397
- # @return [Object]
432
+ # @return [Fieldable]
398
433
  def [](field)
399
434
  @fields[field]
400
435
  end
@@ -414,6 +449,7 @@ module PacketGen
414
449
  end
415
450
 
416
451
  # Get all optional field name
452
+ # @return[Array<Symbol>,nil]
417
453
  def optional_fields
418
454
  @optional_fields.keys
419
455
  end
@@ -444,11 +480,7 @@ module PacketGen
444
480
  next unless present?(field)
445
481
 
446
482
  obj = self[field].read str[start..-1]
447
- if self[field].respond_to? :sz
448
- start += self[field].sz
449
- else
450
- start = str.size
451
- end
483
+ start += self[field].sz
452
484
  self[field] = obj unless obj == self[field]
453
485
  end
454
486
 
@@ -545,22 +577,21 @@ module PacketGen
545
577
  self.class.field_defs
546
578
  end
547
579
 
580
+ # rubocop:disable Metrics/AbcSize
548
581
  def build_field(field)
549
582
  type = field_defs[field].type
550
- builder = field_defs[field].builder
551
- enum = field_defs[field].enum
552
- field_options = field_defs[field].options
553
-
554
- @fields[field] = if builder
555
- builder.call(self, type)
556
- elsif enum
557
- type.new(enum)
558
- elsif !field_options.empty?
559
- type.new(field_options)
583
+
584
+ @fields[field] = if field_defs[field].builder
585
+ field_defs[field].builder.call(self, type)
586
+ elsif field_defs[field].enum
587
+ type.new(field_defs[field].enum)
588
+ elsif !field_defs[field].options.empty?
589
+ type.new(field_defs[field].options)
560
590
  else
561
591
  type.new
562
592
  end
563
593
  end
594
+ # rubocop:enable Metrics/AbcSize
564
595
 
565
596
  def initialize_value(field, val)
566
597
  type = field_defs[field].type
@@ -572,6 +603,8 @@ module PacketGen
572
603
  @fields[field] = value
573
604
  elsif @fields[field].respond_to? :from_human
574
605
  @fields[field].from_human(value)
606
+ else
607
+ @fields[field].read(value)
575
608
  end
576
609
  end
577
610
 
@@ -582,3 +615,5 @@ module PacketGen
582
615
  end
583
616
  end
584
617
  end
618
+
619
+ # rubocop:enable Metrics/ClassLength
@@ -12,7 +12,7 @@ module PacketGen
12
12
  # @abstract
13
13
  # @author Sylvain Daubert
14
14
  class Int
15
- include Inspectable
15
+ include Fieldable
16
16
 
17
17
  # Integer value
18
18
  # @return [Integer]
@@ -47,7 +47,7 @@ module PacketGen
47
47
  @value = if value.is_a?(Integer)
48
48
  value.to_i
49
49
  elsif defined? @packstr
50
- value.to_s.unpack(@packstr[@endian]).first
50
+ value.to_s.unpack1(@packstr[@endian])
51
51
  else
52
52
  raise ParseError, 'Int#read is abstract and cannot read'
53
53
  end
@@ -12,7 +12,7 @@ module PacketGen
12
12
  # By default, a null string will have one byte length (length byte set to 0).
13
13
  # @author Sylvain Daubert
14
14
  class IntString
15
- include Inspectable
15
+ include Fieldable
16
16
 
17
17
  # internal string
18
18
  # @return [String]
@@ -78,7 +78,6 @@ module PacketGen
78
78
  def to_human
79
79
  @string
80
80
  end
81
- alias format_inspect to_human
82
81
 
83
82
  # Set length from internal string length
84
83
  # @return [Integer]
@@ -91,6 +90,12 @@ module PacketGen
91
90
  def sz
92
91
  to_s.size
93
92
  end
93
+
94
+ # Say if IntString is empty
95
+ # @return [Boolean]
96
+ def empty?
97
+ length.zero?
98
+ end
94
99
  end
95
100
  end
96
101
  end
@@ -14,6 +14,9 @@ module PacketGen
14
14
  # @author Sylvain Daubert
15
15
  # @since 3.0.0
16
16
  module LengthFrom
17
+ # Max value returned by {#sz_to_read}.
18
+ MAX_SZ_TO_READ = 65_535
19
+
17
20
  # Initialize +length from+ capacity.
18
21
  # Should be call by extensed object's initialize.
19
22
  # @param [Hash] options
@@ -29,16 +32,21 @@ module PacketGen
29
32
  # @return [String]
30
33
  def read_with_length_from(str)
31
34
  s = PacketGen.force_binary(str.to_s)
32
- str_end = case @length_from
33
- when Types::Int
34
- @length_from.to_i
35
- when Proc
36
- @length_from.call
37
- else
38
- s.size
39
- end
40
- str_end = 0 if str_end.negative?
41
- s[0, str_end]
35
+ s[0, sz_to_read]
36
+ end
37
+
38
+ # Size to read, from length_from
39
+ # @return [Integer]
40
+ def sz_to_read
41
+ len = case @length_from
42
+ when Types::Int
43
+ @length_from.to_i
44
+ when Proc
45
+ @length_from.call
46
+ else
47
+ MAX_SZ_TO_READ
48
+ end
49
+ [0, len].max
42
50
  end
43
51
  end
44
52
  end
@@ -14,7 +14,7 @@ module PacketGen
14
14
  # oui.to_human # => "00:01:02"
15
15
  # @author Sylvain Daubert
16
16
  class OUI < Types::Fields
17
- include Inspectable
17
+ include Fieldable
18
18
 
19
19
  # @attribute b2
20
20
  # @return [Integer] left-most byte
@@ -46,7 +46,6 @@ module PacketGen
46
46
  def to_human
47
47
  fields.map { |m| '%02x' % self[m] }.join(':')
48
48
  end
49
- alias format_inspect to_human
50
49
  end
51
50
  end
52
51
  end
@@ -6,16 +6,23 @@
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
- # This class is just like regular String. It only adds {#read}, {#sz},
12
- # #{to_human} and {#from_human} methods
13
- # to be compatible with others {Types}.
13
+ # This class mimics regular String, but it is {Fieldable}.
14
14
  # @author Sylvain Daubert
15
- class String < ::String
15
+ # @since 3.1.6 no more a subclass or regular String
16
+ class String
17
+ extend Forwardable
18
+ include Fieldable
16
19
  include LengthFrom
17
- include Inspectable
18
20
 
21
+ def_delegators :@string, :[], :to_s, :length, :size, :inspect, :==, :<<,
22
+ :unpack, :force_encoding, :encoding, :index, :empty?
23
+
24
+ # @return [::String]
25
+ attr_reader :string
19
26
  # @return [Integer]
20
27
  attr_reader :static_length
21
28
 
@@ -24,7 +31,7 @@ module PacketGen
24
31
  # takes length when reading
25
32
  # @option options [Integer] :static_length set a static length for this string
26
33
  def initialize(options={})
27
- super()
34
+ register_internal_string ''
28
35
  initialize_length_from(options)
29
36
  @static_length = options[:static_length]
30
37
  end
@@ -33,14 +40,44 @@ module PacketGen
33
40
  # @return [String] self
34
41
  def read(str)
35
42
  s = read_with_length_from(str)
36
- s = s[0, static_length] if static_length
37
- self.replace(s)
43
+ register_internal_string s
38
44
  self
39
45
  end
40
46
 
47
+ alias old_sz_to_read sz_to_read
48
+ private :old_sz_to_read
49
+
50
+ # Size to read.
51
+ # Computed from static_length or length_from, if defined.
52
+ # @return [Integer]
53
+ # @since 3.1.6
54
+ def sz_to_read
55
+ return static_length if static_length?
56
+
57
+ old_sz_to_read
58
+ end
59
+
60
+ # Say if a static length is defined
61
+ # @return [Boolean]
62
+ # @since 3.1.6
63
+ def static_length?
64
+ !static_length.nil?
65
+ end
66
+
67
+ def format_inspect
68
+ inspect
69
+ end
70
+
41
71
  alias sz length
42
72
  alias to_human to_s
43
73
  alias from_human read
74
+
75
+ private
76
+
77
+ def register_internal_string(str)
78
+ @string = str
79
+ PacketGen.force_binary(@string)
80
+ end
44
81
  end
45
82
  end
46
83
  end