class Net::SSH::Proxy::SOCKS5

  1. lib/net/ssh/proxy/socks5.rb
Parent: Proxy

An implementation of a SOCKS5 proxy. To use it, instantiate it, then pass the instantiated object via the :proxy key to Net::SSH.start:

require 'net/ssh/proxy/socks5'
proxy = Net::SSH::Proxy::SOCKS5.new('proxy.host', proxy_port,
  :user => 'user', :password => "password")
Net::SSH.start('host', 'user', :proxy => proxy) do |ssh|
  ...
end

Methods

Public Class

  1. new

Public Instance

  1. open
  2. options
  3. proxy_host
  4. proxy_port

Constants

ATYP_DOMAIN = 3  

The SOCKS address type for connections via domain name.

ATYP_IPV4 = 1  

The SOCKS address type for connections via IP address.

CMD_CONNECT = 1  

The SOCKS packet type for requesting a proxy connection.

METHOD_NONE = 0xFF  

The SOCKS authentication type for when there are no supported authentication methods.

METHOD_NO_AUTH = 0  

The SOCKS authentication type for requests without authentication

METHOD_PASSWD = 2  

The SOCKS authentication type for requests via username/password

SUCCESS = 0  

The SOCKS response code for a successful operation.

VERSION = 5  

The SOCKS protocol version used by this class

Attributes

options [R]

The map of options given at initialization

proxy_host [R]

The proxy's host name or IP address

proxy_port [R]

The proxy's port number

Public Class methods

new (proxy_host, proxy_port=1080, options={})

Create a new proxy connection to the given proxy host and port. Optionally, :user and :password options may be given to identify the username and password with which to authenticate.

[show source]
# File lib/net/ssh/proxy/socks5.rb, line 57
def initialize(proxy_host, proxy_port=1080, options={})
  @proxy_host = proxy_host
  @proxy_port = proxy_port
  @options = options
end

Public Instance methods

open (host, port, connection_options = nil)

Return a new socket connected to the given host and port via the proxy that was requested when the socket factory was instantiated.

[show source]
# File lib/net/ssh/proxy/socks5.rb, line 65
def open(host, port, connection_options = nil)
  socket = TCPSocket.new(proxy_host, proxy_port)

  methods = [METHOD_NO_AUTH]
  methods << METHOD_PASSWD if options[:user]

  packet = [VERSION, methods.size, *methods].pack("C*")
  socket.send packet, 0

  version, method = socket.recv(2).unpack("CC")
  if version != VERSION
    socket.close
    raise Net::SSH::Proxy::Error, "invalid SOCKS version (#{version})"
  end

  if method == METHOD_NONE
    socket.close
    raise Net::SSH::Proxy::Error, "no supported authorization methods"
  end

  negotiate_password(socket) if method == METHOD_PASSWD

  packet = [VERSION, CMD_CONNECT, 0].pack("C*")

  if host =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/
    packet << [ATYP_IPV4, $1.to_i, $2.to_i, $3.to_i, $4.to_i].pack("C*")
  else
    packet << [ATYP_DOMAIN, host.length, host].pack("CCA*")
  end

  packet << [port].pack("n")
  socket.send packet, 0
  
  version, reply, = socket.recv(2).unpack("C*")
  socket.recv(1)
  address_type = socket.recv(1).getbyte(0)
  case address_type
  when 1
    socket.recv(4)  # get four bytes for IPv4 address
  when 3
    len = socket.recv(1).getbyte(0)
    hostname = socket.recv(len)
  when 4
    ipv6addr hostname = socket.recv(16)
  else
    socket.close
    raise ConnectError, "Illegal response type"
  end
  portnum = socket.recv(2)
  
  unless reply == SUCCESS
    socket.close
    raise ConnectError, "#{reply}"
  end

  return socket
end