rangescan 0.1.0 → 0.2.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a7560ddf707ab1d55befca7e8cdffd4274618845256d4da74575d99d613c4ab9
4
- data.tar.gz: 296098c05eac8e1c7e6294a503aa6b591b2617d2fcdc3f6f4093299e9a174a53
3
+ metadata.gz: 4144b13853677049e63de5a4214bffd4140923b9627aa165793bd08a5fd3664f
4
+ data.tar.gz: 5e9a1489605be94188c2a4310013062451630b12764db065af20081c1bc2d2af
5
5
  SHA512:
6
- metadata.gz: c8a7d6badbbaa041657f86015462f3049cce8e852073a268caadfcfc76fbfd555b31c69b1380ee4846bc92279ce98cb4758d33612d7a4656bad6ca5e2a1d06f0
7
- data.tar.gz: 51537dc5b498beb430a225486b6b2ce259172e485d5bde54ed5400b191f2d909a7cb01bcf4372f1a6af72d8b1dd33e06da605449c53270d69bc60077b8e1748d
6
+ metadata.gz: e82b98bb97b2742ca54bcfd47cecde7a4bee65d7fefe841f7b76cc6dfec7832c397e8d2e422afc5f64a2533ff453bb3cf83c6ad1cbeb98948845ab5710eb84e6
7
+ data.tar.gz: 5e6e6173d6a858749fce649d25fd658a91a0d2941238f2ca730e07511e147e48440d713e07247c4a40c3299765760b403596b4122ac259a52d5e3847bfbfb4c2
@@ -0,0 +1,5 @@
1
+ version = 1
2
+
3
+ [[analyzers]]
4
+ name = "ruby"
5
+ enabled = true
data/.gitignore CHANGED
@@ -45,9 +45,9 @@ build-iPhoneSimulator/
45
45
 
46
46
  # for a library or gem, you might want to ignore these files since the code is
47
47
  # intended to run in multiple environments; otherwise, check them in:
48
- # Gemfile.lock
49
- # .ruby-version
50
- # .ruby-gemset
48
+ Gemfile.lock
49
+ .ruby-version
50
+ .ruby-gemset
51
51
 
52
52
  # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
53
53
  .rvmrc
data/README.md CHANGED
@@ -1,5 +1,6 @@
1
1
  # rangescan
2
2
 
