packetgen 3.2.1 → 3.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 79dc573da10eb15bbd9f38e0c84ecdf9a92d076980628635d1b9e240e3ca2210
4
- data.tar.gz: 0ae2bafa08e5a4a0b176276a141b8c8326265873bb20d86a75e07bb7018275ac
3
+ metadata.gz: de85a6f18ac001823cdbd0802c8946ead811be724c9069266b78c493d99feb6b
4
+ data.tar.gz: ef73ca367e87bb47ca4f37ee387c7a73d0ad0f19cebaca151443560f42508559
5
5
  SHA512:
6
- metadata.gz: 7c405a57b79c4a9bf0d9032b34d7381b33e8c0705c554023f6a8131c19c48fb0118201d94da62e4f74b9d53551cdbcd434cb6363f27a84e5c3e8459cb28d613b
7
- data.tar.gz: 835969235415864e05b227dd5f0fb48535e3f414e60852cf788d6d90bf29be9a3a8dcf9b636ce8d500d29720753246e11459da925fea3f72f6cb8e11291565b8
6
+ metadata.gz: '01605812af7b7d53111bfdc0d4d095cb6f0df4975634db17700d7c15c6acc63ba82585e6c5a5d2d93c130d566f45bb62d9ff3709c27e0f7cf4ea0c20ba7c172f'
7
+ data.tar.gz: e4a54c74e032ee12d744f94c657416591c0f056a005da4b104c017294d73819596a28ec59c71597f8d0f2398da79a8b7784809d5e30cd5da9d53fc655101e2f4
@@ -27,6 +27,11 @@ module PacketGen
27
27
  # @return [Array<String>]
28
28
  attr_reader :raw_packets
29
29
 
30
+ # Get timestamps associated with {#packets} and {#raw_packets}
31
+ # @return [Array<Time>]
32
+ # @since 3.3.0
33
+ attr_reader :timestamps
34
+
30
35
  # Get interface name
31
36
  # @return [String]
32
37
  attr_reader :iface
@@ -54,6 +59,7 @@ module PacketGen
54
59
 
55
60
  @packets = []
56
61
  @raw_packets = []
62
+ @timestamps = []
57
63
  set_options iface, max, timeout, filter, promisc, parse, snaplen, monitor
58
64
  end
59
65
 
@@ -61,16 +67,18 @@ module PacketGen
61
67
  # @see {#initialize} for parameters
62
68
  # @yieldparam [Packet,String] packet if a block is given, yield each
63
69
  # captured packet (Packet or raw data String, depending on +:parse+ option)
70
+ # @yieldparam [Time] timestamp packet timestamp
64
71
  # @since 3.0.0 arguments are kwargs and no more a hash
65
72
  # @since 3.1.5 add monitor argument
73
+ # @since 3.3.0 add packet timestamp as second yield parameter
66
74
  # @author Sylvain Daubert
67
75
  # @author optix2000 - add monitor argument
68
76
  def start(iface: nil, max: nil, timeout: nil, filter: nil, promisc: false, parse: true, snaplen: nil, monitor: nil, &block)
69
77
  set_options iface, max, timeout, filter, promisc, parse, snaplen, monitor
70
78
 
71
79
  @cap_thread = Thread.new do
72
- PCAPRUBWrapper.capture(**capture_args) do |packet_data|
73
- add_packet(packet_data, &block)
80
+ PCAPRUBWrapper.capture(**capture_args) do |packet|
81
+ add_packet(packet, &block)
74
82
  break if defined?(@max) && (raw_packets.size >= @max)
75
83
  end
76
84
  end
@@ -119,18 +127,21 @@ module PacketGen
119
127
  PCAPRUBWrapper.filter_on(pcap: pcap, filter: filter)
120
128
  end
121
129
 
122
- def add_packet(data, &block)
123
- raw_packets << data
130
+ def add_packet(packet, &block)
131
+ raw_packets << packet.data
132
+ ts = Time.at(packet.time, packet.microsec.to_r, :usec)
133
+ timestamps << ts
134
+
124
135
  if @parse
125
136
  begin
126
- packet = Packet.parse(data)
137
+ packet = Packet.parse(packet.data)
127
138
  rescue ParseError
128
- packet = UnknownPacket.new.parse(data)
139
+ packet = UnknownPacket.new.parse(packet.data)
129
140
  end
130
141
  packets << packet
