packetgen 3.1.4 → 3.2.0

Sign up to get free protection for your applications and to get access to all the features.
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+.