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