mirror of
https://github.com/boostorg/mysql.git
synced 2025-05-12 14:11:41 +00:00
Renamed error_info to server_diagnostics. server_diagnostics::message is now correctly treated as user-supplied input (security fix). Added notes on examples on this. Split server and client errors in different categories. Added throw_on_error. Added connection::rebind_executor. Made error categories public. Exception throwing now uses Boost.Exception. Improved error handling in examples. Updated Asio dependencies for stackful coroutines. Close #64 Close #107
147 lines
5.7 KiB
C++
147 lines
5.7 KiB
C++
//
|
|
// Copyright (c) 2019-2023 Ruben Perez Hidalgo (rubenperez038 at gmail dot com)
|
|
//
|
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
//
|
|
|
|
//[example_ssl
|
|
|
|
#include <boost/mysql.hpp>
|
|
|
|
#include <boost/asio/io_context.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::resultset result;
|
|
conn.query(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::server_error& err)
|
|
{
|
|
// Server errors include additional diagnostics provided by the server.
|
|
// Security note: err.diagnostics().message() may contain user-supplied values (e.g. the
|
|
// field value that caused the error) and is encoded using to the connection's encoding
|
|
// (UTF-8 by default). Treat is as untrusted input.
|
|
std::cerr << "Error: " << err.what() << ", error code: " << err.code() << '\n'
|
|
<< "Server diagnostics: " << err.diagnostics().message() << std::endl;
|
|
return 1;
|
|
}
|
|
catch (const std::exception& err)
|
|
{
|
|
std::cerr << "Error: " << err.what() << std::endl;
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
//]
|