131
- block&.call(packet)
142
+ block&.call(packet, ts)
132
143
  elsif block
133
- yield data
144
+ yield data, ts
134
145
  end
135
146
  end
136
147
  end
@@ -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
@@ -67,9 +67,9 @@ module PacketGen
67
67
  @pointer = str[start, 2]
68
68
  break
69
69
  else
70
- label = add_label_from(str[start..-1])
70
+ label = add_label_from(str[start..])
71
71
  start += label.sz
72
- break if label.length.zero? || str[start..-1].length.zero?
72
+ break if label.empty? || str[start..].empty?
73
73
  end
74
74
  end
75
75
  # force resolution of compressed names
@@ -110,7 +110,7 @@ module PacketGen
110
110
  ptr = index & mask
111
111
  name = Name.new
112
112
  name.dns = @dns
113
- @pointer_name = name.read(self.dns.to_s[ptr..-1]).to_human
113
+ @pointer_name = name.read(self.dns.to_s[ptr..]).to_human
114
114
  end
115
115
 
116
116
  def record_from_hash(_hsh)
@@ -108,7 +108,7 @@ module PacketGen
108
108
  name.dns = self[:name].dns
109
109
 
110
110
  pref = Types::Int16.new.read(self[:rdata][0, 2])
111
- exchange = name.read(self[:rdata][2..-1]).to_human
111
+ exchange = name.read(self[:rdata][2..]).to_human
112
112
 
113
113
  '%u %s' % [pref.to_i, exchange]
114
114
  end
@@ -118,7 +118,7 @@ module PacketGen
118
118
  name = Name.new
119
119
  name.dns = self[:name].dns
120
120
  mname = name.read(self[:rdata]).dup
121
- rname = name.read(self[:rdata][mname.sz..-1])
121
+ rname = name.read(self[:rdata][mname.sz..])
122
122
 
123
123
  serial = Types::Int32.new.read(self[:rdata][mname.sz + rname.sz, 4])
124
124
  refresh = Types::Int32.new.read(self[:rdata][mname.sz + rname.sz + 4, 4])
@@ -403,7 +403,7 @@ module PacketGen
403
403
  define_applicable_fields
404
404
  if fcs
405
405
  old_read str[0...-4]
406
- self[:fcs].read str[-4..-1]
406
+ self[:fcs].read str[-4..]
407
407
  else
408
408
  old_read str
409
409
  end
@@ -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
@@ -107,7 +107,7 @@ module PacketGen
107
107
 
108
108
  def headers_and_payload_from_lines(lines)
109
109
  if (data_index = lines.find_index(''))
110
- data = lines[data_index + 1..-1].join("\n")
110
+ data = lines[data_index + 1..].join("\n")
111
111
  headers = lines[0..data_index - 1].join("\n")
112
112
  else
113
113
  headers = lines.join("\n")
@@ -126,7 +126,7 @@ module PacketGen
126
126
 
127
127
  self[:version].read first_line[0]
128
128
  self[:status_code].read first_line[1]
129
- self[:status_mesg].read first_line[2..-1].join(' ')
129
+ self[:status_mesg].read first_line[2..].join(' ')
130
130
  end
131
131
 
132
132
  def raise_on_bad_version_status
@@ -18,7 +18,7 @@ module PacketGen
18
18
  # @return [self]
19
19
  # array << '192.168.1.12'
20
20
  def push(addr)
21
- addr = addr.is_a?(Addr) ? addr : Addr.new.from_human(addr)
21
+ addr = Addr.new.from_human(addr) unless addr.is_a?(Addr)
22
22
  super(addr)
23
23
  end
24
24
  end
@@ -77,7 +77,7 @@ module PacketGen
77
77
  Option.constants.each do |cst|
78
78
  next unless cst.to_s.end_with? '_TYPE'
79
79
 
80
- optname = cst.to_s.sub(/_TYPE/, '')
80
+ optname = cst.to_s.sub('_TYPE', '')
81
81
  @types[optname] = Option.const_get(cst)
82
82
  end
83
83
  @types
@@ -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
@@ -173,6 +190,10 @@ module PacketGen
173
190
  # 16-bit stream ID
174
191
  # @return [Integer]
175
192
  define_field :id, Types::Int16
193
+
194
+ def to_human
195
+ super << ":#{self.id}"
196
+ end
176
197
  end
