packetgen 3.2.0 → 3.2.2

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 (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]