ruby-masscan 0.1.1 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/ruby.yml +0 -2
- data/ChangeLog.md +13 -0
- data/Gemfile +4 -2
- data/LICENSE.txt +1 -1
- data/README.md +14 -9
- data/gemspec.yml +2 -1
- data/lib/masscan/command.rb +240 -0
- data/lib/masscan/output_file.rb +43 -7
- data/lib/masscan/parsers/list.rb +19 -2
- data/lib/masscan/program.rb +17 -66
- data/lib/masscan/version.rb +1 -1
- data/lib/masscan.rb +1 -0
- data/spec/command_spec.rb +142 -0
- data/spec/output_file_spec.rb +41 -1
- data/spec/parsers/list_spec.rb +25 -0
- data/spec/spec_helper.rb +1 -1
- metadata +9 -8
- data/lib/masscan/task.rb +0 -179
- data/spec/task_spec.rb +0 -121
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 064d7200b60b80a1ed4f0459239235d21f96587a3199833988dd5953728cd168
|
4
|
+
data.tar.gz: 619757cb909fcec538c63ef5d37d2cb1f1029509d71e957be9aa6f74726ad4ea
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9d9fa47ab80653e00c4d3f16b02e1cbe824575d0979b43596f7a52c703b9eda0616882e8bd7effdb77a3f9bfafe40b50043ace08a06f4725b59335dab724fee2
|
7
|
+
data.tar.gz: ad559b68f9af88c2c2e1b66711a93b3bceda9962cd0b3f1f478400a0f1dd7d79a7a87eab81eef4ff9c1cca426c135732d05437affb757fdc3d1ebcfc64b681b2
|
data/.github/workflows/ruby.yml
CHANGED
data/ChangeLog.md
CHANGED
@@ -1,3 +1,16 @@
|
|
1
|
+
### 0.2.1 / 2023-03-15
|
2
|
+
|
3
|
+
* Unescape `\\xXX` hex escaped characters in payload strings parsed from `.list`
|
4
|
+
masscan files.
|
5
|
+
|
6
|
+
### 0.2.0 / 2021-11-30
|
7
|
+
|
8
|
+
* Replaced the `rprogram` dependency with [command_mapper].
|
9
|
+
* Fixed a typo in the mapping of the `-oJ` option flag.
|
10
|
+
* Added {Masscan::OutputFile#to_s}.
|
11
|
+
|
12
|
+
[command_mapper]: https://github.com/postmodern/command_mapper.rb#readme
|
13
|
+
|
1
14
|
### 0.1.1 / 2021-09-09
|
2
15
|
|
3
16
|
* Added missing {Masscan::Banner#ttl}.
|
data/Gemfile
CHANGED
@@ -2,14 +2,16 @@ source 'https://rubygems.org'
|
|
2
2
|
|
3
3
|
gemspec
|
4
4
|
|
5
|
+
# gem 'command_mapper', '~> 0.1', github: 'postmodern/command_mapper.rb'
|
6
|
+
|
5
7
|
group :development do
|
6
8
|
gem 'rake'
|
7
9
|
gem 'rubygems-tasks', '~> 0.2'
|
8
10
|
gem 'rspec', '~> 3.0'
|
9
|
-
|
10
|
-
gem 'json'
|
11
11
|
gem 'simplecov', '~> 0.7'
|
12
|
+
|
12
13
|
gem 'kramdown'
|
14
|
+
gem 'redcarpet', platform: :mri
|
13
15
|
gem 'yard', '~> 0.9'
|
14
16
|
gem 'yard-spellcheck', require: false
|
15
17
|
end
|
data/LICENSE.txt
CHANGED
data/README.md
CHANGED
@@ -15,17 +15,22 @@ file formats.
|
|
15
15
|
|
16
16
|
## Features
|
17
17
|
|
18
|
-
* Provides a Ruby interface for running the `masscan`
|
19
|
-
|
18
|
+
* Provides a [Ruby interface][Masscan::Command] for running the `masscan`
|
19
|
+
utility.
|
20
|
+
* Supports [parsing][Masscan::OutputFile] masscan Binary, List, and JSON output
|
21
|
+
files.
|
22
|
+
|
23
|
+
[Masscan::Command]: https://rubydoc.info/gems/ruby-masscan/Masscan/Command
|
24
|
+
[Masscan::OutputFile]: https://rubydoc.info/gems/ruby-masscan/Masscan/OutputFile
|
20
25
|
|
21
26
|
## Examples
|
22
27
|
|
23
28
|
Run `sudo masscan` from Ruby:
|
24
29
|
|
25
30
|
```ruby
|
26
|
-
require 'masscan/
|
31
|
+
require 'masscan/command'
|
27
32
|
|
28
|
-
Masscan::
|
33
|
+
Masscan::Command.sudo do |masscan|
|
29
34
|
masscan.output_format = :list
|
30
35
|
masscan.output_file = 'masscan.txt'
|
31
36
|
|
@@ -108,7 +113,7 @@ end
|
|
108
113
|
|
109
114
|
* [ruby] >= 2.0.0
|
110
115
|
* [masscan] >= 1.0.0
|
111
|
-
* [
|
116
|
+
* [command_mapper] ~> 0.1
|
112
117
|
|
113
118
|
## Install
|
114
119
|
|
@@ -119,21 +124,21 @@ $ gem install ruby-masscan
|
|
119
124
|
### gemspec
|
120
125
|
|
121
126
|
```ruby
|
122
|
-
gemspec.add_dependency 'ruby-masscan', '~> 0.
|
127
|
+
gemspec.add_dependency 'ruby-masscan', '~> 0.2'
|
123
128
|
```
|
124
129
|
|
125
130
|
### Gemfile
|
126
131
|
|
127
132
|
```ruby
|
128
|
-
gem 'ruby-masscan', '~> 0.
|
133
|
+
gem 'ruby-masscan', '~> 0.2'
|
129
134
|
```
|
130
135
|
|
131
136
|
## License
|
132
137
|
|
133
|
-
Copyright (c) 2021 Hal Brodigan
|
138
|
+
Copyright (c) 2021-2023 Hal Brodigan
|
134
139
|
|
135
140
|
See {file:LICENSE.txt} for license information.
|
136
141
|
|
137
142
|
[masscan]: https://github.com/robertdavidgraham/masscan#readme
|
138
143
|
[ruby]: https://www.ruby-lang.org/
|
139
|
-
[
|
144
|
+
[command_mapper]: https://github.com/postmodern/command_mapper.rb#readme
|
data/gemspec.yml
CHANGED
@@ -16,13 +16,14 @@ metadata:
|
|
16
16
|
source_code_uri: https://github.com/postmodern/ruby-masscan
|
17
17
|
bug_tracker_uri: https://github.com/postmodern/ruby-masscan/issues
|
18
18
|
changelog_uri: https://github.com/postmodern/ruby-masscan/blob/master/ChangeLog.md
|
19
|
+
rubygems_mfa_required: 'true'
|
19
20
|
|
20
21
|
required_ruby_version: ">= 2.0.0"
|
21
22
|
|
22
23
|
requirements: masscan >= 1.0.0
|
23
24
|
|
24
25
|
dependencies:
|
25
|
-
|
26
|
+
command_mapper: ~> 0.1
|
26
27
|
|
27
28
|
development_dependencies:
|
28
29
|
bundler: ~> 2.0
|
@@ -0,0 +1,240 @@
|
|
1
|
+
require 'command_mapper/command'
|
2
|
+
|
3
|
+
module Masscan
|
4
|
+
#
|
5
|
+
# Provides an interface for invoking the `masscan` utility.
|
6
|
+
#
|
7
|
+
# ## Example
|
8
|
+
#
|
9
|
+
# require 'masscan/command'
|
10
|
+
#
|
11
|
+
# Masscan::Command.sudo do |masscan|
|
12
|
+
# masscan.output_format = :list
|
13
|
+
# masscan.output_file = 'masscan.txt'
|
14
|
+
#
|
15
|
+
# masscan.ips = '192.168.1.1/24'
|
16
|
+
# masscan.ports = [20,21,22,23,25,80,110,443,512,522,8080,1080]
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# ## `masscan` options:
|
20
|
+
#
|
21
|
+
# * `--range` - `masscan.range`
|
22
|
+
# * `-p` - `masscan.ports`
|
23
|
+
# * `--banners` - `masscan.banners`
|
24
|
+
# * `--rate` - `masscan.rate`
|
25
|
+
# * `--conf` - `masscan.config_file`
|
26
|
+
# * `--resume` - `masscan.resume`
|
27
|
+
# * `--echo` - `masscan.echo`
|
28
|
+
# * `--adapter` - `masscan.adapter`
|
29
|
+
# * `--adapter-ip` - `masscan.adapter_ip`
|
30
|
+
# * `--adapter-port` - `masscan.adapter_port`
|
31
|
+
# * `--adapter-mac` - `masscan.adapter_mac`
|
32
|
+
# * `--adapter-vlan` - `masscan.adapter_vlan`
|
33
|
+
# * `--router-mac` - `masscan.router_mac`
|
34
|
+
# * `--ping` - `masscan.ping`
|
35
|
+
# * `--exclude` - `masscan.exclude`
|
36
|
+
# * `--excludefile` - `masscan.exclude_file`
|
37
|
+
# * `--includefile` - `masscan.include_file`
|
38
|
+
# * `--append-output` - `masscan.append_output`
|
39
|
+
# * `--iflist` - `masscan.list_interfaces`
|
40
|
+
# * `--retries` - `masscan.retries`
|
41
|
+
# * `--nmap` - `masscan.nmap_help`
|
42
|
+
# * `--pcap-payloads` - `masscan.pcap_payloads`
|
43
|
+
# * `--nmap-payloads` - `masscan.nmap_payloads`
|
44
|
+
# * `--http-method` - `masscan.http_method`
|
45
|
+
# * `--http-url` - `masscan.http_url`
|
46
|
+
# * `--http-version` - `masscan.http_version`
|
47
|
+
# * `--http-host` - `masscan.http_host`
|
48
|
+
# * `--http-user-agent` - `masscan.http_user_agent`
|
49
|
+
# * `--http-field` - `masscan.http_field`
|
50
|
+
# * `--http-field-remove` - `masscan.http_field_remove`
|
51
|
+
# * `--http-cookie` - `masscan.http_cookie`
|
52
|
+
# * `--http-payload` - `masscan.http_payload`
|
53
|
+
# * `--show` - `masscan.show`
|
54
|
+
# * `--noshow` - `masscan.hide`
|
55
|
+
# * `--pcap` - `masscan.pcap`
|
56
|
+
# * `--packet-trace` - `masscan.packet_trace`
|
57
|
+
# * `--pfring` - `masscan.pfring`
|
58
|
+
# * `--resume-index` - `masscan.resume_index`
|
59
|
+
# * `--resume-count` - `masscan.resume_count`
|
60
|
+
# * `--shards` - `masscan.shards`
|
61
|
+
# * `--rotate` - `masscan.rotate`
|
62
|
+
# * `--rotate-offset` - `masscan.rotate_offset`
|
63
|
+
# * `--rotate-size` - `masscan.rotate_size`
|
64
|
+
# * `--rotate-dir` - `masscan.rotate_dir`
|
65
|
+
# * `--seed` - `masscan.seed`
|
66
|
+
# * `--regress` - `masscan.regress`
|
67
|
+
# * `--ttl` - `masscan.ttl`
|
68
|
+
# * `--wait` - `masscan.wait`
|
69
|
+
# * `--offline` - `masscan.offline`
|
70
|
+
# * `-sL` - `masscan.print_list`
|
71
|
+
# * `--interactive` - `masscan.interactive`
|
72
|
+
# * `--output-format` - `masscan.output_format`
|
73
|
+
# * `--output-filename` - `masscan.output_file`
|
74
|
+
# * `-oB` - `masscan.output_binary`
|
75
|
+
# * `-oX` - `masscan.output_xml`
|
76
|
+
# * `-oG` - `masscan.output_grepable`
|
77
|
+
# * ` -oJ` - `masscan.output_json`
|
78
|
+
# * `-oL` - `masscan.output_list`
|
79
|
+
# * `--readscan` - `masscan.read_scan`
|
80
|
+
# * `-V` - `masscan.version`
|
81
|
+
# * `-h` - `masscan.help`
|
82
|
+
#
|
83
|
+
# @see https://github.com/robertdavidgraham/masscan/blob/master/doc/masscan.8.markdown
|
84
|
+
#
|
85
|
+
# @since 0.2.0
|
86
|
+
#
|
87
|
+
class Command < CommandMapper::Command
|
88
|
+
|
89
|
+
class PortList < CommandMapper::Types::Num
|
90
|
+
|
91
|
+
def validate(value)
|
92
|
+
case value
|
93
|
+
when Array
|
94
|
+
value.each do |element|
|
95
|
+
valid, message = validate(element)
|
96
|
+
|
97
|
+
unless valid
|
98
|
+
return [valid, message]
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
return true
|
103
|
+
when Range
|
104
|
+
valid, message = super(value.begin)
|
105
|
+
|
106
|
+
unless valid
|
107
|
+
return [valid, message]
|
108
|
+
end
|
109
|
+
|
110
|
+
valid, message = super(value.end)
|
111
|
+
|
112
|
+
unless valid
|
113
|
+
return [valid, message]
|
114
|
+
end
|
115
|
+
|
116
|
+
return true
|
117
|
+
else
|
118
|
+
super(value)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def format(value)
|
123
|
+
case value
|
124
|
+
when Array
|
125
|
+
value.map(&method(:format)).join(',')
|
126
|
+
when Range
|
127
|
+
"#{value.begin}-#{value.end}"
|
128
|
+
else
|
129
|
+
super(value)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
end
|
134
|
+
|
135
|
+
class Shards < CommandMapper::Types::Str
|
136
|
+
|
137
|
+
def validate(value)
|
138
|
+
case value
|
139
|
+
when Array
|
140
|
+
if value.length > 2
|
141
|
+
return [false, "cannot contain more tha two elements (#{value.inspect})"]
|
142
|
+
end
|
143
|
+
|
144
|
+
return true
|
145
|
+
else
|
146
|
+
super(value)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
def format(value)
|
151
|
+
case value
|
152
|
+
when Array
|
153
|
+
"#{value[0]}/#{value[1]}"
|
154
|
+
else
|
155
|
+
super(value)
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
end
|
160
|
+
|
161
|
+
command "masscan" do
|
162
|
+
option '--range', name: :range, value: {type: List.new}
|
163
|
+
option '-p', name: :ports, value: {type: PortList.new}
|
164
|
+
option '--banners', name: :banners
|
165
|
+
option '--rate', name: :rate, value: {type: Num.new}
|
166
|
+
option '--conf', name: :config_file, value: {type: InputFile.new}
|
167
|
+
option '--resume', name: :resume, value: {type: InputFile.new}
|
168
|
+
option '--echo', name: :echo, value: true
|
169
|
+
option '--adapter', name: :adapter, value: true
|
170
|
+
option '--adapter-ip', name: :adapter_ip, value: true
|
171
|
+
option '--adapter-port', name: :adapter_port, value: {type: Num.new}
|
172
|
+
option '--adapter-mac', name: :adapter_mac, value: true
|
173
|
+
option '--adapter-vlan', name: :adapter_vlan, value: true
|
174
|
+
option '--router-mac', name: :router_mac, value: true
|
175
|
+
option '--ping', name: :ping
|
176
|
+
option '--exclude', name: :exclude, value: {type: List.new}
|
177
|
+
option '--excludefile', name: :exclude_file, value: {type: InputFile.new}
|
178
|
+
option '--includefile', name: :include_file, value: {type: InputFile.new}
|
179
|
+
option '--append-output', name: :append_output
|
180
|
+
option '--iflist', name: :list_interfaces
|
181
|
+
option '--retries', name: :retries
|
182
|
+
option '--nmap', name: :nmap_help
|
183
|
+
option '--pcap-payloads', name: :pcap_payloads
|
184
|
+
option '--nmap-payloads', name: :nmap_payloads, value: {type: InputFile.new}
|
185
|
+
|
186
|
+
option '--http-method', name: :http_method, value: true
|
187
|
+
option '--http-url', name: :http_url, value: true
|
188
|
+
option '--http-version', name: :http_version, value: true
|
189
|
+
option '--http-host', name: :http_host, value: true
|
190
|
+
option '--http-user-agent', name: :http_user_agent, value: true
|
191
|
+
|
192
|
+
option '--http-field', value: {type: KeyValue.new}, repeats: true
|
193
|
+
|
194
|
+
option '--http-field-remove', name: :http_field_remove
|
195
|
+
option '--http-cookie', name: :http_cookie
|
196
|
+
option '--http-payload', name: :http_payload
|
197
|
+
|
198
|
+
option '--show', name: :show
|
199
|
+
option '--noshow', name: :hide
|
200
|
+
option '--pcap', name: :pcap, value: true
|
201
|
+
option '--packet-trace', name: :packet_trace
|
202
|
+
option '--pfring', name: :pfring
|
203
|
+
option '--resume-index', name: :resume_index
|
204
|
+
option '--resume-count', name: :resume_count
|
205
|
+
option '--shards', name: :shards, value: {type: Shards.new}
|
206
|
+
option '--rotate', name: :rotate, value: true
|
207
|
+
option '--rotate-offset', name: :rotate_offset, value: true
|
208
|
+
option '--rotate-size', name: :rotate_size, value: true
|
209
|
+
option '--rotate-dir', name: :rotate_dir, value: {type: InputDir.new}
|
210
|
+
option '--seed', name: :seed, value: {type: Num.new}
|
211
|
+
option '--regress', name: :regress
|
212
|
+
option '--ttl', name: :ttl, value: {type: Num.new}
|
213
|
+
option '--wait', name: :wait, value: {type: Num.new}
|
214
|
+
option '--offline', name: :offline
|
215
|
+
option '-sL', name: :print_list
|
216
|
+
option '--interactive', name: :interactive
|
217
|
+
option '--output-format', name: :output_format, value: {
|
218
|
+
type: Enum[
|
219
|
+
:xml,
|
220
|
+
:binary,
|
221
|
+
:grepable,
|
222
|
+
:list,
|
223
|
+
:JSON
|
224
|
+
]
|
225
|
+
}
|
226
|
+
option '--output-filename', name: :output_file, value: true
|
227
|
+
option '-oB', name: :output_binary, value: true
|
228
|
+
option '-oX', name: :output_xml, value: true
|
229
|
+
option '-oG', name: :output_grepable, value: true
|
230
|
+
option '-oJ', name: :output_json, value: true
|
231
|
+
option '-oL', name: :output_list, value: true
|
232
|
+
option '--readscan', name: :read_scan, value: {type: InputFile.new}
|
233
|
+
option '-V', name: :version
|
234
|
+
option '-h', name: :help
|
235
|
+
|
236
|
+
argument :ips, repeats: true
|
237
|
+
end
|
238
|
+
|
239
|
+
end
|
240
|
+
end
|
data/lib/masscan/output_file.rb
CHANGED
@@ -6,8 +6,18 @@ module Masscan
|
|
6
6
|
#
|
7
7
|
# Represents an output file.
|
8
8
|
#
|
9
|
+
# ## Example
|
10
|
+
#
|
11
|
+
# output_file = Masscan::OutputFile.new('masscan.json')
|
12
|
+
# output_file.each do |record|
|
13
|
+
# p record
|
14
|
+
# end
|
15
|
+
#
|
9
16
|
class OutputFile
|
10
17
|
|
18
|
+
# Mapping of formats to parsers.
|
19
|
+
#
|
20
|
+
# @api semipublic
|
11
21
|
PARSERS = {
|
12
22
|
binary: Parsers::Binary,
|
13
23
|
list: Parsers::List,
|
@@ -29,6 +39,8 @@ module Masscan
|
|
29
39
|
# The parser for the output file format.
|
30
40
|
#
|
31
41
|
# @return [Parsers::Binary, Parsers::JSON, Parsers::List]
|
42
|
+
#
|
43
|
+
# @api private
|
32
44
|
attr_reader :parser
|
33
45
|
|
34
46
|
#
|
@@ -52,6 +64,22 @@ module Masscan
|
|
52
64
|
end
|
53
65
|
end
|
54
66
|
|
67
|
+
# Mapping of file extensions to formats
|
68
|
+
#
|
69
|
+
# @api semipublic
|
70
|
+
FILE_FORMATS = {
|
71
|
+
'.bin' => :binary,
|
72
|
+
'.dat' => :binary,
|
73
|
+
|
74
|
+
'.txt' => :list,
|
75
|
+
'.list' => :list,
|
76
|
+
|
77
|
+
'.json' => :json,
|
78
|
+
'.ndjson' => :ndjson,
|
79
|
+
|
80
|
+
'.xml' => :xml
|
81
|
+
}
|
82
|
+
|
55
83
|
#
|
56
84
|
# Infers the format from the output file's extension name.
|
57
85
|
#
|
@@ -64,14 +92,10 @@ module Masscan
|
|
64
92
|
# @raise [ArgumentError]
|
65
93
|
# The output format could not be inferred from the file's name.
|
66
94
|
#
|
95
|
+
# @api semipublic
|
96
|
+
#
|
67
97
|
def self.infer_format(path)
|
68
|
-
|
69
|
-
when '.bin', '.dat' then :binary
|
70
|
-
when '.txt', '.list' then :list
|
71
|
-
when '.json' then :json
|
72
|
-
when '.ndjson' then :ndjson
|
73
|
-
when '.xml' then :xml
|
74
|
-
else
|
98
|
+
FILE_FORMATS.fetch(File.extname(path)) do
|
75
99
|
raise(ArgumentError,"could not infer format of #{path}")
|
76
100
|
end
|
77
101
|
end
|
@@ -96,5 +120,17 @@ module Masscan
|
|
96
120
|
end
|
97
121
|
end
|
98
122
|
|
123
|
+
#
|
124
|
+
# Converts the output file to a String.
|
125
|
+
#
|
126
|
+
# @return [String]
|
127
|
+
# The path to the output file.
|
128
|
+
#
|
129
|
+
# @since 0.2.0
|
130
|
+
#
|
131
|
+
def to_s
|
132
|
+
@path
|
133
|
+
end
|
134
|
+
|
99
135
|
end
|
100
136
|
end
|
data/lib/masscan/parsers/list.rb
CHANGED
@@ -64,7 +64,7 @@ module Masscan
|
|
64
64
|
timestamp: parse_timestamp(timestamp)
|
65
65
|
)
|
66
66
|
elsif line.start_with?('banner ')
|
67
|
-
type, ip_proto, port, ip, timestamp, app_proto,
|
67
|
+
type, ip_proto, port, ip, timestamp, app_proto, payload = line.split(' ',7)
|
68
68
|
|
69
69
|
yield Banner.new(
|
70
70
|
protocol: parse_ip_protocol(ip_proto),
|
@@ -72,11 +72,28 @@ module Masscan
|
|
72
72
|
ip: parse_ip(ip),
|
73
73
|
timestamp: parse_timestamp(timestamp),
|
74
74
|
app_protocol: parse_app_protocol(app_proto),
|
75
|
-
payload:
|
75
|
+
payload: parse_payload(payload)
|
76
76
|
)
|
77
77
|
end
|
78
78
|
end
|
79
79
|
end
|
80
|
+
|
81
|
+
#
|
82
|
+
# Parses a payload string and removes any `\\xXX` hex escaped characters.
|
83
|
+
#
|
84
|
+
# @param [String] payload
|
85
|
+
# The payload string to unescape.
|
86
|
+
#
|
87
|
+
# @return [String]
|
88
|
+
# The raw payload string.
|
89
|
+
#
|
90
|
+
# @api private
|
91
|
+
#
|
92
|
+
def self.parse_payload(payload)
|
93
|
+
payload.gsub(/\\x[0-9a-f]{2}/) do |hex_escape|
|
94
|
+
hex_escape[2..].to_i(16).chr
|
95
|
+
end
|
96
|
+
end
|
80
97
|
end
|
81
98
|
end
|
82
99
|
end
|
data/lib/masscan/program.rb
CHANGED
@@ -1,99 +1,50 @@
|
|
1
|
-
require 'masscan/
|
2
|
-
|
3
|
-
require 'rprogram/program'
|
1
|
+
require 'masscan/command'
|
4
2
|
|
5
3
|
module Masscan
|
6
4
|
#
|
7
|
-
#
|
5
|
+
# @deprecated Please use {Command} instead.
|
8
6
|
#
|
9
|
-
class Program <
|
10
|
-
|
11
|
-
name_program 'masscan'
|
7
|
+
class Program < Command
|
12
8
|
|
13
9
|
#
|
14
|
-
#
|
10
|
+
# Runs `masscan`.
|
15
11
|
#
|
16
12
|
# @param [Hash{Symbol => Object}] options
|
17
13
|
# Additional options for masscan.
|
18
14
|
#
|
19
|
-
# @
|
20
|
-
#
|
21
|
-
#
|
22
|
-
# @yield [task]
|
23
|
-
# If a block is given, it will be passed a task object
|
15
|
+
# @yield [masscan]
|
16
|
+
# If a block is given, it will be passed the new masscan instance
|
24
17
|
# used to specify options for masscan.
|
25
18
|
#
|
26
|
-
# @yieldparam [
|
27
|
-
# The masscan
|
19
|
+
# @yieldparam [Masscan] masscan
|
20
|
+
# The masscan instance.
|
28
21
|
#
|
29
22
|
# @return [Boolean]
|
30
23
|
# Specifies whether the command exited normally.
|
31
24
|
#
|
32
|
-
# @example Specifying `masscan` options via a Hash
|
33
|
-
# Masscan::
|
25
|
+
# @example Specifying `masscan` options via a Hash:
|
26
|
+
# Masscan::Command.scan(
|
34
27
|
# ips: '192.168.1.1/24',
|
35
28
|
# ports: [22, 80, 443],
|
36
29
|
# )
|
37
30
|
#
|
38
|
-
# @example Specifying `masscan` options via a
|
39
|
-
# Masscan::
|
31
|
+
# @example Specifying `masscan` options via a block:
|
32
|
+
# Masscan::Command.scan do |masscan|
|
40
33
|
# masscan.ips = '192.168.1.1/24'
|
41
34
|
# masscan.ports = [22, 80, 443]
|
42
35
|
# end
|
43
36
|
#
|
44
|
-
|
45
|
-
|
46
|
-
def self.scan(options={},exec_options={},&block)
|
47
|
-
find.scan(options,exec_options,&block)
|
37
|
+
def self.scan(options={},&block)
|
38
|
+
run(options,&block)
|
48
39
|
end
|
49
40
|
|
50
41
|
#
|
51
|
-
#
|
52
|
-
# `sudo`.
|
42
|
+
# Runs `masscan` but under `sudo`.
|
53
43
|
#
|
54
44
|
# @see scan
|
55
45
|
#
|
56
|
-
|
57
|
-
|
58
|
-
def self.sudo_scan(options={},exec_options={},&block)
|
59
|
-
find.sudo_scan(options,exec_options,&block)
|
60
|
-
end
|
61
|
-
|
62
|
-
#
|
63
|
-
# Performs a scan.
|
64
|
-
#
|
65
|
-
# @param [Hash{Symbol => Object}] options
|
66
|
-
# Additional options for masscan.
|
67
|
-
#
|
68
|
-
# @param [Hash{Symbol => Object}] exec_options
|
69
|
-
# Additional exec-options.
|
70
|
-
#
|
71
|
-
# @yield [task]
|
72
|
-
# If a block is given, it will be passed a task object
|
73
|
-
# used to specify options for masscan.
|
74
|
-
#
|
75
|
-
# @yieldparam [Task] task
|
76
|
-
# The masscan task object.
|
77
|
-
#
|
78
|
-
# @return [Boolean]
|
79
|
-
# Specifies whether the command exited normally.
|
80
|
-
#
|
81
|
-
# @see http://rubydoc.info/gems/rprogram/0.3.0/RProgram/Program#run-instance_method
|
82
|
-
# For additional exec-options.
|
83
|
-
#
|
84
|
-
def scan(options={},exec_options={},&block)
|
85
|
-
run_task(Task.new(options,&block),exec_options)
|
86
|
-
end
|
87
|
-
|
88
|
-
#
|
89
|
-
# Performs a scan and runs `masscan` under `sudo`.
|
90
|
-
#
|
91
|
-
# @see #scan
|
92
|
-
#
|
93
|
-
# @since 0.8.0
|
94
|
-
#
|
95
|
-
def sudo_scan(options={},exec_options={},&block)
|
96
|
-
sudo_task(Task.new(options,&block),exec_options)
|
46
|
+
def self.sudo_scan(options={},&block)
|
47
|
+
sudo(options,&block)
|
97
48
|
end
|
98
49
|
|
99
50
|
end
|
data/lib/masscan/version.rb
CHANGED
data/lib/masscan.rb
CHANGED
@@ -0,0 +1,142 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'masscan/command'
|
3
|
+
|
4
|
+
describe Masscan::Command do
|
5
|
+
describe described_class::PortList do
|
6
|
+
describe "#validate" do
|
7
|
+
context "when given a single port number" do
|
8
|
+
let(:value) { 443 }
|
9
|
+
|
10
|
+
it "must return true" do
|
11
|
+
expect(subject.validate(value)).to be(true)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
context "when given a Range of port numbers" do
|
16
|
+
let(:value) { (1..1024) }
|
17
|
+
|
18
|
+
it "must return true" do
|
19
|
+
expect(subject.validate(value)).to be(true)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
context "when given an Array of port numbers" do
|
24
|
+
let(:value) { [80, 443] }
|
25
|
+
|
26
|
+
it "must return true" do
|
27
|
+
expect(subject.validate(value)).to be(true)
|
28
|
+
end
|
29
|
+
|
30
|
+
context "and the Array contains Ranges" do
|
31
|
+
let(:value) { [80, (1..42), 443] }
|
32
|
+
|
33
|
+
it "must return true" do
|
34
|
+
expect(subject.validate(value)).to be(true)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
describe "#format" do
|
41
|
+
context "when given a single port number" do
|
42
|
+
let(:value) { 443 }
|
43
|
+
|
44
|
+
it "must return the formatted port number" do
|
45
|
+
expect(subject.format(value)).to eq(value.to_s)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
context "when given a Range of port numbers" do
|
50
|
+
let(:value) { (1..1024) }
|
51
|
+
|
52
|
+
it "must return the formatted port number range (ex: 1-102)" do
|
53
|
+
expect(subject.format(value)).to eq("#{value.begin}-#{value.end}")
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
context "when given an Array of port numbers" do
|
58
|
+
let(:value) { [80, 443] }
|
59
|
+
|
60
|
+
it "must return the formatted list of port numbers" do
|
61
|
+
expect(subject.format(value)).to eq(value.join(','))
|
62
|
+
end
|
63
|
+
|
64
|
+
context "and the Array contains Ranges" do
|
65
|
+
let(:value) { [80, (1..42), 443] }
|
66
|
+
|
67
|
+
it "must return the formatted list of port numbers and port ranges" do
|
68
|
+
expect(subject.format(value)).to eq("#{value[0]},#{value[1].begin}-#{value[1].end},#{value[2]}")
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
describe described_class::Shards do
|
76
|
+
describe "#validate" do
|
77
|
+
context "when given a Rational value" do
|
78
|
+
let(:value) { (1/2r) }
|
79
|
+
|
80
|
+
it "must return true" do
|
81
|
+
expect(subject.validate(value)).to be(true)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
context "when given an Array value" do
|
86
|
+
let(:value) { [1, 2] }
|
87
|
+
|
88
|
+
it "must return true" do
|
89
|
+
expect(subject.validate(value)).to be(true)
|
90
|
+
end
|
91
|
+
|
92
|
+
context "but the Array length is > 2" do
|
93
|
+
let(:value) { [1,2,3] }
|
94
|
+
|
95
|
+
it "must return a validation error" do
|
96
|
+
expect(subject.validate(value)).to eq(
|
97
|
+
[false, "cannot contain more tha two elements (#{value.inspect})"]
|
98
|
+
)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
context "otherwise" do
|
104
|
+
let(:value) { :"1/2" }
|
105
|
+
|
106
|
+
it "must return true" do
|
107
|
+
expect(subject.validate(value)).to be(true)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
describe "#format" do
|
113
|
+
context "when given a Rational value" do
|
114
|
+
let(:value) { (1/2r) }
|
115
|
+
|
116
|
+
it "must format it into \#{numerator}/\#{denominator}" do
|
117
|
+
expect(subject.format(value)).to eq(
|
118
|
+
"#{value.numerator}/#{value.denominator}"
|
119
|
+
)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
context "when given an Array value" do
|
124
|
+
let(:value) { [1, 2] }
|
125
|
+
|
126
|
+
it "must format it into \#{array[0]}/\#{array[1]}" do
|
127
|
+
expect(subject.format(value)).to eq(
|
128
|
+
"#{value[0]}/#{value[1]}"
|
129
|
+
)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
context "otherwise" do
|
134
|
+
let(:value) { :"1/2" }
|
135
|
+
|
136
|
+
it "must convert the value to a String" do
|
137
|
+
expect(subject.format(value)).to eq(value.to_s)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
data/spec/output_file_spec.rb
CHANGED
@@ -68,6 +68,38 @@ describe Masscan::OutputFile do
|
|
68
68
|
end
|
69
69
|
end
|
70
70
|
|
71
|
+
describe "FILE_FORMATS" do
|
72
|
+
subject { described_class::FILE_FORMATS }
|
73
|
+
|
74
|
+
describe ".bin" do
|
75
|
+
it { expect(subject['.bin']).to be(:binary) }
|
76
|
+
end
|
77
|
+
|
78
|
+
describe ".dat" do
|
79
|
+
it { expect(subject['.dat']).to be(:binary) }
|
80
|
+
end
|
81
|
+
|
82
|
+
describe ".txt" do
|
83
|
+
it { expect(subject['.txt']).to be(:list) }
|
84
|
+
end
|
85
|
+
|
86
|
+
describe ".list" do
|
87
|
+
it { expect(subject['.list']).to be(:list) }
|
88
|
+
end
|
89
|
+
|
90
|
+
describe ".json" do
|
91
|
+
it { expect(subject['.json']).to be(:json) }
|
92
|
+
end
|
93
|
+
|
94
|
+
describe ".ndjson" do
|
95
|
+
it { expect(subject['.ndjson']).to be(:ndjson) }
|
96
|
+
end
|
97
|
+
|
98
|
+
describe ".xml" do
|
99
|
+
it { expect(subject['.xml']).to be(:xml) }
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
71
103
|
describe "#initialize" do
|
72
104
|
let(:path) { "/path/to/file.json" }
|
73
105
|
|
@@ -100,7 +132,9 @@ describe Masscan::OutputFile do
|
|
100
132
|
end
|
101
133
|
end
|
102
134
|
|
103
|
-
|
135
|
+
let(:path) { Fixtures.join('masscan.list') }
|
136
|
+
|
137
|
+
subject { described_class.new(path) }
|
104
138
|
|
105
139
|
describe "#each" do
|
106
140
|
context "when given a block" do
|
@@ -132,4 +166,10 @@ describe Masscan::OutputFile do
|
|
132
166
|
end
|
133
167
|
end
|
134
168
|
end
|
169
|
+
|
170
|
+
describe "#to_s" do
|
171
|
+
it "must return #path" do
|
172
|
+
expect(subject.to_s).to eq(path)
|
173
|
+
end
|
174
|
+
end
|
135
175
|
end
|
data/spec/parsers/list_spec.rb
CHANGED
@@ -104,6 +104,31 @@ describe Masscan::Parsers::List do
|
|
104
104
|
expect(yielded_banner.service).to eq(service_keyword)
|
105
105
|
expect(yielded_banner.payload).to eq(payload)
|
106
106
|
end
|
107
|
+
|
108
|
+
context "when the payload field contains '\\xXX' hex escaped characters" do
|
109
|
+
let(:escaped_payload) do
|
110
|
+
"HTTP/1.0 404 Not Found\\x0d\\x0aContent-Type: text/html\\x0d\\x0aDate: Thu, 26 Aug 2021 06:47:52 GMT\\x0d\\x0aServer: ECS (sec/974D)\\x0d\\x0aContent-Length: 345\\x0d\\x0aConnection: close\\x0d\\x0a\\x0d"
|
111
|
+
end
|
112
|
+
let(:unescaped_payload) do
|
113
|
+
"HTTP/1.0 404 Not Found\r\nContent-Type: text/html\r\nDate: Thu, 26 Aug 2021 06:47:52 GMT\r\nServer: ECS (sec/974D)\r\nContent-Length: 345\r\nConnection: close\r\n\r"
|
114
|
+
end
|
115
|
+
|
116
|
+
let(:line) do
|
117
|
+
"banner #{protocol} #{port} #{ip} #{timestamp.to_i} #{service_name} #{escaped_payload}"
|
118
|
+
end
|
119
|
+
|
120
|
+
it "must unescape the '\\xXX' hex escaped characters" do
|
121
|
+
yielded_records = []
|
122
|
+
|
123
|
+
subject.parse(io) do |record|
|
124
|
+
yielded_records << record
|
125
|
+
end
|
126
|
+
|
127
|
+
yielded_banner = yielded_records.first
|
128
|
+
|
129
|
+
expect(yielded_banner.payload).to eq(unescaped_payload)
|
130
|
+
end
|
131
|
+
end
|
107
132
|
end
|
108
133
|
end
|
109
134
|
end
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,29 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby-masscan
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Postmodern
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2023-03-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: command_mapper
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '0.
|
19
|
+
version: '0.1'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '0.
|
26
|
+
version: '0.1'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: bundler
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -62,6 +62,7 @@ files:
|
|
62
62
|
- gemspec.yml
|
63
63
|
- lib/masscan.rb
|
64
64
|
- lib/masscan/banner.rb
|
65
|
+
- lib/masscan/command.rb
|
65
66
|
- lib/masscan/output_file.rb
|
66
67
|
- lib/masscan/parsers.rb
|
67
68
|
- lib/masscan/parsers/binary.rb
|
@@ -70,9 +71,9 @@ files:
|
|
70
71
|
- lib/masscan/parsers/plain_text.rb
|
71
72
|
- lib/masscan/program.rb
|
72
73
|
- lib/masscan/status.rb
|
73
|
-
- lib/masscan/task.rb
|
74
74
|
- lib/masscan/version.rb
|
75
75
|
- ruby-masscan.gemspec
|
76
|
+
- spec/command_spec.rb
|
76
77
|
- spec/fixtures/masscan.bin
|
77
78
|
- spec/fixtures/masscan.json
|
78
79
|
- spec/fixtures/masscan.list
|
@@ -85,7 +86,6 @@ files:
|
|
85
86
|
- spec/parsers/parser_examples.rb
|
86
87
|
- spec/parsers/plain_text_spec.rb
|
87
88
|
- spec/spec_helper.rb
|
88
|
-
- spec/task_spec.rb
|
89
89
|
homepage: https://github.com/postmodern/ruby-masscan#readme
|
90
90
|
licenses:
|
91
91
|
- MIT
|
@@ -94,6 +94,7 @@ metadata:
|
|
94
94
|
source_code_uri: https://github.com/postmodern/ruby-masscan
|
95
95
|
bug_tracker_uri: https://github.com/postmodern/ruby-masscan/issues
|
96
96
|
changelog_uri: https://github.com/postmodern/ruby-masscan/blob/master/ChangeLog.md
|
97
|
+
rubygems_mfa_required: 'true'
|
97
98
|
post_install_message:
|
98
99
|
rdoc_options: []
|
99
100
|
require_paths:
|
@@ -110,7 +111,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
110
111
|
version: '0'
|
111
112
|
requirements:
|
112
113
|
- masscan >= 1.0.0
|
113
|
-
rubygems_version: 3.
|
114
|
+
rubygems_version: 3.3.26
|
114
115
|
signing_key:
|
115
116
|
specification_version: 4
|
116
117
|
summary: A Ruby interface to masscan.
|
data/lib/masscan/task.rb
DELETED
@@ -1,179 +0,0 @@
|
|
1
|
-
require 'rprogram/task'
|
2
|
-
|
3
|
-
module Masscan
|
4
|
-
#
|
5
|
-
# ## `masscan` options:
|
6
|
-
#
|
7
|
-
# * `--range` - `masscan.range`
|
8
|
-
# * `-p` - `masscan.ports`
|
9
|
-
# * `--banners` - `masscan.banners`
|
10
|
-
# * `--rate` - `masscan.rate`
|
11
|
-
# * `--conf` - `masscan.config_file`
|
12
|
-
# * `--resume` - `masscan.resume`
|
13
|
-
# * `--echo` - `masscan.echo`
|
14
|
-
# * `--adapter` - `masscan.adapter`
|
15
|
-
# * `--adapter-ip` - `masscan.adapter_ip`
|
16
|
-
# * `--adapter-port` - `masscan.adapter_port`
|
17
|
-
# * `--adapter-mac` - `masscan.adapter_mac`
|
18
|
-
# * `--adapter-vlan` - `masscan.adapter_vlan`
|
19
|
-
# * `--router-mac` - `masscan.router_mac`
|
20
|
-
# * `--ping` - `masscan.ping`
|
21
|
-
# * `--exclude` - `masscan.exclude`
|
22
|
-
# * `--excludefile` - `masscan.exclude_file`
|
23
|
-
# * `--includefile` - `masscan.include_file`
|
24
|
-
# * `--append-output` - `masscan.append_output`
|
25
|
-
# * `--iflist` - `masscan.list_interfaces`
|
26
|
-
# * `--retries` - `masscan.retries`
|
27
|
-
# * `--nmap` - `masscan.nmap_help`
|
28
|
-
# * `--pcap-payloads` - `masscan.pcap_payloads`
|
29
|
-
# * `--nmap-payloads` - `masscan.nmap_payloads`
|
30
|
-
# * `--http-method` - `masscan.http_method`
|
31
|
-
# * `--http-url` - `masscan.http_url`
|
32
|
-
# * `--http-version` - `masscan.http_version`
|
33
|
-
# * `--http-host` - `masscan.http_host`
|
34
|
-
# * `--http-user-agent` - `masscan.http_user_agent`
|
35
|
-
# * `--http-field` - `masscan.http_field`
|
36
|
-
# * `--http-field-remove` - `masscan.http_field_remove`
|
37
|
-
# * `--http-cookie` - `masscan.http_cookie`
|
38
|
-
# * `--http-payload` - `masscan.http_payload`
|
39
|
-
# * `--show` - `masscan.show`
|
40
|
-
# * `--noshow` - `masscan.hide`
|
41
|
-
# * `--pcap` - `masscan.pcap`
|
42
|
-
# * `--packet-trace` - `masscan.packet_trace`
|
43
|
-
# * `--pfring` - `masscan.pfring`
|
44
|
-
# * `--resume-index` - `masscan.resume_index`
|
45
|
-
# * `--resume-count` - `masscan.resume_count`
|
46
|
-
# * `--shards` - `masscan.shards`
|
47
|
-
# * `--rotate` - `masscan.rotate`
|
48
|
-
# * `--rotate-offset` - `masscan.rotate_offset`
|
49
|
-
# * `--rotate-size` - `masscan.rotate_size`
|
50
|
-
# * `--rotate-dir` - `masscan.rotate_dir`
|
51
|
-
# * `--seed` - `masscan.seed`
|
52
|
-
# * `--regress` - `masscan.regress`
|
53
|
-
# * `--ttl` - `masscan.ttl`
|
54
|
-
# * `--wait` - `masscan.wait`
|
55
|
-
# * `--offline` - `masscan.offline`
|
56
|
-
# * `-sL` - `masscan.print_list`
|
57
|
-
# * `--interactive` - `masscan.interactive`
|
58
|
-
# * `--output-format` - `masscan.output_format`
|
59
|
-
# * `--output-filename` - `masscan.output_file`
|
60
|
-
# * `-oB` - `masscan.output_binary`
|
61
|
-
# * `-oX` - `masscan.output_xml`
|
62
|
-
# * `-oG` - `masscan.output_grepable`
|
63
|
-
# * ` -oJ` - `masscan.output_json`
|
64
|
-
# * `-oL` - `masscan.output_list`
|
65
|
-
# * `--readscan` - `masscan.read_scan`
|
66
|
-
# * `-V` - `masscan.version`
|
67
|
-
# * `-h` - `masscan.help`
|
68
|
-
#
|
69
|
-
# @see https://github.com/robertdavidgraham/masscan/blob/master/doc/masscan.8.markdown
|
70
|
-
#
|
71
|
-
class Task < RProgram::Task
|
72
|
-
|
73
|
-
long_option flag: '--range', name: :range, separator: ','
|
74
|
-
short_option flag: '-p', name: :ports do |opt,value|
|
75
|
-
unless value.empty?
|
76
|
-
[opt.flag, format_port_list(value)]
|
77
|
-
end
|
78
|
-
end
|
79
|
-
long_option flag: '--banners', name: :banners
|
80
|
-
long_option flag: '--rate', name: :rate
|
81
|
-
long_option flag: '--conf', name: :config_file
|
82
|
-
long_option flag: '--resume', name: :resume
|
83
|
-
long_option flag: '--echo', name: :echo
|
84
|
-
long_option flag: '--adapter', name: :adapter
|
85
|
-
long_option flag: '--adapter-ip', name: :adapter_ip
|
86
|
-
long_option flag: '--adapter-port', name: :adapter_port
|
87
|
-
long_option flag: '--adapter-mac', name: :adapter_mac
|
88
|
-
long_option flag: '--adapter-vlan', name: :adapter_vlan
|
89
|
-
long_option flag: '--router-mac', name: :router_mac
|
90
|
-
long_option flag: '--ping', name: :ping
|
91
|
-
long_option flag: '--exclude', name: :exclude, separator: ','
|
92
|
-
long_option flag: '--excludefile', name: :exclude_file
|
93
|
-
long_option flag: '--includefile', name: :include_file
|
94
|
-
long_option flag: '--append-output', name: :append_output
|
95
|
-
long_option flag: '--iflist', name: :list_interfaces
|
96
|
-
long_option flag: '--retries', name: :retries
|
97
|
-
long_option flag: '--nmap', name: :nmap_help
|
98
|
-
long_option flag: '--pcap-payloads', name: :pcap_payloads
|
99
|
-
long_option flag: '--nmap-payloads', name: :nmap_payloads
|
100
|
-
|
101
|
-
long_option flag: '--http-method', name: :http_method
|
102
|
-
long_option flag: '--http-url', name: :http_url
|
103
|
-
long_option flag: '--http-version', name: :http_version
|
104
|
-
long_option flag: '--http-host', name: :http_host
|
105
|
-
long_option flag: '--http-user-agent', name: :http_user_agent
|
106
|
-
|
107
|
-
long_option flag: '--http-field', multiple: true do |opt,value|
|
108
|
-
name, value = value.first
|
109
|
-
|
110
|
-
[opt.flag, "#{name}:#{value}"]
|
111
|
-
end
|
112
|
-
|
113
|
-
long_option flag: '--http-field-remove', name: :http_field_remove
|
114
|
-
long_option flag: '--http-cookie', name: :http_cookie
|
115
|
-
long_option flag: '--http-payload', name: :http_payload
|
116
|
-
|
117
|
-
long_option flag: '--show', name: :show
|
118
|
-
long_option flag: '--noshow', name: :hide
|
119
|
-
long_option flag: '--pcap', name: :pcap
|
120
|
-
long_option flag: '--packet-trace', name: :packet_trace
|
121
|
-
long_option flag: '--pfring', name: :pfring
|
122
|
-
long_option flag: '--resume-index', name: :resume_index
|
123
|
-
long_option flag: '--resume-count', name: :resume_count
|
124
|
-
long_option flag: '--shards', name: :shards do |opt,value|
|
125
|
-
case value.length
|
126
|
-
when 2 then [opt.flag, "#{value[0]}/#{value[1]}"]
|
127
|
-
when 1 then [opt.flag, "#{value[0]}"]
|
128
|
-
else
|
129
|
-
raise(ArgumentError,"#{self}#shards= does not accept more than two values")
|
130
|
-
end
|
131
|
-
end
|
132
|
-
long_option flag: '--rotate', name: :rotate
|
133
|
-
long_option flag: '--rotate-offset', name: :rotate_offset
|
134
|
-
long_option flag: '--rotate-size', name: :rotate_size
|
135
|
-
long_option flag: '--rotate-dir', name: :rotate_dir
|
136
|
-
long_option flag: '--seed', name: :seed
|
137
|
-
long_option flag: '--regress', name: :regress
|
138
|
-
long_option flag: '--ttl', name: :ttl
|
139
|
-
long_option flag: '--wait', name: :wait
|
140
|
-
long_option flag: '--offline', name: :offline
|
141
|
-
short_option flag: '-sL', name: :print_list
|
142
|
-
long_option flag: '--interactive', name: :interactive
|
143
|
-
long_option flag: '--output-format', name: :output_format
|
144
|
-
long_option flag: '--output-filename', name: :output_file
|
145
|
-
short_option flag: '-oB', name: :output_binary
|
146
|
-
short_option flag: '-oX', name: :output_xml
|
147
|
-
short_option flag: '-oG', name: :output_grepable
|
148
|
-
short_option flag:' -oJ', name: :output_json
|
149
|
-
short_option flag: '-oL', name: :output_list
|
150
|
-
long_option flag: '--readscan', name: :read_scan
|
151
|
-
short_option :flag => '-V', :name => :version
|
152
|
-
short_option :flag => '-h', :name => :help
|
153
|
-
|
154
|
-
non_option :tailing => true, :name => :ips
|
155
|
-
|
156
|
-
private
|
157
|
-
|
158
|
-
#
|
159
|
-
# Formats a port list.
|
160
|
-
#
|
161
|
-
# @param [Array<Integer,Range>] ports
|
162
|
-
# The port ranges.
|
163
|
-
#
|
164
|
-
# @return [String]
|
165
|
-
# Comma separated string.
|
166
|
-
#
|
167
|
-
def self.format_port_list(ports)
|
168
|
-
ports.map { |port|
|
169
|
-
case port
|
170
|
-
when Range
|
171
|
-
"#{port.first}-#{port.last}"
|
172
|
-
else
|
173
|
-
port.to_s
|
174
|
-
end
|
175
|
-
}.join(',')
|
176
|
-
end
|
177
|
-
|
178
|
-
end
|
179
|
-
end
|
data/spec/task_spec.rb
DELETED
@@ -1,121 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
require 'masscan/task'
|
3
|
-
|
4
|
-
describe Masscan::Task do
|
5
|
-
describe "#ports=" do
|
6
|
-
context "when given an empty Array" do
|
7
|
-
before { subject.ports = [] }
|
8
|
-
|
9
|
-
it "should ignore empty port Arrays" do
|
10
|
-
subject.ports = []
|
11
|
-
|
12
|
-
expect(subject.arguments).to eq([])
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
context "when given a String" do
|
17
|
-
let(:ports) { '80,21,25' }
|
18
|
-
|
19
|
-
before { subject.ports = ports }
|
20
|
-
|
21
|
-
it "should emit the String as is" do
|
22
|
-
expect(subject.arguments).to eq(['-p', ports])
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
context "when given an Array of Strings" do
|
27
|
-
let(:ports) { %w[80 21 25] }
|
28
|
-
|
29
|
-
before { subject.ports = ports }
|
30
|
-
|
31
|
-
it "should format an Array of String ports" do
|
32
|
-
expect(subject.arguments).to eq(['-p', ports.join(',')])
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
context "when given an Array of Integers" do
|
37
|
-
let(:ports) { [80, 21, 25] }
|
38
|
-
|
39
|
-
before { subject.ports = ports }
|
40
|
-
|
41
|
-
it "should format an Array of Integer ports" do
|
42
|
-
expect(subject.arguments).to eq(['-p', ports.join(',')])
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
context "when given an Array containing a Range" do
|
47
|
-
let(:ports) { [80, 21..25] }
|
48
|
-
|
49
|
-
before { subject.ports = ports }
|
50
|
-
|
51
|
-
it "should format the Range" do
|
52
|
-
expect(subject.arguments).to eq([
|
53
|
-
'-p', "#{ports[0]},#{ports[1].begin}-#{ports[1].end}"
|
54
|
-
])
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
describe "#shards=" do
|
60
|
-
context "when given a Rational value" do
|
61
|
-
let(:rational) { (1/2r) }
|
62
|
-
|
63
|
-
before { subject.shards = rational }
|
64
|
-
|
65
|
-
it "must format it into \#{numerator}/\#{denominator}" do
|
66
|
-
expect(subject.arguments).to eq([
|
67
|
-
"--shards", "#{rational.numerator}/#{rational.denominator}"
|
68
|
-
])
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
context "when given an Array value" do
|
73
|
-
let(:array) { [1, 2] }
|
74
|
-
|
75
|
-
before { subject.shards = array }
|
76
|
-
|
77
|
-
it "must format it into \#{array[0]}/\#{array[1]}" do
|
78
|
-
expect(subject.arguments).to eq([
|
79
|
-
"--shards", "#{array[0]}/#{array[1]}"
|
80
|
-
])
|
81
|
-
end
|
82
|
-
|
83
|
-
context "but the Array length is > 2" do
|
84
|
-
let(:array) { [1,2,3] }
|
85
|
-
|
86
|
-
before { subject.shards = array }
|
87
|
-
|
88
|
-
it do
|
89
|
-
expect {
|
90
|
-
subject.arguments
|
91
|
-
}.to raise_error(ArgumentError,"#{described_class}#shards= does not accept more than two values")
|
92
|
-
end
|
93
|
-
end
|
94
|
-
end
|
95
|
-
|
96
|
-
context "otherwise" do
|
97
|
-
let(:object) { :"1/2" }
|
98
|
-
|
99
|
-
before { subject.shards = object }
|
100
|
-
|
101
|
-
it "must convert the value to a String" do
|
102
|
-
expect(subject.arguments).to eq([
|
103
|
-
"--shards", object.to_s
|
104
|
-
])
|
105
|
-
end
|
106
|
-
end
|
107
|
-
end
|
108
|
-
|
109
|
-
describe "#http_field=" do
|
110
|
-
let(:name) { 'X-Foo' }
|
111
|
-
let(:value) { 'bar' }
|
112
|
-
|
113
|
-
before { subject.http_field = [ [name, value] ] }
|
114
|
-
|
115
|
-
it "must join two values together with a ':'" do
|
116
|
-
expect(subject.arguments).to eq([
|
117
|
-
"--http-field", "#{name}:#{value}"
|
118
|
-
])
|
119
|
-
end
|
120
|
-
end
|
121
|
-
end
|