177
198
 
178
199
  # Router Alert IP option
@@ -183,6 +204,10 @@ module PacketGen
183
204
  # 16-bit value. Should be 0.
184
205
  # @return [Integer]
185
206
  define_field :value, Types::Int16, default: 0
207
+
208
+ def to_human
209
+ super << ":#{self.value}"
210
+ end
186
211
  end
187
212
  end
188
213
  end
@@ -107,7 +107,7 @@ module PacketGen
107
107
  # @return [self]
108
108
  # array << '2001:1234::125'
109
109
  def push(addr)
110
- addr = addr.is_a?(Addr) ? addr : Addr.new.from_human(addr)
110
+ addr = Addr.new.from_human(addr) unless addr.is_a?(Addr)
111
111
  super(addr)
112
112
  end
113
113
  end
@@ -78,7 +78,7 @@ module PacketGen
78
78
  # @return [Integer]
79
79
  def calc_checksum
80
80
  c0 = c1 = 0
81
- to_s[2..-1].unpack('C*').each do |byte|
81
+ to_s[2..].unpack('C*').each do |byte|
82
82
  c0 += byte
83
83
  c1 += c0
84
84
  end
@@ -79,7 +79,7 @@ module PacketGen
79
79
  # @return [Integer]
80
80
  def calc_checksum
81
81
  c0 = c1 = 0
82
- to_s[2..-1].unpack('C*').each do |byte|
82
+ to_s[2..].unpack('C*').each do |byte|
83
83
  c0 += byte
84
84
  c1 += c0
85
85
  end
@@ -74,6 +74,18 @@ module PacketGen
74
74
  # @author Sylvain Daubert
75
75
  class VariableBindings < RASN1::Model
76
76
  sequence_of :bindings, VarBind
77
+
78
+ # Get 'idx'th element from the list
79
+ # @return [VarBind,nil]
80
+ def [](idx)
81
+ value[idx]
82
+ end
83
+
84
+ # Get element counts in list
85
+ # @return [Integer]
86
+ def size
87
+ value.size
88
+ end
77
89
  end
78
90
 
79
91
  # Class to handle GetRequest PDU
@@ -119,6 +131,10 @@ module PacketGen
119
131
  integer(:error, value: 0, enum: ERRORS),
120
132
  integer(:error_index, value: 0),
121
133
  model(:varbindlist, VariableBindings)]
134
+
135
+ def initialize(args={})
136
+ super
137
+ end
122
138
  end
123
139
 
124
140
  # Class to handle GetNextRequest PDU
@@ -264,11 +280,12 @@ module PacketGen
264
280
  data.chosen = options[:chosen_pdu] if options[:chosen_pdu]
265
281
  return unless options[:pdu]
266
282
 
267
- data.root.value[data.chosen] = data.root.chosen_value.class.new(options[:pdu])
283
+ klass = data.root.chosen_value.class
284
+ data.root.value[data.chosen] = klass.new(options[:pdu])
268
285
  end
269
286
 
270
287
  # accessor to data payload
271
- # @return [GetRequest]
288
+ # @return [ASN1::Types::Choice]
272
289
  def data
273
290
  @elements[:data]
274
291
  end
@@ -24,7 +24,7 @@ module PacketGen
24
24
  Option.constants.each do |cst|
25
25
  next unless cst.to_s.end_with? '_KIND'
26
26
 
27
- optname = cst.to_s.sub(/_KIND/, '')
27
+ optname = cst.to_s.sub('_KIND', '')
28
28
  @klasses[Option.const_get(cst)] = TCP.const_get(optname)
29
29
  end
30
30
  @klasses
@@ -46,7 +46,7 @@ module PacketGen
46
46
  def method_name
47
47
  return @method_name if defined? @method_name
48
48
 
49
- @method_name = protocol_name.downcase.gsub(/::/, '_')
49
+ @method_name = protocol_name.downcase.gsub('::', '_')
50
50
  end
51
51
 
52
52
  # @abstract Should be redefined by subclasses. This method should check invariant
@@ -36,7 +36,8 @@ module PacketGen
36
36
  # @param [Integer] hexsize
37
37
  # @return [String]
38
38
  def self.int_dec_hex(value, hexsize)
39
- "%-16s (0x%0#{hexsize}x)" % [value.to_i, value.to_i]
39
+ fmt = "%-16s (0x%0#{hexsize}x)"
40
+ fmt % [value.to_i, value.to_i]
40
41
  end
