require'socket'require'net/ssh/proxy/errors'moduleNetmoduleSSHmoduleProxy# 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|# ...# endclassSOCKS5# The SOCKS protocol version used by this classVERSION=5# The SOCKS authentication type for requests without authenticationMETHOD_NO_AUTH=0# The SOCKS authentication type for requests via username/passwordMETHOD_PASSWD=2# The SOCKS authentication type for when there are no supported# authentication methods.METHOD_NONE=0xFF# The SOCKS packet type for requesting a proxy connection.CMD_CONNECT=1# The SOCKS address type for connections via IP address.ATYP_IPV4=1# The SOCKS address type for connections via domain name.ATYP_DOMAIN=3# The SOCKS response code for a successful operation.SUCCESS=0# The proxy's host name or IP addressattr_reader:proxy_host# The proxy's port numberattr_reader:proxy_port# The map of options given at initializationattr_reader: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.definitialize(proxy_host,proxy_port=1080,options={})@proxy_host=proxy_host@proxy_port=proxy_port@options=optionsend# Return a new socket connected to the given host and port via the# proxy that was requested when the socket factory was instantiated.defopen(host,port,connection_options)socket=Socket.tcp(proxy_host,proxy_port,nil,nil,connect_timeout: connection_options[:timeout])methods=[METHOD_NO_AUTH]methods<<METHOD_PASSWDifoptions[:user]packet=[VERSION,methods.size,*methods].pack("C*")socket.sendpacket,0version,method=socket.recv(2).unpack("CC")ifversion!=VERSIONsocket.closeraiseNet::SSH::Proxy::Error,"invalid SOCKS version (#{version})"endifmethod==METHOD_NONEsocket.closeraiseNet::SSH::Proxy::Error,"no supported authorization methods"endnegotiate_password(socket)ifmethod==METHOD_PASSWDpacket=[VERSION,CMD_CONNECT,0].pack("C*")ifhost=~/^(\d+)\.(\d+)\.(\d+)\.(\d+)$/packet<<[ATYP_IPV4,$1.to_i,$2.to_i,$3.to_i,$4.to_i].pack("C*")elsepacket<<[ATYP_DOMAIN,host.length,host].pack("CCA*")endpacket<<[port].pack("n")socket.sendpacket,0version,reply,=socket.recv(2).unpack("C*")socket.recv(1)address_type=socket.recv(1).getbyte(0)caseaddress_typewhen1socket.recv(4)# get four bytes for IPv4 addresswhen3len=socket.recv(1).getbyte(0)hostname=socket.recv(len)when4ipv6addrhostname=socket.recv(16)elsesocket.closeraiseConnectError,"Illegal response type"endportnum=socket.recv(2)unlessreply==SUCCESSsocket.closeraiseConnectError,"#{reply}"endreturnsocketendprivate# Simple username/password negotiation with the SOCKS5 server.defnegotiate_password(socket)packet=[0x01,options[:user].length,options[:user],options[:password].length,options[:password]].pack("CCA*CA*")socket.sendpacket,0version,status=socket.recv(2).unpack("CC")ifstatus!=SUCCESSsocket.closeraiseUnauthorizedError,"could not authorize user"endendendendendend