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.
@@ -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