Module: Socksproxyable::InstanceMethodsConnect

Included in:
TCPSocket
Defined in:
lib/socksify/socksproxyable.rb

Overview

instance methods #socks_connect & #socks_receive_reply

Instance Method Summary collapse

Instance Method Details

#socks_connect(host, port) ⇒ Object

rubocop:disable Metrics



70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/socksify/socksproxyable.rb', line 70

def socks_connect(host, port)
  port = Socket.getservbyname(port) if port.is_a?(String)
  req = +''
  Socksify.debug_debug 'Sending destination address'
  req << TCPSocket.socks_version_hex
  Socksify.debug_debug TCPSocket.socks_version_hex.unpack 'H*'
  req << "\001"
  req << "\000" if self.class.socks_version == '5'
  req << [port].pack('n') if /^4/.match?(self.class.socks_version)
  host = Resolv::DNS.new.getaddress(host).to_s if self.class.socks_version == '4'
  Socksify.debug_debug host
  if host =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/ # to IPv4 address
    req << "\001" if self.class.socks_version == '5'
    ip = (1..4).map { |i| Regexp.last_match(i).to_i }.pack('CCCC')
    req << ip
  elsif /^[:0-9a-f]+$/.match?(host) # to IPv6 address
    raise 'TCP/IPv6 over SOCKS is not yet supported (inet_pton missing in Ruby & not supported by Tor'
    # req << "\004" # UNREACHABLE
  elsif self.class.socks_version == '5' # to hostname
    # req << "\003" + [host.size].pack('C') + host
    req << "\003#{[host.size].pack('C')}#{host}"
  else
    req << "\000\000\000\001" << "\007\000"
    Socksify.debug_notice host
    req << host << "\000"
  end
  req << [port].pack('n') if self.class.socks_version == '5'
  write req
  socks_receive_reply
  Socksify.debug_notice "Connected to #{host}:#{port} over SOCKS"
end

#socks_receive_replyObject

returns [bind_addr: String, bind_port: Fixnum] rubocop:disable Metrics



105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
# File 'lib/socksify/socksproxyable.rb', line 105

def socks_receive_reply
  Socksify.debug_debug 'Waiting for SOCKS reply'
  if self.class.socks_version == '5'
    connect_reply = recv(4)
    raise SOCKSError, "Server doesn't reply" if connect_reply.empty?

    Socksify.debug_debug connect_reply.unpack 'H*'
    raise SOCKSError, "SOCKS version #{connect_reply[0..0]} is not 5" if connect_reply[0..0] != "\005"
    raise SOCKSError.for_response_code(connect_reply.bytes.to_a[1]) if connect_reply[1..1] != "\000"

    Socksify.debug_debug 'Waiting for bind_addr'
    bind_addr_len = case connect_reply[3..3]
                    when "\001"
                      4
                    when "\003"
                      recv(1).bytes.first
                    when "\004"
                      16
                    else
                      raise 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|
                    ip6 += ':' if i.positive? && i.even?
                    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" && connect_reply[1] == "\x5A"
      Socksify.debug_debug connect_reply.unpack 'H'
      raise SOCKSError, 'Failed while connecting througth socks'
    end
  end
end