portfinder 0.0.1 → 0.0.2
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/.codeclimate.yml +18 -0
- data/.gitignore +11 -0
- data/.reek +26 -0
- data/.rubocop.yml +19 -0
- data/.travis.yml +29 -0
- data/Appraisals +7 -0
- data/Gemfile +3 -0
- data/LICENSE +21 -0
- data/README.md +174 -0
- data/Rakefile +32 -0
- data/bin/pf +101 -0
- data/bin/portfinder +101 -0
- data/gemfiles/slop_4.4.0.gemfile +7 -0
- data/gemfiles/slop_4.5.0.gemfile +7 -0
- data/lib/portfinder.rb +67 -3
- data/lib/portfinder/constants.rb +31 -0
- data/lib/portfinder/error.rb +9 -0
- data/lib/portfinder/monitor.rb +48 -0
- data/lib/portfinder/option.rb +23 -0
- data/lib/portfinder/parser.rb +91 -0
- data/lib/portfinder/pool.rb +59 -0
- data/lib/portfinder/scanner.rb +211 -2
- data/lib/portfinder/version.rb +5 -0
- data/portfinder.gemspec +46 -0
- metadata +142 -11
data/lib/portfinder/scanner.rb
CHANGED
@@ -1,3 +1,212 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
module Portfinder
|
2
|
+
# Portfinder Scanner base class
|
3
|
+
class Scanner
|
4
|
+
# Provides access to raw scan result
|
5
|
+
attr_reader :result
|
6
|
+
# Readonly port randomization flag
|
7
|
+
attr_reader :randomize
|
8
|
+
# Scan monitor accessor for in-scan monitoring and logging (readonly)
|
9
|
+
attr_reader :monitor
|
10
|
+
|
11
|
+
# Scanner initializer
|
12
|
+
def initialize target, port, config = {
|
13
|
+
randomize: true, threaded: true, threads: 10, thread_for: :port
|
14
|
+
}
|
15
|
+
@hosts = host_range target
|
16
|
+
@randomize = config[:randomize]
|
17
|
+
@ports = port_range port
|
18
|
+
@threaded = config[:threaded]
|
19
|
+
@thread_for = config[:thread_for]
|
20
|
+
@result = {}
|
21
|
+
@monitor = Monitor.new
|
22
|
+
@pool =
|
23
|
+
Pool.new(
|
24
|
+
threads_to_open(
|
25
|
+
@thread_for, config[:threads], @hosts, @ports
|
26
|
+
)
|
27
|
+
)
|
28
|
+
end
|
29
|
+
|
30
|
+
# Scans host(s) according to provided specification
|
31
|
+
def scan synchronus = true, &callback
|
32
|
+
@monitor.start
|
33
|
+
if @threaded
|
34
|
+
case @thread_for
|
35
|
+
when :ip
|
36
|
+
scan_threaded_ip
|
37
|
+
when :port
|
38
|
+
scan_threaded_port
|
39
|
+
end
|
40
|
+
|
41
|
+
if synchronus
|
42
|
+
@pool.shutdown
|
43
|
+
@monitor.stop
|
44
|
+
elsif callback
|
45
|
+
@pool.shutdown(false) do
|
46
|
+
callback.call
|
47
|
+
@monitor
|
48
|
+
end
|
49
|
+
end
|
50
|
+
else
|
51
|
+
synchronus_scan
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# Defines a logger (accepts logger as a block)
|
56
|
+
def log &logger
|
57
|
+
Thread.new { logger.call @monitor }
|
58
|
+
end
|
59
|
+
|
60
|
+
# Generates scan result in the specified format
|
61
|
+
def generate_result pretty = false
|
62
|
+
@result = @pool.complete_result if @threaded
|
63
|
+
pretty ? pretty_print(@result) : @result
|
64
|
+
end
|
65
|
+
|
66
|
+
# Generates a report in the specified format from the scan result
|
67
|
+
def report_as format
|
68
|
+
case format
|
69
|
+
when :json
|
70
|
+
JSON.pretty_generate @result
|
71
|
+
when :yml
|
72
|
+
YAML.dump @result
|
73
|
+
else
|
74
|
+
raise ArgumentError, "Unknown format: #{format}"
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# Generates a JSON report
|
79
|
+
def json_report
|
80
|
+
report_as :json
|
81
|
+
end
|
82
|
+
|
83
|
+
# Generates a YAML report
|
84
|
+
def yml_report
|
85
|
+
report_as :yml
|
86
|
+
end
|
87
|
+
|
88
|
+
private
|
89
|
+
|
90
|
+
def pretty_print result
|
91
|
+
formatted = "Scan complete!\n\n"
|
92
|
+
result.keys.each do |ip|
|
93
|
+
ports = result[ip].to_s.gsub(/[\[\]]/, "")
|
94
|
+
formatted << "IP: #{ip}\n\tOpen ports: #{ports}\n\n"
|
95
|
+
end
|
96
|
+
|
97
|
+
formatted
|
98
|
+
end
|
99
|
+
|
100
|
+
def scannable_pairs hosts, ports
|
101
|
+
Enumerator.new(hosts.size * ports.size) do |pair|
|
102
|
+
hosts.each do |host|
|
103
|
+
ports.each do |port|
|
104
|
+
pair << [host, port]
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
3
109
|
|
110
|
+
def threads_to_open thread_for, max, hosts, ports
|
111
|
+
case thread_for
|
112
|
+
when :ip
|
113
|
+
host_count = hosts.size
|
114
|
+
host_count > max ? max : host_count
|
115
|
+
when :port
|
116
|
+
tasks = scannable_pairs(hosts, ports).size
|
117
|
+
tasks > max ? max : tasks
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def host_range target
|
122
|
+
type = target.class
|
123
|
+
if [Array, Enumerator].include? type
|
124
|
+
target
|
125
|
+
elsif type == String
|
126
|
+
[target]
|
127
|
+
else
|
128
|
+
[]
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
# :reek:FeatureEnvy
|
133
|
+
def port_range port
|
134
|
+
port = port.dup
|
135
|
+
port = [port] if port.class == Integer
|
136
|
+
@randomize ? port.to_a.shuffle : port
|
137
|
+
end
|
138
|
+
|
139
|
+
def scan_threaded_ip
|
140
|
+
@monitor.threads = @pool.size
|
141
|
+
@pool.result_format do |result, args, value|
|
142
|
+
result = result.dup
|
143
|
+
if value.any?
|
144
|
+
host = args[0]
|
145
|
+
result[host] = value
|
146
|
+
end
|
147
|
+
result
|
148
|
+
end
|
149
|
+
|
150
|
+
@hosts.each do |host|
|
151
|
+
@pool.schedule host do
|
152
|
+
open_ports = []
|
153
|
+
@ports.each do |port|
|
154
|
+
@monitor.update host, port
|
155
|
+
open_ports << port if port_open? host, port
|
156
|
+
end
|
157
|
+
open_ports.sort
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
def scan_threaded_port
|
163
|
+
@monitor.threads = @pool.size
|
164
|
+
@pool.result_format do |result, args, value|
|
165
|
+
result = result.dup
|
166
|
+
if value
|
167
|
+
host = args[0][0]
|
168
|
+
ports = result[host]
|
169
|
+
result[host] = ports ? ports.push(value).sort : [value]
|
170
|
+
end
|
171
|
+
result
|
172
|
+
end
|
173
|
+
|
174
|
+
scannable_pairs(@hosts, @ports).each do |pair|
|
175
|
+
@pool.schedule(pair) do
|
176
|
+
@monitor.update(*pair)
|
177
|
+
port_open?(*pair)
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
def scan_synchronized_port
|
183
|
+
scannable_pairs(@hosts, @ports).each do |pair|
|
184
|
+
host, port = pair
|
185
|
+
@monitor.update(*pair)
|
186
|
+
|
187
|
+
if port_open?(*pair)
|
188
|
+
ports = @result[host]
|
189
|
+
@result[host] = ports ? ports.push(port).sort : [port]
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
def synchronus_scan
|
195
|
+
scan_synchronized_port
|
196
|
+
@monitor.stop
|
197
|
+
end
|
198
|
+
|
199
|
+
def port_open? host, port
|
200
|
+
socket = Socket.new :INET, :STREAM, 0
|
201
|
+
address = Socket.pack_sockaddr_in(port, host)
|
202
|
+
|
203
|
+
begin
|
204
|
+
socket.connect address
|
205
|
+
socket.close
|
206
|
+
port
|
207
|
+
rescue StandardError
|
208
|
+
nil
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
data/portfinder.gemspec
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
lib = File.expand_path("../lib", __FILE__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
require "portfinder/version"
|
6
|
+
|
7
|
+
Gem::Specification.new do |s|
|
8
|
+
s.name = "portfinder"
|
9
|
+
s.version = Portfinder::VERSION
|
10
|
+
s.authors = ["Tahmid Shakil"]
|
11
|
+
s.email = ["at.shakil.92@gmail.com"]
|
12
|
+
|
13
|
+
s.summary = "A port scanner implementation in pure ruby"
|
14
|
+
s.description = "Portfinder is a ruby based port scanner with features like \
|
15
|
+
network/CIDR scanning, port randomization, hostname discovery and banner \
|
16
|
+
grabbing."
|
17
|
+
s.homepage = "https://github.com/at-shakil/portfinder"
|
18
|
+
s.license = "MIT"
|
19
|
+
|
20
|
+
s.files = `git ls-files -z`.split("\x0").reject do |f|
|
21
|
+
f.match(%r{^(test|spec|features)/})
|
22
|
+
end
|
23
|
+
|
24
|
+
s.require_paths = ["lib"]
|
25
|
+
|
26
|
+
s.bindir = "bin"
|
27
|
+
s.executables = s.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
28
|
+
|
29
|
+
s.required_ruby_version = [">= 2.2.0"]
|
30
|
+
|
31
|
+
s.extra_rdoc_files = ["README.md"]
|
32
|
+
s.rdoc_options =
|
33
|
+
%w[-t Portfinder -m README.md -N --markup markdown]
|
34
|
+
|
35
|
+
s.add_dependency "slop", "~> 4.4"
|
36
|
+
|
37
|
+
s.add_development_dependency "bundler", "~> 1.15"
|
38
|
+
s.add_development_dependency "rake", "~> 12.0"
|
39
|
+
s.add_development_dependency "minitest", "~> 5.0"
|
40
|
+
s.add_development_dependency "simplecov", "~> 0.13"
|
41
|
+
s.add_development_dependency "appraisal", "~> 2.2"
|
42
|
+
|
43
|
+
if ENV["TRAVIS"]
|
44
|
+
s.add_development_dependency "codeclimate-test-reporter", "~> 1.0.8"
|
45
|
+
end
|
46
|
+
end
|
metadata
CHANGED
@@ -1,37 +1,168 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: portfinder
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tahmid Shakil
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
12
|
-
dependencies:
|
11
|
+
date: 2017-07-18 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: slop
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '4.4'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '4.4'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: bundler
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.15'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.15'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '12.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '12.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: minitest
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '5.0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '5.0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: simplecov
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0.13'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0.13'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: appraisal
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '2.2'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '2.2'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: codeclimate-test-reporter
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: 1.0.8
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: 1.0.8
|
13
111
|
description: Portfinder is a ruby based port scanner with features like network/CIDR
|
14
112
|
scanning, port randomization, hostname discovery and banner grabbing.
|
15
|
-
email:
|
16
|
-
|
113
|
+
email:
|
114
|
+
- at.shakil.92@gmail.com
|
115
|
+
executables:
|
116
|
+
- pf
|
117
|
+
- portfinder
|
17
118
|
extensions: []
|
18
|
-
extra_rdoc_files:
|
119
|
+
extra_rdoc_files:
|
120
|
+
- README.md
|
19
121
|
files:
|
122
|
+
- ".codeclimate.yml"
|
123
|
+
- ".gitignore"
|
124
|
+
- ".reek"
|
125
|
+
- ".rubocop.yml"
|
126
|
+
- ".travis.yml"
|
127
|
+
- Appraisals
|
128
|
+
- Gemfile
|
129
|
+
- LICENSE
|
130
|
+
- README.md
|
131
|
+
- Rakefile
|
132
|
+
- bin/pf
|
133
|
+
- bin/portfinder
|
134
|
+
- gemfiles/slop_4.4.0.gemfile
|
135
|
+
- gemfiles/slop_4.5.0.gemfile
|
20
136
|
- lib/portfinder.rb
|
137
|
+
- lib/portfinder/constants.rb
|
138
|
+
- lib/portfinder/error.rb
|
139
|
+
- lib/portfinder/monitor.rb
|
140
|
+
- lib/portfinder/option.rb
|
141
|
+
- lib/portfinder/parser.rb
|
142
|
+
- lib/portfinder/pool.rb
|
21
143
|
- lib/portfinder/scanner.rb
|
22
|
-
|
144
|
+
- lib/portfinder/version.rb
|
145
|
+
- portfinder.gemspec
|
146
|
+
homepage: https://github.com/at-shakil/portfinder
|
23
147
|
licenses:
|
24
|
-
-
|
148
|
+
- MIT
|
25
149
|
metadata: {}
|
26
150
|
post_install_message:
|
27
|
-
rdoc_options:
|
151
|
+
rdoc_options:
|
152
|
+
- "-t"
|
153
|
+
- Portfinder
|
154
|
+
- "-m"
|
155
|
+
- README.md
|
156
|
+
- "-N"
|
157
|
+
- "--markup"
|
158
|
+
- markdown
|
28
159
|
require_paths:
|
29
160
|
- lib
|
30
161
|
required_ruby_version: !ruby/object:Gem::Requirement
|
31
162
|
requirements:
|
32
163
|
- - ">="
|
33
164
|
- !ruby/object:Gem::Version
|
34
|
-
version:
|
165
|
+
version: 2.2.0
|
35
166
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
36
167
|
requirements:
|
37
168
|
- - ">="
|
@@ -39,7 +170,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
39
170
|
version: '0'
|
40
171
|
requirements: []
|
41
172
|
rubyforge_project:
|
42
|
-
rubygems_version: 2.5
|
173
|
+
rubygems_version: 2.4.5
|
43
174
|
signing_key:
|
44
175
|
specification_version: 4
|
45
176
|
summary: A port scanner implementation in pure ruby
|