3
+ [![Gem Version](https://badge.fury.io/rb/rangescan.svg)](https://badge.fury.io/rb/rangescan)
3
4
  [![Build Status](https://travis-ci.com/ninoseki/rangescan.svg?branch=master)](https://travis-ci.com/ninoseki/rangescan)
4
5
  [![Coverage Status](https://coveralls.io/repos/github/ninoseki/rangescan/badge.svg?branch=master)](https://coveralls.io/github/ninoseki/rangescan?branch=master)
5
6
  [![CodeFactor](https://www.codefactor.io/repository/github/ninoseki/rangescan/badge)](https://www.codefactor.io/repository/github/ninoseki/rangescan)
@@ -26,13 +27,14 @@ Usage:
26
27
 
27
28
  Options:
28
29
  [--host=HOST] # Host header
29
- [--port=N] # Port to check (80 or 443)
30
- [--scheme=SCHEME] # Scheme to use (http or https)
30
+ [--port=N] # Port
31
+ [--scheme=SCHEME] # Scheme (http or https)
31
32
  [--timeout=N] # Timeout in seconds
32
- [--user-agent=USER_AGENT] # User Agent header
33
- [--veryfy-ssl], [--no-veryfy-ssl] # Verify SSL or not
33
+ [--user-agent=USER_AGENT] # User Agent
34
+ [--verify-ssl], [--no-verify-ssl] # Whether to verify SSL or not
35
+ [--max-concurrency=N] # Concurrency limit for HTTP requests to scan
34
36
 
35
- Scan an IP range & filter by a regexp
37
+ Scan an IP range & filter by a regexp (default regexp = .)
36
38
  ```
37
39
 
38
40
  ## License
@@ -3,6 +3,7 @@
3
3
  require "rangescan/version"
4
4
 
5
5
  require "rangescan/range"
6
+ require "rangescan/utils"
6
7
  require "rangescan/scanner"
7
8
  require "rangescan/matcher"
8
9
 
@@ -5,14 +5,15 @@ require "json"
5
5
 
6
6
  module RangeScan
7
7
  class CLI < Thor
8
- desc "scan [IP_WITH_SUBNET_MASK, REGEXP]", "Scan an IP range & filter by a regexp"
8
+ desc "scan [IP_WITH_SUBNET_MASK, REGEXP]", "Scan an IP range & filter by a regexp (default regexp = .)"
9
9
  method_option :host, type: :string, desc: "Host header"
10
10
  method_option :port, type: :numeric, desc: "Port"
11
11
  method_option :scheme, type: :string, desc: "Scheme (http or https)"
12
12
  method_option :timeout, type: :numeric, desc: "Timeout in seconds"
13
13
  method_option :user_agent, type: :string, desc: "User Agent"
14
14
  method_option :verify_ssl, type: :boolean, desc: "Whether to verify SSL or not"
15
- def scan(ip_with_subnet_mask, regexp)
15
+ method_option :max_concurrency, type: :numeric, desc: "Concurrency limit for HTTP requests to scan"
16
+ def scan(ip_with_subnet_mask, regexp = ".")
16
17
  symbolized_options = symbolize_hash_keys(options)
17
18
  range = Range.new(ip_with_subnet_mask)
18
19
 
@@ -11,7 +11,11 @@ module RangeScan
11
11
  def filter(results)
12
12
  results.select do |result|
13
13
  body = result.dig(:body) || ""
14
- body =~ regexp
14
+ begin
15
+ body =~ regexp
16
+ rescue ArgumentError, Encoding::CompatibilityError
17
+ false
18
+ end
15
19
  end
16
20
  end
17
21
  end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "protocol/http1/connection"
4
+
5
+ module Protocol
6
+ module HTTP1
7
+ class Connection
8
+ def write_request(authority, method, path, version, headers)
9
+ host = authority
10
+ if headers.include?("host")
11
+ host = headers["host"]
12
+ headers.delete "host"
13
+ end
14
+
15
+ @stream.write("#{method} #{path} #{version}\r\n")
16
+ @stream.write("host: #{host}\r\n")
17
+
18
+ write_headers(headers)
19
+ end
20
+ end
21
+ end
22
+ end
@@ -1,54 +1,88 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "http"
4
- require "parallel"
3
+ require "async"
4
+ require "async/barrier"
5
+ require "async/semaphore"
6
+ require "async/http"
7
+ require "etc"
8
+
9
+ require "rangescan/monkey_patch"
5
10
 
6
11
  module RangeScan
7
12
  class Scanner
8
13
  attr_reader :context
9
14
  attr_reader :host
15
+ attr_reader :max_concurrency
10
16
  attr_reader :port
17
+ attr_reader :processor_count
11
18
  attr_reader :scheme
12
19
  attr_reader :ssl_context
13
20
  attr_reader :timeout
14
21
  attr_reader :user_agent
22
+ attr_reader :verify_ssl
15
23
 
16
- def initialize(host: nil, port: nil, scheme: "http", verify_ssl: true, timeout: 5, user_agent: nil)
24
+ def initialize(host: nil, port: nil, scheme: "http", verify_ssl: true, timeout: 5, user_agent: nil, max_concurrency: nil)
17
25
  @host = host
18
26
  @port = port || (scheme == "http" ? 80 : 443)
19
27
  @timeout = timeout
20
28
  @scheme = scheme
21
29
  @user_agent = user_agent
22
30
 
31
+ @verify_ssl = verify_ssl
32
+
23
33
  @ssl_context = OpenSSL::SSL::SSLContext.new
24
34
  @ssl_context.verify_mode = OpenSSL::SSL::VERIFY_NONE unless verify_ssl
35
+
36
+ @max_concurrency = max_concurrency || Etc.nprocessors * 2
37
+ end
38
+
39
+ def url_for(ipv4)
40
+ return "#{scheme}://#{ipv4}" if (port == 80 && scheme == "http") || (port == 443 && scheme == "https")
41
+
42
+ "#{scheme}://#{ipv4}:#{port}"
25
43
  end
26
44
 
27
45
  def scan(ipv4s)
28
- Parallel.map(ipv4s) do |ipv4|
29
- get ipv4
30
- end.compact
46
+ results = []
47
+ Async do
48
+ barrier = Async::Barrier.new
49
+ semaphore = Async::Semaphore.new(max_concurrency, parent: barrier)
50
+
51
+ ipv4s.each do |ipv4|
52
+ semaphore.async do
53
+ url = url_for(ipv4)
54
+
55
+ endpoint = Async::HTTP::Endpoint.parse(url, ssl_context: ssl_context, timeout: timeout)
56
+ client = Async::HTTP::Client.new(endpoint, retries: 0)
57
+ res = client.get(endpoint.path, default_request_headers)
58
+
59
+ headers = res.headers.fields.to_h
60
+ body = res.read || ""
61
+
62
+ results << {
63
+ url: url,
64
+ ipv4: ipv4,
65
+ code: res.status,
66
+ headers: Utils.to_utf8(headers),
67
+ body: Utils.to_utf8(body)
68
+ }
69
+ rescue Errno::ECONNRESET, Errno::ECONNREFUSED, Errno::EHOSTUNREACH, EOFError, OpenSSL::SSL::SSLError, Async::TimeoutError
70
+ next
71
+ end
72
+ end
73
+ barrier.wait
74
+ end
75
+ results.compact
31
76
  end
32
77
 
33
78
  private
34
79
 
35
- def default_headers
36
- { host: host, user_agent: user_agent }.compact
80
+ def default_request_headers
81
+ @default_request_headers ||= { "host" => host, "user-agent" => user_agent }.compact
37
82
  end
38
83
 
39
84
  def ssl_options
40
85
  scheme == "http" ? {} : { ssl_context: ssl_context }
41
86
  end
42
-
43
- def get(ipv4)
44
- url = "#{scheme}://#{ipv4}:#{port}"
45
-
46
- begin
47
- res = HTTP.timeout(timeout).headers(default_headers).get(url, ssl_options)
48
- { ipv4: ipv4, code: res.code, body: res.body.to_s }
49
- rescue StandardError
50
- nil
51
- end
52
- end
53
87
  end
54
88
  end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RangeScan
4
+ class Utils
5
+ class << self
6
+ def to_utf8(obj)
7
+ return obj.dup.force_encoding(Encoding::UTF_8) if obj.is_a?(String)
8
+
9
+ obj.map do |k, v|
10
+ k = k.dup.force_encoding(Encoding::UTF_8) if k.is_a?(String)
11
+ v = v.dup.force_encoding(Encoding::UTF_8) if v.is_a?(String)
12
+ [k, v]
13
+ end.to_h
14
+ end
15
+ end
16
+ end
17
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RangeScan
4
- VERSION = "0.1.0"
4
+ VERSION = "0.2.0"
5
5
  end
@@ -30,10 +30,10 @@ Gem::Specification.new do |spec|
30
30
  spec.add_development_dependency "glint", "~> 0.1"
31
31
  spec.add_development_dependency "rake", "~> 13.0"
32
32
  spec.add_development_dependency "rspec", "~> 3.9"
33
- spec.add_development_dependency "webmock", "~> 3.8"
33
+ spec.add_development_dependency "webmock", "~> 3.9"
34
34
 
35
- spec.add_dependency "http", "~> 4.4"
35
+ spec.add_dependency "async", "~> 1.26"
36
+ spec.add_dependency "async-http", "~> 0.52"
36
37
  spec.add_dependency "ipaddress", "~> 0.8"
37
- spec.add_dependency "parallel", "~> 1.19"
38
38
  spec.add_dependency "thor", "~> 1.0"
39
39
  end
@@ -0,0 +1,5 @@
1
+ {
2
+ "extends": [
3
+ "config:base"
4
+ ]
5
+ }
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rangescan
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Manabu Niseki
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-06-16 00:00:00.000000000 Z
11
+ date: 2020-09-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -86,56 +86,56 @@ dependencies:
86
86
  requirements:
87
87
  - - "~>"
88
88
  - !ruby/object:Gem::Version
89
- version: '3.8'
89
+ version: '3.9'
90
90
  type: :development
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
94
  - - "~>"
95
95
  - !ruby/object:Gem::Version
96
- version: '3.8'
96
+ version: '3.9'
97
97
  - !ruby/object:Gem::Dependency
98
- name: http
98
+ name: async
99
99
  requirement: !ruby/object:Gem::Requirement
100
100
  requirements:
101
101
  - - "~>"
102
102
  - !ruby/object:Gem::Version
103
- version: '4.4'
103
+ version: '1.26'
104
104
  type: :runtime
105
105
  prerelease: false
106
106
  version_requirements: !ruby/object:Gem::Requirement
107
107
  requirements:
108
108
  - - "~>"
109
109
  - !ruby/object:Gem::Version
110
- version: '4.4'
110
+ version: '1.26'
111
111
  - !ruby/object:Gem::Dependency
112
- name: ipaddress
112
+ name: async-http
113
113
  requirement: !ruby/object:Gem::Requirement
114
114
  requirements:
115
115
  - - "~>"
116
116
  - !ruby/object:Gem::Version
117
- version: '0.8'
117
+ version: '0.52'
118
118
  type: :runtime
119
119
  prerelease: false
120
120
  version_requirements: !ruby/object:Gem::Requirement
121
121
  requirements:
122
122
  - - "~>"
123
123
  - !ruby/object:Gem::Version
124
- version: '0.8'
124
+ version: '0.52'
125
125
  - !ruby/object:Gem::Dependency
126
- name: parallel
126
+ name: ipaddress
127
127
  requirement: !ruby/object:Gem::Requirement
128
128
  requirements:
129
129
  - - "~>"
130
130
  - !ruby/object:Gem::Version
131
- version: '1.19'
131
+ version: '0.8'
132
132
  type: :runtime
133
133
  prerelease: false
134
134
  version_requirements: !ruby/object:Gem::Requirement
135
135
  requirements:
136
136
  - - "~>"
137
137
  - !ruby/object:Gem::Version
138
- version: '1.19'
138
+ version: '0.8'
139
139
  - !ruby/object:Gem::Dependency
140
140
  name: thor
141
141
  requirement: !ruby/object:Gem::Requirement
@@ -158,11 +158,11 @@ executables:
158
158
  extensions: []
159
159
  extra_rdoc_files: []
160
160
  files:
161
+ - ".deepsource.toml"
161
162
  - ".gitignore"
162
163
  - ".rspec"
163
164
  - ".travis.yml"
164
165
  - Gemfile
165
- - Gemfile.lock
166
166
  - LICENSE
167
167
  - README.md
168
168
  - Rakefile
@@ -172,16 +172,19 @@ files:
172
172
  - lib/rangescan.rb
173
173
  - lib/rangescan/cli.rb
174
174
  - lib/rangescan/matcher.rb
175
+ - lib/rangescan/monkey_patch.rb
175
176
  - lib/rangescan/range.rb
176
177
  - lib/rangescan/scanner.rb
178
+ - lib/rangescan/utils.rb
177
179
  - lib/rangescan/version.rb
178
180
  - rangescan.gemspec
181
+ - renovate.json
179
182
  homepage: https://github.com/ninoseki/rangescan
180
183
  licenses:
181
184
  - MIT
182
185
  metadata:
183
186
  homepage_uri: https://github.com/ninoseki/rangescan
184
- post_install_message:
187
+ post_install_message:
185
188
  rdoc_options: []
186
189
  require_paths:
187
190
  - lib
@@ -197,7 +200,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
197
200
  version: '0'
198
201
  requirements: []
199
202
  rubygems_version: 3.1.2
200
- signing_key:
203
+ signing_key:
201
204
  specification_version: 4
202
205
  summary: Scan websites on a specific IP range
203
206
  test_files: []
@@ -1,94 +0,0 @@
1
- PATH
2
- remote: .
3
- specs:
4
- rangescan (0.1.0)
5
- http (~> 4.4)
6
- ipaddress (~> 0.8)
7
- parallel (~> 1.19)
8
- thor (~> 1.0)
9
-
10
- GEM
11
- remote: https://rubygems.org/
12
- specs:
13
- addressable (2.7.0)
14
- public_suffix (>= 2.0.2, < 5.0)
15
- coveralls (0.8.23)
16
- json (>= 1.8, < 3)
17
- simplecov (~> 0.16.1)
18
- term-ansicolor (~> 1.3)
19
- thor (>= 0.19.4, < 2.0)
20
- tins (~> 1.6)
21
- crack (0.4.3)
22
- safe_yaml (~> 1.0.0)
23
- diff-lcs (1.3)
24
- docile (1.3.2)
25
- domain_name (0.5.20190701)
26
- unf (>= 0.0.5, < 1.0.0)
27
- ffi (1.13.1)
28
- ffi-compiler (1.0.1)
29
- ffi (>= 1.0.0)
30
- rake
31
- glint (0.1.0)
32
- hashdiff (1.0.1)
33
- http (4.4.1)
34
- addressable (~> 2.3)
35
- http-cookie (~> 1.0)
36
- http-form_data (~> 2.2)
37
- http-parser (~> 1.2.0)
38
- http-cookie (1.0.3)
39
- domain_name (~> 0.5)
40
- http-form_data (2.3.0)
41
- http-parser (1.2.1)
42
- ffi-compiler (>= 1.0, < 2.0)
43
- ipaddress (0.8.3)
44
- json (2.3.0)
45
- parallel (1.19.1)
46
- public_suffix (4.0.5)
47
- rake (13.0.1)
48
- rspec (3.9.0)
49
- rspec-core (~> 3.9.0)
50
- rspec-expectations (~> 3.9.0)
51
- rspec-mocks (~> 3.9.0)
52
- rspec-core (3.9.2)
53
- rspec-support (~> 3.9.3)
54
- rspec-expectations (3.9.2)
55
- diff-lcs (>= 1.2.0, < 2.0)
56
- rspec-support (~> 3.9.0)
57
- rspec-mocks (3.9.1)
58
- diff-lcs (>= 1.2.0, < 2.0)
59
- rspec-support (~> 3.9.0)
60
- rspec-support (3.9.3)
61
- safe_yaml (1.0.5)
62
- simplecov (0.16.1)
63
- docile (~> 1.1)
64
- json (>= 1.8, < 3)
65
- simplecov-html (~> 0.10.0)
66
- simplecov-html (0.10.2)
67
- sync (0.5.0)
68
- term-ansicolor (1.7.1)
69
- tins (~> 1.0)
70
- thor (1.0.1)
71
- tins (1.25.0)
72
- sync
73
- unf (0.1.4)
74
- unf_ext
75
- unf_ext (0.0.7.7)
76
- webmock (3.8.3)
77
- addressable (>= 2.3.6)
78
- crack (>= 0.3.2)
79
- hashdiff (>= 0.4.0, < 2.0.0)
80
-
81
- PLATFORMS
82
- ruby
83
-
84
- DEPENDENCIES
85
- bundler (~> 2.1)
86
- coveralls (~> 0.8)
87
- glint (~> 0.1)
88
- rake (~> 13.0)
89
- rangescan!
90
- rspec (~> 3.9)
91
- webmock (~> 3.8)
92
-
93
- BUNDLED WITH
94
- 2.1.4