41
42
 
42
43
  # @param [String] str
@@ -44,7 +45,8 @@ module PacketGen
44
45
  # @param [Integer] hexsize
45
46
  # @return [String]
46
47
  def self.enum_human_hex(str, int, hexsize)
47
- "%-16s (0x%0#{hexsize}x)" % [str, int]
48
+ fmt = "%-16s (0x%0#{hexsize}x)"
49
+ fmt % [str, int]
48
50
  end
49
51
 
50
52
  # Simple formatter to inspect an attribute
@@ -90,7 +92,8 @@ module PacketGen
90
92
  val = case attr
91
93
  when RASN1::Types::Enumerated
92
94
  hexsize = attr.value_size * 2
93
- "%-16s (0x%0#{hexsize}x)" % [attr.value, attr.to_i]
95
+ fmt = "%-16s (0x%0#{hexsize}x)"
96
+ fmt % [attr.value, attr.to_i]
94
97
  when RASN1::Types::Integer
95
98
  int_dec_hex(attr.value, attr.value_size * 2)
96
99
  when RASN1::Model
@@ -81,7 +81,9 @@ module PacketGen
81
81
  # @see Capture#initialize
82
82
  # @yieldparam [Packet,String] packet if a block is given, yield each
83
83
  # captured packet (Packet or raw data String, depending on +:parse+ option)
84
+ # @yieldparam [Time] timestamp packet timestamp
84
85
  # @return [Array<Packet>] captured packet
86
+ # @since 3.3.0 add packet timestamp as second yield parameter
85
87
  def self.capture(**kwargs, &block)
86
88
  capture = Capture.new(**kwargs)
87
89
  if block
@@ -21,11 +21,11 @@ module PacketGen
21
21
  }.freeze
22
22
 
23
23
  # @private
24
- BLOCK_TYPES = PcapNG.constants(false).select { |c| c.to_s.include?('_TYPE') }.map do |c|
24
+ BLOCK_TYPES = PcapNG.constants(false).select { |c| c.to_s.include?('_TYPE') }.to_h do |c|
25
25
  type_value = PcapNG.const_get(c).to_i
26
26
  klass = PcapNG.const_get(c.to_s.delete_suffix('_TYPE'))
27
27
  [type_value, klass]
28
- end.to_h.freeze
28
+ end.freeze
29
29
 
30
30
  # Get file sections
31
31
  # @return [Array]
@@ -44,7 +44,7 @@ module PacketGen
44
44
  # @param [Boolean] promisc
45
45
  # @param [String] filter BPF filter
46
46
  # @param [Boolean] monitor
47
- # @yieldparam [String] packet_data binary packet data
47
+ # @yieldparam [String] packet_data packet data
48
48
  # @return [void]
49
49
  # @author Sylvain Daubert
50
50
  # @author optix2000 - add support for setting monitor mode
@@ -52,7 +52,7 @@ module PacketGen
52
52
  def self.capture(iface:, snaplen: DEFAULT_SNAPLEN, promisc: DEFAULT_PROMISC, filter: nil, monitor: nil, &block)
53
53
  pcap = self.open_iface(iface: iface, snaplen: snaplen, promisc: promisc, monitor: monitor)
54
54
  pcap.setfilter filter unless filter.nil?
55
- pcap.each(&block)
55
+ pcap.each_packet(&block)
56
56
  end
57
57
 
58
58
  # Inject given data onto wire
@@ -19,7 +19,7 @@ module PacketGen
19
19
  proto_constants = Socket.constants.grep(/IPPROTO_/)
20
20
  @cache = {}
21
21
  proto_constants.each do |const_sym|
22
- name = const_sym.to_s[8..-1].downcase
22
+ name = const_sym.to_s[8..].downcase
23
23
  number = Socket.const_get(const_sym)
24
24
  @cache[name] = number
25
25
  end
@@ -154,26 +154,19 @@ module PacketGen
154
154
  self
155
155
  end
156
156
 
157
- # rubocop:disable Metrics/CyclomaticComplexity
158
-
159
- # Populate object from a string
160
- # @param [String] str
157
+ # Populate object from a string or from an array of hashes
158
+ # @param [String, Array<Hash>] data
161
159
  # @return [self]
162
- def read(str)
160
+ def read(data)
163
161
  clear
