def parse_socks_response
if @socks_state == :method_negotiation
return false unless has_bytes? 2
_, method = @data.read(2).unpack('CC')
if socks_methods.include?(method)
if method == 0
@socks_state = :connecting
return send_socks_connect_request
elsif method == 2
@socks_state = :authenticating
credentials = @options[:proxy][:authorization]
if credentials.size < 2
@state = :invalid
on_error "username and password are not supplied"
return false
end
username, password = credentials
send_data [5, username.length, username, password.length, password].pack('CCA*CA*')
end
else
@state = :invalid
on_error "proxy did not accept method"
return false
end
elsif @socks_state == :authenticating
return false unless has_bytes? 2
_, status_code = @data.read(2).unpack('CC')
if status_code == 0
@socks_state = :connecting
return send_socks_connect_request
else
@state = :invalid
on_error "access denied by proxy"
return false
end
elsif @socks_state == :connecting
return false unless has_bytes? 10
_, response_code, _, address_type, _, _ = @data.read(10).unpack('CCCCNn')
if response_code == 0
@socks_state = :connected
@state = :proxy_connected
@response_header = HttpResponseHeader.new
connection_completed
else
@state = :invalid
error_messages = {
1 => "general socks server failure",
2 => "connection not allowed by ruleset",
3 => "network unreachable",
4 => "host unreachable",
5 => "connection refused",
6 => "TTL expired",
7 => "command not supported",
8 => "address type not supported"
}
error_message = error_messages[response_code] || "unknown error (code: #{response_code})"
on_error "socks5 connect error: #{error_message}"
return false
end
end
true
end