packetgen 3.2.1 → 3.3.0

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.
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
  - - ">="