Fix support for HTTP proxies (#11245)

* Disable incorrect check for hidden services in Socket

Hidden services can only be accessed with an HTTP proxy, in which
case the host seen by the Socket class will be the proxy, not the
target host.

Hidden services are already filtered in `Request#initialize`.

* Use our Socket class to connect to HTTP proxies

Avoid the timeout logic being bypassed

* Add support for IP addresses in Request::Socket

* Refactor a bit, no need to keep the DNS resolver around
remotes/1727458204337373841/tmp_refs/heads/signup-info-prompt
ThibG 2019-07-07 02:05:38 +02:00 committed by Thibaut Girka
parent 7039dca12c
commit a0b614f10a
1 changed files with 48 additions and 35 deletions

View File

@ -30,7 +30,8 @@ class Request
@verb = verb @verb = verb
@url = Addressable::URI.parse(url).normalize @url = Addressable::URI.parse(url).normalize
@http_client = options.delete(:http_client) @http_client = options.delete(:http_client)
@options = options.merge(use_proxy? ? Rails.configuration.x.http_client_proxy : { socket_class: Socket }) @options = options.merge(socket_class: use_proxy? ? ProxySocket : Socket)
@options = @options.merge(Rails.configuration.x.http_client_proxy) if use_proxy?
@headers = {} @headers = {}
raise Mastodon::HostValidationError, 'Instance does not support hidden service connections' if block_hidden_service? raise Mastodon::HostValidationError, 'Instance does not support hidden service connections' if block_hidden_service?
@ -177,19 +178,22 @@ class Request
class Socket < TCPSocket class Socket < TCPSocket
class << self class << self
def open(host, *args) def open(host, *args)
return super(host, *args) if thru_hidden_service?(host)
outer_e = nil outer_e = nil
port = args.first port = args.first
addresses = []
begin
addresses = [IPAddr.new(host)]
rescue IPAddr::InvalidAddressError
Resolv::DNS.open do |dns| Resolv::DNS.open do |dns|
dns.timeouts = 5 dns.timeouts = 5
addresses = dns.getaddresses(host).take(2) addresses = dns.getaddresses(host).take(2)
end
end
addresses.each do |address| addresses.each do |address|
begin begin
raise Mastodon::HostValidationError if PrivateAddressCheck.private_address?(IPAddr.new(address.to_s)) check_private_address(address)
sock = ::Socket.new(address.is_a?(Resolv::IPv6) ? ::Socket::AF_INET6 : ::Socket::AF_INET, ::Socket::SOCK_STREAM, 0) sock = ::Socket.new(address.is_a?(Resolv::IPv6) ? ::Socket::AF_INET6 : ::Socket::AF_INET, ::Socket::SOCK_STREAM, 0)
sockaddr = ::Socket.pack_sockaddr_in(port, address.to_s) sockaddr = ::Socket.pack_sockaddr_in(port, address.to_s)
@ -219,7 +223,6 @@ class Request
outer_e = e outer_e = e
end end
end end
end
if outer_e if outer_e
raise outer_e raise outer_e
@ -230,11 +233,21 @@ class Request
alias new open alias new open
def thru_hidden_service?(host) def check_private_address(address)
Rails.configuration.x.access_to_hidden_service && /\.(onion|i2p)$/.match(host) raise Mastodon::HostValidationError if PrivateAddressCheck.private_address?(IPAddr.new(address.to_s))
end end
end end
end end
private_constant :ClientLimit, :Socket class ProxySocket < Socket
class << self
def check_private_address(_address)
# Accept connections to private addresses as HTTP proxies will usually
# be on local addresses
nil
end
end
end
private_constant :ClientLimit, :Socket, :ProxySocket
end end