class DressSocks::Socket

Attributes

socks_ignores[RW]
socks_password[RW]
socks_port[RW]
socks_server[RW]
socks_username[RW]
socks_version[RW]

Public Class Methods

new(remote_host=nil, remote_port=0, local_host=nil, local_port=nil, socks_username: nil, socks_password: nil, socks_server: nil, socks_port: nil, socks_ignores: [], socks_version: '5') click to toggle source

See tools.ietf.org/html/rfc1928

# File lib/dress_socks/socket.rb, line 13
def initialize(remote_host=nil, remote_port=0, local_host=nil, local_port=nil, 
               socks_username: nil, socks_password: nil, socks_server: nil, socks_port: nil, 
               socks_ignores: [], socks_version: '5')

  self.socks_server = socks_server
  self.socks_port = socks_port
  self.socks_username = socks_username
  self.socks_password = socks_password
  self.socks_ignores = socks_ignores
  self.socks_version = socks_version


  if socks_server and socks_port and not socks_ignores.include?(remote_host)
    initialize_tcp socks_server, socks_port
  
    socks_authenticate unless socks_version =~ /^4/
  
    if remote_host
      socks_connect(remote_host, remote_port)
    end
  else
    initialize_tcp remote_host, remote_port, local_host, local_port
  end
end
Also aliased as: initialize_tcp

Public Instance Methods

encoded_socks_version() click to toggle source
# File lib/dress_socks/socket.rb, line 8
def encoded_socks_version
  (self.socks_version == "4a" or self.socks_version == "4") ? "\004" : "\005"
end
initialize_tcp(remote_host=nil, remote_port=0, local_host=nil, local_port=nil, socks_username: nil, socks_password: nil, socks_server: nil, socks_port: nil, socks_ignores: [], socks_version: '5')
Alias for: new
socks_authenticate() click to toggle source

Authentication

# File lib/dress_socks/socket.rb, line 39
def socks_authenticate
  if self.socks_username || self.socks_password
            write "\005\001\002"
  else
            write "\005\001\000"
  end
  auth_reply = recv(2)
  if auth_reply.empty?
    raise DressSocks::SOCKSError.new("Server doesn't reply authentication")
  end
  if auth_reply[0..0] != "\004" and auth_reply[0..0] != "\005"
    raise DressSocks::SOCKSError.new("SOCKS version #{auth_reply[0..0]} not supported")
  end
  if self.socks_username || self.socks_password
    if auth_reply[1..1] != "\002"
      raise DressSocks::SOCKSError.new("SOCKS authentication method #{auth_reply[1..1]} neither requested nor supported")
    end
    auth = "\001"
    auth += self.socks_username.to_s.length.chr
    auth += self.socks_username.to_s
    auth += self.socks_password.to_s.length.chr
    auth += self.socks_password.to_s
    write auth
    auth_reply = recv(2)
    if auth_reply[1..1] != "\000"
      raise DressSocks::SOCKSError.new("SOCKS authentication failed")
    end
  else
    if auth_reply[1..1] != "\000"
      raise DressSocks::SOCKSError.new("SOCKS authentication method #{auth_reply[1..1]} neither requested nor supported")
    end
  end
end
socks_connect(host, port) click to toggle source

Connect

# File lib/dress_socks/socket.rb, line 74
def socks_connect(host, port)
  port = ::Socket.getservbyname(port) if port.is_a?(String)
  req = String.new
  req << self.encoded_socks_version
  req << "\001"
  req << "\000" if self.socks_version == "5"
  req << [port].pack('n') if self.socks_version =~ /^4/
  
  if self.socks_version == "4"
    host = Resolv::DNS.new.getaddress(host).to_s
  end
  if host =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/  # to IPv4 address
    req << "\001" if self.socks_version == "5"
    _ip = [$1.to_i,
           $2.to_i,
           $3.to_i,
           $4.to_i
          ].pack('CCCC')
    req << _ip
  elsif host =~ /^[:0-9a-f]+$/  # to IPv6 address
    raise "TCP/IPv6 over SOCKS is not yet supported (inet_pton missing in Ruby & not supported by Tor"
    req << "\004"
  else                          # to hostname
    if self.socks_version == "5"
      req << "\003" + [host.size].pack('C') + host
    else
      req << "\000\000\000\001"
      req << "\007\000"
      req << host
      req << "\000"
    end
  end
  req << [port].pack('n') if self.socks_version == "5"

  write req
  
  socks_receive_reply
end
socks_receive_reply() click to toggle source

returns [bind_addr: String, bind_port: Fixnum]

# File lib/dress_socks/socket.rb, line 114
def socks_receive_reply
  if self.socks_version == "5"
    connect_reply = recv(4)
    if connect_reply.empty?
      raise DressSocks::SOCKSError.new("Server doesn't reply")
    end
    if connect_reply[0..0] != "\005"
      raise DressSocks::SOCKSError.new("SOCKS version #{connect_reply[0..0]} is not 5")
    end
    if connect_reply[1..1] != "\000"
      raise DressSocks::SOCKSError.for_response_code(connect_reply.bytes.to_a[1])
    end
    bind_addr_len = case connect_reply[3..3]
            when "\001"
              4
            when "\003"
              recv(1).bytes.first
            when "\004"
              16
            else
              raise DressSocks::SOCKSError.for_response_code(connect_reply.bytes.to_a[3])
            end
    bind_addr_s = recv(bind_addr_len)
    bind_addr = case connect_reply[3..3]
                when "\001"
                  bind_addr_s.bytes.to_a.join('.')
                when "\003"
                  bind_addr_s
                when "\004"  # Untested!
                  i = 0
                  ip6 = ""
                  bind_addr_s.each_byte do |b|
                    if i > 0 and i % 2 == 0
                      ip6 += ":"
                    end
                    i += 1
  
                    ip6 += b.to_s(16).rjust(2, '0')
                  end
                end
    bind_port = recv(bind_addr_len + 2)
    [bind_addr, bind_port.unpack('n')]
  else
    connect_reply = recv(8)
    unless connect_reply[0] == "\000" and connect_reply[1] == "\x5A"
      raise DressSocks::SOCKSError.new("Failed while connecting througth socks")
    end
  end
end