164
- return self if str.nil?
165
- return self if @counter&.to_i&.zero?
166
-
167
- str = read_with_length_from(str)
168
- until str.empty?
169
- obj = create_object_from_str(str)
170
- @array << obj
171
- str.slice!(0, obj.sz)
172
- break if @counter && self.size == @counter.to_i
162
+ case data
163
+ when ::Array
164
+ read_from_array(data)
165
+ else
166
+ read_from_string(data)
173
167
  end
174
168
  self
175
169
  end
176
- # rubocop:enable Metrics/CyclomaticComplexity
177
170
 
178
171
  # Get size in bytes
179
172
  # @return [Integer]
@@ -201,6 +194,28 @@ module PacketGen
201
194
 
202
195
  private
203
196
 
197
+ # rubocop:disable Metrics/CyclomaticComplexity
198
+
199
+ def read_from_string(str)
200
+ return self if str.nil? || @counter&.to_i&.zero?
201
+
202
+ str = read_with_length_from(str)
203
+ until str.empty? || (@counter && self.size == @counter.to_i)
204
+ obj = create_object_from_str(str)
205
+ @array << obj
206
+ str.slice!(0, obj.sz)
207
+ end
208
+ end
209
+ # rubocop:enable Metrics/CyclomaticComplexity
210
+
211
+ def read_from_array(ary)
212
+ return self if ary.empty?
213
+
214
+ ary.each do |hsh|
215
+ self << hsh
216
+ end
217
+ end
218
+
204
219
  def record_from_hash(hsh)
205
220
  obj_klass = self.class.set_of_klass
206
221
  raise NotImplementedError, 'class should define #record_from_hash or declare type of elements in set with .set_of' unless obj_klass
@@ -322,12 +322,14 @@ module PacketGen
322
322
  define << "def #{name}; self[:#{name}].to_i; end"
323
323
  define << "def #{name}=(val) self[:#{name}].value = val; end"
324
324
  else
325
+ # rubocop:disable Layout/LineContinuationLeadingSpace
325
326
  define << "def #{name}\n" \
326
327
  " to_and_from_human?(:#{name}) ? self[:#{name}].to_human : self[:#{name}]\n" \
327
328
  'end'
328
329
  define << "def #{name}=(val)\n" \
329
330
  " to_and_from_human?(:#{name}) ? self[:#{name}].from_human(val) : self[:#{name}].read(val)\n" \
330
331
  'end'
332
+ # rubocop:enable Layout/LineContinuationLeadingSpace
331
333
  end
332
334
 
333
335
  define.delete_at(1) if instance_methods.include? "#{name}=".to_sym
@@ -479,7 +481,7 @@ module PacketGen
479
481
  fields.each do |field|
480
482
  next unless present?(field)
481
483
 
482
- obj = self[field].read str[start..-1]
484
+ obj = self[field].read str[start..]
483
485
  start += self[field].sz
484
486
  self[field] = obj unless obj == self[field]
485
487
  end
@@ -523,7 +525,7 @@ module PacketGen
523
525
  # Return object as a hash
524
526
  # @return [Hash] keys: attributes, values: attribute values
525
527
  def to_h
526
- fields.map { |f| [f, @fields[f].to_human] }.to_h
528
+ fields.to_h { |f| [f, @fields[f].to_human] }
527
529
  end
528
530
 
529
531
  # Get offset of given field in {Fields} structure.
@@ -61,7 +61,8 @@ module PacketGen
61
61
 
62
62
  # @return [String]
63
63
  def inspect
64
- # TODO
64
+ str = Inspect.dashed_line(self.class)
65
+ str << Inspect.inspect_body(body)
65
66
  end
66
67
 
67
68
  # equality if {#to_s} is equal
@@ -74,7 +75,7 @@ module PacketGen
74
75
  # @return [Boolean]
75
76
  def ===(other)
76
77
  case other
77
- when UnknwonPacket
78
+ when UnknownPacket
78
79
  self == other
79
80
  else
80
81
  false
@@ -112,9 +112,11 @@ module PacketGen
112
112
 
113
113
  # Say if spoofing on given target is active or not
114
114
  # @param [String] target_ip target IP address
115
- # @return [Boolean,nil]
115
+ # @return [Boolean]
116
116
  def active?(target_ip)
