@ -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,47 +178,49 @@ 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
Resolv :: DNS . open do | dns |
addresses = [ ]
dns . timeouts = 5
begin
addresses = [ IPAddr . new ( host ) ]
rescue IPAddr :: InvalidAddressError
Resolv :: DNS . open do | dns |
dns . timeouts = 5
addresses = dns . getaddresses ( host ) . take ( 2 )
end
end
addresses = dns . getaddresses ( host ) . take ( 2 )
addresses . each do | address |
begin
check_private_address ( address )
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 )
sock . setsockopt ( :: Socket :: IPPROTO_TCP , :: Socket :: TCP_NODELAY , 1 )
addresses . each do | address |
begin
begin
raise Mastodon :: HostValidationError if PrivateAddressCheck . private_address? ( IPAddr . new ( address . to_s ) )
sock . connect_nonblock ( sockaddr )
rescue IO :: WaitWritable
sock = :: Socket . new ( address . is_a? ( Resolv :: IPv6 ) ? :: Socket :: AF_INET6 : :: Socket :: AF_INET , :: Socket :: SOCK_STREAM , 0 )
if IO . select ( nil , [ sock ] , nil , Request :: TIMEOUT [ :connect ] )
sockaddr = :: Socket . pack_sockaddr_in ( port , address . to_s )
begin
sock . connect_nonblock ( sockaddr )
sock . setsockopt ( :: Socket :: IPPROTO_TCP , :: Socket :: TCP_NODELAY , 1 )
rescue Errno :: EISCONN
# Yippee!
begin
rescue
sock . connect_nonblock ( sockaddr )
rescue IO :: WaitWritable
if IO . select ( nil , [ sock ] , nil , Request :: TIMEOUT [ :connect ] )
begin
sock . connect_nonblock ( sockaddr )
rescue Errno :: EISCONN
# Yippee!
rescue
sock . close
raise
end
else
sock . close
sock . close
raise HTTP :: TimeoutError , " Connect timed out after #{ Request :: TIMEOUT [ :connect ] } seconds "
raise
end
end
else
sock . close
raise HTTP :: TimeoutError , " Connect timed out after #{ Request :: TIMEOUT [ :connect ] } seconds "
end
end
return sock
rescue = > e
outer_e = e
end
end
return sock
rescue = > e
outer_e = e
end
end
end
end
@ -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
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
end
end
end
private_constant :ClientLimit , :Socket
private_constant :ClientLimit , :Socket , :ProxySocket
end
end