rangescan 0.1.1 → 0.3.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: bacbfd805037d62c75718f21ef09116eb30a675606267959c9008b6a23c11607
4
- data.tar.gz: 60c0236c232a57d66a72e7926e820b8a0940b20d4bdd5970c2dd5dccdef2bfd5
3
+ metadata.gz: 643ca26936dec4a00aabc6551e9cea7c87cecfda46d5055c26f1c3e3c967893b
4
+ data.tar.gz: 6f034dcd1e4d5c59b36baec5ea6e4911aba47d88cd462d92a33898194b871839
5
5
  SHA512:
6
- metadata.gz: 3cf068f8884b6abf3231ae183a9190573d9371ec3054a66ee5fe85e9ed5babf5dcf3ff7ffc1f7e9ded672bc99e128fee3ec37023fb47f5e124c73afeefd30570
7
- data.tar.gz: aa5cb7c38d41199fec63a3b04b0456e20fe36a6cf51a95fa8870fe5220e00c1ea11e277a5dc7810c8fb9c5a7e6528dbb40cd4fdfe7a7f6f05b9c5923427f9bbf
6
+ metadata.gz: e2ce5509d388bc2d8026a8d9534339c5428bb974751d4e48ca73964d517a6de38665b8fb070247ee28cab5203b3bbad495890b6576bee1a89bb0008437130a1b
7
+ data.tar.gz: '098beb8cbee3e0f21302a2cffbaa83f9bdae11b9d80270517e7c92210bb8bf0db8050e337f180ba31a128e2cb4d858405f6e0ea0e4604905c54790abfcb27bef'
@@ -0,0 +1,5 @@
1
+ version = 1
2
+
3
+ [[analyzers]]
4
+ name = "ruby"
5
+ enabled = true
data/README.md CHANGED
@@ -27,13 +27,14 @@ Usage:
27
27
 
28
28
  Options:
29
29
  [--host=HOST] # Host header
30
- [--port=N] # Port to check (80 or 443)
31
- [--scheme=SCHEME] # Scheme to use (http or https)
30
+ [--port=N] # Port
31
+ [--scheme=SCHEME] # Scheme (http or https)
32
32
  [--timeout=N] # Timeout in seconds