117
+ # rubocop:disable Style/ReturnNilInPredicateMethodDefinition
117
118
  return unless @targets.key?(target_ip)
119
+ # rubocop:enable Style/ReturnNilInPredicateMethodDefinition
118
120
 
119
121
  @targets[target_ip][:active]
120
122
  end
@@ -19,29 +19,38 @@ module PacketGen
19
19
  # @private
20
20
  ARP_FILTER = 'arp src %<ipaddr>s and ether dst %<hwaddr>s'
21
21
  # @private
22
- MITM_FILTER = '((ip src %<target1>s and not ip dst %<local_ip>s) or' \
23
- ' (ip src %<target2>s and not ip dst %<local_ip>s) or' \
24
- ' (ip dst %<target1>s and not ip src %<local_ip>s) or' \
25
- ' (ip dst %<target2>s and not ip src %<local_ip>s))' \
26
- ' and ether dst %<local_mac>s'
22
+ MITM_FILTER = '((ip src %<target1>s and not ip dst %<local_ip>s) or ' \
23
+ '(ip src %<target2>s and not ip dst %<local_ip>s) or ' \
24
+ '(ip dst %<target1>s and not ip src %<local_ip>s) or ' \
25
+ '(ip dst %<target2>s and not ip src %<local_ip>s)) ' \
26
+ 'and ether dst %<local_mac>s'
27
+
28
+ # @private
29
+ ARP_PATH = '/usr/sbin/arp'
30
+ # @private
31
+ IP_PATH = '/usr/bin/ip'
32
+ # @private
33
+ ARP_LINE_RE = /\((\d+\.\d+\.\d+\.\d+)\) at (([a-fA-F0-9]{2}:){5}[a-fA-F0-9]{2})(?: \[ether\])? on (\w+)/.freeze
34
+ # @private
35
+ IP_LINE_RE = /^(\d+\.\d+\.\d+\.\d+) dev (\w+) lladdr (([a-fA-F0-9]{2}:){5}[a-fA-F0-9]{2})/.freeze
27
36
 
28
37
  # Get local ARP cache
29
38
  # @return [Hash] key: IP address, value: array containing MAC address and
30
39
  # interface name
31
40
  def self.arp_cache
32
- return self.cache_from_arp_command if File.exist?('/usr/sbin/arp')
33
- return self.cache_from_ip_command if File.exist?('/usr/bin/ip')
41
+ return self.cache_from_arp_command if File.exist?(ARP_PATH)
42
+ return self.cache_from_ip_command if File.exist?(IP_PATH)
34
43
 
35
44
  {}
36
45
  end
37
46
 
38
47
  # @private
39
- def self.cache_from_arp_command
40
- raw_cache = `/usr/sbin/arp -an`
48
+ def self.cache_from_arp_command(raw_cache=nil)
49
+ raw_cache ||= `#{ARP_PATH} -an`
41
50
 
42
51
  cache = {}
43
52
  raw_cache.split("\n").each do |line|
44
- match = line.match(/\((\d+\.\d+\.\d+\.\d+)\) at (([a-fA-F0-9]{2}:){5}[a-fA-F0-9]{2})(?: \[ether\])? on (\w+)/)
53
+ match = line.match(ARP_LINE_RE)
45
54
  cache[match[1]] = [match[2], match[4]] if match
46
55
  end
47
56
 
@@ -49,12 +58,12 @@ module PacketGen
49
58
  end
50
59
 
51
60
  # @private
52
- def self.cache_from_ip_command
53
- raw_cache = `ip neigh`
61
+ def self.cache_from_ip_command(raw_cache=nil)
62
+ raw_cache ||= `#{IP_PATH} neigh`
54
63
 
55
64
  cache = {}
56
65
  raw_cache.split("\n").each do |line|
57
- match = line.match(/^(\d+\.\d+\.\d+\.\d+) dev (\w+) lladdr (([a-fA-F0-9]{2}:){5}[a-fA-F0-9]{2})/)
66
+ match = line.match(IP_LINE_RE)
58
67
  cache[match[1]] = [match[3], match[2]] if match
59
68
  end
60
69
 
@@ -155,7 +164,7 @@ module PacketGen
155
164
  # end
156
165
  # @since 2.2.0
157
166
  # @raise [RuntimeError] user don't have permission to capture packets on network device.
