2022-11-02 00:33:17 +08:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2023-03-08 11:26:32 +08:00
|
|
|
class FinalDestination::HTTP < Net::HTTP
|
|
|
|
def connect
|
2024-05-27 18:27:13 +08:00
|
|
|
raise ArgumentError.new("address cannot be nil or empty") if @address.blank?
|
2024-01-25 05:50:42 +08:00
|
|
|
|
2023-03-08 11:26:32 +08:00
|
|
|
original_open_timeout = @open_timeout
|
|
|
|
return super if @ipaddr
|
|
|
|
|
|
|
|
timeout_at = current_time + @open_timeout
|
|
|
|
|
|
|
|
# This iteration through addresses would normally happen in Socket#tcp
|
|
|
|
# We do it here because we're tightly controlling addresses rather than
|
|
|
|
# handing Socket#tcp a hostname
|
|
|
|
ips = FinalDestination::SSRFDetector.lookup_and_filter_ips(@address, timeout: @connect_timeout)
|
2023-03-08 10:40:01 +08:00
|
|
|
|
2023-03-08 11:26:32 +08:00
|
|
|
ips.each_with_index do |ip, index|
|
|
|
|
debug "[FinalDestination] Attempting connection to #{ip}..."
|
|
|
|
self.ipaddr = ip
|
2022-11-02 00:33:17 +08:00
|
|
|
|
2023-03-08 11:26:32 +08:00
|
|
|
remaining_time = timeout_at - current_time
|
|
|
|
if remaining_time <= 0
|
|
|
|
raise Net::OpenTimeout.new("Operation timed out - FinalDestination::HTTP")
|
|
|
|
end
|
|
|
|
|
|
|
|
@open_timeout = remaining_time
|
|
|
|
return super
|
2023-07-25 15:01:02 +08:00
|
|
|
rescue OpenSSL::SSL::SSLError, SystemCallError, Net::OpenTimeout => e
|
2023-03-08 11:26:32 +08:00
|
|
|
debug "[FinalDestination] Error connecting to #{ip}... #{e.message}"
|
|
|
|
was_last_attempt = index == ips.length - 1
|
|
|
|
raise if was_last_attempt
|
2022-11-02 00:33:17 +08:00
|
|
|
end
|
2023-03-08 11:26:32 +08:00
|
|
|
ensure
|
|
|
|
@open_timeout = original_open_timeout
|
2022-11-02 00:33:17 +08:00
|
|
|
end
|
|
|
|
|
2023-03-08 11:26:32 +08:00
|
|
|
private
|
|
|
|
|
|
|
|
def current_time
|
|
|
|
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
2022-11-02 00:33:17 +08:00
|
|
|
end
|
|
|
|
end
|