...one of the most highly
regarded and expertly designed C++ library projects in the
world.
— Herb Sutter and Andrei
Alexandrescu, C++
Coding Standards
This example demonstrates how to configure SSL options like server certificate verification and hostname validation.
The example employs synchronous functions with exceptions as error handling. See this section for more info on error handling.
This example assumes you have gone through the setup.
Additionally, you should run your MySQL server with some test certificates
we created for you, just for this example. You can find them in this project's
GitHub repository, under tools/ssl
. If
you're using the docker container, the setup has already been done for you.
#include <boost/mysql/error_with_diagnostics.hpp> #include <boost/mysql/handshake_params.hpp> #include <boost/mysql/results.hpp> #include <boost/mysql/tcp_ssl.hpp> #include <boost/asio/buffer.hpp> #include <boost/asio/io_context.hpp> #include <boost/asio/ip/tcp.hpp> #include <boost/asio/ssl/context.hpp> #include <boost/asio/ssl/host_name_verification.hpp> #include <iostream> #define ASSERT(expr) \ if (!(expr)) \ { \ std::cerr << "Assertion failed: " #expr << std::endl; \ exit(1); \ } // The CA file that signed the server's certificate constexpr const char CA_PEM[] = R"%(-----BEGIN CERTIFICATE----- MIIDZzCCAk+gAwIBAgIUWznm2UoxXw3j7HCcp9PpiayTvFQwDQYJKoZIhvcNAQEL BQAwQjELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxDjAMBgNVBAoM BW15c3FsMQ4wDAYDVQQDDAVteXNxbDAgFw0yMDA0MDQxNDMwMjNaGA8zMDE5MDgw NjE0MzAyM1owQjELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxDjAM BgNVBAoMBW15c3FsMQ4wDAYDVQQDDAVteXNxbDCCASIwDQYJKoZIhvcNAQEBBQAD ggEPADCCAQoCggEBAN0WYdvsDb+a0TxOGPejcwZT0zvTrf921mmDUlrLN1Z0hJ/S ydgQCSD7Q+6za4lTFZCXcvs52xvvS2gfC0yXyYLCT/jA4RQRxuF+/+w1gDWEbGk0 KzEpsBuKrEIvEaVdoS78SxInnW/aegshdrRRocp4JQ6KHsZgkLTxSwPfYSUmMUo0 cRO0Q/ak3VK8NP13A6ZFvZjrBxjS3cSw9HqilgADcyj1D4EokvfI1C9LrgwgLlZC XVkjjBqqoMXGGlnXOEK+pm8bU68HM/QvMBkb1Amo8pioNaaYgqJUCP0Ch0iu1nUU HtsWt6emXv0jANgIW0oga7xcT4MDGN/M+IRWLTECAwEAAaNTMFEwHQYDVR0OBBYE FNxhaGwf5ePPhzK7yOAKD3VF6wm2MB8GA1UdIwQYMBaAFNxhaGwf5ePPhzK7yOAK D3VF6wm2MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAAoeJCAX IDCFoAaZoQ1niI6Ac/cds8G8It0UCcFGSg+HrZ0YujJxWIruRCUG60Q2OAbEvn0+ uRpTm+4tV1Wt92WFeuRyqkomozx0g4CyfsxGX/x8mLhKPFK/7K9iTXM4/t+xQC4f J+iRmPVsMKQ8YsHYiWVhlOMH9XJQiqERCB2kOKJCH6xkaF2k0GbM2sGgbS7Z6lrd fsFTOIVx0VxLVsZnWX3byE9ghnDR5jn18u30Cpb/R/ShxNUGIHqRa4DkM5la6uZX W1fpSW11JBSUv4WnOO0C2rlIu7UJWOROqZZ0OsybPRGGwagcyff2qVRuI2XFvAMk OzBrmpfHEhF6NDU= -----END CERTIFICATE----- )%"; void print_employee(boost::mysql::row_view employee) { std::cout << "Employee '" << employee.at(0) << " " // first_name (string) << employee.at(1) << "' earns " // last_name (string) << employee.at(2) << " dollars yearly\n"; // salary (double) } void main_impl(int argc, char** argv) { if (argc != 4) { std::cerr << "Usage: " << argv[0] << " <username> <password> <server-hostname>\n"; exit(1); } // I/O context boost::asio::io_context ctx; // Resolver for hostname resolution boost::asio::ip::tcp::resolver resolver(ctx.get_executor()); // Connection params boost::mysql::handshake_params params( argv[1], // username argv[2], // password "boost_mysql_examples" // database to use; leave empty or omit the parameter for no // database ); // This context will be used by the underlying SSL stream object. We can // set up here any SSL-related options, like peer verification or CA // certificates. We will do these in the next lines. boost::asio::ssl::context ssl_ctx(boost::asio::ssl::context::tls_client); // Check whether the server's certificate is valid and signed by a trusted CA. // If it's not, our handshake or connect operation will fail. ssl_ctx.set_verify_mode(boost::asio::ssl::verify_peer); // Load a trusted CA, which was used to sign the server's certificate. // This will allow the signature verification to succeed in our example. // You will have to run your MySQL server with the test certificates // located under $BOOST_MYSQL_ROOT/tools/ssl/ ssl_ctx.add_certificate_authority(boost::asio::buffer(CA_PEM)); // We expect the server certificate's common name to be "mysql". // If it's not, the certificate will be rejected and handshake or connect will fail. ssl_ctx.set_verify_callback(boost::asio::ssl::host_name_verification("mysql")); // Pass in our SSL context to the connection. Note that we // can create many connections out of a single context. We need to keep the // context alive until we finish using the connection. boost::mysql::tcp_ssl_connection conn(ctx, ssl_ctx); // Hostname resolution auto endpoints = resolver.resolve(argv[3], boost::mysql::default_port_string); // Connect to the server. This operation will perform the SSL handshake as part of // it, and thus will fail if the certificate is found to be invalid. conn.connect(*endpoints.begin(), params); // We can now use the connection as we would normally do. const char* sql = "SELECT first_name, last_name, salary FROM employee"; boost::mysql::results result; conn.execute(sql, result); for (auto employee : result.rows()) { print_employee(employee); } // Cleanup conn.close(); } int main(int argc, char** argv) { try { main_impl(argc, argv); } catch (const boost::mysql::error_with_diagnostics& err) { // Some errors include additional diagnostics, like server-provided error messages. // Security note: diagnostics::server_message may contain user-supplied values (e.g. the // field value that caused the error) and is encoded using to the connection's character set // (UTF-8 by default). Treat is as untrusted input. std::cerr << "Error: " << err.what() << ", error code: " << err.code() << '\n' << "Server diagnostics: " << err.get_diagnostics().server_message() << std::endl; return 1; } catch (const std::exception& err) { std::cerr << "Error: " << err.what() << std::endl; return 1; } }