158
- def self.mitm(target1, target2, options={})
167
+ def self.mitm(target1, target2, options={}, &block)
159
168
  options = { iface: PacketGen.default_iface }.merge(options)
160
169
 
161
170
  spoofer = Utils::ARPSpoofer.new(options)
@@ -168,7 +177,7 @@ module PacketGen
168
177
  filter: MITM_FILTER % { target1: target1, target2: target2, local_ip: cfg.ipaddr(options[:iface]), local_mac: my_mac })
169
178
 
170
179
  spoofer.start_all
171
- mitm_core(capture, target1, target2, my_mac, &proc)
180
+ mitm_core(capture, target1, target2, my_mac, &block)
172
181
  spoofer.stop_all
173
182
  end
174
183
 
@@ -10,5 +10,5 @@
10
10
  # @author Sylvain Daubert
11
11
  module PacketGen
12
12
  # PacketGen version
13
- VERSION = '3.2.1'
13
+ VERSION = '3.3.0'
14
14
  end
data/lib/packetgen.rb CHANGED
@@ -64,8 +64,10 @@ module PacketGen
64
64
  # Shortcut for {Packet.capture}
65
65
  # Same arguments as {Capture#initialize}
66
66
  # @see Capture#initialize
67
- # @yieldparam [Packet] packet
67
+ # @yieldparam [Packet,String] packet
68
+ # @yieldparam [Time] timestamp
68
69
  # @return [Array<Packet>]
70
+ # @since 3.3.0 add packet timestamp as second yield parameter
69
71
  def self.capture(**kwargs)
70
72
  Packet.capture(**kwargs) { |packet| yield packet if block_given? }
71
73
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: packetgen
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.2.1
4
+ version: 3.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sylvain Daubert
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-12-27 00:00:00.000000000 Z
11
+ date: 2023-08-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: interfacez
@@ -30,76 +30,28 @@ dependencies:
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: 0.12.0
33
+ version: 0.13.0
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: 0.12.0
40
+ version: 0.13.0
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: rasn1
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '0.8'
48
- - - ">="
49
- - !ruby/object:Gem::Version
50
- version: 0.8.0
47
+ version: '0.12'
51
48
  type: :runtime
52
49
  prerelease: false
53
50
  version_requirements: !ruby/object:Gem::Requirement
54
51
  requirements:
55
52
  - - "~>"
56
53
  - !ruby/object:Gem::Version
57
- version: '0.8'
58
- - - ">="
59
- - !ruby/object:Gem::Version
60
- version: 0.8.0
61
- - !ruby/object:Gem::Dependency
62
- name: rake
63
- requirement: !ruby/object:Gem::Requirement
64
- requirements:
65
- - - "~>"
66
- - !ruby/object:Gem::Version
67
- version: '13.0'
68
- type: :development
69
- prerelease: false
70
- version_requirements: !ruby/object:Gem::Requirement
71
- requirements:
72
- - - "~>"
73
- - !ruby/object:Gem::Version
74
- version: '13.0'
75
- - !ruby/object:Gem::Dependency
76
- name: rspec
77
- requirement: !ruby/object:Gem::Requirement
78
- requirements:
79
- - - "~>"
80
- - !ruby/object:Gem::Version
81
- version: '3.7'
82
- type: :development
83
- prerelease: false
84
- version_requirements: !ruby/object:Gem::Requirement
85
- requirements:
86
- - - "~>"
87
- - !ruby/object:Gem::Version
88
- version: '3.7'
89
- - !ruby/object:Gem::Dependency
90
- name: yard
91
- requirement: !ruby/object:Gem::Requirement
92
- requirements:
93
- - - "~>"
94
- - !ruby/object:Gem::Version
95
- version: '0.9'
96
- type: :development
97
- prerelease: false
98
- version_requirements: !ruby/object:Gem::Requirement
99
- requirements:
100
- - - "~>"
101
- - !ruby/object:Gem::Version
102
- version: '0.9'
54
+ version: '0.12'
103
55
  description: |
104
56
  PacketGen is a network packet manipulation library. It allows reading, parsing
105
57
  and sending network packets with fun.
@@ -260,7 +212,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
260
212
  requirements:
261
213
  - - ">="
262
214
  - !ruby/object:Gem::Version
263
- version: 2.5.0
215
+ version: 2.6.0
264
216
  required_rubygems_version: !ruby/object:Gem::Requirement
265
217
  requirements:
266
218
  - - ">="