portfinder 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,212 @@
1
- class Portfinder::Scanner
2
- end
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
@@ -0,0 +1,5 @@
1
+ # Portfinder base module
2
+ module Portfinder
3
+ # Portfinder version specifier
4
+ VERSION = "0.0.2".freeze
5
+ end
@@ -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.1
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: 2016-08-02 00:00:00.000000000 Z
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: at.shakil.92@gmail.com
16
- executables: []
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
- homepage: https://github.com/atshakil/portfinder
144
+ - lib/portfinder/version.rb
145
+ - portfinder.gemspec
146
+ homepage: https://github.com/at-shakil/portfinder
23
147
  licenses:
24
- - CC-BY-SA-4.0
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: '0'
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.1
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