Renamed project to ruby-proxifier and renamed gem to proxifier.
This commit is contained in:
parent
653e57fa03
commit
dad9540ef4
80
README.md
80
README.md
|
@ -1,46 +1,84 @@
|
|||
# ruby-sockets
|
||||
# ruby-proxifier
|
||||
|
||||
## Installing
|
||||
|
||||
### Recommended
|
||||
|
||||
```
|
||||
gem install sockets
|
||||
gem install proxifier
|
||||
```
|
||||
|
||||
### Edge
|
||||
|
||||
```
|
||||
git clone https://github.com/samuelkadolph/ruby-sockets
|
||||
cd ruby-sockets && rake install
|
||||
git clone https://github.com/samuelkadolph/ruby-proxifier
|
||||
cd ruby-proxifier && rake install
|
||||
```
|
||||
|
||||
## Rationale
|
||||
|
||||
This gem was created for 2 purposes.
|
||||
|
||||
First is to enable ruby programmers to use HTTP or SOCKS proxies interchangeably when using TCPSockets. Either manually with `Sockets::Proxy#open` or by `require "sockets/env"`.
|
||||
First is to enable ruby programmers to use HTTP or SOCKS proxies
|
||||
interchangeably when using TCPSockets. Either manually with
|
||||
`Proxifier::Proxy#open` or by `require "proxifier/env"`.
|
||||
|
||||
The second purpose is to use ruby code that doesn't consider a proxy for users that have to use proxies.<br>
|
||||
The pruby and pirb executables are simple wrappers for their respective ruby executables that support proxies from environment variables.
|
||||
The second purpose is to use ruby code that doesn't user proxies for users that
|
||||
have to use proxies.<br>The pruby and pirb executables are simple wrappers for
|
||||
their respective ruby executables that support proxies from environment
|
||||
variables.
|
||||
|
||||
## Usage
|
||||
|
||||
### Environment Variables & Executable Wrappers
|
||||
### Executable Wrappers & Environment Variables
|
||||
|
||||
sockets provides two executables: `pruby` and `pirb`. They are simple wrappers
|
||||
for your current `ruby` and `irb` executables that `require "sockets/env"`
|
||||
which installs hooks to `TCPSocket` which will use your proxy environment
|
||||
variables whenever a `TCPSocket` is created. sockets will use the
|
||||
`proxy`, `PROXY`, `socks_proxy` and `http_proxy` environment variables (in that
|
||||
order) to determine what proxy to use.
|
||||
proxifier provides two executables: `pruby` and `pirb`. They are simple
|
||||
wrappers for your current `ruby` and `irb` executables that requires the
|
||||
`"proxifier/env"` script which installs hooks into `TCPSocket` which will use
|
||||
the proxy environment variables to proxy any `TCPSocket`.
|
||||
|
||||
The environment variables that proxifier will check are (in order of descending
|
||||
precedence):
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th>Variable Name</th>
|
||||
<th>Alternatives</th>
|
||||
<th>Notes</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>proxy</td>
|
||||
<td>PROXY</td>
|
||||
<td>Requires the proxy scheme to be present.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>socks_proxy</td>
|
||||
<td>SOCKS_PROXY<br>socks5_proxy<br>SOCKS5_PROXY</td>
|
||||
<td>Implies the SOCKS5 proxy scheme.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>socks4a_proxy</td>
|
||||
<td>SOCKS4A_PROXY</td>
|
||||
<td>Implies the SOCKS4A proxy scheme.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>socks4_proxy</td>
|
||||
<td>PROXY</td>
|
||||
<td>Implies the SOCKS4 proxy scheme.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>http_proxy</td>
|
||||
<td>HTTP_PROXY</td>
|
||||
<td>Implies the HTTP proxy scheme.</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
### Ruby
|
||||
|
||||
```ruby
|
||||
require "sockets/proxy"
|
||||
require "proxifier/proxy"
|
||||
|
||||
proxy = Sockets::Proxy("socks://localhost")
|
||||
proxy = Proxifier::Proxy("socks://localhost")
|
||||
socket = proxy.open("www.google.com", 80)
|
||||
socket << "GET / HTTP/1.1\r\nHost: www.google.com\r\n\r\n"
|
||||
socket.gets # => "HTTP/1.1 200 OK\r\n"
|
||||
|
@ -69,14 +107,14 @@ socks5://[username[:password]@]host[:port]</pre></td>
|
|||
Port defaults to 1080.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>SOCKS4</td>
|
||||
<td><pre>socks4://[username@]ip1.ip2.ip3.ip4[:port]</pre></td>
|
||||
<td>Currently hangs. Not sure if the problem is with code or server.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>SOCKS4A</td>
|
||||
<td><pre>socks4a://[username@]host[:port]</pre></td>
|
||||
<td>Not yet implemented.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>SOCKS4</td>
|
||||
<td><pre>socks4://[username@]host[:port]</pre></td>
|
||||
<td>Currently hangs. Not sure if the problem is with code or server.</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
|
6
bin/pirb
6
bin/pirb
|
@ -1,8 +1,8 @@
|
|||
#!/usr/bin/env ruby
|
||||
|
||||
executable = File.expand_path("../" + Gem.default_exec_format % "irb", Gem.ruby)
|
||||
full_gem_path = Gem.loaded_specs["sockets"].full_gem_path
|
||||
load_paths = Gem.loaded_specs["sockets"].require_paths.map { |p| "-I#{File.join(full_gem_path, p)}" }
|
||||
full_gem_path = Gem.loaded_specs["proxifier"].full_gem_path
|
||||
load_paths = Gem.loaded_specs["proxifier"].require_paths.map { |p| "-I#{File.join(full_gem_path, p)}" }
|
||||
# TODO: support argument switches
|
||||
|
||||
exec(executable, *load_paths, "-rsockets/env", *ARGV)
|
||||
exec(executable, *load_paths, "-rproxifier/env", *ARGV)
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
#!/usr/bin/env ruby
|
||||
|
||||
executable = Gem.ruby
|
||||
full_gem_path = Gem.loaded_specs["sockets"].full_gem_path
|
||||
load_paths = Gem.loaded_specs["sockets"].require_paths.map { |p| "-I#{File.join(full_gem_path, p)}" }
|
||||
full_gem_path = Gem.loaded_specs["proxifier"].full_gem_path
|
||||
load_paths = Gem.loaded_specs["proxifier"].require_paths.map { |p| "-I#{File.join(full_gem_path, p)}" }
|
||||
# TODO: support argument switches
|
||||
|
||||
exec(executable, *load_paths, "-rsockets/env", *ARGV)
|
||||
exec(executable, *load_paths, "-rproxifier/env", *ARGV)
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
require "uri"
|
||||
require "uri/socks"
|
||||
|
||||
module Proxifier
|
||||
require "proxifier/version"
|
||||
|
||||
autoload :HTTPProxy, "proxifier/proxies/http"
|
||||
autoload :SOCKSProxy, "proxifier/proxies/socks"
|
||||
autoload :SOCKS5Proxy, "proxifier/proxies/socks"
|
||||
autoload :SOCKS4Proxy, "proxifier/proxies/socks4"
|
||||
autoload :SOCKS4AProxy, "proxifier/proxies/socks4a"
|
||||
|
||||
def self.Proxy(url, options = {})
|
||||
url = URI.parse(url)
|
||||
|
||||
raise(ArgumentError, "proxy url has no scheme") unless url.scheme
|
||||
begin
|
||||
klass = const_get("#{url.scheme.upcase}Proxy")
|
||||
rescue NameError
|
||||
raise(ArgumentError, "unknown proxy scheme `#{url.scheme}'")
|
||||
end
|
||||
|
||||
klass.new(url, options)
|
||||
end
|
||||
end
|
|
@ -1,6 +1,24 @@
|
|||
require "sockets/proxy"
|
||||
require "socket"
|
||||
require "proxifier"
|
||||
|
||||
module Proxifier
|
||||
class Proxy
|
||||
def open(host, port, local_host = nil, local_port = nil)
|
||||
return TCPSocket.new(host, port, local_host, local_port, :proxy => nil) unless proxify?(host)
|
||||
|
||||
socket = TCPSocket.new(proxy.host, proxy.port, local_host, local_port, :proxy => nil)
|
||||
|
||||
begin
|
||||
proxify(socket, host, port)
|
||||
rescue
|
||||
socket.close
|
||||
raise
|
||||
end
|
||||
|
||||
socket
|
||||
end
|
||||
end
|
||||
|
||||
module Sockets
|
||||
module Proxify
|
||||
def self.included(klass)
|
||||
klass.class_eval do
|
||||
|
@ -18,11 +36,11 @@ module Sockets
|
|||
options = options_if_local_host
|
||||
end
|
||||
|
||||
if options[:proxy] && (proxy = Sockets::Proxy(options.delete(:proxy), options)) && proxy.proxify?(host)
|
||||
if options[:proxy] && (proxy = Proxifier::Proxy(options.delete(:proxy), options)) && proxy.proxify?(host)
|
||||
initialize_without_proxy(proxy.host, proxy.port, local_host, local_port)
|
||||
begin
|
||||
proxy.proxify(self, host, port)
|
||||
rescue Exception
|
||||
rescue
|
||||
close
|
||||
raise
|
||||
end
|
||||
|
@ -31,9 +49,7 @@ module Sockets
|
|||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module Sockets
|
||||
module EnvironmentProxify
|
||||
def self.included(klass)
|
||||
klass.class_eval do
|
||||
|
@ -66,12 +82,30 @@ module Sockets
|
|||
|
||||
module ClassMethods
|
||||
def environment_proxy
|
||||
ENV["proxy"] || ENV["PROXY"] || ENV["socks_proxy"] || ENV["http_proxy"]
|
||||
ENV["proxy"] || ENV["PROXY"] || specific_environment_proxy
|
||||
end
|
||||
|
||||
def environment_no_proxy
|
||||
ENV["no_proxy"]
|
||||
ENV["no_proxy"] || ENV["NO_PROXY"]
|
||||
end
|
||||
|
||||
private
|
||||
def specific_environment_proxy
|
||||
%w(socks socks5 socks4a socks4 http).each do |type|
|
||||
if proxy = ENV["#{type}_proxy"] || ENV["#{type.upcase}_PROXY"]
|
||||
scheme = "#{type}://"
|
||||
|
||||
proxy = proxy.dup
|
||||
proxy.insert(0, scheme) unless proxy.index(scheme) == 0
|
||||
return proxy
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class TCPSocket
|
||||
include Proxifier::Proxify
|
||||
include Proxifier::EnvironmentProxify
|
||||
end
|
|
@ -0,0 +1,19 @@
|
|||
require "net/http"
|
||||
require "proxifier/proxy"
|
||||
|
||||
module Proxifier
|
||||
class HTTPProxy < Proxy
|
||||
def do_proxify(socket, host, port)
|
||||
return if query_options["tunnel"] == "false"
|
||||
|
||||
socket << "CONNECT #{host}:#{port} HTTP/1.1\r\n"
|
||||
socket << "Host: #{host}:#{port}\r\n"
|
||||
socket << "Proxy-Authorization: Basic #{["#{user}:#{password}"].pack("m").chomp}\r\n" if user
|
||||
socket << "\r\n"
|
||||
|
||||
buffer = Net::BufferedIO.new(socket)
|
||||
response = Net::HTTPResponse.read_new(buffer)
|
||||
response.error! unless response.is_a?(Net::HTTPOK)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,103 @@
|
|||
require "ipaddr"
|
||||
require "proxifier/proxy"
|
||||
|
||||
module Proxifier
|
||||
class SOCKSProxy < Proxy
|
||||
VERSION = 0x05
|
||||
|
||||
def do_proxify(socket, host, port)
|
||||
authenticaton_method = greet(socket)
|
||||
authenticate(socket, authenticaton_method)
|
||||
connect(socket, host, port)
|
||||
end
|
||||
|
||||
protected
|
||||
def greet(socket)
|
||||
methods = authentication_methods
|
||||
|
||||
socket << [VERSION, methods.size, *methods].pack("CCC#{methods.size}")
|
||||
version, authentication_method = socket.read(2).unpack("CC")
|
||||
check_version(version)
|
||||
|
||||
authentication_method
|
||||
end
|
||||
|
||||
def authenticate(socket, method)
|
||||
case method
|
||||
when 0x00 # NO AUTHENTICATION REQUIRED
|
||||
when 0x02 # USERNAME/PASSWORD
|
||||
user &&= user[0, 0xFF]
|
||||
password &&= password[0, 0xFF]
|
||||
|
||||
socket << [user.size, user, password.size, password].pack("CA#{user.size}CA#{password.size}")
|
||||
version, status = socket.read(2).unpack("CC")
|
||||
check_version(version)
|
||||
|
||||
case status
|
||||
when 0x00 # SUCCESS
|
||||
else
|
||||
raise "SOCKS5 username/password authentication failed"
|
||||
end
|
||||
else
|
||||
raise "no acceptable SOCKS5 authentication methods"
|
||||
end
|
||||
end
|
||||
|
||||
def connect(socket, host, port)
|
||||
host = host[0, 0xFF]
|
||||
socket << [VERSION, 0x01, 0x00, 0x03, host.size, host, port].pack("CCCCCA#{host.size}n")
|
||||
version, status, _, type = socket.read(4).unpack("CCCC")
|
||||
check_version(version)
|
||||
|
||||
case status
|
||||
when 0x00 # succeeded
|
||||
when 0x01 # general SOCKS server failure
|
||||
raise "general SOCKS server failure"
|
||||
when 0x02 # connection not allowed by ruleset
|
||||
raise "connection not allowed by ruleset"
|
||||
when 0x03 # Network unreachable
|
||||
raise "network unreachable"
|
||||
when 0x04 # Host unreachable
|
||||
raise "host unreachable"
|
||||
when 0x05 # Connection refused
|
||||
raise "connection refused"
|
||||
when 0x06 # TTL expired
|
||||
raise "TTL expired"
|
||||
when 0x07 # Command not supported
|
||||
raise "command not supported"
|
||||
when 0x08 # Address type not supported
|
||||
raise "address type not supported"
|
||||
else # unassigned
|
||||
raise "unknown SOCKS error"
|
||||
end
|
||||
|
||||
case type
|
||||
when 0x01 # IP V4 address
|
||||
destination = IPAddr.ntop(socket.read(4))
|
||||
when 0x03 # DOMAINNAME
|
||||
length = socket.read(1).unpack("C").first
|
||||
destination = socket.read(length).unpack("A#{length}")
|
||||
when 0x04 # IP V6 address
|
||||
destination = IPAddr.ntop(socket.read(16))
|
||||
else
|
||||
raise "unsupported SOCKS5 address type"
|
||||
end
|
||||
|
||||
port = socket.read(2).unpack("n").first
|
||||
end
|
||||
|
||||
def check_version(version, should_be = VERSION)
|
||||
raise "mismatched SOCKS version" unless version == should_be
|
||||
end
|
||||
|
||||
private
|
||||
def authentication_methods
|
||||
methods = []
|
||||
methods << 0x00 # NO AUTHENTICATION REQUIRED
|
||||
methods << 0x02 if user # USERNAME/PASSWORD
|
||||
methods
|
||||
end
|
||||
end
|
||||
|
||||
SOCKS5Proxy = SOCKSProxy
|
||||
end
|
|
@ -0,0 +1,44 @@
|
|||
require "proxifier/proxies/socks"
|
||||
|
||||
module Proxifier
|
||||
class SOCKS4Proxy < SOCKSProxy
|
||||
VERSION = 0x04
|
||||
|
||||
protected
|
||||
def greet(socket)
|
||||
# noop
|
||||
end
|
||||
|
||||
def authenticate(socket, method)
|
||||
# noop
|
||||
end
|
||||
|
||||
def connect(socket, host, port)
|
||||
begin
|
||||
ip = IPAddr.new(host)
|
||||
rescue ArgumentError
|
||||
ip = IPAddr.new(Socket.getaddrinfo(host, nil, :INET, :STREAM).first)
|
||||
end
|
||||
|
||||
socket << [VERSION, 0x01, port].pack("CCn") << ip.hton
|
||||
socket << user if user
|
||||
socket << 0x00
|
||||
|
||||
version, status, port = socket.read(4).unpack("CCn")
|
||||
check_version(version, 0x00)
|
||||
ip = IPAddr.ntop(socket.read(4))
|
||||
|
||||
case status
|
||||
when 0x5A # request granted
|
||||
when 0x5B # request rejected or failed
|
||||
raise "request rejected or failed"
|
||||
when 0x5C # request rejected becasue SOCKS server cannot connect to identd on the client
|
||||
raise "request rejected becasue SOCKS server cannot connect to identd on the client"
|
||||
when 0x5D # request rejected because the client program and identd report different user-ids
|
||||
raise "request rejected because the client program and identd report different user-ids"
|
||||
else
|
||||
raise "unknown SOCKS error"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,9 @@
|
|||
require "proxifier/proxies/socks"
|
||||
|
||||
module Proxifier
|
||||
class SOCKS4AProxy < SOCKSProxy
|
||||
def do_proxify(*)
|
||||
raise NotImplementedError, "SOCKS4A is not yet implemented"
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,63 @@
|
|||
require "socket"
|
||||
require "uri"
|
||||
require "uri/socks"
|
||||
|
||||
module Proxifier
|
||||
class Proxy
|
||||
class << self
|
||||
def proxify?(host, no_proxy = nil)
|
||||
return true unless no_proxy
|
||||
|
||||
dont_proxy = no_proxy.split(",")
|
||||
dont_proxy.none? { |h| host =~ /#{h}\Z/ }
|
||||
end
|
||||
end
|
||||
|
||||
attr_reader :url, :options
|
||||
|
||||
def initialize(url, options = {})
|
||||
url = URI.parse(uri) unless url.is_a?(URI::Generic)
|
||||
@url, @options = url, options
|
||||
end
|
||||
|
||||
def open(host, port, local_host = nil, local_port = nil)
|
||||
return TCPSocket.new(host, port, local_host, local_port) unless proxify?(host)
|
||||
|
||||
socket = TCPSocket.new(proxy.host, proxy.port, local_host, local_port)
|
||||
|
||||
begin
|
||||
proxify(socket, host, port)
|
||||
rescue
|
||||
socket.close
|
||||
raise
|
||||
end
|
||||
|
||||
socket
|
||||
end
|
||||
|
||||
def proxify?(host)
|
||||
self.class.proxify?(host, options[:no_proxy])
|
||||
end
|
||||
|
||||
def proxify(socket, host, port)
|
||||
do_proxify(socket, host, port)
|
||||
end
|
||||
|
||||
%w(host port user password query version).each do |attr|
|
||||
class_eval "def #{attr}; url.#{attr} end"
|
||||
end
|
||||
|
||||
def query_options
|
||||
@query_options ||= query ? Hash[query.split("&").map { |q| q.split("=") }] : {}
|
||||
end
|
||||
|
||||
%w(no_proxy).each do |option|
|
||||
class_eval "def #{option}; options[:#{option}] end"
|
||||
end
|
||||
|
||||
protected
|
||||
def do_proxify(socket, host, port)
|
||||
raise NotImplementedError, "#{self} must implement do_proxify"
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,3 +1,3 @@
|
|||
module Sockets
|
||||
module Proxifier
|
||||
VERSION = "1.0.0"
|
||||
end
|
|
@ -1,5 +0,0 @@
|
|||
require "sockets/version"
|
||||
require "sockets/proxy"
|
||||
|
||||
module Sockets
|
||||
end
|
|
@ -1,28 +0,0 @@
|
|||
require "socket"
|
||||
|
||||
require "sockets"
|
||||
require "sockets/proxify"
|
||||
|
||||
module Sockets
|
||||
class Proxy
|
||||
def open(host, port, local_host = nil, local_port = nil)
|
||||
if proxify?(host)
|
||||
socket = TCPSocket.new(proxy.host, proxy.port, local_host, local_port, :proxy => nil)
|
||||
begin
|
||||
proxify(socket, host, port)
|
||||
rescue Exception
|
||||
socket.close
|
||||
raise
|
||||
end
|
||||
socket
|
||||
else
|
||||
TCPSocket.new(host, port, local_host, local_port, :proxy => nil)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class TCPSocket
|
||||
include Sockets::Proxify
|
||||
include Sockets::EnvironmentProxify
|
||||
end
|
|
@ -1,4 +0,0 @@
|
|||
module Sockets
|
||||
module Proxies
|
||||
end
|
||||
end
|
|
@ -1,21 +0,0 @@
|
|||
require "net/http"
|
||||
require "sockets/proxy"
|
||||
|
||||
module Sockets
|
||||
module Proxies
|
||||
class HTTP < Proxy
|
||||
def do_proxify(socket, host, port)
|
||||
return if query_options["tunnel"] == "false"
|
||||
|
||||
socket << "CONNECT #{host}:#{port} HTTP/1.1\r\n"
|
||||
socket << "Host: #{host}:#{port}\r\n"
|
||||
socket << "Proxy-Authorization: Basic #{["#{user}:#{password}"].pack("m").chomp}\r\n" if user
|
||||
socket << "\r\n"
|
||||
|
||||
buffer = Net::BufferedIO.new(socket)
|
||||
response = Net::HTTPResponse.read_new(buffer)
|
||||
response.error! unless response.is_a?(Net::HTTPOK)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,103 +0,0 @@
|
|||
require "ipaddr"
|
||||
require "sockets/proxy"
|
||||
|
||||
module Sockets
|
||||
module Proxies
|
||||
class SOCKS < Proxy
|
||||
VERSION = 0x05
|
||||
|
||||
def do_proxify(socket, host, port)
|
||||
authenticaton_method = greet(socket)
|
||||
authenticate(socket, authenticaton_method)
|
||||
connect(socket, host, port)
|
||||
end
|
||||
|
||||
protected
|
||||
def greet(socket)
|
||||
methods = authentication_methods
|
||||
|
||||
socket << [VERSION, methods.size, *methods].pack("CCC#{methods.size}")
|
||||
version, authentication_method = socket.read(2).unpack("CC")
|
||||
check_version(version)
|
||||
|
||||
authentication_method
|
||||
end
|
||||
|
||||
def authenticate(socket, method)
|
||||
case method
|
||||
when 0x00 # NO AUTHENTICATION REQUIRED
|
||||
when 0x02 # USERNAME/PASSWORD
|
||||
user &&= user[0, 0xFF]
|
||||
password &&= password[0, 0xFF]
|
||||
|
||||
socket << [user.size, user, password.size, password].pack("CA#{user.size}CA#{password.size}")
|
||||
version, status = socket.read(2).unpack("CC")
|
||||
check_version(version)
|
||||
|
||||
case status
|
||||
when 0x00 # SUCCESS
|
||||
else
|
||||
raise "SOCKS5 username/password authentication failed"
|
||||
end
|
||||
else
|
||||
raise "no acceptable SOCKS5 authentication methods"
|
||||
end
|
||||
end
|
||||
|
||||
def connect(socket, host, port)
|
||||
host = host[0, 0xFF]
|
||||
socket << [VERSION, 0x01, 0x00, 0x03, host.size, host, port].pack("CCCCCA#{host.size}n")
|
||||
version, status, _, type = socket.read(4).unpack("CCCC")
|
||||
check_version(version)
|
||||
|
||||
case status
|
||||
when 0x00 # succeeded
|
||||
when 0x01 # general SOCKS server failure
|
||||
raise "general SOCKS server failure"
|
||||
when 0x02 # connection not allowed by ruleset
|
||||
raise "connection not allowed by ruleset"
|
||||
when 0x03 # Network unreachable
|
||||
raise "network unreachable"
|
||||
when 0x04 # Host unreachable
|
||||
raise "host unreachable"
|
||||
when 0x05 # Connection refused
|
||||
raise "connection refused"
|
||||
when 0x06 # TTL expired
|
||||
raise "TTL expired"
|
||||
when 0x07 # Command not supported
|
||||
raise "command not supported"
|
||||
when 0x08 # Address type not supported
|
||||
raise "address type not supported"
|
||||
else # unassigned
|
||||
raise "unknown SOCKS error"
|
||||
end
|
||||
|
||||
case type
|
||||
when 0x01 # IP V4 address
|
||||
destination = IPAddr.ntop(socket.read(4))
|
||||
when 0x03 # DOMAINNAME
|
||||
length = socket.read(1).unpack("C").first
|
||||
destination = socket.read(length).unpack("A#{length}")
|
||||
when 0x04 # IP V6 address
|
||||
destination = IPAddr.ntop(socket.read(16))
|
||||
else
|
||||
raise "unsupported SOCKS5 address type"
|
||||
end
|
||||
|
||||
port = socket.read(2).unpack("n").first
|
||||
end
|
||||
|
||||
def check_version(version, should_be = VERSION)
|
||||
raise "mismatched SOCKS version" unless version == should_be
|
||||
end
|
||||
|
||||
private
|
||||
def authentication_methods
|
||||
methods = []
|
||||
methods << 0x00 # NO AUTHENTICATION REQUIRED
|
||||
methods << 0x02 if user # USERNAME/PASSWORD
|
||||
methods
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,46 +0,0 @@
|
|||
require "sockets/proxies/socks"
|
||||
|
||||
module Sockets
|
||||
module Proxies
|
||||
class SOCKS4 < SOCKS
|
||||
VERSION = 0x04
|
||||
|
||||
protected
|
||||
def greet(socket)
|
||||
# noop
|
||||
end
|
||||
|
||||
def authenticate(socket, method)
|
||||
# noop
|
||||
end
|
||||
|
||||
def connect(socket, host, port)
|
||||
begin
|
||||
ip = IPAddr.new(host)
|
||||
rescue ArgumentError
|
||||
ip = IPAddr.new(Socket.getaddrinfo(host, nil, :INET, :STREAM).first)
|
||||
end
|
||||
|
||||
socket << [VERSION, 0x01, port].pack("CCn") << ip.hton
|
||||
socket << user if user
|
||||
socket << 0x00
|
||||
|
||||
version, status, port = socket.read(4).unpack("CCn")
|
||||
check_version(version, 0x00)
|
||||
ip = IPAddr.ntop(socket.read(4))
|
||||
|
||||
case status
|
||||
when 0x5A # request granted
|
||||
when 0x5B # request rejected or failed
|
||||
raise "request rejected or failed"
|
||||
when 0x5C # request rejected becasue SOCKS server cannot connect to identd on the client
|
||||
raise "request rejected becasue SOCKS server cannot connect to identd on the client"
|
||||
when 0x5D # request rejected because the client program and identd report different user-ids
|
||||
raise "request rejected because the client program and identd report different user-ids"
|
||||
else
|
||||
raise "unknown SOCKS error"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,9 +0,0 @@
|
|||
module Sockets
|
||||
module Proxies
|
||||
class SOCKS4A < Proxy
|
||||
def do_proxify(*)
|
||||
raise NotImplementedError, "SOCKS4A is not yet implemented"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,75 +0,0 @@
|
|||
require "socket"
|
||||
require "uri"
|
||||
require "uri/socks"
|
||||
|
||||
module Sockets
|
||||
class Proxy
|
||||
attr_reader :url, :options
|
||||
|
||||
def initialize(url, options = {})
|
||||
url = URI.parse(uri) unless url.is_a?(URI::Generic)
|
||||
@url, @options = url, options
|
||||
end
|
||||
|
||||
def open(host, port, local_host = nil, local_port = nil)
|
||||
if proxify?(host)
|
||||
socket = TCPSocket.new(proxy.host, proxy.port, local_host, local_port)
|
||||
begin
|
||||
proxify(socket, host, port)
|
||||
rescue Exception
|
||||
socket.close
|
||||
raise
|
||||
end
|
||||
socket
|
||||
else
|
||||
TCPSocket.new(host, port, local_host, local_port)
|
||||
end
|
||||
end
|
||||
|
||||
def proxify?(host)
|
||||
return true unless options[:no_proxy]
|
||||
|
||||
dont_proxy = options[:no_proxy].split(",")
|
||||
dont_proxy.none? { |h| host =~ /#{h}\Z/ }
|
||||
end
|
||||
|
||||
def proxify(socket, host, port)
|
||||
do_proxify(socket, host, port)
|
||||
end
|
||||
|
||||
%w(host port user password query version).each do |attr|
|
||||
class_eval "def #{attr}; url.#{attr} end", __FILE__, __LINE__
|
||||
end
|
||||
|
||||
def query_options
|
||||
@query ||= query ? Hash[query.split("&").map { |q| q.split("=") }] : {}
|
||||
end
|
||||
|
||||
%w(no_proxy).each do |option|
|
||||
class_eval "def #{option}; options[:#{option}] end", __FILE__, __LINE__
|
||||
end
|
||||
|
||||
protected
|
||||
def do_proxify(socket, host, port)
|
||||
raise NotImplementedError, "#{self} must implement do_proxify"
|
||||
end
|
||||
end
|
||||
|
||||
def self.Proxy(url, options = {})
|
||||
url = URI.parse(url)
|
||||
|
||||
raise(ArgumentError, "proxy has no scheme") unless url.scheme
|
||||
begin
|
||||
klass = Proxies.const_get(url.scheme.upcase)
|
||||
rescue NameError
|
||||
begin
|
||||
require "sockets/proxies/#{url.scheme}"
|
||||
klass = Proxies.const_get(url.scheme.upcase)
|
||||
rescue LoadError, NameError
|
||||
raise(ArgumentError, "unknown proxy scheme `#{url.scheme}'")
|
||||
end
|
||||
end
|
||||
|
||||
klass.new(url, options)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,16 @@
|
|||
$:.push File.expand_path("../lib", __FILE__)
|
||||
require "proxifier/version"
|
||||
|
||||
Gem::Specification.new do |s|
|
||||
s.name = "proxifier"
|
||||
s.version = Proxifier::VERSION
|
||||
s.platform = Gem::Platform::RUBY
|
||||
s.authors = ["Samuel Kadolph"]
|
||||
s.email = ["samuel@kadolph.com"]
|
||||
s.homepage = "https://github.com/samuelkadolph/ruby-proxifier"
|
||||
s.summary = %q{}
|
||||
s.description = %q{}
|
||||
|
||||
s.files = Dir["bin/*", "lib/**/*"] + ["LICENSE", "README.md"]
|
||||
s.executables = ["pirb", "pruby"]
|
||||
end
|
|
@ -1,16 +0,0 @@
|
|||
$:.push File.expand_path("../lib", __FILE__)
|
||||
require "sockets/version"
|
||||
|
||||
Gem::Specification.new do |s|
|
||||
s.name = "sockets"
|
||||
s.version = Sockets::VERSION
|
||||
s.platform = Gem::Platform::RUBY
|
||||
s.authors = ["Samuel Kadolph"]
|
||||
s.email = ["samuel@kadolph.com"]
|
||||
s.homepage = "https://github.com/samuelkadolph/ruby-sockets"
|
||||
s.summary = %q{}
|
||||
s.description = %q{}
|
||||
|
||||
s.files = Dir["{bin,lib}/**/*"] + ["LICENSE", "README.md"]
|
||||
s.executables = ["pruby", "pirb"]
|
||||
end
|
Loading…
Reference in New Issue