-
Notifications
You must be signed in to change notification settings - Fork 642
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Create dynamic libraries with C interfaces for Dart FFI #3098
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
#ifndef MULTIPASS_DART_FFI_H | ||
#define MULTIPASS_DART_FFI_H | ||
|
||
extern "C" const char* multipass_version(); | ||
|
||
extern "C" const char* generate_petname(); | ||
|
||
extern "C" const char* get_server_address(); | ||
|
||
extern "C" struct KeyCertificatePair | ||
{ | ||
const char* pem_cert; | ||
const char* pem_priv_key; | ||
}; | ||
|
||
extern "C" struct KeyCertificatePair get_cert_pair(); | ||
|
||
#endif // MULTIPASS_DART_FFI_H |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -74,7 +74,7 @@ auto make_handler_unregisterer(mp::SettingsHandler* handler) | |
} // namespace | ||
|
||
mp::Client::Client(ClientConfig& config) | ||
: stub{mp::Rpc::NewStub(mp::client::make_channel(config.server_address, config.cert_provider.get()))}, | ||
: stub{mp::Rpc::NewStub(mp::client::make_channel(config.server_address, *config.cert_provider))}, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Isn't this There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think so. We need to pass a reference to |
||
term{config.term}, | ||
aliases{config.term} | ||
{ | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -78,9 +78,8 @@ std::string message_box(const std::string& message) | |
return '\n' + divider + '\n' + message + '\n' + divider + '\n'; | ||
} | ||
|
||
grpc::SslCredentialsOptions get_ssl_credentials_opts_from(const QString& cert_dir_path) | ||
grpc::SslCredentialsOptions get_ssl_credentials_opts_from(const mp::CertProvider& cert_provider) | ||
{ | ||
mp::SSLCertProvider cert_provider{cert_dir_path}; | ||
auto opts = grpc::SslCredentialsOptions(); | ||
|
||
opts.server_certificate_request = GRPC_SSL_REQUEST_SERVER_CERTIFICATE_BUT_DONT_VERIFY; | ||
|
@@ -191,51 +190,9 @@ void mp::client::register_global_settings_handlers() | |
} | ||
|
||
std::shared_ptr<grpc::Channel> mp::client::make_channel(const std::string& server_address, | ||
mp::CertProvider* cert_provider) | ||
const mp::CertProvider& cert_provider) | ||
Comment on lines
-194
to
+193
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I thnk this interface update is a drive-by change. But nevermind, it's a good change so I'm perfectly OK with it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, we could've kept the pointer but since after this refactoring we never pass a null pointer to this function, we can use a reference instead for more safety |
||
{ | ||
// No common client certificates exist yet. | ||
// TODO: Remove the following logic when we are comfortable all installed clients are using the common cert | ||
if (!cert_provider) | ||
{ | ||
auto data_location{MP_STDPATHS.writableLocation(StandardPaths::GenericDataLocation)}; | ||
auto common_client_cert_dir_path{data_location + common_client_cert_dir}; | ||
|
||
// The following logic is for determing which certificate to use when the client starts up. | ||
// 1. Check if the multipass-gui certificate exists and determine if it's authenticated | ||
// with the daemon already. If it is, copy it to the common client certificate directory and use it. | ||
// 2. If that fails, then try the certificate from the cli client in the same manner. | ||
// 3. Delete any per-client certificate dirs. | ||
// 4. Lastly, no known certificate for the user exists, so create a new common certificate and use that. | ||
|
||
const std::vector<QString> cert_dirs{data_location + gui_client_cert_dir, data_location + cli_client_cert_dir}; | ||
for (const auto& cert_dir : cert_dirs) | ||
{ | ||
if (client_certs_exist(cert_dir)) | ||
{ | ||
if (auto rpc_channel{ | ||
create_channel_and_validate(server_address, get_ssl_credentials_opts_from(cert_dir))}) | ||
{ | ||
copy_client_certs_to_common_dir(cert_dir, common_client_cert_dir_path); | ||
mp::utils::remove_directories(cert_dirs); | ||
|
||
return rpc_channel; | ||
} | ||
} | ||
} | ||
|
||
mp::utils::remove_directories(cert_dirs); | ||
MP_UTILS.make_dir(common_client_cert_dir_path); | ||
|
||
return grpc::CreateChannel(server_address, | ||
grpc::SslCredentials(get_ssl_credentials_opts_from(common_client_cert_dir_path))); | ||
} | ||
|
||
auto opts = grpc::SslCredentialsOptions(); | ||
opts.server_certificate_request = GRPC_SSL_REQUEST_SERVER_CERTIFICATE_BUT_DONT_VERIFY; | ||
opts.pem_cert_chain = cert_provider->PEM_certificate(); | ||
opts.pem_private_key = cert_provider->PEM_signing_key(); | ||
|
||
return grpc::CreateChannel(server_address, grpc::SslCredentials(opts)); | ||
return grpc::CreateChannel(server_address, grpc::SslCredentials(get_ssl_credentials_opts_from(cert_provider))); | ||
} | ||
|
||
std::string mp::client::get_server_address() | ||
|
@@ -250,17 +207,46 @@ std::string mp::client::get_server_address() | |
return mp::platform::default_server_address(); | ||
} | ||
|
||
std::unique_ptr<mp::SSLCertProvider> mp::client::get_cert_provider() | ||
std::unique_ptr<mp::SSLCertProvider> mp::client::get_cert_provider(const std::string& server_address) | ||
{ | ||
auto data_location{MP_STDPATHS.writableLocation(StandardPaths::GenericDataLocation)}; | ||
auto common_client_cert_dir_path{data_location + common_client_cert_dir}; | ||
|
||
if (client_certs_exist(common_client_cert_dir_path)) | ||
{ | ||
return std::make_unique<mp::SSLCertProvider>(common_client_cert_dir_path); | ||
return std::make_unique<SSLCertProvider>(common_client_cert_dir_path); | ||
} | ||
|
||
// No common client certificates exist yet. | ||
// TODO: Remove the following logic when we are comfortable all installed clients are using the common cert | ||
|
||
// The following logic is for determining which certificate to use when the client starts up. | ||
// 1. Check if the multipass-gui certificate exists and determine if it's authenticated | ||
// with the daemon already. If it is, copy it to the common client certificate directory and use it. | ||
// 2. If that fails, then try the certificate from the cli client in the same manner. | ||
// 3. Delete any per-client certificate dirs. | ||
// 4. Lastly, no known certificate for the user exists, so create a new common certificate and use that. | ||
|
||
const std::vector<QString> cert_dirs{data_location + gui_client_cert_dir, data_location + cli_client_cert_dir}; | ||
for (const auto& cert_dir : cert_dirs) | ||
{ | ||
if (client_certs_exist(cert_dir)) | ||
{ | ||
auto ssl_cert_provider = std::make_unique<SSLCertProvider>(cert_dir); | ||
if (auto rpc_channel{ | ||
create_channel_and_validate(server_address, get_ssl_credentials_opts_from(*ssl_cert_provider))}) | ||
{ | ||
copy_client_certs_to_common_dir(cert_dir, common_client_cert_dir_path); | ||
utils::remove_directories(cert_dirs); | ||
|
||
return ssl_cert_provider; | ||
} | ||
} | ||
} | ||
|
||
return nullptr; | ||
utils::remove_directories(cert_dirs); | ||
MP_UTILS.make_dir(common_client_cert_dir_path); | ||
return std::make_unique<SSLCertProvider>(common_client_cert_dir_path); | ||
} | ||
|
||
void mp::client::set_logger() | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
#include "multipass/dart_ffi.h" | ||
#include "multipass/cli/client_common.h" | ||
#include "multipass/format.h" | ||
#include "multipass/logging/log.h" | ||
#include "multipass/name_generator.h" | ||
#include "multipass/version.h" | ||
|
||
namespace mp = multipass; | ||
namespace mpc = multipass::client; | ||
namespace mpl = multipass::logging; | ||
|
||
constexpr auto category = "dart-ffi"; | ||
|
||
extern "C" const char* multipass_version() | ||
{ | ||
return mp::version_string; | ||
} | ||
|
||
extern "C" const char* generate_petname() | ||
try | ||
{ | ||
static mp::NameGenerator::UPtr generator = mp::make_default_name_generator(); | ||
const auto name = generator->make_name(); | ||
return strdup(name.c_str()); | ||
} | ||
catch (const std::exception& e) | ||
{ | ||
mpl::log(mpl::Level::warning, category, fmt::format("failed generating petname: {}", e.what())); | ||
return nullptr; | ||
} | ||
catch (...) | ||
{ | ||
mpl::log(mpl::Level::warning, category, "failed generating petname"); | ||
return nullptr; | ||
} | ||
|
||
extern "C" const char* get_server_address() | ||
try | ||
{ | ||
const auto address = mpc::get_server_address(); | ||
return strdup(address.c_str()); | ||
} | ||
catch (const std::exception& e) | ||
{ | ||
mpl::log(mpl::Level::warning, category, fmt::format("failed retrieving server address: {}", e.what())); | ||
return nullptr; | ||
} | ||
catch (...) | ||
{ | ||
mpl::log(mpl::Level::warning, category, "failed retrieving server address"); | ||
return nullptr; | ||
} | ||
|
||
extern "C" struct KeyCertificatePair get_cert_pair() | ||
try | ||
{ | ||
const auto provider = mpc::get_cert_provider(mpc::get_server_address()); | ||
const auto cert = provider->PEM_certificate(); | ||
const auto key = provider->PEM_signing_key(); | ||
struct KeyCertificatePair pair | ||
{ | ||
}; | ||
pair.pem_cert = strdup(cert.c_str()); | ||
pair.pem_priv_key = strdup(key.c_str()); | ||
return pair; | ||
} | ||
catch (const std::exception& e) | ||
{ | ||
mpl::log(mpl::Level::warning, category, fmt::format("failed retrieving certificate key pair: {}", e.what())); | ||
return KeyCertificatePair{nullptr, nullptr}; | ||
} | ||
catch (...) | ||
{ | ||
mpl::log(mpl::Level::warning, category, "failed retrieving certificate key pair"); | ||
return KeyCertificatePair{nullptr, nullptr}; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we need PIC here? Anyway, I'm OK with it, especially because it's third party. Just asking out of curiosity.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Without it, I was simply getting a compilation error that said it needs PIC to compile some functions in that library :)