class Net::SSH::Multiplex::ControlPath

%u local user
%r remote user
%p remote port
%n the host name as given
%l local host name
%L local host name up to the first dot
%k the host key alias, or the host name as given
%j host of the last ProxyJump hop, or empty
%i local uid
%h remote host name, after HostName
%d local home directory
%C SHA1 hex of %l%h%p%r%j
%% a literal %
percent_dollar_expand).
DEFAULT_CLIENT_PERCENT_EXPAND_ARGS in sshconnect.h, misc.c
then ${ENV} and the % tokens below in a single pass (ssh.c,
Expands a ControlPath the way OpenSSH’s ssh does: a leading ~ first,

def self.jump_host(proxy_jump)

user or port (readconf.c, parse_jump).
The host ssh uses for %j: the last hop of a ProxyJump list, without
def self.jump_host(proxy_jump)
  last = proxy_jump.to_s.split(',').last.to_s.strip.delete_prefix('ssh://')
  last = last.split('@').last.to_s
  if last.start_with?('[')
    last[1...last.index(']').to_i]
  else
    last.split(':').first.to_s
  end
end

def address?(host)

def address?(host)
  IPAddr.new(host)
  true
rescue IPAddr::Error
  false
end

def expand(path)

too.
variable or an unknown ~user, which make ssh refuse the ControlPath
Raises ArgumentError for an unknown % token, an unset ${ENV}
def expand(path)
  expand_tokens(expand_tilde(path))
end

def expand_tilde(path)

(misc.c, tilde_expand).
ssh uses the home directory from the password database, not $HOME
def expand_tilde(path)
  return path unless path.start_with?('~')
  user, rest = path[1..].split('/', 2)
  home = user.nil? || user.empty? ? Etc.getpwuid(Process.uid).dir : Etc.getpwnam(user).dir
  rest.nil? ? home : File.join(home, rest)
end

def expand_tokens(path)

def expand_tokens(path)
  path.gsub(/\$\{([^}]*)\}|%(.?)|\$\{.*/m) do
    if Regexp.last_match(1)
      name = Regexp.last_match(1)
      raise ArgumentError, "empty ${} variable" if name.empty?
      ENV.fetch(name) { raise ArgumentError, "variable ${#{name}} is not set" }
    elsif Regexp.last_match(2)
      token = Regexp.last_match(2)
      next '%' if token == '%'
      tokens.fetch(token) { raise ArgumentError, "unknown token %#{token}" }
    else
      raise ArgumentError, "unterminated ${"
    end
  end
end

def initialize(host_arg:, host:, port:, remote_user:, jump_host: '', host_key_alias: nil) # rubocop:disable Metrics/ParameterLists

rubocop:disable Metrics/ParameterLists
HostName.
+host_arg+ is the host as given, +host+ the one connected to after
def initialize(host_arg:, host:, port:, remote_user:, jump_host: '', host_key_alias: nil) # rubocop:disable Metrics/ParameterLists
  @host_arg = host_arg.to_s
  # ssh lowercases host names, but not addresses, before expanding.
  @host = address?(host.to_s) ? host.to_s : host.to_s.downcase
  @port = port.to_s
  @remote_user = remote_user.to_s
  @jump_host = jump_host.to_s
  @host_key_alias = host_key_alias
end

def tokens

def tokens
  @tokens ||= begin
    pw = Etc.getpwuid(Process.uid)
    local_host = Socket.gethostname
    {
      'C' => Digest::SHA1.hexdigest("#{local_host}#{@host}#{@port}#{@remote_user}#{@jump_host}"),
      'd' => pw.dir,
      'h' => @host,
      'i' => Process.uid.to_s,
      'j' => @jump_host,
      'k' => @host_key_alias || @host_arg,
      'L' => local_host.split('.', 2).first,
      'l' => local_host,
      'n' => @host_arg,
      'p' => @port,
      'r' => @remote_user,
      'u' => pw.name
    }
  end
end