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 +4 -4
- data/lib/packetgen/capture.rb +19 -8
- data/lib/packetgen/header/dhcpv6/duid.rb +6 -0
- data/lib/packetgen/header/dns/name.rb +3 -3
- data/lib/packetgen/header/dns/rr.rb +2 -2
- data/lib/packetgen/header/dot11.rb +1 -1
- data/lib/packetgen/header/http/headers.rb +16 -1
- data/lib/packetgen/header/http/request.rb +1 -1
- data/lib/packetgen/header/http/response.rb +1 -1
- data/lib/packetgen/header/ip/option.rb +36 -11
- data/lib/packetgen/header/ipv6/addr.rb +1 -1
- data/lib/packetgen/header/ospfv2/lsa_header.rb +1 -1
- data/lib/packetgen/header/ospfv3/lsa_header.rb +1 -1
- data/lib/packetgen/header/snmp.rb +19 -2
- data/lib/packetgen/header/tcp/options.rb +1 -1
- data/lib/packetgen/headerable.rb +1 -1
- data/lib/packetgen/inspect.rb +6 -3
- data/lib/packetgen/packet.rb +2 -0
- data/lib/packetgen/pcapng/file.rb +2 -2
- data/lib/packetgen/pcaprub_wrapper.rb +2 -2
- data/lib/packetgen/proto.rb +1 -1
- data/lib/packetgen/types/array.rb +30 -15
- data/lib/packetgen/types/fields.rb +4 -2
- data/lib/packetgen/unknown_packet.rb +3 -2
- data/lib/packetgen/utils/arp_spoofer.rb +3 -1
- data/lib/packetgen/utils.rb +24 -15
- data/lib/packetgen/version.rb +1 -1
- data/lib/packetgen.rb +3 -1
- metadata +7 -55
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: de85a6f18ac001823cdbd0802c8946ead811be724c9069266b78c493d99feb6b
|
4
|
+
data.tar.gz: ef73ca367e87bb47ca4f37ee387c7a73d0ad0f19cebaca151443560f42508559
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: '01605812af7b7d53111bfdc0d4d095cb6f0df4975634db17700d7c15c6acc63ba82585e6c5a5d2d93c130d566f45bb62d9ff3709c27e0f7cf4ea0c20ba7c172f'
|
7
|
+
data.tar.gz: e4a54c74e032ee12d744f94c657416591c0f056a005da4b104c017294d73819596a28ec59c71597f8d0f2398da79a8b7784809d5e30cd5da9d53fc655101e2f4
|
data/lib/packetgen/capture.rb
CHANGED
@@ -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 |
|
73
|
-
add_packet(
|
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(
|
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
|
@@ -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
|
70
|
+
label = add_label_from(str[start..])
|
71
71
|
start += label.sz
|
72
|
-
break if label.
|
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
|
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
|
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
|
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])
|
@@ -34,13 +34,28 @@ module PacketGen
|
|
34
34
|
|
35
35
|
k, v = h.split(':', 2)
|
36
36
|
[k, v.strip]
|
37
|
-
end.
|
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
|
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
|
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 =
|
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(
|
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
|
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
|
-
|
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
|
-
|
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
|
@@ -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
|
-
|
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 [
|
288
|
+
# @return [ASN1::Types::Choice]
|
272
289
|
def data
|
273
290
|
@elements[:data]
|
274
291
|
end
|
data/lib/packetgen/headerable.rb
CHANGED
@@ -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
|
data/lib/packetgen/inspect.rb
CHANGED
@@ -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)"
|
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)"
|
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)"
|
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
|
data/lib/packetgen/packet.rb
CHANGED
@@ -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') }.
|
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.
|
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
|
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.
|
55
|
+
pcap.each_packet(&block)
|
56
56
|
end
|
57
57
|
|
58
58
|
# Inject given data onto wire
|
data/lib/packetgen/proto.rb
CHANGED
@@ -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
|
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
|
-
#
|
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(
|
160
|
+
def read(data)
|
163
161
|
clear
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
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
|
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.
|
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
|
-
|
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
|
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
|
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
|
data/lib/packetgen/utils.rb
CHANGED
@@ -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
|
-
'
|
24
|
-
'
|
25
|
-
'
|
26
|
-
'
|
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?(
|
33
|
-
return self.cache_from_ip_command if File.exist?(
|
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
|
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(
|
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
|
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(
|
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, &
|
180
|
+
mitm_core(capture, target1, target2, my_mac, &block)
|
172
181
|
spoofer.stop_all
|
173
182
|
end
|
174
183
|
|
data/lib/packetgen/version.rb
CHANGED
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.
|
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:
|
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.
|
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.
|
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.
|
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.
|
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.
|
215
|
+
version: 2.6.0
|
264
216
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
265
217
|
requirements:
|
266
218
|
- - ">="
|