ruby-masscan 0.1.0 → 0.1.1
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/ChangeLog.md +7 -0
- data/lib/masscan/banner.rb +31 -1
- data/lib/masscan/parsers/binary.rb +52 -46
- data/lib/masscan/parsers/json.rb +13 -13
- data/lib/masscan/parsers/list.rb +11 -13
- data/lib/masscan/status.rb +33 -0
- data/lib/masscan/version.rb +1 -1
- data/spec/parsers/binary_spec.rb +504 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f03fa94f7c501646be712a24b98fe07a6dd156c054c7ae27714cac80b1fbb5e8
|
4
|
+
data.tar.gz: a346c7b055da689cad8571a38383b43a5e797889d6206b85ca7d2a64e25b904f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ecdea1cc3b62cf1d0dbb2b7771807c05f2e6339b573f6208dcfb21e94fdaad160e51adc077e158ec6c4b4bc9426c7925dfd376d53d455922c5e318e6ee999936
|
7
|
+
data.tar.gz: 88bbf9a1332946ae7688fae852a1a3565269a853eff17434dc8e0b1fd7eae2c3e14b09cc64fa3fc80a06781f3fd644b97e5f97fcc52ccd2aa71cbc2a32b15f3c
|
data/ChangeLog.md
CHANGED
@@ -1,3 +1,10 @@
|
|
1
|
+
### 0.1.1 / 2021-09-09
|
2
|
+
|
3
|
+
* Added missing {Masscan::Banner#ttl}.
|
4
|
+
* Fixed {Masscan::Parsers::Binary} to populate {Masscan::Banner#ttl}.
|
5
|
+
* Fixed {Masscan::Parsers::Binary#parse_status} to populate
|
6
|
+
{Masscan::Status#reason} and {Masscan::Status#ttl}.
|
7
|
+
|
1
8
|
### 0.1.0 / 2021-08-31
|
2
9
|
|
3
10
|
* Initial release:
|
data/lib/masscan/banner.rb
CHANGED
@@ -2,7 +2,37 @@ module Masscan
|
|
2
2
|
#
|
3
3
|
# Represents a banner record.
|
4
4
|
#
|
5
|
-
class Banner < Struct.new(:protocol,:port,:ip,:timestamp,:app_protocol,:payload)
|
5
|
+
class Banner < Struct.new(:protocol,:port,:ttl,:ip,:timestamp,:app_protocol,:payload)
|
6
|
+
|
7
|
+
#
|
8
|
+
# Initializes the banner.
|
9
|
+
#
|
10
|
+
# @param [:icmp, :tcp, :udp, :sctp] protocol
|
11
|
+
# The IP protocol.
|
12
|
+
#
|
13
|
+
# @param [Integer] port
|
14
|
+
# The port number.
|
15
|
+
#
|
16
|
+
# @param [Integer, nil] ttl
|
17
|
+
# The optional TTL.
|
18
|
+
#
|
19
|
+
# @param [IPAddr] ip
|
20
|
+
# The IP address.
|
21
|
+
#
|
22
|
+
# @param [Time] timestamp
|
23
|
+
# The record timestamp.
|
24
|
+
#
|
25
|
+
# @param [Symbol] app_protocol
|
26
|
+
# The application protocol.
|
27
|
+
#
|
28
|
+
# @param [String] payload
|
29
|
+
# The banner/capture payload.
|
30
|
+
#
|
31
|
+
# @api private
|
32
|
+
#
|
33
|
+
def initialize(protocol: , port: , ttl: nil, ip: , timestamp: , app_protocol: , payload: )
|
34
|
+
super(protocol,port,ttl,ip,timestamp,app_protocol,payload)
|
35
|
+
end
|
6
36
|
|
7
37
|
alias service app_protocol
|
8
38
|
alias banner payload
|
@@ -366,12 +366,14 @@ module Masscan
|
|
366
366
|
end
|
367
367
|
|
368
368
|
return Status.new(
|
369
|
-
status,
|
370
|
-
protocol,
|
371
|
-
port,
|
372
|
-
|
373
|
-
|
374
|
-
|
369
|
+
status: status,
|
370
|
+
protocol: protocol,
|
371
|
+
port: port,
|
372
|
+
reason: reason,
|
373
|
+
ttl: ttl,
|
374
|
+
ip: ip,
|
375
|
+
timestamp: timestamp,
|
376
|
+
mac: mac
|
375
377
|
)
|
376
378
|
end
|
377
379
|
|
@@ -396,12 +398,13 @@ module Masscan
|
|
396
398
|
ttl = 0
|
397
399
|
|
398
400
|
return Banner.new(
|
399
|
-
ip_proto,
|
400
|
-
port,
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
401
|
+
protocol: ip_proto,
|
402
|
+
port: port,
|
403
|
+
ttl: ttl,
|
404
|
+
ip: ip,
|
405
|
+
timestamp: timestamp,
|
406
|
+
app_protocol: app_proto,
|
407
|
+
payload: payload
|
405
408
|
)
|
406
409
|
end
|
407
410
|
|
@@ -419,7 +422,7 @@ module Masscan
|
|
419
422
|
return
|
420
423
|
end
|
421
424
|
|
422
|
-
timestamp, ip,
|
425
|
+
timestamp, ip, ip_proto, port, app_proto, payload = buffer.unpack('L>L>CS>S>A*')
|
423
426
|
|
424
427
|
timestamp = decode_timestamp(timestamp)
|
425
428
|
ip = decode_ipv4(ip)
|
@@ -430,12 +433,13 @@ module Masscan
|
|
430
433
|
ttl = 0
|
431
434
|
|
432
435
|
return Banner.new(
|
433
|
-
ip_proto,
|
434
|
-
port,
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
436
|
+
protocol: ip_proto,
|
437
|
+
port: port,
|
438
|
+
ttl: ttl,
|
439
|
+
ip: ip,
|
440
|
+
timestamp: timestamp,
|
441
|
+
app_protocol: app_proto,
|
442
|
+
payload: payload
|
439
443
|
)
|
440
444
|
end
|
441
445
|
|
@@ -467,14 +471,14 @@ module Masscan
|
|
467
471
|
end
|
468
472
|
|
469
473
|
return Status.new(
|
470
|
-
status,
|
471
|
-
ip_proto,
|
472
|
-
port,
|
473
|
-
reason,
|
474
|
-
ttl,
|
475
|
-
ip,
|
476
|
-
timestamp,
|
477
|
-
mac
|
474
|
+
status: status,
|
475
|
+
protocol: ip_proto,
|
476
|
+
port: port,
|
477
|
+
reason: reason,
|
478
|
+
ttl: ttl,
|
479
|
+
ip: ip,
|
480
|
+
timestamp: timestamp,
|
481
|
+
mac: mac
|
478
482
|
)
|
479
483
|
end
|
480
484
|
|
@@ -499,12 +503,13 @@ module Masscan
|
|
499
503
|
app_proto = lookup_app_protocol(app_proto)
|
500
504
|
|
501
505
|
return Banner.new(
|
502
|
-
ip_proto,
|
503
|
-
port,
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
506
|
+
protocol: ip_proto,
|
507
|
+
port: port,
|
508
|
+
ttl: ttl,
|
509
|
+
ip: ip,
|
510
|
+
timestamp: timestamp,
|
511
|
+
app_protocol: app_proto,
|
512
|
+
payload: payload
|
508
513
|
)
|
509
514
|
end
|
510
515
|
|
@@ -541,13 +546,13 @@ module Masscan
|
|
541
546
|
ipv6 = decode_ipv6(ipv6_hi,ipv6_lo)
|
542
547
|
|
543
548
|
return Status.new(
|
544
|
-
status,
|
545
|
-
ip_proto,
|
546
|
-
port,
|
547
|
-
reason,
|
548
|
-
ttl,
|
549
|
-
ipv6,
|
550
|
-
timestamp
|
549
|
+
status: status,
|
550
|
+
protocol: ip_proto,
|
551
|
+
port: port,
|
552
|
+
reason: reason,
|
553
|
+
ttl: ttl,
|
554
|
+
ip: ipv6,
|
555
|
+
timestamp: timestamp
|
551
556
|
)
|
552
557
|
end
|
553
558
|
|
@@ -577,12 +582,13 @@ module Masscan
|
|
577
582
|
ipv6 = decode_ipv6(ipv6_hi,ipv6_lo)
|
578
583
|
|
579
584
|
return Banner.new(
|
580
|
-
ip_proto,
|
581
|
-
port,
|
582
|
-
|
583
|
-
|
584
|
-
|
585
|
-
|
585
|
+
protocol: ip_proto,
|
586
|
+
port: port,
|
587
|
+
ttl: ttl,
|
588
|
+
ip: ipv6,
|
589
|
+
timestamp: timestamp,
|
590
|
+
app_protocol: app_proto,
|
591
|
+
payload: payload
|
586
592
|
)
|
587
593
|
end
|
588
594
|
|
data/lib/masscan/parsers/json.rb
CHANGED
@@ -74,12 +74,12 @@ module Masscan
|
|
74
74
|
service_banner = service_json['banner']
|
75
75
|
|
76
76
|
yield Banner.new(
|
77
|
-
proto,
|
78
|
-
port,
|
79
|
-
ip,
|
80
|
-
timestamp,
|
81
|
-
service_name,
|
82
|
-
service_banner
|
77
|
+
protocol: proto,
|
78
|
+
port: port,
|
79
|
+
ip: ip,
|
80
|
+
timestamp: timestamp,
|
81
|
+
app_protocol: service_name,
|
82
|
+
payload: service_banner
|
83
83
|
)
|
84
84
|
else
|
85
85
|
status = parse_status(port_json['status'])
|
@@ -87,13 +87,13 @@ module Masscan
|
|
87
87
|
reason = parse_reason(port_json['reason'])
|
88
88
|
|
89
89
|
yield Status.new(
|
90
|
-
status,
|
91
|
-
proto,
|
92
|
-
port,
|
93
|
-
reason,
|
94
|
-
ttl,
|
95
|
-
ip,
|
96
|
-
timestamp
|
90
|
+
status: status,
|
91
|
+
protocol: proto,
|
92
|
+
port: port,
|
93
|
+
reason: reason,
|
94
|
+
ttl: ttl,
|
95
|
+
ip: ip,
|
96
|
+
timestamp: timestamp
|
97
97
|
)
|
98
98
|
end
|
99
99
|
end
|
data/lib/masscan/parsers/list.rb
CHANGED
@@ -57,24 +57,22 @@ module Masscan
|
|
57
57
|
type, ip_proto, port, ip, timestamp = line.split(' ',5)
|
58
58
|
|
59
59
|
yield Status.new(
|
60
|
-
parse_status(type),
|
61
|
-
parse_ip_protocol(ip_proto),
|
62
|
-
port.to_i,
|
63
|
-
|
64
|
-
|
65
|
-
parse_ip(ip),
|
66
|
-
parse_timestamp(timestamp)
|
60
|
+
status: parse_status(type),
|
61
|
+
protocol: parse_ip_protocol(ip_proto),
|
62
|
+
port: port.to_i,
|
63
|
+
ip: parse_ip(ip),
|
64
|
+
timestamp: parse_timestamp(timestamp)
|
67
65
|
)
|
68
66
|
elsif line.start_with?('banner ')
|
69
67
|
type, ip_proto, port, ip, timestamp, app_proto, banner = line.split(' ',7)
|
70
68
|
|
71
69
|
yield Banner.new(
|
72
|
-
parse_ip_protocol(ip_proto),
|
73
|
-
port.to_i,
|
74
|
-
parse_ip(ip),
|
75
|
-
parse_timestamp(timestamp),
|
76
|
-
parse_app_protocol(app_proto),
|
77
|
-
banner
|
70
|
+
protocol: parse_ip_protocol(ip_proto),
|
71
|
+
port: port.to_i,
|
72
|
+
ip: parse_ip(ip),
|
73
|
+
timestamp: parse_timestamp(timestamp),
|
74
|
+
app_protocol: parse_app_protocol(app_proto),
|
75
|
+
payload: banner
|
78
76
|
)
|
79
77
|
end
|
80
78
|
end
|
data/lib/masscan/status.rb
CHANGED
@@ -3,5 +3,38 @@ module Masscan
|
|
3
3
|
# Represents a port status record.
|
4
4
|
#
|
5
5
|
class Status < Struct.new(:status,:protocol,:port,:reason,:ttl,:ip,:timestamp,:mac)
|
6
|
+
|
7
|
+
#
|
8
|
+
# Initializes the status record.
|
9
|
+
#
|
10
|
+
# @param [:open, :closed] status
|
11
|
+
# The status of the port.
|
12
|
+
#
|
13
|
+
# @param [:icmp, :tcp, :udp, :sctp] protocol
|
14
|
+
# The IP protocol.
|
15
|
+
#
|
16
|
+
# @param [Integer] port
|
17
|
+
# The port number.
|
18
|
+
#
|
19
|
+
# @param [Array<:fin, :syn, :rst, :psh, :ack, :urg, :ece, :cwr>, nil] reason
|
20
|
+
# Flags indicating why the port was open or closed.
|
21
|
+
#
|
22
|
+
# @param [Integer, nil] ttl
|
23
|
+
# TTL.
|
24
|
+
#
|
25
|
+
# @param [IPAddr] ip
|
26
|
+
# The IP address.
|
27
|
+
#
|
28
|
+
# @param [Time] timestamp
|
29
|
+
# The record timestamp.
|
30
|
+
#
|
31
|
+
# @param [String, nil] mac
|
32
|
+
# Optional mac address.
|
33
|
+
#
|
34
|
+
# @api private
|
35
|
+
#
|
36
|
+
def initialize(status: , protocol: , port: , reason: nil, ttl: nil, ip: , timestamp: , mac: nil)
|
37
|
+
super(status,protocol,port,reason,ttl,ip,timestamp,mac)
|
38
|
+
end
|
6
39
|
end
|
7
40
|
end
|
data/lib/masscan/version.rb
CHANGED
data/spec/parsers/binary_spec.rb
CHANGED
@@ -221,4 +221,508 @@ describe Masscan::Parsers::Binary do
|
|
221
221
|
end
|
222
222
|
end
|
223
223
|
end
|
224
|
+
|
225
|
+
describe ".parse_status" do
|
226
|
+
let(:timestamp) { 1629960470 }
|
227
|
+
let(:time) { Time.at(timestamp) }
|
228
|
+
|
229
|
+
let(:ipaddr) { IPAddr.new("1.2.3.4") }
|
230
|
+
let(:ip_uint) { ipaddr.to_i }
|
231
|
+
|
232
|
+
let(:ip_proto) { :tcp }
|
233
|
+
let(:ip_proto_uint) { described_class::IP_PROTOCOLS.invert[:tcp] }
|
234
|
+
|
235
|
+
let(:port) { 1111 }
|
236
|
+
|
237
|
+
let(:ttl) { 54 }
|
238
|
+
|
239
|
+
let(:reason) { [:syn, :ack] }
|
240
|
+
let(:reason_uint) { 0x02 | 0x10 }
|
241
|
+
|
242
|
+
let(:buffer) do
|
243
|
+
[
|
244
|
+
timestamp, ip_uint, port, reason_uint, ttl
|
245
|
+
].pack("L>L>S>CC")
|
246
|
+
end
|
247
|
+
|
248
|
+
let(:status) { :open }
|
249
|
+
|
250
|
+
subject { super().parse_status(buffer,status) }
|
251
|
+
|
252
|
+
context "when the buffer length is less than 12" do
|
253
|
+
let(:buffer) { "A" * 11 }
|
254
|
+
|
255
|
+
it "must return nil" do
|
256
|
+
expect(subject).to be(nil)
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
it "must decode the timestamp field" do
|
261
|
+
expect(subject.timestamp).to eq(time)
|
262
|
+
end
|
263
|
+
|
264
|
+
it "must set #status" do
|
265
|
+
expect(subject.status).to eq(status)
|
266
|
+
end
|
267
|
+
|
268
|
+
it "must decode the ip field" do
|
269
|
+
expect(subject.ip).to eq(ipaddr)
|
270
|
+
end
|
271
|
+
|
272
|
+
context "when the port is 53" do
|
273
|
+
let(:port) { 53 }
|
274
|
+
let(:ip_proto) { :udp }
|
275
|
+
|
276
|
+
it "must default the ip_proto :udp" do
|
277
|
+
expect(subject.protocol).to eq(ip_proto)
|
278
|
+
end
|
279
|
+
end
|
280
|
+
|
281
|
+
context "when the port is 123" do
|
282
|
+
let(:port) { 123 }
|
283
|
+
let(:ip_proto) { :udp }
|
284
|
+
|
285
|
+
it "must default the ip_proto :udp" do
|
286
|
+
expect(subject.protocol).to eq(ip_proto)
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
context "when the port is 137" do
|
291
|
+
let(:port) { 137 }
|
292
|
+
let(:ip_proto) { :udp }
|
293
|
+
|
294
|
+
it "must default the ip_proto :udp" do
|
295
|
+
expect(subject.protocol).to eq(ip_proto)
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
299
|
+
context "when the port is 161" do
|
300
|
+
let(:port) { 161 }
|
301
|
+
let(:ip_proto) { :udp }
|
302
|
+
|
303
|
+
it "must default the ip_proto :udp" do
|
304
|
+
expect(subject.protocol).to eq(ip_proto)
|
305
|
+
end
|
306
|
+
end
|
307
|
+
|
308
|
+
context "when the port is 36422" do
|
309
|
+
let(:port) { 36422 }
|
310
|
+
let(:ip_proto) { :sctp }
|
311
|
+
|
312
|
+
it "must default the ip_proto :sctp" do
|
313
|
+
expect(subject.protocol).to eq(ip_proto)
|
314
|
+
end
|
315
|
+
end
|
316
|
+
|
317
|
+
context "when the port is 36412" do
|
318
|
+
let(:port) { 36412 }
|
319
|
+
let(:ip_proto) { :sctp }
|
320
|
+
|
321
|
+
it "must default the ip_proto :sctp" do
|
322
|
+
expect(subject.protocol).to eq(ip_proto)
|
323
|
+
end
|
324
|
+
end
|
325
|
+
|
326
|
+
context "when the port is 2905" do
|
327
|
+
let(:port) { 2905 }
|
328
|
+
let(:ip_proto) { :sctp }
|
329
|
+
|
330
|
+
it "must default the ip_proto :sctp" do
|
331
|
+
expect(subject.protocol).to eq(ip_proto)
|
332
|
+
end
|
333
|
+
end
|
334
|
+
|
335
|
+
it "must default the ip_proto to :tcp" do
|
336
|
+
expect(subject.protocol).to eq(:tcp)
|
337
|
+
end
|
338
|
+
|
339
|
+
it "must decode the port field" do
|
340
|
+
expect(subject.port).to eq(port)
|
341
|
+
end
|
342
|
+
|
343
|
+
it "must decode the ttl field" do
|
344
|
+
expect(subject.ttl).to eq(ttl)
|
345
|
+
end
|
346
|
+
|
347
|
+
it "must decode the reason field" do
|
348
|
+
expect(subject.reason).to eq(reason)
|
349
|
+
end
|
350
|
+
end
|
351
|
+
|
352
|
+
describe ".parser_banner3" do
|
353
|
+
let(:timestamp) { 1629960470 }
|
354
|
+
let(:time) { Time.at(timestamp) }
|
355
|
+
|
356
|
+
let(:ipaddr) { IPAddr.new("1.2.3.4") }
|
357
|
+
let(:ip_uint) { ipaddr.to_i }
|
358
|
+
|
359
|
+
let(:port) { 1111 }
|
360
|
+
|
361
|
+
let(:app_proto) { :html_title }
|
362
|
+
let(:app_proto_uint) do
|
363
|
+
described_class::APP_PROTOCOLS.index(app_proto)
|
364
|
+
end
|
365
|
+
|
366
|
+
let(:payload) { "404 - Not Found" }
|
367
|
+
|
368
|
+
let(:buffer) do
|
369
|
+
p([timestamp, ip_uint, port, app_proto_uint, payload]).pack("L>L>S>S>A*")
|
370
|
+
end
|
371
|
+
|
372
|
+
subject { super().parse_banner3(buffer) }
|
373
|
+
|
374
|
+
it "must default #ip_proto to :tcp" do
|
375
|
+
expect(subject.protocol).to eq(:tcp)
|
376
|
+
end
|
377
|
+
|
378
|
+
it "must default #ttl to 0" do
|
379
|
+
expect(subject.ttl).to eq(0)
|
380
|
+
end
|
381
|
+
|
382
|
+
it "must decode the port field" do
|
383
|
+
expect(subject.port).to eq(port)
|
384
|
+
end
|
385
|
+
|
386
|
+
it "must decode the ip field" do
|
387
|
+
expect(subject.ip).to eq(ipaddr)
|
388
|
+
end
|
389
|
+
|
390
|
+
it "must decode the timestamp field" do
|
391
|
+
expect(subject.timestamp).to eq(time)
|
392
|
+
end
|
393
|
+
|
394
|
+
it "must decode the app_proto field" do
|
395
|
+
expect(subject.app_protocol).to eq(app_proto)
|
396
|
+
end
|
397
|
+
|
398
|
+
it "must decode the payload field" do
|
399
|
+
expect(subject.payload).to eq(payload)
|
400
|
+
end
|
401
|
+
end
|
402
|
+
|
403
|
+
describe ".parser_banner4" do
|
404
|
+
let(:timestamp) { 1629960470 }
|
405
|
+
let(:time) { Time.at(timestamp) }
|
406
|
+
|
407
|
+
let(:ipaddr) { IPAddr.new("1.2.3.4") }
|
408
|
+
let(:ip_uint) { ipaddr.to_i }
|
409
|
+
|
410
|
+
let(:ip_proto) { :tcp }
|
411
|
+
let(:ip_proto_uint) { described_class::IP_PROTOCOLS.invert[:tcp] }
|
412
|
+
|
413
|
+
let(:port) { 1111 }
|
414
|
+
|
415
|
+
let(:app_proto) { :html_title }
|
416
|
+
let(:app_proto_uint) do
|
417
|
+
described_class::APP_PROTOCOLS.index(app_proto)
|
418
|
+
end
|
419
|
+
|
420
|
+
let(:payload) { "404 - Not Found" }
|
421
|
+
|
422
|
+
let(:buffer) do
|
423
|
+
[
|
424
|
+
timestamp, ip_uint, ip_proto_uint, port, app_proto_uint, payload
|
425
|
+
].pack("L>L>CS>S>A*")
|
426
|
+
end
|
427
|
+
|
428
|
+
subject { super().parse_banner4(buffer) }
|
429
|
+
|
430
|
+
context "when the buffer length is less than 13" do
|
431
|
+
let(:buffer) { "A" * 12 }
|
432
|
+
|
433
|
+
it "must return nil" do
|
434
|
+
expect(subject).to be(nil)
|
435
|
+
end
|
436
|
+
end
|
437
|
+
|
438
|
+
it "must decode the ip_proto field" do
|
439
|
+
expect(subject.protocol).to eq(:tcp)
|
440
|
+
end
|
441
|
+
|
442
|
+
it "must default #ttl to 0" do
|
443
|
+
expect(subject.ttl).to eq(0)
|
444
|
+
end
|
445
|
+
|
446
|
+
it "must decode the port field" do
|
447
|
+
expect(subject.port).to eq(port)
|
448
|
+
end
|
449
|
+
|
450
|
+
it "must decode the ip field" do
|
451
|
+
expect(subject.ip).to eq(ipaddr)
|
452
|
+
end
|
453
|
+
|
454
|
+
it "must decode the timestamp field" do
|
455
|
+
expect(subject.timestamp).to eq(time)
|
456
|
+
end
|
457
|
+
|
458
|
+
it "must decode the app_proto field" do
|
459
|
+
expect(subject.app_protocol).to eq(app_proto)
|
460
|
+
end
|
461
|
+
|
462
|
+
it "must decode the payload field" do
|
463
|
+
expect(subject.payload).to eq(payload)
|
464
|
+
end
|
465
|
+
end
|
466
|
+
|
467
|
+
describe ".parse_status2" do
|
468
|
+
let(:timestamp) { 1629960470 }
|
469
|
+
let(:time) { Time.at(timestamp) }
|
470
|
+
|
471
|
+
let(:ipaddr) { IPAddr.new("1.2.3.4") }
|
472
|
+
let(:ip_uint) { ipaddr.to_i }
|
473
|
+
|
474
|
+
let(:ip_proto) { :tcp }
|
475
|
+
let(:ip_proto_uint) { described_class::IP_PROTOCOLS.invert[:tcp] }
|
476
|
+
|
477
|
+
let(:port) { 1111 }
|
478
|
+
|
479
|
+
let(:ttl) { 54 }
|
480
|
+
|
481
|
+
let(:reason) { [:syn, :ack] }
|
482
|
+
let(:reason_uint) { 0x02 | 0x10 }
|
483
|
+
|
484
|
+
let(:buffer) do
|
485
|
+
[
|
486
|
+
timestamp, ip_uint, ip_proto_uint, port, reason_uint, ttl
|
487
|
+
].pack("L>L>CS>CC")
|
488
|
+
end
|
489
|
+
|
490
|
+
let(:status) { :open }
|
491
|
+
|
492
|
+
subject { super().parse_status2(buffer,status) }
|
493
|
+
|
494
|
+
context "when the buffer length is less than 13" do
|
495
|
+
let(:buffer) { "A" * 12 }
|
496
|
+
|
497
|
+
it "must return nil" do
|
498
|
+
expect(subject).to be(nil)
|
499
|
+
end
|
500
|
+
end
|
501
|
+
|
502
|
+
it "must decode the timestamp field" do
|
503
|
+
expect(subject.timestamp).to eq(time)
|
504
|
+
end
|
505
|
+
|
506
|
+
it "must set #status" do
|
507
|
+
expect(subject.status).to eq(status)
|
508
|
+
end
|
509
|
+
|
510
|
+
it "must decode the ip field" do
|
511
|
+
expect(subject.ip).to eq(ipaddr)
|
512
|
+
end
|
513
|
+
|
514
|
+
it "must decode the ip_proto field" do
|
515
|
+
expect(subject.protocol).to eq(ip_proto)
|
516
|
+
end
|
517
|
+
|
518
|
+
it "must decode the port field" do
|
519
|
+
expect(subject.port).to eq(port)
|
520
|
+
end
|
521
|
+
|
522
|
+
it "must decode the ttl field" do
|
523
|
+
expect(subject.ttl).to eq(ttl)
|
524
|
+
end
|
525
|
+
|
526
|
+
it "must decode the reason field" do
|
527
|
+
expect(subject.reason).to eq(reason)
|
528
|
+
end
|
529
|
+
end
|
530
|
+
|
531
|
+
describe ".parser_banner9" do
|
532
|
+
let(:timestamp) { 1629960470 }
|
533
|
+
let(:time) { Time.at(timestamp) }
|
534
|
+
|
535
|
+
let(:ipaddr) { IPAddr.new("1.2.3.4") }
|
536
|
+
let(:ip_uint) { ipaddr.to_i }
|
537
|
+
|
538
|
+
let(:ip_proto) { :tcp }
|
539
|
+
let(:ip_proto_uint) { described_class::IP_PROTOCOLS.invert[:tcp] }
|
540
|
+
|
541
|
+
let(:port) { 1111 }
|
542
|
+
|
543
|
+
let(:app_proto) { :html_title }
|
544
|
+
let(:app_proto_uint) do
|
545
|
+
described_class::APP_PROTOCOLS.index(app_proto)
|
546
|
+
end
|
547
|
+
|
548
|
+
let(:ttl) { 54 }
|
549
|
+
|
550
|
+
let(:payload) { "404 - Not Found" }
|
551
|
+
|
552
|
+
let(:buffer) do
|
553
|
+
[
|
554
|
+
timestamp, ip_uint, ip_proto_uint, port, app_proto_uint, ttl, payload
|
555
|
+
].pack("L>L>CS>S>CA*")
|
556
|
+
end
|
557
|
+
|
558
|
+
subject { super().parse_banner9(buffer) }
|
559
|
+
|
560
|
+
context "when the buffer length is less than 14" do
|
561
|
+
let(:buffer) { "A" * 13 }
|
562
|
+
|
563
|
+
it "must return nil" do
|
564
|
+
expect(subject).to be(nil)
|
565
|
+
end
|
566
|
+
end
|
567
|
+
|
568
|
+
it "must decode the ip_proto field" do
|
569
|
+
expect(subject.protocol).to eq(:tcp)
|
570
|
+
end
|
571
|
+
|
572
|
+
it "must decode the ttl field" do
|
573
|
+
expect(subject.ttl).to eq(ttl)
|
574
|
+
end
|
575
|
+
|
576
|
+
it "must decode the port field" do
|
577
|
+
expect(subject.port).to eq(port)
|
578
|
+
end
|
579
|
+
|
580
|
+
it "must decode the ip field" do
|
581
|
+
expect(subject.ip).to eq(ipaddr)
|
582
|
+
end
|
583
|
+
|
584
|
+
it "must decode the timestamp field" do
|
585
|
+
expect(subject.timestamp).to eq(time)
|
586
|
+
end
|
587
|
+
|
588
|
+
it "must decode the app_proto field" do
|
589
|
+
expect(subject.app_protocol).to eq(app_proto)
|
590
|
+
end
|
591
|
+
|
592
|
+
it "must decode the payload field" do
|
593
|
+
expect(subject.payload).to eq(payload)
|
594
|
+
end
|
595
|
+
end
|
596
|
+
|
597
|
+
describe ".parse_status6" do
|
598
|
+
let(:timestamp) { 1629960470 }
|
599
|
+
let(:time) { Time.at(timestamp) }
|
600
|
+
|
601
|
+
let(:ipaddr) { IPAddr.new("2606:2800:220:1:248:1893:25c8:1946") }
|
602
|
+
let(:ip_uint) { ipaddr.to_i }
|
603
|
+
let(:ipv6_hi) { (ip_uint & (0xffffffff_ffffffff << 64)) >> 64 }
|
604
|
+
let(:ipv6_lo) { (ip_uint & 0xffffffff_ffffffff) }
|
605
|
+
|
606
|
+
let(:ip_version) { 6 }
|
607
|
+
|
608
|
+
let(:ip_proto) { :tcp }
|
609
|
+
let(:ip_proto_uint) { described_class::IP_PROTOCOLS.invert[:tcp] }
|
610
|
+
|
611
|
+
let(:port) { 1111 }
|
612
|
+
|
613
|
+
let(:ttl) { 54 }
|
614
|
+
|
615
|
+
let(:reason) { [:syn, :ack] }
|
616
|
+
let(:reason_uint) { 0x02 | 0x10 }
|
617
|
+
|
618
|
+
let(:buffer) do
|
619
|
+
[
|
620
|
+
timestamp, ip_proto_uint, port, reason_uint, ttl, ip_version, ipv6_hi, ipv6_lo
|
621
|
+
].pack("L>CS>CCCQ>Q>")
|
622
|
+
end
|
623
|
+
|
624
|
+
let(:status) { :open }
|
625
|
+
|
626
|
+
subject { super().parse_status6(buffer,status) }
|
627
|
+
|
628
|
+
context "if the ip_version is not 6" do
|
629
|
+
let(:ip_version) { 9 }
|
630
|
+
|
631
|
+
it do
|
632
|
+
expect {
|
633
|
+
described_class.parse_status6(buffer,status)
|
634
|
+
}.to raise_error(described_class::CorruptedFile,"expected ip_version to be 6: #{ip_version.inspect}")
|
635
|
+
end
|
636
|
+
end
|
637
|
+
|
638
|
+
it "must decode the timestamp field" do
|
639
|
+
expect(subject.timestamp).to eq(time)
|
640
|
+
end
|
641
|
+
|
642
|
+
it "must set #status" do
|
643
|
+
expect(subject.status).to eq(status)
|
644
|
+
end
|
645
|
+
|
646
|
+
it "must decode the ip field" do
|
647
|
+
expect(subject.ip).to eq(ipaddr)
|
648
|
+
end
|
649
|
+
|
650
|
+
it "must decode the ip_proto field" do
|
651
|
+
expect(subject.protocol).to eq(ip_proto)
|
652
|
+
end
|
653
|
+
|
654
|
+
it "must decode the port field" do
|
655
|
+
expect(subject.port).to eq(port)
|
656
|
+
end
|
657
|
+
|
658
|
+
it "must decode the ttl field" do
|
659
|
+
expect(subject.ttl).to eq(ttl)
|
660
|
+
end
|
661
|
+
|
662
|
+
it "must decode the reason field" do
|
663
|
+
expect(subject.reason).to eq(reason)
|
664
|
+
end
|
665
|
+
end
|
666
|
+
|
667
|
+
describe ".parser_banner6" do
|
668
|
+
let(:timestamp) { 1629960470 }
|
669
|
+
let(:time) { Time.at(timestamp) }
|
670
|
+
|
671
|
+
let(:ipaddr) { IPAddr.new("2606:2800:220:1:248:1893:25c8:1946") }
|
672
|
+
let(:ip_uint) { ipaddr.to_i }
|
673
|
+
let(:ipv6_hi) { (ip_uint & (0xffffffff_ffffffff << 64)) >> 64 }
|
674
|
+
let(:ipv6_lo) { (ip_uint & 0xffffffff_ffffffff) }
|
675
|
+
|
676
|
+
let(:ip_version) { 6 }
|
677
|
+
|
678
|
+
let(:ip_proto) { :tcp }
|
679
|
+
let(:ip_proto_uint) { described_class::IP_PROTOCOLS.invert[:tcp] }
|
680
|
+
|
681
|
+
let(:port) { 1111 }
|
682
|
+
|
683
|
+
let(:app_proto) { :html_title }
|
684
|
+
let(:app_proto_uint) do
|
685
|
+
described_class::APP_PROTOCOLS.index(app_proto)
|
686
|
+
end
|
687
|
+
|
688
|
+
let(:ttl) { 54 }
|
689
|
+
|
690
|
+
let(:payload) { "404 - Not Found" }
|
691
|
+
|
692
|
+
let(:buffer) do
|
693
|
+
[
|
694
|
+
timestamp, ip_proto_uint, port, app_proto_uint, ttl, ip_version, ipv6_hi, ipv6_lo, payload
|
695
|
+
].pack("L>CS>S>CCQ>Q>A*")
|
696
|
+
end
|
697
|
+
|
698
|
+
subject { super().parse_banner6(buffer) }
|
699
|
+
|
700
|
+
it "must decode the ip_proto field" do
|
701
|
+
expect(subject.protocol).to eq(:tcp)
|
702
|
+
end
|
703
|
+
|
704
|
+
it "must decode the ttl field" do
|
705
|
+
expect(subject.ttl).to eq(ttl)
|
706
|
+
end
|
707
|
+
|
708
|
+
it "must decode the port field" do
|
709
|
+
expect(subject.port).to eq(port)
|
710
|
+
end
|
711
|
+
|
712
|
+
it "must decode the ip field" do
|
713
|
+
expect(subject.ip).to eq(ipaddr)
|
714
|
+
end
|
715
|
+
|
716
|
+
it "must decode the timestamp field" do
|
717
|
+
expect(subject.timestamp).to eq(time)
|
718
|
+
end
|
719
|
+
|
720
|
+
it "must decode the app_proto field" do
|
721
|
+
expect(subject.app_protocol).to eq(app_proto)
|
722
|
+
end
|
723
|
+
|
724
|
+
it "must decode the payload field" do
|
725
|
+
expect(subject.payload).to eq(payload)
|
726
|
+
end
|
727
|
+
end
|
224
728
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby-masscan
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Postmodern
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-09-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rprogram
|