mysqlshdk/libs/ssh/ssh_session_options.cc (86 lines of code) (raw):
/*
* Copyright (c) 2021, 2024, Oracle and/or its affiliates.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2.0,
* as published by the Free Software Foundation.
*
* This program is designed to work with certain software (including
* but not limited to OpenSSL) that is licensed under separate terms,
* as designated in a particular file or component or in included license
* documentation. The authors of MySQL hereby grant you an additional
* permission to link the program and your derivative works with the
* separately licensed software that they have either included with
* the program or referenced in the documentation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
* the GNU General Public License, version 2.0, for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "mysqlshdk/libs/ssh/ssh_session_options.h"
#include "mysqlshdk/libs/ssh/ssh_common.h"
#include "mysqlshdk/libs/utils/logger.h"
#include "mysqlshdk/libs/utils/utils_file.h"
#include "mysqlshdk/libs/utils/utils_path.h"
#include "mysqlshdk/libs/utils/utils_string.h"
namespace {
std::string get_value(ssh_session sess, const enum ssh_options_e type) {
char *value = nullptr;
ssh_options_get(sess, type, &value);
std::string ret_val;
if (value != nullptr) {
ret_val.assign(value);
ssh_string_free_char(value);
}
// FIXME: there's a bug in libssh which makes this check unreliable
// it can be enabled once ssh_options_get will be fixed
// if (err == SSH_ERROR) throw
// ::ssh::SshException(m_session->getCSession());
return ret_val;
}
} // namespace
namespace mysqlshdk {
namespace ssh {
Ssh_session_options::Ssh_session_options(const std::string &config,
const std::string &host)
: m_session{std::make_unique<::ssh::Session>()} {
init_libssh();
// The host is critical to read the stanza.
if (host.empty()) {
m_no_host = true;
return;
}
try {
m_session->setOption(SSH_OPTIONS_HOST, host.c_str());
} catch (::ssh::SshException &exc) {
auto error = exc.getError();
if (error.empty()) {
error = shcore::str_format(
"SSH: Error setting remote host in ssh session: host '%s' is not "
"valid",
host.c_str());
}
throw std::invalid_argument(error);
}
try {
// On empty we pass nullptr to trigger loading the default configuration
// files
m_session->optionsParseConfig(config.empty() ? nullptr : config.c_str());
} catch (::ssh::SshException &ex) {
auto logger = shcore::current_logger(true);
if (logger) {
log_error("Error during SSH config parse: %s [%d], path was: %s",
ex.getError().c_str(), ex.getCode(), config.c_str());
} else {
fprintf(stderr, "Error during SSH config parse: %s [%d], path was: %s",
ex.getError().c_str(), ex.getCode(), config.c_str());
}
throw std::invalid_argument("Unable to parse specified SSH config file");
}
}
std::string Ssh_session_options::get_host() const {
return get_value(m_session->getCSession(), SSH_OPTIONS_HOST);
}
std::string Ssh_session_options::get_user() const {
return get_value(m_session->getCSession(), SSH_OPTIONS_USER);
}
std::string Ssh_session_options::get_known_hosts() const {
return get_value(m_session->getCSession(), SSH_OPTIONS_KNOWNHOSTS);
}
std::string Ssh_session_options::get_identity_file() const {
if (m_no_host) return "";
auto identity_file =
get_value(m_session->getCSession(), SSH_OPTIONS_IDENTITY);
identity_file =
shcore::str_replace(identity_file, "%d", shcore::path::home());
// libssh preloads session->options.identity with some predefined keys
// if we are reading from the SSH config we can only check if the file exists
// and if not, log error and return empty string so
// Ssh_connection_config::set_key won't complain about missing file
if (shcore::is_file(identity_file)) return identity_file;
// There's a chance that the logger will try to log
// before actually the logger will be ready.
// and what's worse we can do nothing with it, so the only solution is to just
// skip the log since there's no error log here, there should be no problem
// with it.
auto logger = shcore::current_logger(true);
if (logger) {
log_warning("SSH: The SSH identity file %s doesn't exist.",
identity_file.c_str());
}
return "";
}
unsigned int Ssh_session_options::get_port() const {
if (m_no_host) return 0;
unsigned int port = 0;
if (ssh_options_get_port(m_session->getCSession(), &port) == SSH_ERROR)
log_error("SSH: Unable to obtain port");
return port;
}
} // namespace ssh
} // namespace mysqlshdk