33
- [--user-agent=USER_AGENT] # User Agent header
34
- [--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
35
36
 
36
- Scan an IP range & filter by a regexp
37
+ Scan an IP range & filter by a regexp (default regexp = .)
37
38
  ```
38
39
 
39
40
  ## License
@@ -5,4 +5,5 @@ $LOAD_PATH.unshift("#{__dir__}/../lib")
5
5
 
6
6
  require "rangescan"
7
7
 
8
- RangeScan::CLI.start
8
+ ARGV.unshift(RangeScan::CLI.default_task) unless RangeScan::CLI.all_tasks.key?(ARGV[0])
9
+ RangeScan::CLI.start(ARGV)
@@ -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,16 @@ 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
+ method_option :headers, type: :hash, default: {}, desc: "Custom headers"
17
+ def scan(ip_with_subnet_mask, regexp = ".")
16
18
  symbolized_options = symbolize_hash_keys(options)
17
19
  range = Range.new(ip_with_subnet_mask)
18
20
 
@@ -25,10 +27,18 @@ module RangeScan
25
27
  puts JSON.pretty_generate(filtered)
26
28
  end
27
29
 
30
+ default_command :scan
31
+
28
32
  no_commands do
29
33
  def symbolize_hash_keys(hash)
30
34
  hash.map { |k, v| [k.to_sym, v] }.to_h
31
35
  end
32
36
  end
37
+
38
+ class << self
39
+ def exit_on_failure?
40
+ true
41
+ end
42
+ end
33
43
  end
34
44
  end
@@ -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,60 +1,98 @@
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
14
+ attr_reader :headers
9
15
  attr_reader :host
16
+ attr_reader :max_concurrency
10
17
  attr_reader :port
18
+ attr_reader :processor_count
11
19
  attr_reader :scheme
12
20
  attr_reader :ssl_context
13
21
  attr_reader :timeout
14
22
  attr_reader :user_agent
23
+ attr_reader :verify_ssl
15
24
 
16
- def initialize(host: nil, port: nil, scheme: "http", verify_ssl: true, timeout: 5, user_agent: nil)
25
+ def initialize(
26
+ headers: {},
27
+ host: nil,
28
+ max_concurrency: nil,
29
+ port: nil,
30
+ scheme: "http",
31
+ timeout: 5,
32
+ user_agent: nil,
33
+ verify_ssl: true
34
+ )
35
+ @headers = headers
17
36
  @host = host
18
37
  @port = port || (scheme == "http" ? 80 : 443)
19
- @timeout = timeout
20
38
  @scheme = scheme
39
+ @timeout = timeout
21
40
  @user_agent = user_agent
41
+ @verify_ssl = verify_ssl
22
42
 
23
43
  @ssl_context = OpenSSL::SSL::SSLContext.new
24
44
  @ssl_context.verify_mode = OpenSSL::SSL::VERIFY_NONE unless verify_ssl
45
+
46
+ @max_concurrency = max_concurrency || Etc.nprocessors * 8
47
+ end
48
+
49
+ def url_for(ipv4)
50
+ return "#{scheme}://#{ipv4}" if (port == 80 && scheme == "http") || (port == 443 && scheme == "https")
51
+
52
+ "#{scheme}://#{ipv4}:#{port}"
25
53
  end
26
54
 
27
55
  def scan(ipv4s)
28
- Parallel.map(ipv4s) do |ipv4|
29
- get ipv4
30
- end.compact
56
+ results = []
57
+ Async do
58
+ barrier = Async::Barrier.new
59
+ semaphore = Async::Semaphore.new(max_concurrency, parent: barrier)
60
+
61
+ ipv4s.each do |ipv4|
62
+ semaphore.async do
63
+ url = url_for(ipv4)
64
+
65
+ endpoint = Async::HTTP::Endpoint.parse(url, ssl_context: ssl_context, timeout: timeout)
66
+ client = Async::HTTP::Client.new(endpoint, retries: 0)
67
+ res = client.get(endpoint.path, default_request_headers)
68
+
69
+ headers = res.headers.fields.to_h
70
+ body = res.read || ""
71
+
72
+ results << {
73
+ url: url,
74
+ ipv4: ipv4,
75
+ code: res.status,
76
+ headers: Utils.to_utf8(headers),
77
+ body: Utils.to_utf8(body)
78
+ }
79
+ rescue Errno::ECONNRESET, Errno::ECONNREFUSED, Errno::EHOSTUNREACH, EOFError, OpenSSL::SSL::SSLError, Async::TimeoutError
80
+ next
81
+ end
82
+ end
83
+ barrier.wait
84
+ end
85
+ results.compact
31
86
  end
32
87
 
33
88
  private
34
89
 
35
- def default_headers
36
- { host: host, user_agent: user_agent }.compact
90
+ def default_request_headers
91
+ @default_request_headers ||= headers.merge({ "host" => host, "user-agent" => user_agent }.compact)
37
92
  end
38
93
 
39
94
  def ssl_options
40
95
  scheme == "http" ? {} : { ssl_context: ssl_context }
41
96
  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
- {
49
- url: url,
50
- ipv4: ipv4,
51
- code: res.code,
52
- headers: res.headers.to_h,
53
- body: res.body.to_s
54
- }
55
- rescue StandardError
56
- nil
57
- end
58
- end
59
97
  end
60
98
  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.1"
4
+ VERSION = "0.3.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.1
4
+ version: 0.3.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-10-17 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,6 +158,7 @@ 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"
@@ -171,16 +172,19 @@ files:
171
172
  - lib/rangescan.rb
172
173
  - lib/rangescan/cli.rb
173
174
  - lib/rangescan/matcher.rb
175
+ - lib/rangescan/monkey_patch.rb
174
176
  - lib/rangescan/range.rb
175
177
  - lib/rangescan/scanner.rb
178
+ - lib/rangescan/utils.rb
176
179
  - lib/rangescan/version.rb
177
180
  - rangescan.gemspec
181
+ - renovate.json
178
182
  homepage: https://github.com/ninoseki/rangescan
179
183
  licenses:
180
184
  - MIT
181
185
  metadata:
182
186
  homepage_uri: https://github.com/ninoseki/rangescan
183
- post_install_message:
187
+ post_install_message:
184
188
  rdoc_options: []
185
189
  require_paths:
186
190
  - lib
@@ -196,7 +200,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
196
200
  version: '0'
197
201
  requirements: []
198
202
  rubygems_version: 3.1.2
199
- signing_key:
203
+ signing_key:
200
204
  specification_version: 4
201
205
  summary: Scan websites on a specific IP range
202
206
  test_files: []