rangescan 0.1.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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: []