ronin-masscan 0.1.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. checksums.yaml +7 -0
  2. data/.document +4 -0
  3. data/.github/workflows/ruby.yml +47 -0
  4. data/.gitignore +14 -0
  5. data/.rspec +1 -0
  6. data/.rubocop.yml +11 -0
  7. data/.ruby-version +1 -0
  8. data/.yardopts +1 -0
  9. data/COPYING.txt +165 -0
  10. data/ChangeLog.md +10 -0
  11. data/Gemfile +40 -0
  12. data/README.md +204 -0
  13. data/Rakefile +43 -0
  14. data/bin/ronin-masscan +34 -0
  15. data/data/completions/ronin-masscan +83 -0
  16. data/data/templates/script.rb.erb +43 -0
  17. data/gemspec.yml +42 -0
  18. data/lib/ronin/masscan/cli/command.rb +40 -0
  19. data/lib/ronin/masscan/cli/commands/completion.rb +61 -0
  20. data/lib/ronin/masscan/cli/commands/convert.rb +133 -0
  21. data/lib/ronin/masscan/cli/commands/dump.rb +194 -0
  22. data/lib/ronin/masscan/cli/commands/grep.rb +235 -0
  23. data/lib/ronin/masscan/cli/commands/import.rb +94 -0
  24. data/lib/ronin/masscan/cli/commands/new.rb +203 -0
  25. data/lib/ronin/masscan/cli/commands/print.rb +162 -0
  26. data/lib/ronin/masscan/cli/commands/scan.rb +206 -0
  27. data/lib/ronin/masscan/cli/filtering_options.rb +312 -0
  28. data/lib/ronin/masscan/cli/importable.rb +68 -0
  29. data/lib/ronin/masscan/cli/port_list.rb +102 -0
  30. data/lib/ronin/masscan/cli.rb +50 -0
  31. data/lib/ronin/masscan/converter.rb +129 -0
  32. data/lib/ronin/masscan/converters/csv.rb +108 -0
  33. data/lib/ronin/masscan/converters/json.rb +142 -0
  34. data/lib/ronin/masscan/converters.rb +54 -0
  35. data/lib/ronin/masscan/exceptions.rb +47 -0
  36. data/lib/ronin/masscan/importer.rb +214 -0
  37. data/lib/ronin/masscan/root.rb +28 -0
  38. data/lib/ronin/masscan/version.rb +26 -0
  39. data/lib/ronin/masscan.rb +114 -0
  40. data/man/ronin-masscan-completion.1 +76 -0
  41. data/man/ronin-masscan-completion.1.md +78 -0
  42. data/man/ronin-masscan-convert.1 +37 -0
  43. data/man/ronin-masscan-convert.1.md +40 -0
  44. data/man/ronin-masscan-dump.1 +116 -0
  45. data/man/ronin-masscan-dump.1.md +94 -0
  46. data/man/ronin-masscan-grep.1 +56 -0
  47. data/man/ronin-masscan-grep.1.md +59 -0
  48. data/man/ronin-masscan-import.1 +52 -0
  49. data/man/ronin-masscan-import.1.md +57 -0
  50. data/man/ronin-masscan-new.1 +78 -0
  51. data/man/ronin-masscan-new.1.md +70 -0
  52. data/man/ronin-masscan-print.1 +53 -0
  53. data/man/ronin-masscan-print.1.md +56 -0
  54. data/man/ronin-masscan-scan.1 +86 -0
  55. data/man/ronin-masscan-scan.1.md +84 -0
  56. data/man/ronin-masscan.1 +61 -0
  57. data/man/ronin-masscan.1.md +58 -0
  58. data/ronin-masscan.gemspec +62 -0
  59. data/scripts/setup +161 -0
  60. metadata +168 -0
