ruby-masscan 0.1.1 → 0.2.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/.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
|