proxifier 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (C) 2011 by Samuel Kadolph
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
@@ -0,0 +1,120 @@
1
+ # ruby-proxifier
2
+
3
+ ## Installing
4
+
5
+ ### Recommended
6
+
7
+ ```
8
+ gem install proxifier
9
+ ```
10
+
11
+ ### Edge
12
+
13
+ ```
14
+ git clone https://github.com/samuelkadolph/ruby-proxifier
15
+ cd ruby-proxifier && rake install
16
+ ```
17
+
18
+ ## Rationale
19
+
20
+ This gem was created for 2 purposes.
21
+
22
+ First is to enable ruby programmers to use HTTP or SOCKS proxies
23
+ interchangeably when using TCPSockets. Either manually with
24
+ `Proxifier::Proxy#open` or by `require "proxifier/env"`.
25
+
26
+ The second purpose is to use ruby code that doesn't user proxies for users that
27
+ have to use proxies.<br>The pruby and pirb executables are simple wrappers for
28
+ their respective ruby executables that support proxies from environment
29
+ variables.
30
+
31
+ ## Usage
32
+
33
+ ### Executable Wrappers & Environment Variables
34
+
35
+ proxifier provides two executables: `pruby` and `pirb`. They are simple
36
+ wrappers for your current `ruby` and `irb` executables that requires the
37
+ `"proxifier/env"` script which installs hooks into `TCPSocket` which will use
38
+ the proxy environment variables to proxy any `TCPSocket`.
39
+
40
+ The environment variables that proxifier will check are (in order of descending
41
+ precedence):
42
+
43
+ <table>
44
+ <tr>
45
+ <th>Variable Name</th>
46
+ <th>Alternatives</th>
47
+ <th>Notes</th>
48
+ </tr>
49
+ <tr>
50
+ <td>proxy</td>
51
+ <td>PROXY</td>
52
+ <td>Requires the proxy scheme to be present.</td>
53
+ </tr>
54
+ <tr>
55
+ <td>socks_proxy</td>
56
+ <td>SOCKS_PROXY<br>socks5_proxy<br>SOCKS5_PROXY</td>
57
+ <td>Implies the SOCKS5 proxy scheme.</td>
58
+ </tr>
59
+ <tr>
60
+ <td>socks4a_proxy</td>
61
+ <td>SOCKS4A_PROXY</td>
62
+ <td>Implies the SOCKS4A proxy scheme.</td>
63
+ </tr>
64
+ <tr>
65
+ <td>socks4_proxy</td>
66
+ <td>PROXY</td>
67
+ <td>Implies the SOCKS4 proxy scheme.</td>
68
+ </tr>
69
+ <tr>
70
+ <td>http_proxy</td>
71
+ <td>HTTP_PROXY</td>
72
+ <td>Implies the HTTP proxy scheme.</td>
73
+ </tr>
74
+ </table>
75
+
76
+ ### Ruby
77
+
78
+ ```ruby
79
+ require "proxifier/proxy"
80
+
81
+ proxy = Proxifier::Proxy("socks://localhost")
82
+ socket = proxy.open("www.google.com", 80)
83
+ socket << "GET / HTTP/1.1\r\nHost: www.google.com\r\n\r\n"
84
+ socket.gets # => "HTTP/1.1 200 OK\r\n"
85
+ ```
86
+
87
+ ## Supported Proxies
88
+
89
+ <table>
90
+ <tr>
91
+ <th>Protocol</th>
92
+ <th>Formats</th>
93
+ <th>Notes</th>
94
+ </tr>
95
+ <tr>
96
+ <td>HTTP</td>
97
+ <td><pre>http://[username[:password]@]host[:port][?tunnel=false]</pre></td>
98
+ <td>
99
+ The port defaults to 80. This is currently a limitation that may be solved in the future.<br>
100
+ Appending <code>?tunnel=false</code> forces the proxy to not use <code>CONNECT</code>.</td>
101
+ </tr>
102
+ <tr>
103
+ <td>SOCKS5</td>
104
+ <td><pre>socks://[username[:password]@]host[:port]
105
+ socks5://[username[:password]@]host[:port]</pre></td>
106
+ <td>
107
+ Port defaults to 1080.
108
+ </td>
109
+ </tr>
110
+ <tr>
111
+ <td>SOCKS4A</td>
112
+ <td><pre>socks4a://[username@]host[:port]</pre></td>
113
+ <td>Not yet implemented.</td>
114
+ </tr>
115
+ <tr>
116
+ <td>SOCKS4</td>
117
+ <td><pre>socks4://[username@]host[:port]</pre></td>
118
+ <td>Currently hangs. Not sure if the problem is with code or server.</td>
119
+ </tr>
120
+ </table>
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ executable = File.expand_path("../" + Gem.default_exec_format % "irb", Gem.ruby)
4
+ full_gem_path = Gem.loaded_specs["proxifier"].full_gem_path
5
+ load_paths = Gem.loaded_specs["proxifier"].require_paths.map { |p| "-I#{File.join(full_gem_path, p)}" }
6
+ # TODO: support argument switches
7
+
8
+ exec(executable, *load_paths, "-rproxifier/env", *ARGV)
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ executable = Gem.ruby
4
+ full_gem_path = Gem.loaded_specs["proxifier"].full_gem_path
5
+ load_paths = Gem.loaded_specs["proxifier"].require_paths.map { |p| "-I#{File.join(full_gem_path, p)}" }
6
+ # TODO: support argument switches
7
+
8
+ exec(executable, *load_paths, "-rproxifier/env", *ARGV)
@@ -0,0 +1,25 @@
1
+ require "uri"
2
+ require "uri/socks"
3
+
4
+ module Proxifier
5
+ require "proxifier/version"
6
+
7
+ autoload :HTTPProxy, "proxifier/proxies/http"
8
+ autoload :SOCKSProxy, "proxifier/proxies/socks"
9
+ autoload :SOCKS5Proxy, "proxifier/proxies/socks"
10
+ autoload :SOCKS4Proxy, "proxifier/proxies/socks4"
11
+ autoload :SOCKS4AProxy, "proxifier/proxies/socks4a"
12
+
13
+ def self.Proxy(url, options = {})
14
+ url = URI.parse(url)
15
+
16
+ raise(ArgumentError, "proxy url has no scheme") unless url.scheme
17
+ begin
18
+ klass = const_get("#{url.scheme.upcase}Proxy")
19
+ rescue NameError
20
+ raise(ArgumentError, "unknown proxy scheme `#{url.scheme}'")
21
+ end
22
+
23
+ klass.new(url, options)
24
+ end
25
+ end
@@ -0,0 +1,111 @@
1
+ require "socket"
2
+ require "proxifier"
3
+
4
+ module Proxifier
5
+ class Proxy
6
+ def open(host, port, local_host = nil, local_port = nil)
7
+ return TCPSocket.new(host, port, local_host, local_port, :proxy => nil) unless proxify?(host)
8
+
9
+ socket = TCPSocket.new(proxy.host, proxy.port, local_host, local_port, :proxy => nil)
10
+
11
+ begin
12
+ proxify(socket, host, port)
13
+ rescue
14
+ socket.close
15
+ raise
16
+ end
17
+
18
+ socket
19
+ end
20
+ end
21
+
22
+ module Proxify
23
+ def self.included(klass)
24
+ klass.class_eval do
25
+ alias_method :initialize_without_proxy, :initialize
26
+ alias_method :initialize, :initialize_with_proxy
27
+ end
28
+ end
29
+
30
+ def initialize_with_proxy(host, port, options_or_local_host = {}, local_port = nil, options_if_local_host = {})
31
+ if options_or_local_host.is_a?(Hash)
32
+ local_host = nil
33
+ options = options_or_local_host
34
+ else
35
+ local_host = options_or_local_host
36
+ options = options_if_local_host
37
+ end
38
+
39
+ if options[:proxy] && (proxy = Proxifier::Proxy(options.delete(:proxy), options)) && proxy.proxify?(host)
40
+ initialize_without_proxy(proxy.host, proxy.port, local_host, local_port)
41
+ begin
42
+ proxy.proxify(self, host, port)
43
+ rescue
44
+ close
45
+ raise
46
+ end
47
+ else
48
+ initialize_without_proxy(host, port, local_host, local_port)
49
+ end
50
+ end
51
+ end
52
+
53
+ module EnvironmentProxify
54
+ def self.included(klass)
55
+ klass.class_eval do
56
+ extend ClassMethods
57
+ alias_method :initialize_without_environment_proxy, :initialize
58
+ alias_method :initialize, :initialize_with_environment_proxy
59
+ end
60
+ end
61
+
62
+ def initialize_with_environment_proxy(host, port, options_or_local_host = {}, local_port = nil, options_if_local_host = {})
63
+ if options_or_local_host.is_a?(Hash)
64
+ local_host = nil
65
+ options = options_or_local_host
66
+ else
67
+ local_host = options_or_local_host
68
+ options = options_if_local_host
69
+ end
70
+
71
+ options = { :proxy => environment_proxy, :no_proxy => environment_no_proxy }.merge(options)
72
+ initialize_without_environment_proxy(host, port, local_host, local_port, options)
73
+ end
74
+
75
+ def environment_proxy
76
+ self.class.environment_proxy
77
+ end
78
+
79
+ def environment_no_proxy
80
+ self.class.environment_no_proxy
81
+ end
82
+
83
+ module ClassMethods
84
+ def environment_proxy
85
+ ENV["proxy"] || ENV["PROXY"] || specific_environment_proxy
86
+ end
87
+
88
+ def environment_no_proxy
89
+ ENV["no_proxy"] || ENV["NO_PROXY"]
90
+ end
91
+
92
+ private
93
+ def specific_environment_proxy
94
+ %w(socks socks5 socks4a socks4 http).each do |type|
95
+ if proxy = ENV["#{type}_proxy"] || ENV["#{type.upcase}_PROXY"]
96
+ scheme = "#{type}://"
97
+
98
+ proxy = proxy.dup
99
+ proxy.insert(0, scheme) unless proxy.index(scheme) == 0
100
+ return proxy
101
+ end
102
+ end
103
+ end
104
+ end
105
+ end
106
+ end
107
+
108
+ class TCPSocket
109
+ include Proxifier::Proxify
110
+ include Proxifier::EnvironmentProxify
111
+ end
@@ -0,0 +1,19 @@
1
+ require "net/http"
2
+ require "proxifier/proxy"
3
+
4
+ module Proxifier
5
+ class HTTPProxy < Proxy
6
+ def do_proxify(socket, host, port)
7
+ return if query_options["tunnel"] == "false"
8
+
9
+ socket << "CONNECT #{host}:#{port} HTTP/1.1\r\n"
10
+ socket << "Host: #{host}:#{port}\r\n"
11
+ socket << "Proxy-Authorization: Basic #{["#{user}:#{password}"].pack("m").chomp}\r\n" if user
12
+ socket << "\r\n"
13
+
14
+ buffer = Net::BufferedIO.new(socket)
15
+ response = Net::HTTPResponse.read_new(buffer)
16
+ response.error! unless response.is_a?(Net::HTTPOK)
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,103 @@
1
+ require "ipaddr"
2
+ require "proxifier/proxy"
3
+
4
+ module Proxifier
5
+ class SOCKSProxy < Proxy
6
+ VERSION = 0x05
7
+
8
+ def do_proxify(socket, host, port)
9
+ authenticaton_method = greet(socket)
10
+ authenticate(socket, authenticaton_method)
11
+ connect(socket, host, port)
12
+ end
13
+
14
+ protected
15
+ def greet(socket)
16
+ methods = authentication_methods
17
+
18
+ socket << [VERSION, methods.size, *methods].pack("CCC#{methods.size}")
19
+ version, authentication_method = socket.read(2).unpack("CC")
20
+ check_version(version)
21
+
22
+ authentication_method
23
+ end
24
+
25
+ def authenticate(socket, method)
26
+ case method
27
+ when 0x00 # NO AUTHENTICATION REQUIRED
28
+ when 0x02 # USERNAME/PASSWORD
29
+ user &&= user[0, 0xFF]
30
+ password &&= password[0, 0xFF]
31
+
32
+ socket << [user.size, user, password.size, password].pack("CA#{user.size}CA#{password.size}")
33
+ version, status = socket.read(2).unpack("CC")
34
+ check_version(version)
35
+
36
+ case status
37
+ when 0x00 # SUCCESS
38
+ else
39
+ raise "SOCKS5 username/password authentication failed"
40
+ end
41
+ else
42
+ raise "no acceptable SOCKS5 authentication methods"
43
+ end
44
+ end
45
+
46
+ def connect(socket, host, port)
47
+ host = host[0, 0xFF]
48
+ socket << [VERSION, 0x01, 0x00, 0x03, host.size, host, port].pack("CCCCCA#{host.size}n")
49
+ version, status, _, type = socket.read(4).unpack("CCCC")
50
+ check_version(version)
51
+
52
+ case status
53
+ when 0x00 # succeeded
54
+ when 0x01 # general SOCKS server failure
55
+ raise "general SOCKS server failure"
56
+ when 0x02 # connection not allowed by ruleset
57
+ raise "connection not allowed by ruleset"
58
+ when 0x03 # Network unreachable
59
+ raise "network unreachable"
60
+ when 0x04 # Host unreachable
61
+ raise "host unreachable"
62
+ when 0x05 # Connection refused
63
+ raise "connection refused"
64
+ when 0x06 # TTL expired
65
+ raise "TTL expired"
66
+ when 0x07 # Command not supported
67
+ raise "command not supported"
68
+ when 0x08 # Address type not supported
69
+ raise "address type not supported"
70
+ else # unassigned
71
+ raise "unknown SOCKS error"
72
+ end
73
+
74
+ case type
75
+ when 0x01 # IP V4 address
76
+ destination = IPAddr.ntop(socket.read(4))
77
+ when 0x03 # DOMAINNAME
78
+ length = socket.read(1).unpack("C").first
79
+ destination = socket.read(length).unpack("A#{length}")
80
+ when 0x04 # IP V6 address
81
+ destination = IPAddr.ntop(socket.read(16))
82
+ else
83
+ raise "unsupported SOCKS5 address type"
84
+ end
85
+
86
+ port = socket.read(2).unpack("n").first
87
+ end
88
+
89
+ def check_version(version, should_be = VERSION)
90
+ raise "mismatched SOCKS version" unless version == should_be
91
+ end
92
+
93
+ private
94
+ def authentication_methods
95
+ methods = []
96
+ methods << 0x00 # NO AUTHENTICATION REQUIRED
97
+ methods << 0x02 if user # USERNAME/PASSWORD
98
+ methods
99
+ end
100
+ end
101
+
102
+ SOCKS5Proxy = SOCKSProxy
103
+ end
@@ -0,0 +1,44 @@
1
+ require "proxifier/proxies/socks"
2
+
3
+ module Proxifier
4
+ class SOCKS4Proxy < SOCKSProxy
5
+ VERSION = 0x04
6
+
7
+ protected
8
+ def greet(socket)
9
+ # noop
10
+ end
11
+
12
+ def authenticate(socket, method)
13
+ # noop
14
+ end
15
+
16
+ def connect(socket, host, port)
17
+ begin
18
+ ip = IPAddr.new(host)
19
+ rescue ArgumentError
20
+ ip = IPAddr.new(Socket.getaddrinfo(host, nil, :INET, :STREAM).first)
21
+ end
22
+
23
+ socket << [VERSION, 0x01, port].pack("CCn") << ip.hton
24
+ socket << user if user
25
+ socket << 0x00
26
+
27
+ version, status, port = socket.read(4).unpack("CCn")
28
+ check_version(version, 0x00)
29
+ ip = IPAddr.ntop(socket.read(4))
30
+
31
+ case status
32
+ when 0x5A # request granted
33
+ when 0x5B # request rejected or failed
34
+ raise "request rejected or failed"
35
+ when 0x5C # request rejected becasue SOCKS server cannot connect to identd on the client
36
+ raise "request rejected becasue SOCKS server cannot connect to identd on the client"
37
+ when 0x5D # request rejected because the client program and identd report different user-ids
38
+ raise "request rejected because the client program and identd report different user-ids"
39
+ else
40
+ raise "unknown SOCKS error"
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,9 @@
1
+ require "proxifier/proxies/socks"
2
+
3
+ module Proxifier
4
+ class SOCKS4AProxy < SOCKSProxy
5
+ def do_proxify(*)
6
+ raise NotImplementedError, "SOCKS4A is not yet implemented"
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,63 @@
1
+ require "socket"
2
+ require "uri"
3
+ require "uri/socks"
4
+
5
+ module Proxifier
6
+ class Proxy
7
+ class << self
8
+ def proxify?(host, no_proxy = nil)
9
+ return true unless no_proxy
10
+
11
+ dont_proxy = no_proxy.split(",")
12
+ dont_proxy.none? { |h| host =~ /#{h}\Z/ }
13
+ end
14
+ end
15
+
16
+ attr_reader :url, :options
17
+
18
+ def initialize(url, options = {})
19
+ url = URI.parse(uri) unless url.is_a?(URI::Generic)
20
+ @url, @options = url, options
21
+ end
22
+
23
+ def open(host, port, local_host = nil, local_port = nil)
24
+ return TCPSocket.new(host, port, local_host, local_port) unless proxify?(host)
25
+
26
+ socket = TCPSocket.new(proxy.host, proxy.port, local_host, local_port)
27
+
28
+ begin
29
+ proxify(socket, host, port)
30
+ rescue
31
+ socket.close
32
+ raise
33
+ end
34
+
35
+ socket
36
+ end
37
+
38
+ def proxify?(host)
39
+ self.class.proxify?(host, options[:no_proxy])
40
+ end
41
+
42
+ def proxify(socket, host, port)
43
+ do_proxify(socket, host, port)
44
+ end
45
+
46
+ %w(host port user password query version).each do |attr|
47
+ class_eval "def #{attr}; url.#{attr} end"
48
+ end
49
+
50
+ def query_options
51
+ @query_options ||= query ? Hash[query.split("&").map { |q| q.split("=") }] : {}
52
+ end
53
+
54
+ %w(no_proxy).each do |option|
55
+ class_eval "def #{option}; options[:#{option}] end"
56
+ end
57
+
58
+ protected
59
+ def do_proxify(socket, host, port)
60
+ raise NotImplementedError, "#{self} must implement do_proxify"
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,3 @@
1
+ module Proxifier
2
+ VERSION = "1.0.0"
3
+ end
@@ -0,0 +1,18 @@
1
+ require "uri/generic"
2
+
3
+ module URI
4
+ class SOCKS < Generic
5
+ DEFAULT_PORT = 1080
6
+ COMPONENT = [:scheme, :userinfo, :host, :port, :query].freeze
7
+ end
8
+ @@schemes["SOCKS"] = SOCKS
9
+ @@schemes["SOCKS5"] = SOCKS
10
+
11
+ class SOCKS4 < SOCKS
12
+ end
13
+ @@schemes["SOCKS4"] = SOCKS4
14
+
15
+ class SOCKS4A < SOCKS
16
+ end
17
+ @@schemes["SOCKS4A"] = SOCKS4A
18
+ end
metadata ADDED
@@ -0,0 +1,60 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: proxifier
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Samuel Kadolph
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-08-23 00:00:00.000000000Z
13
+ dependencies: []
14
+ description: ''
15
+ email:
16
+ - samuel@kadolph.com
17
+ executables:
18
+ - pirb
19
+ - pruby
20
+ extensions: []
21
+ extra_rdoc_files: []
22
+ files:
23
+ - bin/pirb
24
+ - bin/pruby
25
+ - lib/proxifier/env.rb
26
+ - lib/proxifier/proxies/http.rb
27
+ - lib/proxifier/proxies/socks.rb
28
+ - lib/proxifier/proxies/socks4.rb
29
+ - lib/proxifier/proxies/socks4a.rb
30
+ - lib/proxifier/proxy.rb
31
+ - lib/proxifier/version.rb
32
+ - lib/proxifier.rb
33
+ - lib/uri/socks.rb
34
+ - LICENSE
35
+ - README.md
36
+ homepage: https://github.com/samuelkadolph/ruby-proxifier
37
+ licenses: []
38
+ post_install_message:
39
+ rdoc_options: []
40
+ require_paths:
41
+ - lib
42
+ required_ruby_version: !ruby/object:Gem::Requirement
43
+ none: false
44
+ requirements:
45
+ - - ! '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ required_rubygems_version: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ requirements: []
55
+ rubyforge_project:
56
+ rubygems_version: 1.8.6
57
+ signing_key:
58
+ specification_version: 3
59
+ summary: ''
60
+ test_files: []