@@ -0,0 +1,235 @@
1
+ # frozen_string_literal: true
2
+ #
3
+ # ronin-masscan - A Ruby library and CLI for working with masscan.
4
+ #
5
+ # Copyright (c) 2023-2024 Hal Brodigan (postmodern.mod3@gmail.com)
6
+ #
7
+ # ronin-masscan is free software: you can redistribute it and/or modify
8
+ # it under the terms of the GNU Lesser General Public License as published
9
+ # by the Free Software Foundation, either version 3 of the License, or
10
+ # (at your option) any later version.
11
+ #
12
+ # ronin-masscan is distributed in the hope that it will be useful,
13
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ # GNU Lesser General Public License for more details.
16
+ #
17
+ # You should have received a copy of the GNU Lesser General Public License
18
+ # along with ronin-masscan. If not, see <https://www.gnu.org/licenses/>.
19
+ #
20
+
21
+ require 'ronin/masscan/cli/command'
22
+ require 'ronin/masscan/cli/filtering_options'
23
+
24
+ require 'command_kit/colors'
25
+ require 'command_kit/printing/indent'
26
+ require 'masscan/output_file'
27
+
28
+ module Ronin
29
+ module Masscan
30
+ class CLI
31
+ module Commands
32
+ #
33
+ # Greps the scanned services from masscan scan file(s) for the given
34
+ # pattern.
35
+ #
36
+ # ## Usage
37
+ #
38
+ # ronin-masscan grep [options] PATTERN MASSCAN_FILE [...]
39
+ #
40
+ # ## Options
41
+ #
42
+ # -P, --protocol tcp|udp Filters the targets by protocol
43
+ # --ip IP Filters the targets by IP
44
+ # --ip-range CIDR Filters the targets by IP range
45
+ # -p, --ports {PORT | PORT1-PORT2},...
46
+ # Filters targets by port number
47
+ # --with-app-protocol APP_PROTOCOL[,...]
48
+ # Filters targets with the app protocol
49
+ # --with-payload STRING Filters targets containing the payload
50
+ # --with-payload-regex /REGEX/ Filters targets with the matching payload
51
+ # -h, --help Print help information
52
+ #
53
+ # ## Arguments
54
+ #
55
+ # PATTERN The pattern to search for
56
+ # MASSCAN_FILE ... The masscan scan file(s) to parse
57
+ #
58
+ class Grep < Command
59
+
60
+ usage '[options] PATTERN MASSCAN_FILE [...]'
61
+
62
+ include CommandKit::Colors
63
+ include CommandKit::Printing::Indent
64
+ include FilteringOptions
65
+
66
+ argument :pattern, required: true,
67
+ desc: 'The pattern to search for'
68
+
69
+ argument :masscan_file, required: true,
70
+ repeats: true,
71
+ desc: 'The masscan scan file(s) to parse'
72
+
73
+ description 'Greps the scanned services from masscan scan file(s)'
74
+
75
+ man_page 'ronin-masscan-grep.1'
76
+
77
+ #
78
+ # Runs the `ronin-masscan grep` command.
79
+ #
80
+ # @param [String] pattern
81
+ # The pattern to search for.
82
+ #
83
+ # @param [Array<String>] masscan_files
84
+ # The nmap `.xml` files to parse.
85
+ #
86
+ def run(pattern,*masscan_files)
87
+ masscan_files.each do |masscan_file|
88
+ unless File.file?(masscan_file)
89
+ print_error "no such file or directory: #{masscan_file}"
90
+ next
91
+ end
92
+
93
+ output_file = begin
94
+ ::Masscan::OutputFile.new(masscan_file)
95
+ rescue ArgumentError => error
96
+ print_error(error.message)
97
+ exit(1)
98
+ end
99
+
100
+ records = grep_records(output_file,pattern)
101
+
102
+ highlight_records(records,pattern)
103
+ end
104
+ end
105
+
106
+ #
107
+ # Greps the masscan output file for the pattern.
108
+ #
109
+ # @param [::Masscan::OutputFile] output_file
110
+ # The masscan output file to search.
111
+ #
112
+ # @param [String] pattern
113
+ # The pattern to search for.
114
+ #
115
+ def grep_records(output_file,pattern)
116
+ records = filter_records(output_file)
117
+
118
+ records.filter { |record| match_record(record,pattern) }
119
+ end
120
+
121
+ #
122
+ # Determines if the masscan record includes the pattern.
123
+ #
124
+ # @param [::Masscan::Status, ::Masscan::Banner] record
125
+ # The masscan record to search.
126
+ #
127
+ # @param [String] pattern
128
+ # The pattern to search for.
129
+ #
130
+ # @return [Boolean]
131
+ # Indicates whether the masscan record contains the pattern.
132
+ #
133
+ def match_record(record,pattern)
134
+ case record
135
+ when ::Masscan::Banner
136
+ record.app_protocol.match(pattern) ||
137
+ record.payload.match(pattern)
138
+ end
139
+ end
140
+
141
+ #
142
+ # Prints the open ports for the IP.
143
+ #
144
+ # @param [Array<::Masscan::Status, ::Masscan::Banner>] records
145
+ # The masscan records to print.
146
+ #
147
+ # @param [String] pattern
148
+ # The pattern to highlight.
149
+ #
150
+ def highlight_records(records,pattern)
151
+ records.group_by(&:ip).each do |ip,records_for_ip|
152
+ puts "[ #{ip} ]"
153
+ puts
154
+
155
+ records_for_ip.group_by { |record|
156
+ [record.port, record.protocol]
157
+ }.each do |(port,protocol),records_for_port|
158
+ indent do
159
+ puts "#{port}/#{protocol}"
160
+
161
+ indent do
162
+ records_for_port.each do |record|
163
+ highlight_record(record,pattern)
164
+ end
165
+ end
166
+ end
167
+ end
168
+
169
+ puts
170
+ end
171
+ end
172
+
173
+ #
174
+ # Prints the masscan record with the pattern highlighted.
175
+ #
176
+ # @param [::Masscan:Status, ::Masscan::Banner] record
177
+ # The masscan record to print.
178
+ #
179
+ # @param [String] pattern
180
+ # The pattern to highlight.
181
+ #
182
+ def highlight_record(record,pattern)
183
+ case record
184
+ when ::Masscan::Banner
185
+ highlight_banner_record(record,pattern)
186
+ end
187
+ end
188
+
189
+ #
190
+ # Prints the masscan banner record with the pattern highlighted.
191
+ #
192
+ # @param [::Masscan::Banner] banner
193
+ # The masscan banner record to print.
194
+ #
195
+ # @param [String] pattern
196
+ # The pattern to highlight.
197
+ #
198
+ def highlight_banner_record(banner,pattern)
199
+ payload = highlight(banner.payload,pattern)
200
+ app_protocol = highlight(banner.app_protocol,pattern)
201
+
202
+ if payload.include?("\n") # multiline?
203
+ puts app_protocol
204
+
205
+ indent do
206
+ payload.chomp.each_line(chomp: true) do |line|
207
+ puts line
208
+ end
209
+ end
210
+ else
211
+ puts "#{app_protocol}\t#{payload}"
212
+ end
213
+ end
214
+
215
+ #
216
+ # Highlights the pattern in the text.
217
+ #
218
+ # @param [String] text
219
+ # The text to modify.
220
+ #
221
+ # @param [String] pattern
222
+ # The pattern to highlight.
223
+ #
224
+ # @return [String]
225
+ # The modified text.
226
+ #
227
+ def highlight(text,pattern)
228
+ text.to_s.gsub(pattern,colors.red(pattern))
229
+ end
230
+
231
+ end
232
+ end
233
+ end
234
+ end
235
+ end
@@ -0,0 +1,94 @@
1
+ # frozen_string_literal: true
2
+ #
3
+ # ronin-masscan - A Ruby library and CLI for working with masscan.
4
+ #
5
+ # Copyright (c) 2023-2024 Hal Brodigan (postmodern.mod3@gmail.com)
6
+ #
7
+ # ronin-masscan is free software: you can redistribute it and/or modify
8
+ # it under the terms of the GNU Lesser General Public License as published
9
+ # by the Free Software Foundation, either version 3 of the License, or
10
+ # (at your option) any later version.
11
+ #
12
+ # ronin-masscan is distributed in the hope that it will be useful,
13
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ # GNU Lesser General Public License for more details.
16
+ #
17
+ # You should have received a copy of the GNU Lesser General Public License
18
+ # along with ronin-masscan. If not, see <https://www.gnu.org/licenses/>.
19
+ #
20
+
21
+ require 'ronin/masscan/cli/command'
22
+ require 'ronin/masscan/cli/importable'
23
+
24
+ module Ronin
25
+ module Masscan
26
+ class CLI
27
+ module Commands
28
+ #
29
+ # The `ronin-masscan import` command.
30
+ #
31
+ # ## Usage
32
+ #
33
+ # ronin-masscan import [options] MASSCAN_FILE
34
+ #
35
+ # ## Options
36
+ #
37
+ # --db NAME The database to connect to (Default: default)
38
+ # --db-uri URI The database URI to connect to
39
+ # -F binary|list|json|ndjson, Specifies the format of the scan file
40
+ # --format
41
+ # -h, --help Print help information
42
+ #
43
+ # ## Arguments
44
+ #
45
+ # MASSCAN_FILE The masscan scan file to import
46
+ #
47
+ class Import < Command
48
+
49
+ include Importable
50
+
51
+ usage '[options] MASSCAN_FILE'
52
+
53
+ option :format, short: '-F',
54
+ value: {
55
+ type: ::Masscan::OutputFile::PARSERS.keys
56
+ },
57
+ desc: 'Specifies the format of the scan file'
58
+
59
+ argument :masscan_file, required: true,
60
+ desc: 'The masscan scan file to import'
61
+
62
+ description 'Imports a masscan scan file into ronin-db'
63
+
64
+ man_page 'ronin-masscan-import.1'
65
+
66
+ #
67
+ # Runs the `ronin-masscan import` command.
68
+ #
69
+ # @param [String] masscan_file
70
+ # The masscan scan file to import.
71
+ #
72
+ def run(masscan_file)
73
+ unless File.file?(masscan_file)
74
+ print_error "no such file or directory: #{masscan_file}"
75
+ exit(1)
76
+ end
77
+
78
+ require 'ronin/db'
79
+ require 'ronin/masscan/importer'
80
+
81
+ DB.connect
82
+
83
+ if options[:format]
84
+ import_file(masscan_file, format: options[:format])
85
+ else
86
+ import_file(masscan_file)
87
+ end
88
+ end
89
+
90
+ end
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,203 @@
1
+ # frozen_string_literal: true
2
+ #
3
+ # ronin-masscan - A Ruby library and CLI for working with masscan.
4
+ #
5
+ # Copyright (c) 2023-2024 Hal Brodigan (postmodern.mod3@gmail.com)
6
+ #
7
+ # ronin-masscan is free software: you can redistribute it and/or modify
8
+ # it under the terms of the GNU Lesser General Public License as published
9
+ # by the Free Software Foundation, either version 3 of the License, or
10
+ # (at your option) any later version.
11
+ #
12
+ # ronin-masscan is distributed in the hope that it will be useful,
13
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ # GNU Lesser General Public License for more details.
16
+ #
17
+ # You should have received a copy of the GNU Lesser General Public License
18
+ # along with ronin-masscan. If not, see <https://www.gnu.org/licenses/>.
19
+ #
20
+
21
+ require 'ronin/masscan/cli/command'
22
+ require 'ronin/masscan/root'
23
+
24
+ require 'ronin/core/cli/generator'
25
+
26
+ module Ronin
27
+ module Masscan
28
+ class CLI
29
+ module Commands
30
+ #
31
+ # ## Usage
32
+ #
33
+ # ronin-masscan new [options] FILE
34
+ #
35
+ # ## Options
36
+ #
37
+ # --parser Generate a masscan output file parser script
38
+ # --scanner Generate a masscan scanner script
39
+ # --printing Adds additional printing of the masscan scan data
40
+ # --import Also import the masscan scan data
41
+ # --output-file OUTPUT_FILE Sets the output file to write to or parse
42
+ # -p {PORT | [PORT1]-[PORT2]},..., Sets the port range to scan
43
+ # --ports
44
+ # --ips {IP | IP-range}[,..] Sets the targets to scan (Defaults: ARGV[0])
45
+ # -h, --help Print help information
46
+ #
47
+ # ## Arguments
48
+ #
49
+ # PATH The path to the new masscan ruby script
50
+ #
51
+ # ## Examples
52
+ #
53
+ # ronin-masscan new scanner.rb --ports 22,80,443,8000-9000 --ips '192.168.1.*'
54
+ # ronin-masscan new parser.rb --parser --output-file path/to/masscan.bin --printing
55
+ #
56
+ class New < Command
57
+
58
+ include Core::CLI::Generator
59
+
60
+ template_dir File.join(ROOT,'data','templates')
61
+
62
+ usage '[options] FILE'
63
+
64
+ option :parser, desc: 'Generate a masscan output file parser script' do
65
+ @script_type = :parser
66
+ end
67
+
68
+ option :scanner, desc: 'Generate a masscan scanner script' do
69
+ @script_type = :scanner
70
+ end
71
+
72
+ option :printing, desc: 'Adds additional printing of the masscan scan data' do
73
+ @features[:printing] = true
74
+ end
75
+
76
+ option :import, desc: 'Also import the masscan scan data' do
77
+ @features[:import] = true
78
+ end
79
+
80
+ option :output_file, value: {
81
+ type: String,
82
+ usage: 'OUTPUT_FILE'
83
+ },
84
+ desc: 'Sets the output file to write to or parse' do |file|
85
+ @output_file = file
86
+ end
87
+
88
+ option :ports, short: '-p',
89
+ value: {
90
+ type: String,
91
+ usage: '{PORT | [PORT1]-[PORT2]},...'
92
+ },
93
+ desc: 'Sets the port range to scan' do |ports|
94
+ @ports = parse_port_range(ports)
95
+ rescue ArgumentError => error
96
+ raise(OptionParser::InvalidArgument,error.message)
97
+ end
98
+
99
+ option :ips, value: {
100
+ type: String,
101
+ usage: '{IP | IP-range}[,..]'
102
+ },
103
+ desc: 'Sets the IPs to scan (Defaults: ARGV)' do |ips|
104
+ @ips << ips
105
+ end
106
+
107
+ argument :path, desc: 'The path to the new masscan ruby script'
108
+
109
+ description 'Generates a new masscan ruby script'
110
+
111
+ man_page 'ronin-masscan-new.1'
112
+
113
+ examples [
114
+ "scanner.rb --ports 22,80,443,8000-9000 --ips '192.168.1.*'",
115
+ "parser.rb --parser --output-file path/to/masscan.bin --printing"
116
+ ]
117
+
118
+ # The script type.
119
+ #
120
+ # @return [:scanner, :parser]
121
+ attr_reader :script_type
122
+
123
+ # The optioanl output file to write to or parse.
124
+ #
125
+ # @return [String, nil]
126
+ attr_reader :output_file
127
+
128
+ # The optional ports to scan.
129
+ #
130
+ # @return [Array<Integer, Range(Integer,Integer)>, nil]
131
+ attr_reader :ports
132
+
133
+ # The IP addresses or ranges to scan.
134
+ #
135
+ # @return [Array<String>]
136
+ attr_reader :ips
137
+
138
+ # Additional features.
139
+ #
140
+ # @return [Hash{Symbol => Boolean}]
141
+ attr_reader :features
142
+
143
+ #
144
+ # Initializes the `ronin-masscan new` command.
145
+ #
146
+ # @param [Hash{Symbol => Object}] kwargs
147
+ # Additional keyword arguments for the command.
148
+ #
149
+ def initialize(**kwargs)
150
+ super(**kwargs)
151
+
152
+ @script_type = :scanner
153
+ @ips = []
154
+ @features = {}
155
+ end
156
+
157
+ #
158
+ # Runs the `ronin-masscan new` command.
159
+ #
160
+ # @param [String] file
161
+ # The path to the new masscan ruby script.
162
+ #
163
+ def run(file)
164
+ @directory = File.dirname(file)
165
+
166
+ mkdir @directory unless File.directory?(@directory)
167
+
168
+ erb "script.rb.erb", file
169
+ chmod '+x', file
170
+ end
171
+
172
+ #
173
+ # Parses a port range.
174
+ #
175
+ # @param [String] ports
176
+ # The port range to parse.
177
+ #
178
+ # @return [Array<Integer, Range(Integer,Integer)>]
179
+ # The parsed port range.
180
+ #
181
+ # @raise [ArgumentError]
182
+ # An invalid port range was given.
183
+ #
184
+ def parse_port_range(ports)
185
+ ports.split(',').map do |port|
186
+ case port
187
+ when /\A\d+-\d+\z/
188
+ start, stop = port.split('-',2)
189
+
190
+ (start.to_i..stop.to_i)
191
+ when /\A\d+\z/
192
+ port.to_i
193
+ else
194
+ raise(ArgumentError,"invalid port range: #{ports.inspect}")
195
+ end
196
+ end
197
+ end
198
+
199
+ end
200
+ end
201
+ end
202
+ end
203
+ end
@@ -0,0 +1,162 @@
1
+ # frozen_string_literal: true
2
+ #
3
+ # ronin-masscan - A Ruby library and CLI for working with masscan.
4
+ #
5
+ # Copyright (c) 2023-2024 Hal Brodigan (postmodern.mod3@gmail.com)
6
+ #
7
+ # ronin-masscan is free software: you can redistribute it and/or modify
8
+ # it under the terms of the GNU Lesser General Public License as published
9
+ # by the Free Software Foundation, either version 3 of the License, or
10
+ # (at your option) any later version.
11
+ #
12
+ # ronin-masscan is distributed in the hope that it will be useful,
13
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ # GNU Lesser General Public License for more details.
16
+ #
17
+ # You should have received a copy of the GNU Lesser General Public License
18
+ # along with ronin-masscan. If not, see <https://www.gnu.org/licenses/>.
19
+ #
20
+
21
+ require 'ronin/masscan/cli/command'
22
+ require 'ronin/masscan/cli/filtering_options'
23
+
24
+ require 'masscan/output_file'
25
+ require 'command_kit/printing/indent'
26
+
27
+ module Ronin
28
+ module Masscan
29
+ class CLI
30
+ module Commands
31
+ #
32
+ # Prints the scanned IPs and ports from masscan scan file(s).
33
+ #
34
+ # ## Usage
35
+ #
36
+ # ronin-masscan print [options] MASSCAN_FILE [...]
37
+ #
38
+ # ## Options
39
+ #
40
+ # -P, --protocol tcp|udp Filters the targets by protocol
41
+ # --ip IP Filters the targets by IP
42
+ # --ip-range CIDR Filters the targets by IP range
43
+ # -p, --ports {PORT | PORT1-PORT2},...
44
+ # Filter targets by port number
45
+ # --with-app-protocol APP_PROTOCOL[,...]
46
+ # Filters targets with the app protocol
47
+ # --with-payload STRING Filters targets containing the payload
48
+ # --with-payload-regex /REGEX/ Filters targets with the matching payload
49
+ # -h, --help Print help information
50
+ #
51
+ # ## Arguments
52
+ #
53
+ # MASSCAN_FILE ... The masscan scan file(s) to parse
54
+ #
55
+ class Print < Command
56
+
57
+ usage '[options] MASSCAN_FILE [...]'
58
+
59
+ include CommandKit::Printing::Indent
60
+ include FilteringOptions
61
+
62
+ argument :masscan_file, required: true,
63
+ repeats: true,
64
+ desc: 'The masscan scan file(s) to parse'
65
+
66
+ description 'Prints the scanned IPs and ports from masscan scan file(s)'
67
+
68
+ man_page 'ronin-masscan-print.1'
69
+
70
+ #
71
+ # Runs the `ronin-masscan print` command.
72
+ #
73
+ # @param [Array<String>] masscan_files
74
+ # The nmap `.xml` files to parse.
75
+ #
76
+ def run(*masscan_files)
77
+ masscan_files.each do |masscan_file|
78
+ output_file = begin
79
+ ::Masscan::OutputFile.new(masscan_file)
80
+ rescue ArgumentError => error
81
+ print_error(error.message)
82
+ exit(1)
83
+ end
84
+
85
+ records = filter_records(output_file)
86
+
87
+ print_records(records)
88
+ end
89
+ end
90
+
91
+ #
92
+ # Prints the open ports for the IP.
93
+ #
94
+ # @param [Array<::Masscan::Status, ::Masscan::Banner>] records
95
+ #
96
+ def print_records(records)
97
+ records.group_by(&:ip).each do |ip,records_for_ip|
98
+ puts "[ #{ip} ]"
99
+ puts
100
+
101
+ records_for_ip.group_by { |record|
102
+ [record.port, record.protocol]
103
+ }.each_value do |records_for_port|
104
+ status = records_for_port.first
105
+ banners = records_for_port[1..]
106
+
107
+ indent do
108
+ print_status_record(status)
109
+
110
+ unless banners.empty?
111
+ indent do
112
+ banners.each do |banner|
113
+ print_banner_record(banner)
114
+ end
115
+ end
116
+
117
+ puts
118
+ end
119
+ end
120
+ end
121
+ end
122
+ end
123
+
124
+ #
125
+ # Prints a masscan status record.
126
+ #
127
+ # @param [::Masscan::Status] status
128
+ # The status record that indicates whether a port is open or not.
129
+ #
130
+ def print_status_record(status)
131
+ puts "#{status.port}/#{status.protocol}\t#{status.status}"
132
+ end
133
+
134
+ #
135
+ # Prints a masscan banner record.
136
+ #
137
+ # @param [::Masscan::Banner] banner
138
+ # The banner record that contains additional information about the
139
+ # port's service.
140
+ #
141
+ def print_banner_record(banner)
142
+ payload = banner.payload
143
+ app_protocol = banner.app_protocol
144
+
145
+ if payload.include?("\n") # multiline?
146
+ puts app_protocol
147
+
148
+ indent do
149
+ payload.chomp.each_line(chomp: true) do |line|
150
+ puts line
151
+ end
152
+ end
153
+ else
154
+ puts "#{app_protocol}\t#{payload}"
155
+ end
156
+ end
157
+
158
+ end
159
+ end
160
+ end
161
+ end
162
+ end