packetgen 3.2.0 → 3.2.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/bin/pgconsole +3 -3
  4. data/lib/packetgen/header/arp.rb +24 -13
  5. data/lib/packetgen/header/asn1_base.rb +2 -2
  6. data/lib/packetgen/header/base.rb +1 -1
  7. data/lib/packetgen/header/dhcpv6/duid.rb +6 -0
  8. data/lib/packetgen/header/dhcpv6/option.rb +11 -13
  9. data/lib/packetgen/header/dns/opt.rb +2 -2
  10. data/lib/packetgen/header/dns/rr.rb +61 -28
  11. data/lib/packetgen/header/dns.rb +13 -6
  12. data/lib/packetgen/header/dot11/management.rb +2 -5
  13. data/lib/packetgen/header/dot11.rb +1 -1
  14. data/lib/packetgen/header/eap.rb +1 -1
  15. data/lib/packetgen/header/eth.rb +1 -1
  16. data/lib/packetgen/header/http/headers.rb +16 -1
  17. data/lib/packetgen/header/http/response.rb +43 -23
  18. data/lib/packetgen/header/igmp.rb +1 -1
  19. data/lib/packetgen/header/ip/option.rb +36 -10
  20. data/lib/packetgen/header/ipv6/addr.rb +3 -0
  21. data/lib/packetgen/header/mdns.rb +19 -25
  22. data/lib/packetgen/header/mld.rb +1 -1
  23. data/lib/packetgen/header/ospfv2/lsa_header.rb +2 -4
  24. data/lib/packetgen/header/ospfv2.rb +1 -1
  25. data/lib/packetgen/header/ospfv3/ipv6_prefix.rb +14 -5
  26. data/lib/packetgen/header/ospfv3/lsa.rb +1 -1
  27. data/lib/packetgen/header/ospfv3/lsa_header.rb +2 -4
  28. data/lib/packetgen/header/ospfv3.rb +1 -1
  29. data/lib/packetgen/header/snmp.rb +39 -16
  30. data/lib/packetgen/header/tcp/option.rb +1 -1
  31. data/lib/packetgen/header/tcp.rb +12 -5
  32. data/lib/packetgen/header/tftp.rb +15 -9
  33. data/lib/packetgen/inspect.rb +15 -9
  34. data/lib/packetgen/packet.rb +48 -2
  35. data/lib/packetgen/pcapng/file.rb +13 -13
  36. data/lib/packetgen/pcapng.rb +1 -0
  37. data/lib/packetgen/pcaprub_wrapper.rb +0 -4
  38. data/lib/packetgen/types/abstract_tlv.rb +1 -1
  39. data/lib/packetgen/types/array.rb +35 -13
  40. data/lib/packetgen/types/fields.rb +19 -19
  41. data/lib/packetgen/types/int.rb +7 -0
  42. data/lib/packetgen/types/oui.rb +1 -1
  43. data/lib/packetgen/types/tlv.rb +17 -9
  44. data/lib/packetgen/unknown_packet.rb +3 -2
  45. data/lib/packetgen/utils.rb +67 -24
  46. data/lib/packetgen/version.rb +1 -1
  47. data/lib/packetgen.rb +3 -3
  48. metadata +3 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8f3faa715cd36e918df79355267c50690bdde15f1566e23947d26890e7b89a82
4
- data.tar.gz: d374447f230500b0c5be84b225d3a5576f019afc4870dc6512c659bc2e069d67
3
+ metadata.gz: ad942df146df9daf8c38e049fc112b13c93fd38a45362e8dd2a43c6e8382b224
4
+ data.tar.gz: 77b48a6af0170a912e91b888456c31881bfe1fd30a693796c849a946b5e27bac
5
5
  SHA512:
6
- metadata.gz: de7911facea630eaa2d3d4afd2ae15e1cbd927241ebf0c63ec096c64eac31c58e519d379476763c50fc1b4290029cec4b14dbf9e96ae9aa05dc06701cf45af32
7
- data.tar.gz: 69881fc66b4af80177bdad4b5b2daa4bd28259596bbf8a36112998aa9a8c117c7c9898180372635fcc1b47376e2fab4fb5cece88be5c000277cdce825fb03168
6
+ metadata.gz: 98f7f9d7dd652741cdce08cbe081310c4f4ca6745702efc5755806614f30c7930498610f9925a6d7abeef1b8b64abfa2857c77bcc638872f31577fe587ad1e63
7
+ data.tar.gz: cc4e18519b09f7eaa753f9f2e4a500c5b3d0de2aed5bbd275b636d5c21ec763dca2a34da27a16336d65e2ec86d827812c0f546c5e67dfd9b500d683f8baac7ac
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
 
2
2
  [![Gem Version](https://badge.fury.io/rb/packetgen.svg)](https://badge.fury.io/rb/packetgen)
3
-
3
+ [![Action status](https://github.com/sdaubert/packetgen/workflows/ci/badge.svg?branch=master)](https://github.com/sdaubert/packetgen/actions?query=workflow%3Aci)
4
4
  # PacketGen
5
5
 
6
6
  PacketGen provides simple ways to generate, send and capture network packets.
data/bin/pgconsole CHANGED
@@ -31,7 +31,7 @@ class PgConsole
31
31
  define_method m, Utils.method(m).to_proc
32
32
  end
33
33
 
34
- def get_binding
34
+ def console_binding
35
35
  binding
36
36
  end
37
37
  end
@@ -52,10 +52,10 @@ if use_pry
52
52
  }
53
53
  ]
54
54
  Pry.config.prompt_name = 'pg'
55
- PgConsole.new.get_binding.pry
55
+ PgConsole.new.console_binding.pry
56
56
  else
57
57
  IRB.setup nil
58
- irb = IRB::Irb.new(IRB::WorkSpace.new(PgConsole.new.get_binding))
58
+ irb = IRB::Irb.new(IRB::WorkSpace.new(PgConsole.new.console_binding))
59
59
  IRB.conf[:MAIN_CONTEXT] = irb.context
60
60
  irb.context.auto_indent_mode = true
61
61
  irb.context.prompt_i = 'pg> '
@@ -81,15 +81,7 @@ module PacketGen
81
81
  # @option options [String] :tha target hardware address
82
82
  # @option options [String] :tpa targetr internet address
83
83
  def initialize(options={})
84
- options[:hrd] ||= options[:htype]
85
- options[:pro] ||= options[:ptype]
86
- options[:hln] ||= options[:hlen]
87
- options[:pln] ||= options[:plen]
88
- options[:op] ||= options[:opcode]
89
- options[:sha] ||= options[:src_mac]
90
- options[:spa] ||= options[:src_ip]
91
- options[:tha] ||= options[:dst_mac]
92
- options[:tpa] ||= options[:dst_ip]
84
+ handle_options(options)
93
85
  super
94
86
  end
95
87
 
@@ -118,16 +110,35 @@ module PacketGen
118
110
  case opcode.to_i
119
111
  when 1
120
112
  self.opcode = 2
121
- self.spa, self.tpa = self.tpa, self.spa
122
- self.sha, self.tha = self.tha, self.sha
113
+ invert_addresses
123
114
  when 2
124
115
  self.opcode = 1
125
- self.spa, self.tpa = self.tpa, self.spa
126
- self.sha = self.tha
116
+ invert_addresses
127
117
  self[:tha].from_human('00:00:00:00:00:00')
128
118
  end
129
119
  self
130
120
  end
121
+
122
+ private
123
+
124
+ # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
125
+ def handle_options(options)
126
+ options[:hrd] ||= options[:htype]
127
+ options[:pro] ||= options[:ptype]
128
+ options[:hln] ||= options[:hlen]
129
+ options[:pln] ||= options[:plen]
130
+ options[:op] ||= options[:opcode]
131
+ options[:sha] ||= options[:src_mac]
132
+ options[:spa] ||= options[:src_ip]
133
+ options[:tha] ||= options[:dst_mac]
134
+ options[:tpa] ||= options[:dst_ip]
135
+ end
136
+ # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
137
+
138
+ def invert_addresses
139
+ self.spa, self.tpa = self.tpa, self.spa
140
+ self.sha, self.tha = self.tha, self.sha
141
+ end
131
142
  end
132
143
 
133
144
  self.add_class ARP
@@ -19,7 +19,7 @@ module PacketGen
19
19
  class ASN1Base < RASN1::Model
20
20
  include Headerable
21
21
 
22
- class <<self
22
+ class << self
23
23
  # Define some methods from given ASN.1 fields to mimic {Base} attributes
24
24
  # @param [Array<Symbol>] attributes
25
25
  # @return [void]
@@ -27,7 +27,7 @@ module PacketGen
27
27
  @attributes = attributes
28
28
  attributes.each do |attr|
29
29
  class_eval "def #{attr}; @elements[:#{attr}].value; end\n" \
30
- "def #{attr}=(v); @elements[:#{attr}].value = v; end"
30
+ "def #{attr}=(v); @elements[:#{attr}].value = v; end"
31
31
  end
32
32
  end
33
33
 
@@ -142,7 +142,7 @@ module PacketGen
142
142
  klass.class_eval { @known_headers = {} }
143
143
  end
144
144
 
145
- class <<self
145
+ class << self
146
146
  # @api private
147
147
  # Get known headers
148
148
  # @return [Hash] keys: header classes, values: hashes
@@ -58,6 +58,12 @@ module PacketGen
58
58
  def to_human
59
59
  "DUID<#{type},#{body.inspect}>"
60
60
  end
61
+
62
+ # Get human-readable type
63
+ # @return [String]
64
+ def human_type
65
+ self[:type].to_human
66
+ end
61
67
  end
62
68
 
63
69
  # rubocop:disable Naming/ClassAndModuleCamelCase
@@ -53,19 +53,17 @@ module PacketGen
53
53
  # @param [Hash] options
54
54
  # @return [Option]
55
55
  def new(options={})
56
- if self == Option
57
- case options[:type]
58
- when Integer
59
- klass = Option.subclasses[options[:type]]
60
- klass&.new(options)
61
- when String
62
- if DHCPv6.const_defined?(options[:type])
63
- klass = DHCPv6.const_get(options[:type])
64
- options.delete :type
65
- klass.new(options) if klass < Option
66
- end
67
- else
68
- super
56
+ return super unless self == Option
57
+
58
+ case options[:type]
59
+ when Integer
60
+ klass = Option.subclasses[options[:type]]
61
+ klass&.new(options)
62
+ when String
63
+ if DHCPv6.const_defined?(options[:type])
64
+ klass = DHCPv6.const_get(options[:type])
65
+ options.delete :type
66
+ klass.new(options) if klass < Option
69
67
  end
70
68
  else
71
69
  super
@@ -125,8 +125,8 @@ module PacketGen
125
125
  # @return [String]
126
126
  def to_human
127
127
  "#{name} #{human_type} UDPsize:#{udp_size} " \
128
- "extRCODE:#{ext_rcode} EDNSversion:#{version} flags:#{human_flags} " \
129
- "options:#{options.empty? ? 'none' : options.to_human}"
128
+ "extRCODE:#{ext_rcode} EDNSversion:#{version} flags:#{human_flags} " \
129
+ "options:#{options.empty? ? 'none' : options.to_human}"
130
130
  end
131
131
  end
132
132
  end
@@ -50,48 +50,29 @@ module PacketGen
50
50
  self[:rdata].read data
51
51
  end
52
52
 
53
+ # rubocop:disable Metrics/AbcSize
54
+
53
55
  # Get human readable rdata
54
56
  # @return [String]
55
57
  def human_rdata
56
- str = self[:rdata].inspect
57
-
58
- # Need to mask: mDNS uses leftmost bit as a flag (CACHE FLUSH)
59
- if self.rrclass & 0x7fff == CLASSES['IN']
60
- case type
61
- when TYPES['A'], TYPES['AAAA']
62
- str = IPAddr.new_ntoh(self[:rdata]).to_s
63
- end
64
- end
58
+ str = human_ip_rdata || self[:rdata].inspect
65
59
 
66
- name = Name.new
67
- name.dns = self[:name].dns
68
60
  case type
69
61
  when TYPES['NS'], TYPES['PTR'], TYPES['CNAME']
62
+ name = Name.new
63
+ name.dns = self[:name].dns
70
64
  str = name.read(self[:rdata]).to_human
71
65
  when TYPES['SOA']
72
- mname = name.read(self[:rdata]).dup
73
- rname = name.read(self[:rdata][mname.sz..-1])
74
- serial = Types::Int32.new.read(self[:rdata][mname.sz + rname.sz, 4])
75
- refresh = Types::Int32.new.read(self[:rdata][mname.sz + rname.sz + 4, 4])
76
- retryi = Types::Int32.new.read(self[:rdata][mname.sz + rname.sz + 8, 4])
77
- expire = Types::Int32.new.read(self[:rdata][mname.sz + rname.sz + 12, 4])
78
- minimum = Types::Int32.new.read(self[:rdata][mname.sz + rname.sz + 16, 4])
79
- str = "#{mname.to_human} #{rname.to_human} #{serial.to_i} #{refresh.to_i} " \
80
- "#{retryi.to_i} #{expire.to_i} #{minimum.to_i}"
66
+ str = human_soa_rdata
81
67
  when TYPES['MX']
82
- pref = Types::Int16.new.read(self[:rdata][0, 2])
83
- exchange = name.read(self[:rdata][2..-1]).to_human
84
- str = '%u %s' % [pref.to_i, exchange]
68
+ str = human_mx_data
85
69
  when TYPES['SRV']
86
- priority = Types::Int16.new.read(self[:rdata][0, 2])
87
- weight = Types::Int16.new.read(self[:rdata][2, 2])
88
- port = Types::Int16.new.read(self[:rdata][4, 2])
89
- target = name.read(self[:rdata][6, self[:rdata].size]).to_human
90
- str = "#{priority.to_i} #{weight.to_i} #{port.to_i} #{target}"
70
+ str = human_srv_data
91
71
  end
92
72
 
93
73
  str
94
74
  end
75
+ # rubocop:enable Metrics/AbcSize
95
76
 
96
77
  # Get human readable class
97
78
  # @return [String]
@@ -109,6 +90,58 @@ module PacketGen
109
90
  def to_human
110
91
  "#{human_type} #{human_rrclass} #{name} TTL #{ttl} #{human_rdata}"
111
92
  end
93
+
94
+ private
95
+
96
+ def human_ip_rdata
97
+ # Need to mask: mDNS uses leftmost bit as a flag (CACHE FLUSH)
98
+ return unless self.rrclass & 0x7fff == CLASSES['IN']
99
+
100
+ case type
101
+ when TYPES['A'], TYPES['AAAA']
102
+ IPAddr.new_ntoh(self[:rdata]).to_s
103
+ end
104
+ end
105
+
106
+ def human_mx_data
107
+ name = Name.new
108
+ name.dns = self[:name].dns
109
+
110
+ pref = Types::Int16.new.read(self[:rdata][0, 2])
111
+ exchange = name.read(self[:rdata][2..-1]).to_human
112
+
113
+ '%u %s' % [pref.to_i, exchange]
114
+ end
115
+
116
+ # rubocop:disable Metrics/AbcSize
117
+ def human_soa_rdata
118
+ name = Name.new
119
+ name.dns = self[:name].dns
120
+ mname = name.read(self[:rdata]).dup
121
+ rname = name.read(self[:rdata][mname.sz..-1])
122
+
123
+ serial = Types::Int32.new.read(self[:rdata][mname.sz + rname.sz, 4])
124
+ refresh = Types::Int32.new.read(self[:rdata][mname.sz + rname.sz + 4, 4])
125
+ retryi = Types::Int32.new.read(self[:rdata][mname.sz + rname.sz + 8, 4])
126
+ expire = Types::Int32.new.read(self[:rdata][mname.sz + rname.sz + 12, 4])
127
+ minimum = Types::Int32.new.read(self[:rdata][mname.sz + rname.sz + 16, 4])
128
+
129
+ "#{mname.to_human} #{rname.to_human} #{serial.to_i} #{refresh.to_i} " \
130
+ "#{retryi.to_i} #{expire.to_i} #{minimum.to_i}"
131
+ end
132
+
133
+ def human_srv_data
134
+ name = Name.new
135
+ name.dns = self[:name].dns
136
+
137
+ priority = Types::Int16.new.read(self[:rdata][0, 2])
138
+ weight = Types::Int16.new.read(self[:rdata][2, 2])
139
+ port = Types::Int16.new.read(self[:rdata][4, 2])
140
+ target = name.read(self[:rdata][6, self[:rdata].size]).to_human
141
+
142
+ "#{priority.to_i} #{weight.to_i} #{port.to_i} #{target}"
143
+ end
144
+ # rubocop:enable Metrics/AbcSize
112
145
  end
113
146
  end
114
147
  end
@@ -241,18 +241,25 @@ module PacketGen
241
241
  super do |attr|
242
242
  next unless attr == :u16
243
243
 
244
- flags = %i[qr aa tc rd ra].select! { |flag| send "#{flag}?" }
245
- .map(&:to_s).join(',')
246
- str = Inspect.shift_level
247
- str << Inspect::FMT_ATTR % ['Flags', 'flags', flags]
248
- opcode = '%-16s (%u)' % [OPCODES.key(self.opcode), self.opcode]
244
+ str = inspect_flags
245
+
249
246
  str << Inspect.shift_level
247
+ opcode = '%-16s (%u)' % [OPCODES.key(self.opcode), self.opcode]
250
248
  str << Inspect::FMT_ATTR % ['Integer', 'opcode', opcode]
251
- rcode = '%-16s (%u)' % [RCODES.key(self.rcode), self.rcode]
249
+
252
250
  str << Inspect.shift_level
251
+ rcode = '%-16s (%u)' % [RCODES.key(self.rcode), self.rcode]
253
252
  str << Inspect::FMT_ATTR % ['Integer', 'rcode', rcode]
254
253
  end
255
254
  end
255
+
256
+ private
257
+
258
+ def inspect_flags
259
+ flags = %i[qr aa tc rd ra].select! { |flag| send "#{flag}?" }.map(&:to_s).join(',')
260
+ str = Inspect.shift_level
261
+ str << Inspect::FMT_ATTR % ['Flags', 'flags', flags]
262
+ end
256
263
  end
257
264
 
258
265
  self.add_class DNS
@@ -53,12 +53,9 @@ module PacketGen
53
53
  # @return [self]
54
54
  # @since 2.1.3
55
55
  def add_element(type:, value:)
56
- if self[:body].is_a? SubMngt
57
- self[:body].elements << { type: type, value: value }
58
- else
59
- raise FormatError, 'Before adding an Element, you have to add a Dot11::SubMngt subclass instance'
60
- end
56
+ raise FormatError, 'Before adding an Element, you have to add a Dot11::SubMngt subclass instance' unless self[:body].is_a? SubMngt
61
57
 
58
+ self[:body].elements << { type: type, value: value }
62
59
  self
63
60
  end
64
61
 
@@ -355,7 +355,7 @@ module PacketGen
355
355
  def added_to_packet(packet)
356
356
  return if packet.respond_to? :dot11
357
357
 
358
- packet.instance_eval("def dot11(arg=nil); header(#{self.class}, arg); end")
358
+ packet.instance_eval("def dot11(arg=nil); header(#{self.class}, arg); end") # def dot11(arg=nil); header(Dot11, arg); end
359
359
  end
360
360
 
361
361
  private
@@ -218,7 +218,7 @@ module PacketGen
218
218
  def added_to_packet(packet)
219
219
  return if packet.respond_to? :eap
220
220
 
221
- packet.instance_eval("def eap(arg=nil); header(#{self.class}, arg); end")
221
+ packet.instance_eval("def eap(arg=nil); header(#{self.class}, arg); end") # def eap(arg=nil); header(EAP, arg); end
222
222
  end
223
223
 
224
224
  # Invert between a request and a response packet. Not action for
@@ -60,7 +60,7 @@ module PacketGen
60
60
  def from_human(str)
61
61
  return self if str.nil?
62
62
 
63
- bytes = str.split(/:/)
63
+ bytes = str.split(':')
64
64
  raise ArgumentError, 'not a MAC address' unless bytes.size == 6
65
65
 
66
66
  6.times do |i|
@@ -34,13 +34,28 @@ module PacketGen
34
34
 
35
35
  k, v = h.split(':', 2)
36
36
  [k, v.strip]
37
- end.reject(&:nil?).to_h
37
+ end.compact.to_h
38
38
  when Hash
39
39
  @data = s_or_h
40
40
  end
41
41
  self
42
42
  end
43
43
 
44
+ # Get header value from its name
45
+ # @param [String] header header name
46
+ # @return [String] header value
47
+ def [](header)
48
+ data[header]
49
+ end
50
+
51
+ # Say if +self+ include +header+ header
52
+ # @param [String] header header name
53
+ # @return [Boolean]
54
+ def header?(header)
55
+ data.key?(header)
56
+ end
57
+ alias has_header? header?
58
+
44
59
  # Get binary string.
45
60
  # @return [String]
46
61
  def to_s
@@ -67,11 +67,42 @@ module PacketGen
67
67
  # Read in the HTTP portion of the packet, and parse it.
68
68
  # @return [PacketGen::HTTP::Response]
69
69
  def read(str)
70
- str = str.bytes.map!(&:chr).join unless str.valid_encoding?
71
- arr = str.split("\r\n")
70
+ headers, data = collect_headers_and_data(str)
71
+
72
+ unless headers.empty?
73
+ extract_info_from_first_line(headers)
74
+ self[:headers].read(headers.join("\n"))
75
+ end
76
+ self[:body].read data.join("\n")
77
+
78
+ self
79
+ end
80
+
81
+ def parse?
82
+ version.start_with?('HTTP/1.')
83
+ end
84
+
85
+ # String representation of data.
86
+ # @return [String]
87
+ def to_s
88
+ raise_on_bad_version_status
89
+
90
+ str = +''
91
+ str << self.version << ' ' << self.status_code << ' ' << self.status_mesg << "\r\n"
92
+ str << self[:headers].to_s if self[:headers].given?
93
+ str << self.body
94
+ end
95
+
96
+ private
97
+
98
+ def collect_headers_and_data(str)
72
99
  headers = [] # header stream
73
100
  data = [] # data stream
74
101
  switch = false
102
+
103
+ str = str.bytes.map!(&:chr).join unless str.valid_encoding?
104
+ arr = str.split("\r\n")
105
+
75
106
  arr.each do |line|
76
107
  if line.empty?
77
108
  data << line if switch # already done
@@ -85,34 +116,23 @@ module PacketGen
85
116
  headers << line
86
117
  end
87
118
  end
88
- unless headers.empty?
89
- first_line = headers.shift.split
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
95
- self[:headers].read(headers.join("\n"))
96
- end
97
- self[:body].read data.join("\n")
98
- self
119
+
120
+ [headers, data]
99
121
  end
100
122
 
101
- def parse?
102
- version.start_with?('HTTP/1.')
123
+ def extract_info_from_first_line(headers)
124
+ first_line = headers.shift.split
125
+ return if first_line.size < 3
126
+
127
+ self[:version].read first_line[0]
128
+ self[:status_code].read first_line[1]
129
+ self[:status_mesg].read first_line[2..-1].join(' ')
103
130
  end
104
131
 
105
- # String representation of data.
106
- # @return [String]
107
- def to_s
132
+ def raise_on_bad_version_status
108
133
  raise FormatError, 'Missing #status_code.' if self.status_code.empty?
109
134
  raise FormatError, 'Missing #status_mesg.' if self.status_mesg.empty?
110
135
  raise FormatError, 'Missing #version.' if self.version.empty?
111
-
112
- str = +''
113
- str << self.version << ' ' << self.status_code << ' ' << self.status_mesg << "\r\n"
114
- str << self[:headers].to_s if self[:headers].given?
115
- str << self.body
116
136
  end
117
137
  end
118
138
  end
@@ -77,7 +77,7 @@ module PacketGen
77
77
  # directly called
78
78
  def added_to_packet(packet)
79
79
  igmp_idx = packet.headers.size
80
- packet.instance_eval "def igmpize() @headers[#{igmp_idx}].igmpize; end"
80
+ packet.instance_eval "def igmpize() @headers[#{igmp_idx}].igmpize; end" # def igmpize() @headers[2].igmpize; end
81
81
  end
82
82
 
83
83
  # Get human readbale type
@@ -55,7 +55,7 @@ module PacketGen
55
55
  # option data
56
56
  # @return [String]
57
57
  define_field :data, Types::String, optional: ->(h) { h.length > 2 },
58
- builder: ->(h, t) { t.new(length_from: -> { h.length - 2 }) }
58
+ builder: ->(h, t) { t.new(length_from: -> { h.length - 2 }) }
59
59
 
60
60
  # @!attribute copied
61
61
  # 1-bit copied flag from {#type} field
@@ -86,25 +86,23 @@ module PacketGen
86
86
  # Factory to build an option from its type
87
87
  # @return [Option]
88
88
  def self.build(options={})
89
- type = options.delete(:type)
89
+ type = options[:type]
90
90
  klass = case type
91
91
  when String
92
92
  types.key?(type) ? IP.const_get(type) : self
93
- when Integer
94
- types.value?(type) ? IP.const_get(types.key(type)) : self
95
93
  else
96
- self
94
+ types.value?(type) ? IP.const_get(types.key(type.to_i)) : self
97
95
  end
96
+ options.delete(:type) if klass != self
98
97
  klass.new(options)
99
98
  end
100
99
 
101
100
  def initialize(options={})
102
- unless options[:type]
103
- opt_name = self.class.to_s.gsub(/.*::/, '')
104
- options[:type] = Option.const_get("#{opt_name}_TYPE") if Option.const_defined? "#{opt_name}_TYPE"
105
- end
101
+ options[:type] = class2type unless options[:type]
102
+
106
103
  super
107
- self.length = sz if respond_to?(:length) && options[:length].nil?
104
+ initialize_length_if_needed(options)
105
+ initialize_data_if_needed(options)
108
106
  end
109
107
 
110
108
  # Get binary string. Set {#length} field.
@@ -121,6 +119,25 @@ module PacketGen
121
119
  str << ":#{self[:data].to_s.inspect}" if respond_to?(:length) && (length > 2) && !self[:data].to_s.empty?
122
120
  str
123
121
  end
122
+
123
+ private
124
+
125
+ def class2type
126
+ opt_name = self.class.to_s.gsub(/.*::/, '')
127
+ Option.const_get("#{opt_name}_TYPE") if Option.const_defined? "#{opt_name}_TYPE"
128
+ end
129
+
130
+ def initialize_length_if_needed(options)
131
+ self.length = sz if respond_to?(:length) && options[:length].nil?
132
+ end
133
+
134
+ def initialize_data_if_needed(options)
135
+ return unless fields.include?(:data) && self[:data].respond_to?(:from_human) && options.key?(:data)
136
+
137
+ # Force data if data is set in options but not length
138
+ self.length += options[:data].size
139
+ self[:data].from_human(options[:data])
140
+ end
124
141
  end
125
142
 
126
143
  # End-of-option-List IP option
@@ -161,6 +178,7 @@ module PacketGen
161
178
 
162
179
  # Strict Source and Record Route IP option
163
180
  class SSRR < LSRR; end
181
+
164
182
  # Record Route IP option
165
183
  class RR < LSRR; end
166
184
 
@@ -172,6 +190,10 @@ module PacketGen
172
190
  # 16-bit stream ID
173
191
  # @return [Integer]
174
192
  define_field :id, Types::Int16
193
+
194
+ def to_human
195
+ super << ":#{self.id}"
196
+ end
175
197
  end
176
198
 
177
199
  # Router Alert IP option
@@ -182,6 +204,10 @@ module PacketGen
182
204
  # 16-bit value. Should be 0.
183
205
  # @return [Integer]
184
206
  define_field :value, Types::Int16, default: 0
207
+
208
+ def to_human
209
+ super << ":#{self.value}"
210
+ end
185
211
  end
186
212
  end
187
213
  end
@@ -49,6 +49,8 @@ module PacketGen
49
49
  # @return [Integer]
50
50
  define_field :a8, Types::Int16
51
51
 
52
+ # rubocop:disable Metrics/AbcSize
53
+
52
54
  # Read a colon-delimited address
53
55
  # @param [String] str
54
56
  # @return [self]
@@ -69,6 +71,7 @@ module PacketGen
69
71
  self.a8 = addri & 0xffff
70
72
  self
71
73
  end
74
+ # rubocop:enable Metrics/AbcSize
72
75
 
73
76
  # Addr6 in human readable form (colon-delimited hex string)
74
77
  # @return [String]